Message ID | 1444881890-4012-2-git-send-email-yangds.fnst@cn.fujitsu.com |
---|---|
State | Superseded |
Headers | show |
Really sorry for the large path, will resend it with `git format-patch -m ` On 10/15/2015 12:04 PM, Dongsheng Yang wrote: > * There is no code modification in this commit, only moving > * the files to proper place. > > The user tools looks a little messy as we place almost > the all tools in the root directory of mtd-utils. To make > it more clear, I propose to introduce the following structure > for our source code. > > mtd-utils/ > |-- lib > |-- include > |-- misc-utils > |-- flash-utils > |-- jffsX-utils > |-- nand-utils > |-- nor-utils > |-- ubi-utils > |-- ubifs-utils > `-- tests > > Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> > --- > MAKEDEV | 41 - > Makefile | 66 +- > compr.c | 538 ----- > compr.h | 119 - > compr_lzo.c | 135 -- > compr_rtime.c | 119 - > compr_zlib.c | 148 -- > device_table.txt | 128 -- > doc_loadbios.c | 150 -- > docfdisk.c | 318 --- > fectest.c | 91 - > flash-utils/flash_erase.c | 295 +++ > flash-utils/flash_eraseall | 4 + > flash-utils/flash_lock.c | 8 + > flash-utils/flash_otp_dump.c | 56 + > flash-utils/flash_otp_info.c | 65 + > flash-utils/flash_otp_lock.c | 72 + > flash-utils/flash_otp_write.c | 122 + > flash-utils/flash_unlock.c | 90 + > flash-utils/flashcp.c | 389 ++++ > flash_erase.c | 295 --- > flash_eraseall | 4 - > flash_lock.c | 8 - > flash_otp_dump.c | 56 - > flash_otp_info.c | 65 - > flash_otp_lock.c | 72 - > flash_otp_write.c | 122 - > flash_unlock.c | 90 - > flashcp.c | 389 ---- > ftl_check.c | 217 -- > ftl_format.c | 324 --- > jffs-dump.c | 359 --- > jffs2dump.c | 805 ------- > jffs2reader.c | 918 -------- > jffsX-utils/compr.c | 538 +++++ > jffsX-utils/compr.h | 119 + > jffsX-utils/compr_lzo.c | 135 ++ > jffsX-utils/compr_rtime.c | 119 + > jffsX-utils/compr_zlib.c | 148 ++ > jffsX-utils/device_table.txt | 128 ++ > jffsX-utils/jffs-dump.c | 359 +++ > jffsX-utils/jffs2dump.c | 805 +++++++ > jffsX-utils/jffs2reader.c | 918 ++++++++ > jffsX-utils/mkfs.jffs2.1 | 268 +++ > jffsX-utils/mkfs.jffs2.c | 1805 +++++++++++++++ > jffsX-utils/rbtree.c | 390 ++++ > jffsX-utils/rbtree.h | 171 ++ > jffsX-utils/summary.h | 177 ++ > jffsX-utils/sumtool.c | 872 ++++++++ > load_nandsim.sh | 127 -- > mcast_image.h | 54 - > misc-utils/MAKEDEV | 41 + > misc-utils/doc_loadbios.c | 150 ++ > misc-utils/docfdisk.c | 318 +++ > misc-utils/fectest.c | 91 + > misc-utils/ftl_check.c | 217 ++ > misc-utils/ftl_format.c | 324 +++ > misc-utils/mcast_image.h | 54 + > misc-utils/mtd_debug.c | 397 ++++ > misc-utils/mtdpart.c | 194 ++ > misc-utils/recv_image.c | 484 ++++ > misc-utils/serve_image.c | 300 +++ > mkfs.jffs2.1 | 268 --- > mkfs.jffs2.c | 1805 --------------- > mkfs.ubifs/.gitignore | 1 - > mkfs.ubifs/COPYING | 340 --- > mkfs.ubifs/README | 9 - > mkfs.ubifs/compr.c | 219 -- > mkfs.ubifs/compr.h | 46 - > mkfs.ubifs/crc16.c | 56 - > mkfs.ubifs/crc16.h | 27 - > mkfs.ubifs/defs.h | 92 - > mkfs.ubifs/devtable.c | 524 ----- > mkfs.ubifs/hashtable/hashtable.c | 277 --- > mkfs.ubifs/hashtable/hashtable.h | 199 -- > mkfs.ubifs/hashtable/hashtable_itr.c | 176 -- > mkfs.ubifs/hashtable/hashtable_itr.h | 112 - > mkfs.ubifs/hashtable/hashtable_private.h | 85 - > mkfs.ubifs/key.h | 189 -- > mkfs.ubifs/lpt.c | 578 ----- > mkfs.ubifs/lpt.h | 28 - > mkfs.ubifs/mkfs.ubifs.c | 2324 -------------------- > mkfs.ubifs/mkfs.ubifs.h | 150 -- > mkfs.ubifs/ubifs.h | 441 ---- > mtd_debug.c | 397 ---- > mtdpart.c | 194 -- > nand-utils/load_nandsim.sh | 127 ++ > nand-utils/nanddump.c | 490 +++++ > nand-utils/nandtest.c | 313 +++ > nand-utils/nandwrite.c | 578 +++++ > nand-utils/nftl_format.c | 422 ++++ > nand-utils/nftldump.c | 278 +++ > nanddump.c | 490 ----- > nandtest.c | 313 --- > nandwrite.c | 578 ----- > nftl_format.c | 422 ---- > nftldump.c | 278 --- > nor-utils/rfddump.c | 337 +++ > nor-utils/rfdformat.c | 160 ++ > rbtree.c | 390 ---- > rbtree.h | 171 -- > recv_image.c | 484 ---- > rfddump.c | 337 --- > rfdformat.c | 160 -- > serve_image.c | 300 --- > summary.h | 177 -- > sumtool.c | 872 -------- > ubifs-utils/mkfs.ubifs/.gitignore | 1 + > ubifs-utils/mkfs.ubifs/COPYING | 340 +++ > ubifs-utils/mkfs.ubifs/README | 9 + > ubifs-utils/mkfs.ubifs/compr.c | 219 ++ > ubifs-utils/mkfs.ubifs/compr.h | 46 + > ubifs-utils/mkfs.ubifs/crc16.c | 56 + > ubifs-utils/mkfs.ubifs/crc16.h | 27 + > ubifs-utils/mkfs.ubifs/defs.h | 92 + > ubifs-utils/mkfs.ubifs/devtable.c | 524 +++++ > ubifs-utils/mkfs.ubifs/hashtable/hashtable.c | 277 +++ > ubifs-utils/mkfs.ubifs/hashtable/hashtable.h | 199 ++ > ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c | 176 ++ > ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h | 112 + > .../mkfs.ubifs/hashtable/hashtable_private.h | 85 + > ubifs-utils/mkfs.ubifs/key.h | 189 ++ > ubifs-utils/mkfs.ubifs/lpt.c | 578 +++++ > ubifs-utils/mkfs.ubifs/lpt.h | 28 + > ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 2324 ++++++++++++++++++++ > ubifs-utils/mkfs.ubifs/mkfs.ubifs.h | 150 ++ > ubifs-utils/mkfs.ubifs/ubifs.h | 441 ++++ > 127 files changed, 19240 insertions(+), 19228 deletions(-) > delete mode 100755 MAKEDEV > delete mode 100644 compr.c > delete mode 100644 compr.h > delete mode 100644 compr_lzo.c > delete mode 100644 compr_rtime.c > delete mode 100644 compr_zlib.c > delete mode 100644 device_table.txt > delete mode 100644 doc_loadbios.c > delete mode 100644 docfdisk.c > delete mode 100644 fectest.c > create mode 100644 flash-utils/flash_erase.c > create mode 100755 flash-utils/flash_eraseall > create mode 100644 flash-utils/flash_lock.c > create mode 100644 flash-utils/flash_otp_dump.c > create mode 100644 flash-utils/flash_otp_info.c > create mode 100644 flash-utils/flash_otp_lock.c > create mode 100644 flash-utils/flash_otp_write.c > create mode 100644 flash-utils/flash_unlock.c > create mode 100644 flash-utils/flashcp.c > delete mode 100644 flash_erase.c > delete mode 100755 flash_eraseall > delete mode 100644 flash_lock.c > delete mode 100644 flash_otp_dump.c > delete mode 100644 flash_otp_info.c > delete mode 100644 flash_otp_lock.c > delete mode 100644 flash_otp_write.c > delete mode 100644 flash_unlock.c > delete mode 100644 flashcp.c > delete mode 100644 ftl_check.c > delete mode 100644 ftl_format.c > delete mode 100644 jffs-dump.c > delete mode 100644 jffs2dump.c > delete mode 100644 jffs2reader.c > create mode 100644 jffsX-utils/compr.c > create mode 100644 jffsX-utils/compr.h > create mode 100644 jffsX-utils/compr_lzo.c > create mode 100644 jffsX-utils/compr_rtime.c > create mode 100644 jffsX-utils/compr_zlib.c > create mode 100644 jffsX-utils/device_table.txt > create mode 100644 jffsX-utils/jffs-dump.c > create mode 100644 jffsX-utils/jffs2dump.c > create mode 100644 jffsX-utils/jffs2reader.c > create mode 100644 jffsX-utils/mkfs.jffs2.1 > create mode 100644 jffsX-utils/mkfs.jffs2.c > create mode 100644 jffsX-utils/rbtree.c > create mode 100644 jffsX-utils/rbtree.h > create mode 100644 jffsX-utils/summary.h > create mode 100644 jffsX-utils/sumtool.c > delete mode 100755 load_nandsim.sh > delete mode 100644 mcast_image.h > create mode 100755 misc-utils/MAKEDEV > create mode 100644 misc-utils/doc_loadbios.c > create mode 100644 misc-utils/docfdisk.c > create mode 100644 misc-utils/fectest.c > create mode 100644 misc-utils/ftl_check.c > create mode 100644 misc-utils/ftl_format.c > create mode 100644 misc-utils/mcast_image.h > create mode 100644 misc-utils/mtd_debug.c > create mode 100644 misc-utils/mtdpart.c > create mode 100644 misc-utils/recv_image.c > create mode 100644 misc-utils/serve_image.c > delete mode 100644 mkfs.jffs2.1 > delete mode 100644 mkfs.jffs2.c > delete mode 100644 mkfs.ubifs/.gitignore > delete mode 100644 mkfs.ubifs/COPYING > delete mode 100644 mkfs.ubifs/README > delete mode 100644 mkfs.ubifs/compr.c > delete mode 100644 mkfs.ubifs/compr.h > delete mode 100644 mkfs.ubifs/crc16.c > delete mode 100644 mkfs.ubifs/crc16.h > delete mode 100644 mkfs.ubifs/defs.h > delete mode 100644 mkfs.ubifs/devtable.c > delete mode 100644 mkfs.ubifs/hashtable/hashtable.c > delete mode 100644 mkfs.ubifs/hashtable/hashtable.h > delete mode 100644 mkfs.ubifs/hashtable/hashtable_itr.c > delete mode 100644 mkfs.ubifs/hashtable/hashtable_itr.h > delete mode 100644 mkfs.ubifs/hashtable/hashtable_private.h > delete mode 100644 mkfs.ubifs/key.h > delete mode 100644 mkfs.ubifs/lpt.c > delete mode 100644 mkfs.ubifs/lpt.h > delete mode 100644 mkfs.ubifs/mkfs.ubifs.c > delete mode 100644 mkfs.ubifs/mkfs.ubifs.h > delete mode 100644 mkfs.ubifs/ubifs.h > delete mode 100644 mtd_debug.c > delete mode 100644 mtdpart.c > create mode 100755 nand-utils/load_nandsim.sh > create mode 100644 nand-utils/nanddump.c > create mode 100644 nand-utils/nandtest.c > create mode 100644 nand-utils/nandwrite.c > create mode 100644 nand-utils/nftl_format.c > create mode 100644 nand-utils/nftldump.c > delete mode 100644 nanddump.c > delete mode 100644 nandtest.c > delete mode 100644 nandwrite.c > delete mode 100644 nftl_format.c > delete mode 100644 nftldump.c > create mode 100644 nor-utils/rfddump.c > create mode 100644 nor-utils/rfdformat.c > delete mode 100644 rbtree.c > delete mode 100644 rbtree.h > delete mode 100644 recv_image.c > delete mode 100644 rfddump.c > delete mode 100644 rfdformat.c > delete mode 100644 serve_image.c > delete mode 100644 summary.h > delete mode 100644 sumtool.c > create mode 100644 ubifs-utils/mkfs.ubifs/.gitignore > create mode 100644 ubifs-utils/mkfs.ubifs/COPYING > create mode 100644 ubifs-utils/mkfs.ubifs/README > create mode 100644 ubifs-utils/mkfs.ubifs/compr.c > create mode 100644 ubifs-utils/mkfs.ubifs/compr.h > create mode 100644 ubifs-utils/mkfs.ubifs/crc16.c > create mode 100644 ubifs-utils/mkfs.ubifs/crc16.h > create mode 100644 ubifs-utils/mkfs.ubifs/defs.h > create mode 100644 ubifs-utils/mkfs.ubifs/devtable.c > create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable.c > create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable.h > create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c > create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h > create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h > create mode 100644 ubifs-utils/mkfs.ubifs/key.h > create mode 100644 ubifs-utils/mkfs.ubifs/lpt.c > create mode 100644 ubifs-utils/mkfs.ubifs/lpt.h > create mode 100644 ubifs-utils/mkfs.ubifs/mkfs.ubifs.c > create mode 100644 ubifs-utils/mkfs.ubifs/mkfs.ubifs.h > create mode 100644 ubifs-utils/mkfs.ubifs/ubifs.h > > diff --git a/MAKEDEV b/MAKEDEV > deleted file mode 100755 > index b59e90e..0000000 > --- a/MAKEDEV > +++ /dev/null > @@ -1,41 +0,0 @@ > -#!/bin/bash > - > -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 > index f4ce313..bb40929 100644 > --- a/Makefile > +++ b/Makefile > @@ -16,24 +16,32 @@ endif > > TESTS = tests > > -MTD_BINS = \ > - ftl_format flash_erase nanddump doc_loadbios \ > - ftl_check mkfs.jffs2 flash_lock flash_unlock \ > - flash_otp_info flash_otp_dump flash_otp_lock flash_otp_write \ > - mtd_debug flashcp nandwrite nandtest mtdpart \ > - jffs2dump \ > - nftldump nftl_format docfdisk \ > - rfddump rfdformat \ > - serve_image recv_image \ > - sumtool jffs2reader > +MISC_BINS = \ > + ftl_format doc_loadbios ftl_check mtd_debug docfdisk \ > + serve_image recv_image mtdpart > UBI_BINS = \ > ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ > ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock > - > -BINS = $(MTD_BINS) > -BINS += mkfs.ubifs/mkfs.ubifs > +UBIFS_BINS = \ > + mkfs.ubifs/mkfs.ubifs > +JFFSX_BINS = \ > + mkfs.jffs2 sumtool jffs2reader jffs2dump > +FLASH_BINS = \ > + flash_erase flash_lock flash_unlock flash_otp_info flash_otp_dump \ > + flash_otp_lock flash_otp_write flashcp > +NAND_BINS = \ > + nanddump nandwrite nandtest nftldump nftl_format > +NOR_BINS = \ > + rfddump rfdformat > + > +BINS = $(addprefix misc-utils/,$(MISC_BINS)) > BINS += $(addprefix ubi-utils/,$(UBI_BINS)) > -SCRIPTS = flash_eraseall > +BINS += $(addprefix ubifs-utils/,$(UBIFS_BINS)) > +BINS += $(addprefix jffsX-utils/,$(JFFSX_BINS)) > +BINS += $(addprefix flash-utils/,$(FLASH_BINS)) > +BINS += $(addprefix nand-utils/,$(NAND_BINS)) > +BINS += $(addprefix nor-utils/,$(NOR_BINS)) > +SCRIPTS = $(addprefix flash-utils/,flash_eraseall) > > TARGETS = $(BINS) > TARGETS += lib/libmtd.a > @@ -61,11 +69,11 @@ endif > rm -f $(BUILDDIR)/include/version.h > $(MAKE) -C $(TESTS) clean > > -install:: $(addprefix $(BUILDDIR)/,${BINS}) ${SCRIPTS} > +install:: $(addprefix $(BUILDDIR)/,${BINS} ${SCRIPTS}) > mkdir -p ${DESTDIR}/${SBINDIR} > install -m 0755 $^ ${DESTDIR}/${SBINDIR}/ > mkdir -p ${DESTDIR}/${MANDIR}/man1 > - install -m 0644 mkfs.jffs2.1 ${DESTDIR}/${MANDIR}/man1/ > + install -m 0644 jffsX-utils/mkfs.jffs2.1 ${DESTDIR}/${MANDIR}/man1/ > -gzip -9f ${DESTDIR}/${MANDIR}/man1/*.1 > > tests:: > @@ -85,13 +93,17 @@ $(BUILDDIR)/include/version.h.tmp: > # Utils in top level > # > obj-mkfs.jffs2 = compr_rtime.o compr_zlib.o compr_lzo.o compr.o rbtree.o > -LDFLAGS_mkfs.jffs2 = $(ZLIBLDFLAGS) $(LZOLDFLAGS) > +LDFLAGS_mkfs.jffs2 = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(CPPFLAGS) > LDLIBS_mkfs.jffs2 = -lz $(LZOLDLIBS) > > LDFLAGS_jffs2reader = $(ZLIBLDFLAGS) $(LZOLDFLAGS) > LDLIBS_jffs2reader = -lz $(LZOLDLIBS) > > -$(foreach v,$(MTD_BINS),$(eval $(call mkdep,,$(v)))) > +$(foreach v,$(MISC_BINS),$(eval $(call mkdep,misc-utils/,$(v)))) > +$(foreach v,$(JFFSX_BINS),$(eval $(call mkdep,jffsX-utils/,$(v)))) > +$(foreach v,$(FLASH_BINS),$(eval $(call mkdep,flash-utils/,$(v)))) > +$(foreach v,$(NAND_BINS),$(eval $(call mkdep,nand-utils/,$(v)))) > +$(foreach v,$(NOR_BINS),$(eval $(call mkdep,nor-utils/,$(v)))) > > # > # Common libmtd > @@ -100,15 +112,6 @@ obj-libmtd.a = libmtd.o libmtd_legacy.o libcrc32.o libfec.o > $(call _mkdep,lib/,libmtd.a) > > # > -# Utils in mkfs.ubifs subdir > -# > -obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \ > - hashtable/hashtable.o hashtable/hashtable_itr.o > -LDFLAGS_mkfs.ubifs = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS) > -LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid > -$(call mkdep,mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a) > - > -# > # Utils in ubi-utils/ subdir > # > obj-libiniparser.a = libiniparser.o dictionary.o > @@ -122,3 +125,12 @@ obj-ubiformat = libubigen.a libscan.a > > $(foreach v,libubi.a libubigen.a libiniparser.a libscan.a,$(eval $(call _mkdep,ubi-utils/,$(v)))) > $(foreach v,$(UBI_BINS),$(eval $(call mkdep,ubi-utils/,$(v),libubi.a ubiutils-common.o))) > + > +# > +# Utils in ubifs-utils subdir > +# > +obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \ > + hashtable/hashtable.o hashtable/hashtable_itr.o > +LDFLAGS_mkfs.ubifs = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS) > +LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid > +$(call mkdep,ubifs-utils/mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a) > diff --git a/compr.c b/compr.c > deleted file mode 100644 > index cb4432e..0000000 > --- a/compr.c > +++ /dev/null > @@ -1,538 +0,0 @@ > -/* > - * 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. > - */ > - > -#include "compr.h" > -#include <string.h> > -#include <stdlib.h> > -#include <linux/jffs2.h> > - > -#define FAVOUR_LZO_PERCENT 80 > - > -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)) { > - 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; > - } > - } > - } > -} > - > -/* > - * Return 1 to use this compression > - */ > -static int jffs2_is_best_compression(struct jffs2_compressor *this, > - struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) > -{ > - switch (jffs2_compression_mode) { > - case JFFS2_COMPR_MODE_SIZE: > - if (bestsize > size) > - return 1; > - return 0; > - case JFFS2_COMPR_MODE_FAVOURLZO: > - if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) > - return 1; > - if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) > - return 1; > - if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) > - return 1; > - if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) > - return 1; > - > - return 0; > - } > - /* Shouldn't happen */ > - return 0; > -} > - > -/* 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); > - 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_FAVOURLZO: > - case JFFS2_COMPR_MODE_SIZE: > - orig_slen = *datalen; > - orig_dlen = *cdatalen; > - list_for_each_entry(this, &jffs2_compressor_list, list) { > - uint32_t needed_buf_size; > - > - if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) > - needed_buf_size = orig_slen + jffs2_compression_check; > - else > - needed_buf_size = orig_dlen + jffs2_compression_check; > - > - /* 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 < needed_buf_size) && (this->compr_buf)) { > - free(this->compr_buf); > - this->compr_buf_size=0; > - this->compr_buf=NULL; > - } > - if (!this->compr_buf) { > - tmp_buf = malloc(needed_buf_size); > - 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); > - 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) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) > - && (*cdatalen < *datalen)) { > - 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: unknown 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; > - case JFFS2_COMPR_MODE_FAVOURLZO: > - act_buf += sprintf(act_buf, "favourlzo"); > - break; > - default: > - act_buf += sprintf(act_buf, "unknown"); > - 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; > - } > - if (!strcmp("favourlzo", name)) { > - jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; > - 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 > -#ifdef CONFIG_JFFS2_LZO > - jffs2_lzo_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 > -#ifdef CONFIG_JFFS2_LZO > - jffs2_lzo_exit(); > -#endif > - return 0; > -} > diff --git a/compr.h b/compr.h > deleted file mode 100644 > index a21e935..0000000 > --- a/compr.h > +++ /dev/null > @@ -1,119 +0,0 @@ > -/* > - * 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. > - */ > - > -#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 CONFIG_JFFS2_LZO > - > -#define JFFS2_RUBINMIPS_PRIORITY 10 > -#define JFFS2_DYNRUBIN_PRIORITY 20 > -#define JFFS2_RTIME_PRIORITY 50 > -#define JFFS2_ZLIB_PRIORITY 60 > -#define JFFS2_LZO_PRIORITY 80 > - > -#define JFFS2_COMPR_MODE_NONE 0 > -#define JFFS2_COMPR_MODE_PRIORITY 1 > -#define JFFS2_COMPR_MODE_SIZE 2 > -#define JFFS2_COMPR_MODE_FAVOURLZO 3 > - > -#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 */ > - const char *name; > - char compr; /* JFFS2_COMPR_XXX */ > - int (*compress)(unsigned char *data_in, unsigned char *cpage_out, > - uint32_t *srclen, uint32_t *destlen); > - int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, > - uint32_t cdatalen, uint32_t datalen); > - 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 > -#ifdef CONFIG_JFFS2_LZO > -int jffs2_lzo_init(void); > -void jffs2_lzo_exit(void); > -#endif > - > -#endif /* __JFFS2_COMPR_H__ */ > diff --git a/compr_lzo.c b/compr_lzo.c > deleted file mode 100644 > index d2e2afc..0000000 > --- a/compr_lzo.c > +++ /dev/null > @@ -1,135 +0,0 @@ > -/* > - * JFFS2 LZO Compression Interface. > - * > - * Copyright (C) 2007 Nokia Corporation. All rights reserved. > - * > - * Author: Richard Purdie <rpurdie@openedhand.com> > - * > - * 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. > - * > - * 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., 51 Franklin St, Fifth Floor, Boston, MA > - * 02110-1301 USA > - * > - */ > - > -#include <stdint.h> > -#include <stdio.h> > -#include <string.h> > - > -#ifndef WITHOUT_LZO > -#include <asm/types.h> > -#include <linux/jffs2.h> > -#include <lzo/lzo1x.h> > -#include "compr.h" > - > -extern int page_size; > - > -static void *lzo_mem; > -static void *lzo_compress_buf; > - > -/* > - * Note about LZO compression. > - * > - * We want to use the _999_ compression routine which gives better compression > - * rates at the expense of time. Decompression time is unaffected. We might as > - * well use the standard lzo library routines for this but they will overflow > - * the destination buffer since they don't check the destination size. > - * > - * We therefore compress to a temporary buffer and copy if it will fit. > - * > - */ > -static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, > - uint32_t *sourcelen, uint32_t *dstlen) > -{ > - lzo_uint compress_size; > - int ret; > - > - ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); > - > - if (ret != LZO_E_OK) > - return -1; > - > - if (compress_size > *dstlen) > - return -1; > - > - memcpy(cpage_out, lzo_compress_buf, compress_size); > - *dstlen = compress_size; > - > - return 0; > -} > - > -static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, > - uint32_t srclen, uint32_t destlen) > -{ > - int ret; > - lzo_uint dl; > - > - ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); > - > - if (ret != LZO_E_OK || dl != destlen) > - return -1; > - > - return 0; > -} > - > -static struct jffs2_compressor jffs2_lzo_comp = { > - .priority = JFFS2_LZO_PRIORITY, > - .name = "lzo", > - .compr = JFFS2_COMPR_LZO, > - .compress = &jffs2_lzo_cmpr, > - .decompress = &jffs2_lzo_decompress, > - .disabled = 1, > -}; > - > -int jffs2_lzo_init(void) > -{ > - int ret; > - > - lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); > - if (!lzo_mem) > - return -1; > - > - /* Worse case LZO compression size from their FAQ */ > - lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); > - if (!lzo_compress_buf) { > - free(lzo_mem); > - return -1; > - } > - > - ret = jffs2_register_compressor(&jffs2_lzo_comp); > - if (ret < 0) { > - free(lzo_compress_buf); > - free(lzo_mem); > - } > - > - return ret; > -} > - > -void jffs2_lzo_exit(void) > -{ > - jffs2_unregister_compressor(&jffs2_lzo_comp); > - free(lzo_compress_buf); > - free(lzo_mem); > -} > - > -#else > - > -int jffs2_lzo_init(void) > -{ > - return 0; > -} > - > -void jffs2_lzo_exit(void) > -{ > -} > - > -#endif > diff --git a/compr_rtime.c b/compr_rtime.c > deleted file mode 100644 > index f24379d..0000000 > --- a/compr_rtime.c > +++ /dev/null > @@ -1,119 +0,0 @@ > -/* > - * 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. > - * > - * 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) > -{ > - short positions[256]; > - int outpos = 0; > - int pos=0; > - > - memset(positions,0,sizeof(positions)); > - > - while (pos < (*sourcelen) && outpos+2 <= (*dstlen)) { > - 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, > - __attribute__((unused)) uint32_t srclen, uint32_t destlen) > -{ > - 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 > deleted file mode 100644 > index 1f94628..0000000 > --- a/compr_zlib.c > +++ /dev/null > @@ -1,148 +0,0 @@ > -/* > - * 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. > - */ > - > -#define PROGRAM_NAME "compr_zlib" > - > -#include <stdint.h> > -#define crc32 __zlib_crc32 > -#include <zlib.h> > -#undef crc32 > -#include <stdio.h> > -#include <asm/types.h> > -#include <linux/jffs2.h> > -#include "common.h" > -#include "compr.h" > - > -/* 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 > - > -static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, > - uint32_t *sourcelen, uint32_t *dstlen) > -{ > - 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; > -} > - > -static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, > - uint32_t srclen, uint32_t destlen) > -{ > - z_stream strm; > - int ret; > - > - strm.zalloc = (void *)0; > - strm.zfree = (void *)0; > - > - if (Z_OK != inflateInit(&strm)) { > - return 1; > - } > - strm.next_in = data_in; > - strm.avail_in = srclen; > - strm.total_in = 0; > - > - strm.next_out = cpage_out; > - strm.avail_out = destlen; > - strm.total_out = 0; > - > - while((ret = inflate(&strm, Z_FINISH)) == Z_OK) > - ; > - > - inflateEnd(&strm); > - return 0; > -} > - > -static struct jffs2_compressor jffs2_zlib_comp = { > - .priority = JFFS2_ZLIB_PRIORITY, > - .name = "zlib", > - .disabled = 0, > - .compr = JFFS2_COMPR_ZLIB, > - .compress = &jffs2_zlib_compress, > - .decompress = &jffs2_zlib_decompress, > -}; > - > -int jffs2_zlib_init(void) > -{ > - return jffs2_register_compressor(&jffs2_zlib_comp); > -} > - > -void jffs2_zlib_exit(void) > -{ > - jffs2_unregister_compressor(&jffs2_zlib_comp); > -} > diff --git a/device_table.txt b/device_table.txt > deleted file mode 100644 > index 394a62b..0000000 > --- a/device_table.txt > +++ /dev/null > @@ -1,128 +0,0 @@ > -# 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 > deleted file mode 100644 > index b999c73..0000000 > --- a/doc_loadbios.c > +++ /dev/null > @@ -1,150 +0,0 @@ > -#define PROGRAM_NAME "doc_loadbios" > - > -#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> > - > -#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 > deleted file mode 100644 > index 9956de5..0000000 > --- a/docfdisk.c > +++ /dev/null > @@ -1,318 +0,0 @@ > -/* > - * docfdisk.c: Modify INFTL partition tables > - * > - * 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 PROGRAM_NAME "docfdisk" > - > -#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> > -#include <mtd_swab.h> > - > -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", > - PROGRAM_NAME); > - 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.writesize) * 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.writesize) { > - if (ioctl(fd, MEMREADOOB, &oob)) { > - perror("ioctl(MEMREADOOB)"); > - return 1; > - } > - oob.start += meminfo.writesize; > - 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.writesize) { > - 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.writesize, oob.start)) < 0) { > - perror("Write page"); > - printf("Your MediaHeader may be hosed. UHOH!\n"); > - return 1; > - } > - if (ret != meminfo.writesize) { > - printf("Short write!\n"); > - printf("Your MediaHeader may be hosed. UHOH!\n"); > - return 1; > - } > - > - oob.start += meminfo.writesize; > - oob.ptr += meminfo.oobsize; > - buf += meminfo.writesize; > - } > - > - printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); > - return 0; > -} > diff --git a/fectest.c b/fectest.c > deleted file mode 100644 > index fd577f3..0000000 > --- a/fectest.c > +++ /dev/null > @@ -1,91 +0,0 @@ > -#include <stdlib.h> > -#include <string.h> > -#include <unistd.h> > -#include <fcntl.h> > -#include <stdio.h> > -#include <sys/time.h> > -#include <sys/types.h> > -#include <sys/stat.h> > - > -#include "mcast_image.h" > -#include <crc32.h> > - > -#define ERASE_SIZE 131072 > -#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE) > -#define DROPS 8 > - > -int main(void) > -{ > - int i, j; > - unsigned char buf[NR_PKTS * PKT_SIZE]; > - unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE]; > - struct fec_parms *fec; > - unsigned char *srcs[NR_PKTS]; > - unsigned char *pkt[NR_PKTS + DROPS]; > - int pktnr[NR_PKTS + DROPS]; > - struct timeval then, now; > - > - srand(3453); > - for (i=0; i < sizeof(buf); i++) > - if (i < ERASE_SIZE) > - buf[i] = rand(); > - else > - buf[i] = 0; > - > - for (i=0; i < NR_PKTS + DROPS; i++) > - srcs[i] = buf + (i * PKT_SIZE); > - > - for (i=0; i < NR_PKTS + DROPS; i++) { > - pkt[i] = malloc(PKT_SIZE); > - pktnr[i] = -1; > - } > - fec = fec_new(NR_PKTS, NR_PKTS + DROPS); > - if (!fec) { > - printf("fec_init() failed\n"); > - exit(1); > - } > - j = 0; > - for (i=0; i < NR_PKTS + DROPS; i++) { > -#if 1 > - if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 ) > - continue; > -#endif > - if (i == 69 || i == 93 || i == 103) > - continue; > - fec_encode(fec, srcs, pkt[j], i, PKT_SIZE); > - pktnr[j] = i; > - j++; > - } > - gettimeofday(&then, NULL); > - if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) { > - printf("Decode failed\n"); > - exit(1); > - } > - > - for (i=0; i < NR_PKTS; i++) > - memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE); > - gettimeofday(&now, NULL); > - now.tv_sec -= then.tv_sec; > - now.tv_usec -= then.tv_usec; > - if (now.tv_usec < 0) { > - now.tv_usec += 1000000; > - now.tv_sec--; > - } > - > - if (memcmp(pktbuf, buf, ERASE_SIZE)) { > - int fd; > - printf("Compare failed\n"); > - fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644); > - if (fd >= 0) > - write(fd, buf, ERASE_SIZE); > - close(fd); > - fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644); > - if (fd >= 0) > - write(fd, pktbuf, ERASE_SIZE); > - > - exit(1); > - } > - > - printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec); > - return 0; > -} > diff --git a/flash-utils/flash_erase.c b/flash-utils/flash_erase.c > new file mode 100644 > index 0000000..933373a > --- /dev/null > +++ b/flash-utils/flash_erase.c > @@ -0,0 +1,295 @@ > +/* flash_erase.c -- erase MTD devices > + > + Copyright (C) 2000 Arcom Control System Ltd > + Copyright (C) 2010 Mike Frysinger <vapier@gentoo.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. > + > + 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 PROGRAM_NAME "flash_erase" > + > +#include <inttypes.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <stdlib.h> > +#include <errno.h> > +#include <string.h> > +#include <stdint.h> > +#include <getopt.h> > +#include <sys/ioctl.h> > +#include <sys/types.h> > + > +#include <common.h> > +#include <crc32.h> > +#include <libmtd.h> > + > +#include <mtd/mtd-user.h> > +#include <mtd/jffs2-user.h> > + > +static const char *mtd_device; > + > +static int quiet; /* true -- don't output progress */ > +static int jffs2; /* format for jffs2 usage */ > +static int noskipbad; /* do not skip bad blocks */ > +static int unlock; /* unlock sectors before erasing */ > + > +static struct jffs2_unknown_node cleanmarker; > +int target_endian = __BYTE_ORDER; > + > +static void show_progress(struct mtd_dev_info *mtd, off_t start, int eb, > + int eb_start, int eb_cnt) > +{ > + bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIxoff_t" -- %2i %% complete ", > + mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt); > + fflush(stdout); > +} > + > +static void display_help (void) > +{ > + printf("Usage: %s [options] MTD_DEVICE <start offset> <block count>\n" > + "Erase blocks of the specified MTD device.\n" > + "Specify a count of 0 to erase to end of device.\n" > + "\n" > + " -j, --jffs2 format the device for jffs2\n" > + " -N, --noskipbad don't skip bad blocks\n" > + " -u, --unlock unlock sectors before erasing\n" > + " -q, --quiet do not display progress messages\n" > + " --silent same as --quiet\n" > + " --help display this help and exit\n" > + " --version output version information and exit\n", > + PROGRAM_NAME); > +} > + > +static void display_version (void) > +{ > + printf("%1$s version " VERSION "\n" > + "\n" > + "Copyright (C) 2000 Arcom Control Systems Ltd\n" > + "\n" > + "%1$s comes with NO WARRANTY\n" > + "to the extent permitted by law.\n" > + "\n" > + "You may redistribute copies of %1$s\n" > + "under the terms of the GNU General Public Licence.\n" > + "See the file `COPYING' for more information.\n", > + PROGRAM_NAME); > +} > + > +int main(int argc, char *argv[]) > +{ > + libmtd_t mtd_desc; > + struct mtd_dev_info mtd; > + int fd, clmpos = 0, clmlen = 8; > + unsigned long long start; > + unsigned int eb, eb_start, eb_cnt; > + bool isNAND; > + int error = 0; > + off_t offset = 0; > + > + /* > + * Process user arguments > + */ > + for (;;) { > + int option_index = 0; > + static const char *short_options = "jNqu"; > + static const struct option long_options[] = { > + {"help", no_argument, 0, 0}, > + {"version", no_argument, 0, 0}, > + {"jffs2", no_argument, 0, 'j'}, > + {"noskipbad", no_argument, 0, 'N'}, > + {"quiet", no_argument, 0, 'q'}, > + {"silent", no_argument, 0, 'q'}, > + {"unlock", no_argument, 0, 'u'}, > + > + {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(); > + return 0; > + case 1: > + display_version(); > + return 0; > + } > + break; > + case 'j': > + jffs2 = 1; > + break; > + case 'N': > + noskipbad = 1; > + break; > + case 'q': > + quiet = 1; > + break; > + case 'u': > + unlock = 1; > + break; > + case '?': > + error = 1; > + break; > + } > + } > + switch (argc - optind) { > + case 3: > + mtd_device = argv[optind]; > + start = simple_strtoull(argv[optind + 1], &error); > + eb_cnt = simple_strtoul(argv[optind + 2], &error); > + break; > + default: > + case 0: > + errmsg("no MTD device specified"); > + case 1: > + errmsg("no start erase block specified"); > + case 2: > + errmsg("no erase block count specified"); > + error = 1; > + break; > + } > + if (error) > + return errmsg("Try `--help' for more information"); > + > + /* > + * Locate MTD and prepare for erasure > + */ > + mtd_desc = libmtd_open(); > + if (mtd_desc == NULL) > + return errmsg("can't initialize libmtd"); > + > + if ((fd = open(mtd_device, O_RDWR)) < 0) > + return sys_errmsg("%s", mtd_device); > + > + if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) > + return errmsg("mtd_get_dev_info failed"); > + > + if (jffs2 && mtd.type == MTD_MLCNANDFLASH) > + return errmsg("JFFS2 cannot support MLC NAND."); > + > + eb_start = start / mtd.eb_size; > + > + isNAND = mtd.type == MTD_NANDFLASH || mtd.type == MTD_MLCNANDFLASH; > + > + if (jffs2) { > + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); > + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); > + if (!isNAND) > + cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker)); > + else { > + struct nand_oobinfo oobinfo; > + > + if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) > + return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); > + > + /* Check for autoplacement */ > + if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { > + /* Get the position of the free bytes */ > + if (!oobinfo.oobfree[0][1]) > + return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); > + clmpos = oobinfo.oobfree[0][0]; > + clmlen = oobinfo.oobfree[0][1]; > + if (clmlen > 8) > + clmlen = 8; > + } else { > + /* Legacy mode */ > + switch (mtd.oob_size) { > + case 8: > + clmpos = 6; > + clmlen = 2; > + break; > + case 16: > + clmpos = 8; > + clmlen = 8; > + break; > + case 64: > + clmpos = 16; > + clmlen = 8; > + break; > + } > + } > + cleanmarker.totlen = cpu_to_je32(8); > + } > + cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); > + } > + > + /* > + * Now do the actual erasing of the MTD device > + */ > + if (eb_cnt == 0) > + eb_cnt = (mtd.size / mtd.eb_size) - eb_start; > + > + for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { > + offset = (off_t)eb * mtd.eb_size; > + > + if (!noskipbad) { > + int ret = mtd_is_bad(&mtd, fd, eb); > + if (ret > 0) { > + verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, offset); > + continue; > + } else if (ret < 0) { > + if (errno == EOPNOTSUPP) { > + noskipbad = 1; > + if (isNAND) > + return errmsg("%s: Bad block check not available", mtd_device); > + } else > + return sys_errmsg("%s: MTD get bad block failed", mtd_device); > + } > + } > + > + show_progress(&mtd, offset, eb, eb_start, eb_cnt); > + > + if (unlock) { > + if (mtd_unlock(&mtd, fd, eb) != 0) { > + sys_errmsg("%s: MTD unlock failure", mtd_device); > + continue; > + } > + } > + > + if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { > + sys_errmsg("%s: MTD Erase failure", mtd_device); > + continue; > + } > + > + /* format for JFFS2 ? */ > + if (!jffs2) > + continue; > + > + /* write cleanmarker */ > + if (isNAND) { > + if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) { > + sys_errmsg("%s: MTD writeoob failure", mtd_device); > + continue; > + } > + } else { > + if (pwrite(fd, &cleanmarker, sizeof(cleanmarker), (loff_t)offset) != sizeof(cleanmarker)) { > + sys_errmsg("%s: MTD write failure", mtd_device); > + continue; > + } > + } > + verbose(!quiet, " Cleanmarker written at %"PRIxoff_t, offset); > + } > + show_progress(&mtd, offset, eb, eb_start, eb_cnt); > + bareverbose(!quiet, "\n"); > + > + return 0; > +} > diff --git a/flash-utils/flash_eraseall b/flash-utils/flash_eraseall > new file mode 100755 > index 0000000..c5539b3 > --- /dev/null > +++ b/flash-utils/flash_eraseall > @@ -0,0 +1,4 @@ > +#!/bin/sh > +echo "${0##*/} has been replaced by \`flash_erase <mtddev> 0 0\`; please use it" 1>&2 > +[ $# -ne 0 ] && set -- "$@" 0 0 > +exec flash_erase "$@" > diff --git a/flash-utils/flash_lock.c b/flash-utils/flash_lock.c > new file mode 100644 > index 0000000..33f76c7 > --- /dev/null > +++ b/flash-utils/flash_lock.c > @@ -0,0 +1,8 @@ > +/* > + * flash_{lock,unlock} > + * > + * utilities for locking/unlocking sectors of flash devices > + */ > + > +#define PROGRAM_NAME "flash_lock" > +#include "flash_unlock.c" > diff --git a/flash-utils/flash_otp_dump.c b/flash-utils/flash_otp_dump.c > new file mode 100644 > index 0000000..f0c0fb9 > --- /dev/null > +++ b/flash-utils/flash_otp_dump.c > @@ -0,0 +1,56 @@ > +/* > + * flash_otp_dump.c -- display One-Time-Programm data > + */ > + > +#define PROGRAM_NAME "flash_otp_dump" > + > +#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", PROGRAM_NAME); > + 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-utils/flash_otp_info.c b/flash-utils/flash_otp_info.c > new file mode 100644 > index 0000000..2061797 > --- /dev/null > +++ b/flash-utils/flash_otp_info.c > @@ -0,0 +1,65 @@ > +/* > + * flash_otp_info.c -- print info about One-Time-Programm data > + */ > + > +#define PROGRAM_NAME "flash_otp_info" > + > +#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", PROGRAM_NAME); > + 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-utils/flash_otp_lock.c b/flash-utils/flash_otp_lock.c > new file mode 100644 > index 0000000..3c39a2d > --- /dev/null > +++ b/flash-utils/flash_otp_lock.c > @@ -0,0 +1,72 @@ > +/* > + * flash_otp_lock.c -- lock area of One-Time-Program data > + */ > + > +#define PROGRAM_NAME "flash_otp_lock" > + > +#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> > +#include "common.h" > + > +int main(int argc,char *argv[]) > +{ > + int fd, val, ret, offset, size; > + char *p; > + > + if (argc != 5 || strcmp(argv[1], "-u")) { > + fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", PROGRAM_NAME); > + 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", PROGRAM_NAME); > + return ERANGE; > + } > + > + size = strtoul(argv[4], &p, 0); > + if (argv[4][0] == 0 || *p != 0) { > + fprintf(stderr, "%s: bad size value\n", PROGRAM_NAME); > + return ERANGE; > + } > + > + printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", > + argv[2], offset, offset + size); > + if (prompt("Are you sure?", false)) { > + 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-utils/flash_otp_write.c b/flash-utils/flash_otp_write.c > new file mode 100644 > index 0000000..111318d > --- /dev/null > +++ b/flash-utils/flash_otp_write.c > @@ -0,0 +1,122 @@ > +/* > + * flash_otp_write.c -- write One-Time-Program data > + */ > + > +#define PROGRAM_NAME "flash_otp_write" > + > +#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 <common.h> > +#include <mtd/mtd-user.h> > + > +ssize_t xread(int fd, void *buf, size_t count) > +{ > + ssize_t ret, done = 0; > + > +retry: > + ret = read(fd, buf + done, count - done); > + if (ret < 0) > + return ret; > + > + done += ret; > + > + if (ret == 0 /* EOF */ || done == count) > + return done; > + else > + goto retry; > +} > + > +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", PROGRAM_NAME); > + 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 = (off_t)strtoull(argv[3], &p, 0); > + if (argv[3][0] == 0 || *p != 0) { > + fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME); > + 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%"PRIxoff_t"\n", argv[2], offset); > + > + if (mtd_type_is_nand_user(&mtdInfo)) > + len = mtdInfo.writesize; > + else > + len = 256; > + > + if (len > sizeof(buf)) { > + printf("huh, writesize (%d) bigger than buffer (%zu)\n", > + len, sizeof(buf)); > + return ENOMEM; > + } > + > + wrote = 0; > + while ((size = xread(0, buf, len))) { > + if (size < 0) { > + perror("read()"); > + return errno; > + } > + p = buf; > + while (size > 0) { > + if (mtd_type_is_nand_user(&mtdInfo)) { > + /* Fill remain buffers with 0xff */ > + memset(buf + size, 0xff, mtdInfo.writesize - size); > + size = mtdInfo.writesize; > + } > + 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-utils/flash_unlock.c b/flash-utils/flash_unlock.c > new file mode 100644 > index 0000000..1cc8c2f > --- /dev/null > +++ b/flash-utils/flash_unlock.c > @@ -0,0 +1,90 @@ > +/* > + * flash_{lock,unlock} > + * > + * utilities for locking/unlocking sectors of flash devices > + */ > + > +#ifndef PROGRAM_NAME > +#define PROGRAM_NAME "flash_unlock" > +#define FLASH_MSG "unlock" > +#define FLASH_UNLOCK 1 > +#else > +#define FLASH_MSG "lock" > +#define FLASH_UNLOCK 0 > +#endif > + > +#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 "common.h" > +#include <mtd/mtd-user.h> > + > +static void usage(int status) > +{ > + fprintf(status ? stderr : stdout, > + "Usage: %s <mtd device> [offset] [block count]\n\n" > + "If offset is not specified, it defaults to 0.\n" > + "If block count is not specified, it defaults to all blocks.\n", > + PROGRAM_NAME); > + exit(status); > +} > + > +int main(int argc, char *argv[]) > +{ > + int fd, request; > + struct mtd_info_user mtdInfo; > + struct erase_info_user mtdLockInfo; > + int count; > + const char *dev; > + > + /* Parse command line options */ > + if (argc < 2 || argc > 4) > + usage(1); > + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) > + usage(0); > + > + dev = argv[1]; > + > + /* Get the device info to compare to command line sizes */ > + fd = open(dev, O_RDWR); > + if (fd < 0) > + sys_errmsg_die("could not open: %s", dev); > + > + if (ioctl(fd, MEMGETINFO, &mtdInfo)) > + sys_errmsg_die("could not get mtd info: %s", dev); > + > + /* Make sure user options are valid */ > + if (argc > 2) > + mtdLockInfo.start = strtol(argv[2], NULL, 0); > + else > + mtdLockInfo.start = 0; > + if (mtdLockInfo.start > mtdInfo.size) > + errmsg_die("%#x is beyond device size %#x", > + mtdLockInfo.start, mtdInfo.size); > + > + if (argc > 3) { > + count = strtol(argv[3], NULL, 0); > + if (count == -1) > + mtdLockInfo.length = mtdInfo.size; > + else > + mtdLockInfo.length = mtdInfo.erasesize * count; > + } else > + mtdLockInfo.length = mtdInfo.size; > + if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size) > + errmsg_die("range is more than device supports: %#x + %#x > %#x", > + mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size); > + > + /* Finally do the operation */ > + request = FLASH_UNLOCK ? MEMUNLOCK : MEMLOCK; > + if (ioctl(fd, request, &mtdLockInfo)) > + sys_errmsg_die("could not %s device: %s\n", > + FLASH_MSG, dev); > + > + return 0; > +} > diff --git a/flash-utils/flashcp.c b/flash-utils/flashcp.c > new file mode 100644 > index 0000000..86334ac > --- /dev/null > +++ b/flash-utils/flashcp.c > @@ -0,0 +1,389 @@ > +/* > + * Copyright (c) 2d3D, Inc. > + * Written by Abraham vd Merwe <abraham@2d3d.co.za> > + * All rights reserved. > + * > + * 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. > + */ > + > +#define PROGRAM_NAME "flashcp" > + > +#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(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: %1$s [ -v | --verbose ] <filename> <device>\n" > + " %1$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", > + PROGRAM_NAME); > + > + 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 *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]; > + > + /********************* > + * 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(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 || device == NULL) > + showusage(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) / 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/flash_erase.c b/flash_erase.c > deleted file mode 100644 > index 933373a..0000000 > --- a/flash_erase.c > +++ /dev/null > @@ -1,295 +0,0 @@ > -/* flash_erase.c -- erase MTD devices > - > - Copyright (C) 2000 Arcom Control System Ltd > - Copyright (C) 2010 Mike Frysinger <vapier@gentoo.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. > - > - 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 PROGRAM_NAME "flash_erase" > - > -#include <inttypes.h> > -#include <stdbool.h> > -#include <stdio.h> > -#include <unistd.h> > -#include <fcntl.h> > -#include <stdlib.h> > -#include <errno.h> > -#include <string.h> > -#include <stdint.h> > -#include <getopt.h> > -#include <sys/ioctl.h> > -#include <sys/types.h> > - > -#include <common.h> > -#include <crc32.h> > -#include <libmtd.h> > - > -#include <mtd/mtd-user.h> > -#include <mtd/jffs2-user.h> > - > -static const char *mtd_device; > - > -static int quiet; /* true -- don't output progress */ > -static int jffs2; /* format for jffs2 usage */ > -static int noskipbad; /* do not skip bad blocks */ > -static int unlock; /* unlock sectors before erasing */ > - > -static struct jffs2_unknown_node cleanmarker; > -int target_endian = __BYTE_ORDER; > - > -static void show_progress(struct mtd_dev_info *mtd, off_t start, int eb, > - int eb_start, int eb_cnt) > -{ > - bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIxoff_t" -- %2i %% complete ", > - mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt); > - fflush(stdout); > -} > - > -static void display_help (void) > -{ > - printf("Usage: %s [options] MTD_DEVICE <start offset> <block count>\n" > - "Erase blocks of the specified MTD device.\n" > - "Specify a count of 0 to erase to end of device.\n" > - "\n" > - " -j, --jffs2 format the device for jffs2\n" > - " -N, --noskipbad don't skip bad blocks\n" > - " -u, --unlock unlock sectors before erasing\n" > - " -q, --quiet do not display progress messages\n" > - " --silent same as --quiet\n" > - " --help display this help and exit\n" > - " --version output version information and exit\n", > - PROGRAM_NAME); > -} > - > -static void display_version (void) > -{ > - printf("%1$s version " VERSION "\n" > - "\n" > - "Copyright (C) 2000 Arcom Control Systems Ltd\n" > - "\n" > - "%1$s comes with NO WARRANTY\n" > - "to the extent permitted by law.\n" > - "\n" > - "You may redistribute copies of %1$s\n" > - "under the terms of the GNU General Public Licence.\n" > - "See the file `COPYING' for more information.\n", > - PROGRAM_NAME); > -} > - > -int main(int argc, char *argv[]) > -{ > - libmtd_t mtd_desc; > - struct mtd_dev_info mtd; > - int fd, clmpos = 0, clmlen = 8; > - unsigned long long start; > - unsigned int eb, eb_start, eb_cnt; > - bool isNAND; > - int error = 0; > - off_t offset = 0; > - > - /* > - * Process user arguments > - */ > - for (;;) { > - int option_index = 0; > - static const char *short_options = "jNqu"; > - static const struct option long_options[] = { > - {"help", no_argument, 0, 0}, > - {"version", no_argument, 0, 0}, > - {"jffs2", no_argument, 0, 'j'}, > - {"noskipbad", no_argument, 0, 'N'}, > - {"quiet", no_argument, 0, 'q'}, > - {"silent", no_argument, 0, 'q'}, > - {"unlock", no_argument, 0, 'u'}, > - > - {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(); > - return 0; > - case 1: > - display_version(); > - return 0; > - } > - break; > - case 'j': > - jffs2 = 1; > - break; > - case 'N': > - noskipbad = 1; > - break; > - case 'q': > - quiet = 1; > - break; > - case 'u': > - unlock = 1; > - break; > - case '?': > - error = 1; > - break; > - } > - } > - switch (argc - optind) { > - case 3: > - mtd_device = argv[optind]; > - start = simple_strtoull(argv[optind + 1], &error); > - eb_cnt = simple_strtoul(argv[optind + 2], &error); > - break; > - default: > - case 0: > - errmsg("no MTD device specified"); > - case 1: > - errmsg("no start erase block specified"); > - case 2: > - errmsg("no erase block count specified"); > - error = 1; > - break; > - } > - if (error) > - return errmsg("Try `--help' for more information"); > - > - /* > - * Locate MTD and prepare for erasure > - */ > - mtd_desc = libmtd_open(); > - if (mtd_desc == NULL) > - return errmsg("can't initialize libmtd"); > - > - if ((fd = open(mtd_device, O_RDWR)) < 0) > - return sys_errmsg("%s", mtd_device); > - > - if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) > - return errmsg("mtd_get_dev_info failed"); > - > - if (jffs2 && mtd.type == MTD_MLCNANDFLASH) > - return errmsg("JFFS2 cannot support MLC NAND."); > - > - eb_start = start / mtd.eb_size; > - > - isNAND = mtd.type == MTD_NANDFLASH || mtd.type == MTD_MLCNANDFLASH; > - > - if (jffs2) { > - cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); > - cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); > - if (!isNAND) > - cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker)); > - else { > - struct nand_oobinfo oobinfo; > - > - if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) > - return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); > - > - /* Check for autoplacement */ > - if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { > - /* Get the position of the free bytes */ > - if (!oobinfo.oobfree[0][1]) > - return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); > - clmpos = oobinfo.oobfree[0][0]; > - clmlen = oobinfo.oobfree[0][1]; > - if (clmlen > 8) > - clmlen = 8; > - } else { > - /* Legacy mode */ > - switch (mtd.oob_size) { > - case 8: > - clmpos = 6; > - clmlen = 2; > - break; > - case 16: > - clmpos = 8; > - clmlen = 8; > - break; > - case 64: > - clmpos = 16; > - clmlen = 8; > - break; > - } > - } > - cleanmarker.totlen = cpu_to_je32(8); > - } > - cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); > - } > - > - /* > - * Now do the actual erasing of the MTD device > - */ > - if (eb_cnt == 0) > - eb_cnt = (mtd.size / mtd.eb_size) - eb_start; > - > - for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { > - offset = (off_t)eb * mtd.eb_size; > - > - if (!noskipbad) { > - int ret = mtd_is_bad(&mtd, fd, eb); > - if (ret > 0) { > - verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, offset); > - continue; > - } else if (ret < 0) { > - if (errno == EOPNOTSUPP) { > - noskipbad = 1; > - if (isNAND) > - return errmsg("%s: Bad block check not available", mtd_device); > - } else > - return sys_errmsg("%s: MTD get bad block failed", mtd_device); > - } > - } > - > - show_progress(&mtd, offset, eb, eb_start, eb_cnt); > - > - if (unlock) { > - if (mtd_unlock(&mtd, fd, eb) != 0) { > - sys_errmsg("%s: MTD unlock failure", mtd_device); > - continue; > - } > - } > - > - if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { > - sys_errmsg("%s: MTD Erase failure", mtd_device); > - continue; > - } > - > - /* format for JFFS2 ? */ > - if (!jffs2) > - continue; > - > - /* write cleanmarker */ > - if (isNAND) { > - if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) { > - sys_errmsg("%s: MTD writeoob failure", mtd_device); > - continue; > - } > - } else { > - if (pwrite(fd, &cleanmarker, sizeof(cleanmarker), (loff_t)offset) != sizeof(cleanmarker)) { > - sys_errmsg("%s: MTD write failure", mtd_device); > - continue; > - } > - } > - verbose(!quiet, " Cleanmarker written at %"PRIxoff_t, offset); > - } > - show_progress(&mtd, offset, eb, eb_start, eb_cnt); > - bareverbose(!quiet, "\n"); > - > - return 0; > -} > diff --git a/flash_eraseall b/flash_eraseall > deleted file mode 100755 > index c5539b3..0000000 > --- a/flash_eraseall > +++ /dev/null > @@ -1,4 +0,0 @@ > -#!/bin/sh > -echo "${0##*/} has been replaced by \`flash_erase <mtddev> 0 0\`; please use it" 1>&2 > -[ $# -ne 0 ] && set -- "$@" 0 0 > -exec flash_erase "$@" > diff --git a/flash_lock.c b/flash_lock.c > deleted file mode 100644 > index 33f76c7..0000000 > --- a/flash_lock.c > +++ /dev/null > @@ -1,8 +0,0 @@ > -/* > - * flash_{lock,unlock} > - * > - * utilities for locking/unlocking sectors of flash devices > - */ > - > -#define PROGRAM_NAME "flash_lock" > -#include "flash_unlock.c" > diff --git a/flash_otp_dump.c b/flash_otp_dump.c > deleted file mode 100644 > index f0c0fb9..0000000 > --- a/flash_otp_dump.c > +++ /dev/null > @@ -1,56 +0,0 @@ > -/* > - * flash_otp_dump.c -- display One-Time-Programm data > - */ > - > -#define PROGRAM_NAME "flash_otp_dump" > - > -#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", PROGRAM_NAME); > - 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 > deleted file mode 100644 > index 2061797..0000000 > --- a/flash_otp_info.c > +++ /dev/null > @@ -1,65 +0,0 @@ > -/* > - * flash_otp_info.c -- print info about One-Time-Programm data > - */ > - > -#define PROGRAM_NAME "flash_otp_info" > - > -#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", PROGRAM_NAME); > - 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 > deleted file mode 100644 > index 3c39a2d..0000000 > --- a/flash_otp_lock.c > +++ /dev/null > @@ -1,72 +0,0 @@ > -/* > - * flash_otp_lock.c -- lock area of One-Time-Program data > - */ > - > -#define PROGRAM_NAME "flash_otp_lock" > - > -#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> > -#include "common.h" > - > -int main(int argc,char *argv[]) > -{ > - int fd, val, ret, offset, size; > - char *p; > - > - if (argc != 5 || strcmp(argv[1], "-u")) { > - fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", PROGRAM_NAME); > - 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", PROGRAM_NAME); > - return ERANGE; > - } > - > - size = strtoul(argv[4], &p, 0); > - if (argv[4][0] == 0 || *p != 0) { > - fprintf(stderr, "%s: bad size value\n", PROGRAM_NAME); > - return ERANGE; > - } > - > - printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", > - argv[2], offset, offset + size); > - if (prompt("Are you sure?", false)) { > - 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 > deleted file mode 100644 > index 111318d..0000000 > --- a/flash_otp_write.c > +++ /dev/null > @@ -1,122 +0,0 @@ > -/* > - * flash_otp_write.c -- write One-Time-Program data > - */ > - > -#define PROGRAM_NAME "flash_otp_write" > - > -#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 <common.h> > -#include <mtd/mtd-user.h> > - > -ssize_t xread(int fd, void *buf, size_t count) > -{ > - ssize_t ret, done = 0; > - > -retry: > - ret = read(fd, buf + done, count - done); > - if (ret < 0) > - return ret; > - > - done += ret; > - > - if (ret == 0 /* EOF */ || done == count) > - return done; > - else > - goto retry; > -} > - > -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", PROGRAM_NAME); > - 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 = (off_t)strtoull(argv[3], &p, 0); > - if (argv[3][0] == 0 || *p != 0) { > - fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME); > - 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%"PRIxoff_t"\n", argv[2], offset); > - > - if (mtd_type_is_nand_user(&mtdInfo)) > - len = mtdInfo.writesize; > - else > - len = 256; > - > - if (len > sizeof(buf)) { > - printf("huh, writesize (%d) bigger than buffer (%zu)\n", > - len, sizeof(buf)); > - return ENOMEM; > - } > - > - wrote = 0; > - while ((size = xread(0, buf, len))) { > - if (size < 0) { > - perror("read()"); > - return errno; > - } > - p = buf; > - while (size > 0) { > - if (mtd_type_is_nand_user(&mtdInfo)) { > - /* Fill remain buffers with 0xff */ > - memset(buf + size, 0xff, mtdInfo.writesize - size); > - size = mtdInfo.writesize; > - } > - 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 > deleted file mode 100644 > index 1cc8c2f..0000000 > --- a/flash_unlock.c > +++ /dev/null > @@ -1,90 +0,0 @@ > -/* > - * flash_{lock,unlock} > - * > - * utilities for locking/unlocking sectors of flash devices > - */ > - > -#ifndef PROGRAM_NAME > -#define PROGRAM_NAME "flash_unlock" > -#define FLASH_MSG "unlock" > -#define FLASH_UNLOCK 1 > -#else > -#define FLASH_MSG "lock" > -#define FLASH_UNLOCK 0 > -#endif > - > -#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 "common.h" > -#include <mtd/mtd-user.h> > - > -static void usage(int status) > -{ > - fprintf(status ? stderr : stdout, > - "Usage: %s <mtd device> [offset] [block count]\n\n" > - "If offset is not specified, it defaults to 0.\n" > - "If block count is not specified, it defaults to all blocks.\n", > - PROGRAM_NAME); > - exit(status); > -} > - > -int main(int argc, char *argv[]) > -{ > - int fd, request; > - struct mtd_info_user mtdInfo; > - struct erase_info_user mtdLockInfo; > - int count; > - const char *dev; > - > - /* Parse command line options */ > - if (argc < 2 || argc > 4) > - usage(1); > - if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) > - usage(0); > - > - dev = argv[1]; > - > - /* Get the device info to compare to command line sizes */ > - fd = open(dev, O_RDWR); > - if (fd < 0) > - sys_errmsg_die("could not open: %s", dev); > - > - if (ioctl(fd, MEMGETINFO, &mtdInfo)) > - sys_errmsg_die("could not get mtd info: %s", dev); > - > - /* Make sure user options are valid */ > - if (argc > 2) > - mtdLockInfo.start = strtol(argv[2], NULL, 0); > - else > - mtdLockInfo.start = 0; > - if (mtdLockInfo.start > mtdInfo.size) > - errmsg_die("%#x is beyond device size %#x", > - mtdLockInfo.start, mtdInfo.size); > - > - if (argc > 3) { > - count = strtol(argv[3], NULL, 0); > - if (count == -1) > - mtdLockInfo.length = mtdInfo.size; > - else > - mtdLockInfo.length = mtdInfo.erasesize * count; > - } else > - mtdLockInfo.length = mtdInfo.size; > - if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size) > - errmsg_die("range is more than device supports: %#x + %#x > %#x", > - mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size); > - > - /* Finally do the operation */ > - request = FLASH_UNLOCK ? MEMUNLOCK : MEMLOCK; > - if (ioctl(fd, request, &mtdLockInfo)) > - sys_errmsg_die("could not %s device: %s\n", > - FLASH_MSG, dev); > - > - return 0; > -} > diff --git a/flashcp.c b/flashcp.c > deleted file mode 100644 > index 86334ac..0000000 > --- a/flashcp.c > +++ /dev/null > @@ -1,389 +0,0 @@ > -/* > - * Copyright (c) 2d3D, Inc. > - * Written by Abraham vd Merwe <abraham@2d3d.co.za> > - * All rights reserved. > - * > - * 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. > - */ > - > -#define PROGRAM_NAME "flashcp" > - > -#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(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: %1$s [ -v | --verbose ] <filename> <device>\n" > - " %1$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", > - PROGRAM_NAME); > - > - 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 *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]; > - > - /********************* > - * 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(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 || device == NULL) > - showusage(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) / 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 > deleted file mode 100644 > index 0eada8f..0000000 > --- a/ftl_check.c > +++ /dev/null > @@ -1,217 +0,0 @@ > -/* Ported to MTD system. > - * 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. > - > - ======================================================================*/ > - > -#define PROGRAM_NAME "ftl_check" > - > -#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 <mtd/ftl-user.h> > -#include <mtd_swab.h> > - > -#include "common.h" > - > -/*====================================================================*/ > - > -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) > -{ > - mtd_info_t mtd; > - erase_unit_header_t hdr, hdr2; > - off_t i; > - u_int 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 ((le32_to_cpu(hdr.FormattedSize) > 0) && > - (le32_to_cpu(hdr.FormattedSize) <= mtd.size) && > - (le16_to_cpu(hdr.NumEraseUnits) > 0) && > - (le16_to_cpu(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(le32_to_cpu(hdr.FormattedSize)); > - printf(", erase units = %d, transfer units = %d\n", > - le16_to_cpu(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 < le16_to_cpu(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 %"PRIdoff_t":\n", i); > - if ((hdr2.FormattedSize != hdr.FormattedSize) || > - (hdr2.NumEraseUnits != hdr.NumEraseUnits) || > - (hdr2.SerialNumber != hdr.SerialNumber)) > - printf(" Erase unit header is corrupt.\n"); > - else if (le16_to_cpu(hdr2.LogicalEUN) == 0xffff) > - printf(" Transfer unit, erase count = %d\n", le32_to_cpu(hdr2.EraseCount)); > - else { > - printf(" Logical unit %d, erase count = %d\n", > - le16_to_cpu(hdr2.LogicalEUN), le32_to_cpu(hdr2.EraseCount)); > - if (lseek(fd, (i << hdr.EraseUnitSize)+le32_to_cpu(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(le32_to_cpu(bam[j]))) > - free++; > - else if (BLOCK_DELETED(le32_to_cpu(bam[j]))) > - deleted++; > - else switch (BLOCK_TYPE(le32_to_cpu(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(void) > -{ > - fprintf(stderr, "usage: %s device\n", PROGRAM_NAME); > -} > - > -/*====================================================================*/ > - > -int main(int argc, char *argv[]) > -{ > - int optch, errflg, fd; > - struct stat buf; > - > - errflg = 0; > - while ((optch = getopt(argc, argv, "h")) != -1) { > - switch (optch) { > - case 'h': > - errflg = 1; break; > - default: > - errflg = -1; break; > - } > - } > - if (errflg || (optind != argc-1)) { > - showusage(); > - 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); > - close(fd); > - > - exit(EXIT_SUCCESS); > - return 0; > -} > diff --git a/ftl_format.c b/ftl_format.c > deleted file mode 100644 > index b58677f..0000000 > --- a/ftl_format.c > +++ /dev/null > @@ -1,324 +0,0 @@ > -/* Ported to MTD system. > - * 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. > - > - ======================================================================*/ > - > -#define PROGRAM_NAME "ftl_format" > - > -#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 <mtd/ftl-user.h> > -#include <mtd_swab.h> > -#include "common.h" > - > -/*====================================================================*/ > - > -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 = cpu_to_le32(0); > - hdr->FirstPhysicalEUN = cpu_to_le16(BootUnits); > - hdr->NumEraseUnits = cpu_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 = cpu_to_le32(0x80); > - /* Adjust size to account for BAM space */ > - nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int) > - + le32_to_cpu(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize; > - > - __FormattedSize -= > - (le16_to_cpu(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize); > - __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff); > - > - hdr->FormattedSize = cpu_to_le32(__FormattedSize); > - > - /* hdr->FirstVMAddress defaults to erased state */ > - hdr->NumVMPages = cpu_to_le16(0); > - hdr->Flags = 0; > - /* hdr->Code defaults to erased state */ > - hdr->SerialNumber = cpu_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(le16_to_cpu(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize); > - printf(" allocated for boot image\n"); > - } > - printf("Reserved %d%%, formatted size = ", reserve); > - print_size(le32_to_cpu(hdr.FormattedSize)); > - printf("\n"); > - fflush(stdout); > - } > - > - if (interrogate) > - if (!prompt("This will destroy all data on the target device. Confirm?", false)) > - return -1; > - > - /* Create basic block allocation table for control blocks */ > - nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int) > - + le32_to_cpu(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize; > - bam = malloc(nbam * sizeof(u_int)); > - for (i = 0; i < nbam; i++) > - bam[i] = cpu_to_le32(BLOCK_CONTROL); > - > - /* Erase partition */ > - if (!quiet) { > - printf("Erasing all blocks...\n"); > - fflush(stdout); > - } > - erase.length = mtd.erasesize; > - erase.start = mtd.erasesize * le16_to_cpu(hdr.FirstPhysicalEUN); > - for (i = 0; i < le16_to_cpu(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 ? (le16_to_cpu(hdr.NumEraseUnits) / spare) : (le16_to_cpu(hdr.NumEraseUnits) + 1); > - for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) { > - off_t ofs = (off_t) (i + le16_to_cpu(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 = cpu_to_le16(0xffff); > - else { > - hdr.LogicalEUN = cpu_to_le16(lun); > - lun++; > - } > - if (write(fd, &hdr, sizeof(hdr)) == -1) { > - perror("write failed"); > - break; > - } > - if (lseek(fd, ofs + le32_to_cpu(hdr.BAMOffset), SEEK_SET) == -1) { > - perror("seek failed"); > - break; > - } > - if (write(fd, bam, nbam * sizeof(u_int)) == -1) { > - perror("write failed"); > - break; > - } > - } > - if (i < le16_to_cpu(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", PROGRAM_NAME); > - 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 > deleted file mode 100644 > index 3176469..0000000 > --- a/jffs-dump.c > +++ /dev/null > @@ -1,359 +0,0 @@ > -/* > - * Dump JFFS filesystem. > - * Useful when it buggers up. > - */ > - > -#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> > - > -#include "common.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 \ > - - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ > - % JFFS_ALIGN_SIZE) > - > -#define JFFS_EMPTY_BITMASK 0xffffffff > -#define JFFS_MAGIC_BITMASK 0x34383931 > -#define JFFS_DIRTY_BITMASK 0x00000000 > - > -struct jffs_raw_inode > -{ > - uint32_t magic; /* A constant magic number. */ > - uint32_t ino; /* Inode number. */ > - uint32_t pino; /* Parent's inode number. */ > - uint32_t version; /* Version number. */ > - uint32_t mode; /* file_type, mode */ > - uint16_t uid; > - uint16_t gid; > - uint32_t atime; > - uint32_t mtime; > - uint32_t ctime; > - uint32_t offset; /* Where to begin to write. */ > - uint32_t dsize; /* Size of the file data. */ > - uint32_t rsize; /* How much are going to be replaced? */ > - uint8_t nsize; /* Name length. */ > - uint8_t nlink; /* Number of links. */ > - uint8_t spare : 6; /* For future use. */ > - uint8_t rename : 1; /* Is this a special rename? */ > - uint8_t deleted : 1; /* Has this file been deleted? */ > - uint8_t accurate; /* The inode is obsolete if accurate == 0. */ > - uint32_t dchksum; /* Checksum for the data. */ > - uint16_t nchksum; /* Checksum for the name. */ > - uint16_t 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 uint32_t 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 uint32_t > -jffs_checksum(void *data, int size) > -{ > - uint32_t sum = 0; > - uint8_t *ptr = (uint8_t *)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(uint32_t *adr, uint32_t 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(uint16_t *adr, uint16_t 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 uint32_t read_val32(uint32_t *adr) > -{ > - uint32_t 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 uint16_t read_val16(uint16_t *adr) > -{ > - uint16_t 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; > - uint32_t 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 > deleted file mode 100644 > index f8b8ac7..0000000 > --- a/jffs2dump.c > +++ /dev/null > @@ -1,805 +0,0 @@ > -/* > - * dumpjffs2.c > - * > - * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) > - * > - * This program is free software; you can redistribute it and/or modify > - * it under the terms of the GNU General Public License version 2 as > - * published by the Free Software Foundation. > - * > - * Overview: > - * This utility dumps the contents of a binary JFFS2 image > - * > - * > - * Bug/ToDo: > - */ > - > -#define PROGRAM_NAME "jffs2dump" > - > -#include <errno.h> > -#include <stdint.h> > -#include <stdio.h> > -#include <stdlib.h> > -#include <string.h> > -#include <unistd.h> > -#include <fcntl.h> > -#include <time.h> > -#include <sys/types.h> > -#include <sys/stat.h> > -#include <sys/param.h> > -#include <asm/types.h> > -#include <dirent.h> > -#include <mtd/jffs2-user.h> > -#include <endian.h> > -#include <byteswap.h> > -#include <getopt.h> > -#include <crc32.h> > -#include "summary.h" > -#include "common.h" > - > -#define PAD(x) (((x)+3)&~3) > - > -/* For outputting a byte-swapped version of the input image. */ > -#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) > -#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) > - > -#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) > -#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) > - > -// Global variables > -long imglen; // length of image > -char *data; // image data > - > -void display_help (void) > -{ > - printf("Usage: %s [OPTION]... INPUTFILE\n" > - "Dump the contents of a binary JFFS2 image.\n\n" > - " --help display this help and exit\n" > - " --version display version information and exit\n" > - " -b, --bigendian image is big endian\n" > - " -l, --littleendian image is little endian\n" > - " -c, --content dump image contents\n" > - " -e, --endianconvert=FNAME convert image endianness, output to file fname\n" > - " -r, --recalccrc recalc name and data crc on endian conversion\n" > - " -d, --datsize=LEN size of data chunks, when oob data in binary image (NAND only)\n" > - " -o, --oobsize=LEN size of oob data chunk in binary image (NAND only)\n" > - " -v, --verbose verbose output\n", > - PROGRAM_NAME); > - exit(0); > -} > - > -void display_version (void) > -{ > - printf("%1$s " VERSION "\n" > - "\n" > - "Copyright (C) 2003 Thomas Gleixner \n" > - "\n" > - "%1$s comes with NO WARRANTY\n" > - "to the extent permitted by law.\n" > - "\n" > - "You may redistribute copies of %1$s\n" > - "under the terms of the GNU General Public Licence.\n" > - "See the file `COPYING' for more information.\n", > - PROGRAM_NAME); > - exit(0); > -} > - > -// Option variables > - > -int verbose; // verbose output > -char *img; // filename of image > -int dumpcontent; // dump image content > -int target_endian = __BYTE_ORDER; // image endianess > -int convertendian; // convert endianness > -int recalccrc; // recalc name and data crc's on endian conversion > -char cnvfile[256]; // filename for conversion output > -int datsize; // Size of data chunks, when oob data is inside the binary image > -int oobsize; // Size of oob chunks, when oob data is inside the binary image > - > -void process_options (int argc, char *argv[]) > -{ > - int error = 0; > - > - for (;;) { > - int option_index = 0; > - static const char *short_options = "blce:rd:o:v"; > - static const struct option long_options[] = { > - {"help", no_argument, 0, 0}, > - {"version", no_argument, 0, 0}, > - {"bigendian", no_argument, 0, 'b'}, > - {"littleendian", no_argument, 0, 'l'}, > - {"content", no_argument, 0, 'c'}, > - {"endianconvert", required_argument, 0, 'e'}, > - {"datsize", required_argument, 0, 'd'}, > - {"oobsize", required_argument, 0, 'o'}, > - {"recalccrc", required_argument, 0, 'r'}, > - {"verbose", no_argument, 0, 'v'}, > - {0, 0, 0, 0}, > - }; > - > - int c = getopt_long(argc, argv, short_options, > - long_options, &option_index); > - if (c == EOF) { > - break; > - } > - > - switch (c) { > - case 0: > - switch (option_index) { > - case 0: > - display_help(); > - break; > - case 1: > - display_version(); > - break; > - } > - break; > - case 'v': > - verbose = 1; > - break; > - case 'b': > - target_endian = __BIG_ENDIAN; > - break; > - case 'l': > - target_endian = __LITTLE_ENDIAN; > - break; > - case 'c': > - dumpcontent = 1; > - break; > - case 'd': > - datsize = atoi(optarg); > - break; > - case 'o': > - oobsize = atoi(optarg); > - break; > - case 'e': > - convertendian = 1; > - strcpy (cnvfile, optarg); > - break; > - case 'r': > - recalccrc = 1; > - break; > - case '?': > - error = 1; > - break; > - } > - } > - > - if ((argc - optind) != 1 || error) > - display_help (); > - > - img = argv[optind]; > -} > - > - > -/* > - * Dump image contents > - */ > -void do_dumpcontent (void) > -{ > - char *p = data, *p_free_begin; > - union jffs2_node_union *node; > - int empty = 0, dirty = 0; > - char name[256]; > - uint32_t crc; > - uint16_t type; > - int bitchbitmask = 0; > - int obsolete; > - > - p_free_begin = NULL; > - while ( p < (data + imglen)) { > - node = (union jffs2_node_union*) p; > - > - /* Skip empty space */ > - if (!p_free_begin) > - p_free_begin = p; > - if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { > - p += 4; > - empty += 4; > - continue; > - } > - > - if (p != p_free_begin) > - printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data); > - p_free_begin = NULL; > - > - if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { > - if (!bitchbitmask++) > - printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); > - p += 4; > - dirty += 4; > - continue; > - } > - bitchbitmask = 0; > - > - type = je16_to_cpu(node->u.nodetype); > - if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { > - obsolete = 1; > - type |= JFFS2_NODE_ACCURATE; > - } else > - obsolete = 0; > - /* Set accurate for CRC check */ > - node->u.nodetype = cpu_to_je16(type); > - > - crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); > - if (crc != je32_to_cpu (node->u.hdr_crc)) { > - printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); > - p += 4; > - dirty += 4; > - continue; > - } > - > - switch(je16_to_cpu(node->u.nodetype)) { > - > - case JFFS2_NODETYPE_INODE: > - printf ("%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", > - obsolete ? "Obsolete" : "", > - p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), > - je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), > - je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); > - > - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); > - if (crc != je32_to_cpu (node->i.node_crc)) { > - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); > - p += PAD(je32_to_cpu (node->i.totlen)); > - dirty += PAD(je32_to_cpu (node->i.totlen));; > - continue; > - } > - > - crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); > - if (crc != je32_to_cpu(node->i.data_crc)) { > - printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); > - p += PAD(je32_to_cpu (node->i.totlen)); > - dirty += PAD(je32_to_cpu (node->i.totlen));; > - continue; > - } > - > - p += PAD(je32_to_cpu (node->i.totlen)); > - break; > - > - case JFFS2_NODETYPE_DIRENT: > - memcpy (name, node->d.name, node->d.nsize); > - name [node->d.nsize] = 0x0; > - printf ("%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", > - obsolete ? "Obsolete" : "", > - p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), > - je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), > - node->d.nsize, name); > - > - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); > - if (crc != je32_to_cpu (node->d.node_crc)) { > - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); > - p += PAD(je32_to_cpu (node->d.totlen)); > - dirty += PAD(je32_to_cpu (node->d.totlen));; > - continue; > - } > - > - crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); > - if (crc != je32_to_cpu(node->d.name_crc)) { > - printf ("Wrong name_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); > - p += PAD(je32_to_cpu (node->d.totlen)); > - dirty += PAD(je32_to_cpu (node->d.totlen));; > - continue; > - } > - > - p += PAD(je32_to_cpu (node->d.totlen)); > - break; > - > - case JFFS2_NODETYPE_XATTR: > - memcpy(name, node->x.data, node->x.name_len); > - name[node->x.name_len] = '\x00'; > - printf ("%8s Xattr node at 0x%08zx, totlen 0x%08x, xid %5d, version %5d, name_len %3d, name %s\n", > - obsolete ? "Obsolete" : "", > - p - data, > - je32_to_cpu (node->x.totlen), > - je32_to_cpu (node->x.xid), > - je32_to_cpu (node->x.version), > - node->x.name_len, > - name); > - > - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_xattr) - sizeof (node->x.node_crc)); > - if (crc != je32_to_cpu (node->x.node_crc)) { > - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.node_crc), crc); > - p += PAD(je32_to_cpu (node->x.totlen)); > - dirty += PAD(je32_to_cpu (node->x.totlen)); > - continue; > - } > - > - crc = mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1); > - if (crc != je32_to_cpu (node->x.data_crc)) { > - printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.data_crc), crc); > - p += PAD(je32_to_cpu (node->x.totlen)); > - dirty += PAD(je32_to_cpu (node->x.totlen)); > - continue; > - } > - p += PAD(je32_to_cpu (node->x.totlen)); > - break; > - > - case JFFS2_NODETYPE_XREF: > - printf ("%8s Xref node at 0x%08zx, totlen 0x%08x, xid %5d, xseqno %5d, #ino %8d\n", > - obsolete ? "Obsolete" : "", > - p - data, > - je32_to_cpu (node->r.totlen), > - je32_to_cpu (node->r.xid), > - je32_to_cpu (node->r.xseqno), > - je32_to_cpu (node->r.ino)); > - p += PAD(je32_to_cpu (node->r.totlen)); > - break; > - > - case JFFS2_NODETYPE_SUMMARY: { > - > - int i; > - struct jffs2_sum_marker * sm; > - > - printf("%8s Inode Sum node at 0x%08zx, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", > - obsolete ? "Obsolete" : "", > - p - data, > - je32_to_cpu (node->s.totlen), > - je32_to_cpu (node->s.sum_num), > - je32_to_cpu (node->s.cln_mkr)); > - > - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); > - if (crc != je32_to_cpu (node->s.node_crc)) { > - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); > - p += PAD(je32_to_cpu (node->s.totlen)); > - dirty += PAD(je32_to_cpu (node->s.totlen));; > - continue; > - } > - > - crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); > - if (crc != je32_to_cpu(node->s.sum_crc)) { > - printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); > - p += PAD(je32_to_cpu (node->s.totlen)); > - dirty += PAD(je32_to_cpu (node->s.totlen));; > - continue; > - } > - > - if (verbose) { > - void *sp; > - sp = (p + sizeof(struct jffs2_raw_summary)); > - > - for(i=0; i<je32_to_cpu(node->s.sum_num); i++) { > - > - switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { > - case JFFS2_NODETYPE_INODE : { > - > - struct jffs2_sum_inode_flash *spi; > - spi = sp; > - > - printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", > - "", > - je32_to_cpu (spi->inode), > - je32_to_cpu (spi->version), > - je32_to_cpu (spi->offset), > - je32_to_cpu (spi->totlen)); > - > - sp += JFFS2_SUMMARY_INODE_SIZE; > - break; > - } > - > - case JFFS2_NODETYPE_DIRENT : { > - > - char name[255]; > - struct jffs2_sum_dirent_flash *spd; > - spd = sp; > - > - memcpy(name,spd->name,spd->nsize); > - name [spd->nsize] = 0x0; > - > - printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", > - "", > - je32_to_cpu (spd->offset), > - je32_to_cpu (spd->totlen), > - je32_to_cpu (spd->pino), > - je32_to_cpu (spd->version), > - je32_to_cpu (spd->ino), > - spd->nsize, > - name); > - > - sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); > - break; > - } > - > - case JFFS2_NODETYPE_XATTR : { > - struct jffs2_sum_xattr_flash *spx; > - spx = sp; > - printf ("%14s Xattr offset 0x%08x, totlen 0x%08x, version %5d, #xid %8d\n", > - "", > - je32_to_cpu (spx->offset), > - je32_to_cpu (spx->totlen), > - je32_to_cpu (spx->version), > - je32_to_cpu (spx->xid)); > - sp += JFFS2_SUMMARY_XATTR_SIZE; > - break; > - } > - > - case JFFS2_NODETYPE_XREF : { > - struct jffs2_sum_xref_flash *spr; > - spr = sp; > - printf ("%14s Xref offset 0x%08x\n", > - "", > - je32_to_cpu (spr->offset)); > - sp += JFFS2_SUMMARY_XREF_SIZE; > - break; > - } > - > - default : > - printf("Unknown summary node!\n"); > - break; > - } > - } > - > - sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); > - > - printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", > - "", > - je32_to_cpu(sm->offset), > - je32_to_cpu(sm->magic), > - je32_to_cpu(node->s.padded)); > - } > - > - p += PAD(je32_to_cpu (node->s.totlen)); > - break; > - } > - > - case JFFS2_NODETYPE_CLEANMARKER: > - if (verbose) { > - printf ("%8s Cleanmarker at 0x%08zx, totlen 0x%08x\n", > - obsolete ? "Obsolete" : "", > - p - data, je32_to_cpu (node->u.totlen)); > - } > - p += PAD(je32_to_cpu (node->u.totlen)); > - break; > - > - case JFFS2_NODETYPE_PADDING: > - if (verbose) { > - printf ("%8s Padding node at 0x%08zx, totlen 0x%08x\n", > - obsolete ? "Obsolete" : "", > - p - data, je32_to_cpu (node->u.totlen)); > - } > - p += PAD(je32_to_cpu (node->u.totlen)); > - break; > - > - case 0xffff: > - p += 4; > - empty += 4; > - break; > - > - default: > - if (verbose) { > - printf ("%8s Unknown node at 0x%08zx, totlen 0x%08x\n", > - obsolete ? "Obsolete" : "", > - p - data, je32_to_cpu (node->u.totlen)); > - } > - p += PAD(je32_to_cpu (node->u.totlen)); > - dirty += PAD(je32_to_cpu (node->u.totlen)); > - > - } > - } > - > - if (verbose) > - printf ("Empty space: %d, dirty space: %d\n", empty, dirty); > -} > - > -/* > - * Convert endianess > - */ > -void do_endianconvert (void) > -{ > - char *p = data; > - union jffs2_node_union *node, newnode; > - int fd, len; > - jint32_t mode; > - uint32_t crc; > - > - fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); > - if (fd < 0) { > - fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); > - return; > - } > - > - while ( p < (data + imglen)) { > - node = (union jffs2_node_union*) p; > - > - /* Skip empty space */ > - if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { > - write (fd, p, 4); > - p += 4; > - continue; > - } > - > - if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { > - printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); > - newnode.u.magic = cnv_e16 (node->u.magic); > - newnode.u.nodetype = cnv_e16 (node->u.nodetype); > - write (fd, &newnode, 4); > - p += 4; > - continue; > - } > - > - crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); > - if (crc != je32_to_cpu (node->u.hdr_crc)) { > - printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); > - } > - > - switch(je16_to_cpu(node->u.nodetype)) { > - > - case JFFS2_NODETYPE_INODE: > - > - newnode.i.magic = cnv_e16 (node->i.magic); > - newnode.i.nodetype = cnv_e16 (node->i.nodetype); > - newnode.i.totlen = cnv_e32 (node->i.totlen); > - newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > - newnode.i.ino = cnv_e32 (node->i.ino); > - newnode.i.version = cnv_e32 (node->i.version); > - mode.v32 = node->i.mode.m; > - mode = cnv_e32 (mode); > - newnode.i.mode.m = mode.v32; > - newnode.i.uid = cnv_e16 (node->i.uid); > - newnode.i.gid = cnv_e16 (node->i.gid); > - newnode.i.isize = cnv_e32 (node->i.isize); > - newnode.i.atime = cnv_e32 (node->i.atime); > - newnode.i.mtime = cnv_e32 (node->i.mtime); > - newnode.i.ctime = cnv_e32 (node->i.ctime); > - newnode.i.offset = cnv_e32 (node->i.offset); > - newnode.i.csize = cnv_e32 (node->i.csize); > - newnode.i.dsize = cnv_e32 (node->i.dsize); > - newnode.i.compr = node->i.compr; > - newnode.i.usercompr = node->i.usercompr; > - newnode.i.flags = cnv_e16 (node->i.flags); > - if (recalccrc) { > - len = je32_to_cpu(node->i.csize); > - newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len)); > - } else > - newnode.i.data_crc = cnv_e32 (node->i.data_crc); > - > - newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); > - > - write (fd, &newnode, sizeof (struct jffs2_raw_inode)); > - write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); > - > - p += PAD(je32_to_cpu (node->i.totlen)); > - break; > - > - case JFFS2_NODETYPE_DIRENT: > - newnode.d.magic = cnv_e16 (node->d.magic); > - newnode.d.nodetype = cnv_e16 (node->d.nodetype); > - newnode.d.totlen = cnv_e32 (node->d.totlen); > - newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > - newnode.d.pino = cnv_e32 (node->d.pino); > - newnode.d.version = cnv_e32 (node->d.version); > - newnode.d.ino = cnv_e32 (node->d.ino); > - newnode.d.mctime = cnv_e32 (node->d.mctime); > - newnode.d.nsize = node->d.nsize; > - newnode.d.type = node->d.type; > - newnode.d.unused[0] = node->d.unused[0]; > - newnode.d.unused[1] = node->d.unused[1]; > - newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); > - if (recalccrc) > - newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); > - else > - newnode.d.name_crc = cnv_e32 (node->d.name_crc); > - > - write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); > - write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); > - p += PAD(je32_to_cpu (node->d.totlen)); > - break; > - > - case JFFS2_NODETYPE_XATTR: > - newnode.x.magic = cnv_e16 (node->x.magic); > - newnode.x.nodetype = cnv_e16 (node->x.nodetype); > - newnode.x.totlen = cnv_e32 (node->x.totlen); > - newnode.x.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > - newnode.x.xid = cnv_e32 (node->x.xid); > - newnode.x.version = cnv_e32 (node->x.version); > - newnode.x.xprefix = node->x.xprefix; > - newnode.x.name_len = node->x.name_len; > - newnode.x.value_len = cnv_e16 (node->x.value_len); > - if (recalccrc) > - newnode.x.data_crc = cpu_to_e32 (mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1)); > - else > - newnode.x.data_crc = cnv_e32 (node->x.data_crc); > - newnode.x.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xattr) - sizeof (newnode.x.node_crc))); > - > - write (fd, &newnode, sizeof (struct jffs2_raw_xattr)); > - write (fd, p + sizeof (struct jffs2_raw_xattr), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_xattr))); > - p += PAD(je32_to_cpu (node->x.totlen)); > - break; > - > - case JFFS2_NODETYPE_XREF: > - newnode.r.magic = cnv_e16 (node->r.magic); > - newnode.r.nodetype = cnv_e16 (node->r.nodetype); > - newnode.r.totlen = cnv_e32 (node->r.totlen); > - newnode.r.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - sizeof (newnode.r.hdr_crc))); > - newnode.r.ino = cnv_e32 (node->r.ino); > - newnode.r.xid = cnv_e32 (node->r.xid); > - newnode.r.xseqno = cnv_e32 (node->r.xseqno); > - newnode.r.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xref) - sizeof (newnode.r.node_crc))); > - p += PAD(je32_to_cpu (node->x.totlen)); > - break; > - > - case JFFS2_NODETYPE_CLEANMARKER: > - case JFFS2_NODETYPE_PADDING: > - newnode.u.magic = cnv_e16 (node->u.magic); > - newnode.u.nodetype = cnv_e16 (node->u.nodetype); > - newnode.u.totlen = cnv_e32 (node->u.totlen); > - newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > - > - write (fd, &newnode, sizeof (struct jffs2_unknown_node)); > - len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); > - if (len > 0) > - write (fd, p + sizeof (struct jffs2_unknown_node), len); > - > - p += PAD(je32_to_cpu (node->u.totlen)); > - break; > - > - case JFFS2_NODETYPE_SUMMARY : { > - struct jffs2_sum_marker *sm_ptr; > - int i,sum_len; > - int counter = 0; > - > - newnode.s.magic = cnv_e16 (node->s.magic); > - newnode.s.nodetype = cnv_e16 (node->s.nodetype); > - newnode.s.totlen = cnv_e32 (node->s.totlen); > - newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > - newnode.s.sum_num = cnv_e32 (node->s.sum_num); > - newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); > - newnode.s.padded = cnv_e32 (node->s.padded); > - > - newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); > - > - // summary header > - p += sizeof (struct jffs2_raw_summary); > - > - // summary data > - sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); > - > - for (i=0; i<je32_to_cpu (node->s.sum_num); i++) { > - union jffs2_sum_flash *fl_ptr; > - > - fl_ptr = (union jffs2_sum_flash *) p; > - > - switch (je16_to_cpu (fl_ptr->u.nodetype)) { > - case JFFS2_NODETYPE_INODE: > - > - fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); > - fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); > - fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); > - fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); > - fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); > - p += sizeof (struct jffs2_sum_inode_flash); > - counter += sizeof (struct jffs2_sum_inode_flash); > - break; > - > - case JFFS2_NODETYPE_DIRENT: > - fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); > - fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); > - fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); > - fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); > - fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); > - fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); > - p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; > - counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; > - break; > - > - case JFFS2_NODETYPE_XATTR: > - fl_ptr->x.nodetype = cnv_e16 (fl_ptr->x.nodetype); > - fl_ptr->x.xid = cnv_e32 (fl_ptr->x.xid); > - fl_ptr->x.version = cnv_e32 (fl_ptr->x.version); > - fl_ptr->x.offset = cnv_e32 (fl_ptr->x.offset); > - fl_ptr->x.totlen = cnv_e32 (fl_ptr->x.totlen); > - p += sizeof (struct jffs2_sum_xattr_flash); > - counter += sizeof (struct jffs2_sum_xattr_flash); > - break; > - > - case JFFS2_NODETYPE_XREF: > - fl_ptr->r.nodetype = cnv_e16 (fl_ptr->r.nodetype); > - fl_ptr->r.offset = cnv_e32 (fl_ptr->r.offset); > - p += sizeof (struct jffs2_sum_xref_flash); > - counter += sizeof (struct jffs2_sum_xref_flash); > - break; > - > - default : > - printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); > - exit(EXIT_FAILURE); > - break; > - } > - > - } > - > - //pad > - p += sum_len - counter; > - > - // summary marker > - sm_ptr = (struct jffs2_sum_marker *) p; > - sm_ptr->offset = cnv_e32 (sm_ptr->offset); > - sm_ptr->magic = cnv_e32 (sm_ptr->magic); > - p += sizeof (struct jffs2_sum_marker); > - > - // generate new crc on sum data > - newnode.s.sum_crc = cpu_to_e32 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), > - je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); > - > - // write out new node header > - write(fd, &newnode, sizeof (struct jffs2_raw_summary)); > - // write out new summary data > - write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); > - > - break; > - } > - > - case 0xffff: > - write (fd, p, 4); > - p += 4; > - break; > - > - default: > - printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); > - p += PAD(je32_to_cpu (node->u.totlen)); > - > - } > - } > - > - close (fd); > - > -} > - > -/* > - * Main program > - */ > -int main(int argc, char **argv) > -{ > - int fd; > - > - process_options(argc, argv); > - > - /* Open the input file */ > - if ((fd = open(img, O_RDONLY)) == -1) { > - perror("open input file"); > - exit(1); > - } > - > - // get image length > - imglen = lseek(fd, 0, SEEK_END); > - lseek (fd, 0, SEEK_SET); > - > - data = malloc (imglen); > - if (!data) { > - perror("out of memory"); > - close (fd); > - exit(1); > - } > - > - if (datsize && oobsize) { > - int idx = 0; > - long len = imglen; > - uint8_t oob[oobsize]; > - printf ("Peeling data out of combined data/oob image\n"); > - while (len) { > - // read image data > - read (fd, &data[idx], datsize); > - read (fd, oob, oobsize); > - idx += datsize; > - imglen -= oobsize; > - len -= datsize + oobsize; > - } > - > - } else { > - // read image data > - read (fd, data, imglen); > - } > - // Close the input file > - close(fd); > - > - if (dumpcontent) > - do_dumpcontent (); > - > - if (convertendian) > - do_endianconvert (); > - > - // free memory > - free (data); > - > - // Return happy > - exit (0); > -} > diff --git a/jffs2reader.c b/jffs2reader.c > deleted file mode 100644 > index a62da9a..0000000 > --- a/jffs2reader.c > +++ /dev/null > @@ -1,918 +0,0 @@ > -/* 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 > - */ > - > - > -/* > -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. > - */ > - > -#define PROGRAM_NAME "jffs2reader" > - > -#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 <dirent.h> > -#include <zlib.h> > - > -#include "mtd/jffs2-user.h" > -#include "common.h" > - > -#define SCRATCH_SIZE (5*1024*1024) > - > -/* macro to avoid "lvalue required as left operand of assignment" error */ > -#define ADD_BYTES(p, n) ((p) = (typeof(p))((char *)(p) + (n))) > - > -#define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0) > -#define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0) > - > -struct dir { > - struct dir *next; > - uint8_t type; > - uint8_t nsize; > - uint32_t ino; > - char name[256]; > -}; > - > -int target_endian = __BYTE_ORDER; > - > -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, const char *path, > - int recurse, int want_ctime); > -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, const char *, > - uint32_t *, int); > -struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *, > - uint32_t *); > - > -void lsdir(char *, size_t, const char *, int, 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 = je32_to_cpu(n->dsize); > - > - if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize) > - errmsg_die("File does not fit into buffer!"); > - > - if (*rsize < je32_to_cpu(n->isize)) > - bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize); > - > - switch (n->compr) { > - case JFFS2_COMPR_ZLIB: > - uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen, > - (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), > - (uLongf) je32_to_cpu(n->csize)); > - break; > - > - case JFFS2_COMPR_NONE: > - memcpy(b + je32_to_cpu(n->offset), > - ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); > - break; > - > - case JFFS2_COMPR_ZERO: > - bzero(b + je32_to_cpu(n->offset), dlen); > - break; > - > - /* [DYN]RUBIN support required! */ > - > - default: > - errmsg_die("Unsupported compression method!"); > - } > - > - *rsize = je32_to_cpu(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 (je32_to_cpu(n->ino)) { > - if (dd == NULL) { > - d = xmalloc(sizeof(struct dir)); > - d->type = n->type; > - memcpy(d->name, n->name, n->nsize); > - d->nsize = n->nsize; > - d->ino = je32_to_cpu(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 = je32_to_cpu(n->ino); > - > - return o; > - } > - > - if (dd->next == NULL) { > - dd->next = xmalloc(sizeof(struct dir)); > - dd->next->type = n->type; > - memcpy(dd->next->name, n->name, n->nsize); > - dd->next->nsize = n->nsize; > - dd->next->ino = je32_to_cpu(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, const char *path, int recurse, > - int want_ctime) > -{ > - char m; > - char *filetime; > - time_t age; > - struct jffs2_raw_inode *ri; > - jint32_t mode; > - > - 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) { > - warnmsg("bug: raw_inode missing!"); > - d = d->next; > - continue; > - } > - > - filetime = ctime((const time_t *) &(ri->ctime)); > - age = time(NULL) - je32_to_cpu(ri->ctime); > - mode.v32 = ri->mode.m; > - printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)), > - 1, je16_to_cpu(ri->uid), je16_to_cpu(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 ", major(rdev), minor(rdev)); > - } else { > - printf("%9ld ", (long)je32_to_cpu(ri->dsize)); > - } > - d->name[d->nsize]='\0'; > - if (want_ctime) { > - if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) > - /* hh:mm if less than 6 months old */ > - printf("%6.6s %5.5s ", filetime + 4, filetime + 11); > - else > - printf("%6.6s %4.4s ", filetime + 4, filetime + 20); > - } > - printf("%s/%s%c", 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 = xmalloc(BUFSIZ); > - sprintf(tmp, "%s/%s", path, d->name); > - lsdir(o, size, tmp, recurse, want_ctime); /* 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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) > - ADD_BYTES(n, 4); > - > - if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { > - if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE && > - je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(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)); > - } > - > - ADD_BYTES(n, ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) > - ADD_BYTES(n, 4); > - > - if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { > - if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && > - je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(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); > - } > - } > - > - ADD_BYTES(n, ((je32_to_cpu(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) + > - ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) > - ADD_BYTES(n, 4); > - > - if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { > - if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && > - (!ino || je32_to_cpu(n->d.ino) == ino) && > - (v = je32_to_cpu(n->d.version)) > vmax && > - (!pino || (je32_to_cpu(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); > - } > - } > - > - ADD_BYTES(n, ((je32_to_cpu(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, > - const 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 = xstrdup(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, > - const 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, const char *path, int recurse, int want_ctime) > -{ > - 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)) > - errmsg_die("%s: No such file or directory", path); > - > - d = collectdir(o, size, ino, d); > - printdir(o, size, d, path, recurse, want_ctime); > - 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) > - errmsg_die("%s: No such file or directory", path); > - > - if (dd == NULL || dd->type != DT_REG) > - errmsg_die("%s: Not a regular file", path); > - > - 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, want_ctime = 0; > - struct stat st; > - > - char *scratch, *dir = NULL, *file = NULL; > - size_t ssize = 0; > - > - char *buf; > - > - while ((opt = getopt(argc, argv, "rd:f:t")) > 0) { > - switch (opt) { > - case 'd': > - dir = optarg; > - break; > - case 'f': > - file = optarg; > - break; > - case 'r': > - recurse++; > - break; > - case 't': > - want_ctime++; > - break; > - default: > - fprintf(stderr, > - "Usage: %s <image> [-d|-f] < path >\n", > - PROGRAM_NAME); > - exit(EXIT_FAILURE); > - } > - } > - > - fd = open(argv[optind], O_RDONLY); > - if (fd == -1) > - sys_errmsg_die("%s", argv[optind]); > - > - if (fstat(fd, &st)) > - sys_errmsg_die("%s", argv[optind]); > - > - buf = xmalloc((size_t) st.st_size); > - > - if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) > - sys_errmsg_die("%s", argv[optind]); > - > - if (dir) > - lsdir(buf, st.st_size, dir, recurse, want_ctime); > - > - if (file) { > - scratch = xmalloc(SCRATCH_SIZE); > - > - catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); > - free(scratch); > - } > - > - if (!dir && !file) > - lsdir(buf, st.st_size, "/", 1, want_ctime); > - > - > - free(buf); > - exit(EXIT_SUCCESS); > -} > diff --git a/jffsX-utils/compr.c b/jffsX-utils/compr.c > new file mode 100644 > index 0000000..cb4432e > --- /dev/null > +++ b/jffsX-utils/compr.c > @@ -0,0 +1,538 @@ > +/* > + * 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. > + */ > + > +#include "compr.h" > +#include <string.h> > +#include <stdlib.h> > +#include <linux/jffs2.h> > + > +#define FAVOUR_LZO_PERCENT 80 > + > +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)) { > + 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; > + } > + } > + } > +} > + > +/* > + * Return 1 to use this compression > + */ > +static int jffs2_is_best_compression(struct jffs2_compressor *this, > + struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) > +{ > + switch (jffs2_compression_mode) { > + case JFFS2_COMPR_MODE_SIZE: > + if (bestsize > size) > + return 1; > + return 0; > + case JFFS2_COMPR_MODE_FAVOURLZO: > + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) > + return 1; > + if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) > + return 1; > + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) > + return 1; > + if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) > + return 1; > + > + return 0; > + } > + /* Shouldn't happen */ > + return 0; > +} > + > +/* 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); > + 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_FAVOURLZO: > + case JFFS2_COMPR_MODE_SIZE: > + orig_slen = *datalen; > + orig_dlen = *cdatalen; > + list_for_each_entry(this, &jffs2_compressor_list, list) { > + uint32_t needed_buf_size; > + > + if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) > + needed_buf_size = orig_slen + jffs2_compression_check; > + else > + needed_buf_size = orig_dlen + jffs2_compression_check; > + > + /* 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 < needed_buf_size) && (this->compr_buf)) { > + free(this->compr_buf); > + this->compr_buf_size=0; > + this->compr_buf=NULL; > + } > + if (!this->compr_buf) { > + tmp_buf = malloc(needed_buf_size); > + 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); > + 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) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) > + && (*cdatalen < *datalen)) { > + 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: unknown 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; > + case JFFS2_COMPR_MODE_FAVOURLZO: > + act_buf += sprintf(act_buf, "favourlzo"); > + break; > + default: > + act_buf += sprintf(act_buf, "unknown"); > + 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; > + } > + if (!strcmp("favourlzo", name)) { > + jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; > + 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 > +#ifdef CONFIG_JFFS2_LZO > + jffs2_lzo_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 > +#ifdef CONFIG_JFFS2_LZO > + jffs2_lzo_exit(); > +#endif > + return 0; > +} > diff --git a/jffsX-utils/compr.h b/jffsX-utils/compr.h > new file mode 100644 > index 0000000..a21e935 > --- /dev/null > +++ b/jffsX-utils/compr.h > @@ -0,0 +1,119 @@ > +/* > + * 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. > + */ > + > +#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 CONFIG_JFFS2_LZO > + > +#define JFFS2_RUBINMIPS_PRIORITY 10 > +#define JFFS2_DYNRUBIN_PRIORITY 20 > +#define JFFS2_RTIME_PRIORITY 50 > +#define JFFS2_ZLIB_PRIORITY 60 > +#define JFFS2_LZO_PRIORITY 80 > + > +#define JFFS2_COMPR_MODE_NONE 0 > +#define JFFS2_COMPR_MODE_PRIORITY 1 > +#define JFFS2_COMPR_MODE_SIZE 2 > +#define JFFS2_COMPR_MODE_FAVOURLZO 3 > + > +#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 */ > + const char *name; > + char compr; /* JFFS2_COMPR_XXX */ > + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, > + uint32_t *srclen, uint32_t *destlen); > + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, > + uint32_t cdatalen, uint32_t datalen); > + 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 > +#ifdef CONFIG_JFFS2_LZO > +int jffs2_lzo_init(void); > +void jffs2_lzo_exit(void); > +#endif > + > +#endif /* __JFFS2_COMPR_H__ */ > diff --git a/jffsX-utils/compr_lzo.c b/jffsX-utils/compr_lzo.c > new file mode 100644 > index 0000000..d2e2afc > --- /dev/null > +++ b/jffsX-utils/compr_lzo.c > @@ -0,0 +1,135 @@ > +/* > + * JFFS2 LZO Compression Interface. > + * > + * Copyright (C) 2007 Nokia Corporation. All rights reserved. > + * > + * Author: Richard Purdie <rpurdie@openedhand.com> > + * > + * 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. > + * > + * 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., 51 Franklin St, Fifth Floor, Boston, MA > + * 02110-1301 USA > + * > + */ > + > +#include <stdint.h> > +#include <stdio.h> > +#include <string.h> > + > +#ifndef WITHOUT_LZO > +#include <asm/types.h> > +#include <linux/jffs2.h> > +#include <lzo/lzo1x.h> > +#include "compr.h" > + > +extern int page_size; > + > +static void *lzo_mem; > +static void *lzo_compress_buf; > + > +/* > + * Note about LZO compression. > + * > + * We want to use the _999_ compression routine which gives better compression > + * rates at the expense of time. Decompression time is unaffected. We might as > + * well use the standard lzo library routines for this but they will overflow > + * the destination buffer since they don't check the destination size. > + * > + * We therefore compress to a temporary buffer and copy if it will fit. > + * > + */ > +static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, > + uint32_t *sourcelen, uint32_t *dstlen) > +{ > + lzo_uint compress_size; > + int ret; > + > + ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); > + > + if (ret != LZO_E_OK) > + return -1; > + > + if (compress_size > *dstlen) > + return -1; > + > + memcpy(cpage_out, lzo_compress_buf, compress_size); > + *dstlen = compress_size; > + > + return 0; > +} > + > +static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, > + uint32_t srclen, uint32_t destlen) > +{ > + int ret; > + lzo_uint dl; > + > + ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); > + > + if (ret != LZO_E_OK || dl != destlen) > + return -1; > + > + return 0; > +} > + > +static struct jffs2_compressor jffs2_lzo_comp = { > + .priority = JFFS2_LZO_PRIORITY, > + .name = "lzo", > + .compr = JFFS2_COMPR_LZO, > + .compress = &jffs2_lzo_cmpr, > + .decompress = &jffs2_lzo_decompress, > + .disabled = 1, > +}; > + > +int jffs2_lzo_init(void) > +{ > + int ret; > + > + lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); > + if (!lzo_mem) > + return -1; > + > + /* Worse case LZO compression size from their FAQ */ > + lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); > + if (!lzo_compress_buf) { > + free(lzo_mem); > + return -1; > + } > + > + ret = jffs2_register_compressor(&jffs2_lzo_comp); > + if (ret < 0) { > + free(lzo_compress_buf); > + free(lzo_mem); > + } > + > + return ret; > +} > + > +void jffs2_lzo_exit(void) > +{ > + jffs2_unregister_compressor(&jffs2_lzo_comp); > + free(lzo_compress_buf); > + free(lzo_mem); > +} > + > +#else > + > +int jffs2_lzo_init(void) > +{ > + return 0; > +} > + > +void jffs2_lzo_exit(void) > +{ > +} > + > +#endif > diff --git a/jffsX-utils/compr_rtime.c b/jffsX-utils/compr_rtime.c > new file mode 100644 > index 0000000..f24379d > --- /dev/null > +++ b/jffsX-utils/compr_rtime.c > @@ -0,0 +1,119 @@ > +/* > + * 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. > + * > + * 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) > +{ > + short positions[256]; > + int outpos = 0; > + int pos=0; > + > + memset(positions,0,sizeof(positions)); > + > + while (pos < (*sourcelen) && outpos+2 <= (*dstlen)) { > + 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, > + __attribute__((unused)) uint32_t srclen, uint32_t destlen) > +{ > + 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/jffsX-utils/compr_zlib.c b/jffsX-utils/compr_zlib.c > new file mode 100644 > index 0000000..1f94628 > --- /dev/null > +++ b/jffsX-utils/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. > + */ > + > +#define PROGRAM_NAME "compr_zlib" > + > +#include <stdint.h> > +#define crc32 __zlib_crc32 > +#include <zlib.h> > +#undef crc32 > +#include <stdio.h> > +#include <asm/types.h> > +#include <linux/jffs2.h> > +#include "common.h" > +#include "compr.h" > + > +/* 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 > + > +static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, > + uint32_t *sourcelen, uint32_t *dstlen) > +{ > + 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; > +} > + > +static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, > + uint32_t srclen, uint32_t destlen) > +{ > + z_stream strm; > + int ret; > + > + strm.zalloc = (void *)0; > + strm.zfree = (void *)0; > + > + if (Z_OK != inflateInit(&strm)) { > + return 1; > + } > + strm.next_in = data_in; > + strm.avail_in = srclen; > + strm.total_in = 0; > + > + strm.next_out = cpage_out; > + strm.avail_out = destlen; > + strm.total_out = 0; > + > + while((ret = inflate(&strm, Z_FINISH)) == Z_OK) > + ; > + > + inflateEnd(&strm); > + return 0; > +} > + > +static struct jffs2_compressor jffs2_zlib_comp = { > + .priority = JFFS2_ZLIB_PRIORITY, > + .name = "zlib", > + .disabled = 0, > + .compr = JFFS2_COMPR_ZLIB, > + .compress = &jffs2_zlib_compress, > + .decompress = &jffs2_zlib_decompress, > +}; > + > +int jffs2_zlib_init(void) > +{ > + return jffs2_register_compressor(&jffs2_zlib_comp); > +} > + > +void jffs2_zlib_exit(void) > +{ > + jffs2_unregister_compressor(&jffs2_zlib_comp); > +} > diff --git a/jffsX-utils/device_table.txt b/jffsX-utils/device_table.txt > new file mode 100644 > index 0000000..394a62b > --- /dev/null > +++ b/jffsX-utils/device_table.txt > @@ -0,0 +1,128 @@ > +# 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/jffsX-utils/jffs-dump.c b/jffsX-utils/jffs-dump.c > new file mode 100644 > index 0000000..3176469 > --- /dev/null > +++ b/jffsX-utils/jffs-dump.c > @@ -0,0 +1,359 @@ > +/* > + * Dump JFFS filesystem. > + * Useful when it buggers up. > + */ > + > +#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> > + > +#include "common.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 \ > + - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ > + % JFFS_ALIGN_SIZE) > + > +#define JFFS_EMPTY_BITMASK 0xffffffff > +#define JFFS_MAGIC_BITMASK 0x34383931 > +#define JFFS_DIRTY_BITMASK 0x00000000 > + > +struct jffs_raw_inode > +{ > + uint32_t magic; /* A constant magic number. */ > + uint32_t ino; /* Inode number. */ > + uint32_t pino; /* Parent's inode number. */ > + uint32_t version; /* Version number. */ > + uint32_t mode; /* file_type, mode */ > + uint16_t uid; > + uint16_t gid; > + uint32_t atime; > + uint32_t mtime; > + uint32_t ctime; > + uint32_t offset; /* Where to begin to write. */ > + uint32_t dsize; /* Size of the file data. */ > + uint32_t rsize; /* How much are going to be replaced? */ > + uint8_t nsize; /* Name length. */ > + uint8_t nlink; /* Number of links. */ > + uint8_t spare : 6; /* For future use. */ > + uint8_t rename : 1; /* Is this a special rename? */ > + uint8_t deleted : 1; /* Has this file been deleted? */ > + uint8_t accurate; /* The inode is obsolete if accurate == 0. */ > + uint32_t dchksum; /* Checksum for the data. */ > + uint16_t nchksum; /* Checksum for the name. */ > + uint16_t 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 uint32_t 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 uint32_t > +jffs_checksum(void *data, int size) > +{ > + uint32_t sum = 0; > + uint8_t *ptr = (uint8_t *)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(uint32_t *adr, uint32_t 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(uint16_t *adr, uint16_t 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 uint32_t read_val32(uint32_t *adr) > +{ > + uint32_t 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 uint16_t read_val16(uint16_t *adr) > +{ > + uint16_t 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; > + uint32_t 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/jffsX-utils/jffs2dump.c b/jffsX-utils/jffs2dump.c > new file mode 100644 > index 0000000..f8b8ac7 > --- /dev/null > +++ b/jffsX-utils/jffs2dump.c > @@ -0,0 +1,805 @@ > +/* > + * dumpjffs2.c > + * > + * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Overview: > + * This utility dumps the contents of a binary JFFS2 image > + * > + * > + * Bug/ToDo: > + */ > + > +#define PROGRAM_NAME "jffs2dump" > + > +#include <errno.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <time.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/param.h> > +#include <asm/types.h> > +#include <dirent.h> > +#include <mtd/jffs2-user.h> > +#include <endian.h> > +#include <byteswap.h> > +#include <getopt.h> > +#include <crc32.h> > +#include "summary.h" > +#include "common.h" > + > +#define PAD(x) (((x)+3)&~3) > + > +/* For outputting a byte-swapped version of the input image. */ > +#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) > +#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) > + > +#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) > +#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) > + > +// Global variables > +long imglen; // length of image > +char *data; // image data > + > +void display_help (void) > +{ > + printf("Usage: %s [OPTION]... INPUTFILE\n" > + "Dump the contents of a binary JFFS2 image.\n\n" > + " --help display this help and exit\n" > + " --version display version information and exit\n" > + " -b, --bigendian image is big endian\n" > + " -l, --littleendian image is little endian\n" > + " -c, --content dump image contents\n" > + " -e, --endianconvert=FNAME convert image endianness, output to file fname\n" > + " -r, --recalccrc recalc name and data crc on endian conversion\n" > + " -d, --datsize=LEN size of data chunks, when oob data in binary image (NAND only)\n" > + " -o, --oobsize=LEN size of oob data chunk in binary image (NAND only)\n" > + " -v, --verbose verbose output\n", > + PROGRAM_NAME); > + exit(0); > +} > + > +void display_version (void) > +{ > + printf("%1$s " VERSION "\n" > + "\n" > + "Copyright (C) 2003 Thomas Gleixner \n" > + "\n" > + "%1$s comes with NO WARRANTY\n" > + "to the extent permitted by law.\n" > + "\n" > + "You may redistribute copies of %1$s\n" > + "under the terms of the GNU General Public Licence.\n" > + "See the file `COPYING' for more information.\n", > + PROGRAM_NAME); > + exit(0); > +} > + > +// Option variables > + > +int verbose; // verbose output > +char *img; // filename of image > +int dumpcontent; // dump image content > +int target_endian = __BYTE_ORDER; // image endianess > +int convertendian; // convert endianness > +int recalccrc; // recalc name and data crc's on endian conversion > +char cnvfile[256]; // filename for conversion output > +int datsize; // Size of data chunks, when oob data is inside the binary image > +int oobsize; // Size of oob chunks, when oob data is inside the binary image > + > +void process_options (int argc, char *argv[]) > +{ > + int error = 0; > + > + for (;;) { > + int option_index = 0; > + static const char *short_options = "blce:rd:o:v"; > + static const struct option long_options[] = { > + {"help", no_argument, 0, 0}, > + {"version", no_argument, 0, 0}, > + {"bigendian", no_argument, 0, 'b'}, > + {"littleendian", no_argument, 0, 'l'}, > + {"content", no_argument, 0, 'c'}, > + {"endianconvert", required_argument, 0, 'e'}, > + {"datsize", required_argument, 0, 'd'}, > + {"oobsize", required_argument, 0, 'o'}, > + {"recalccrc", required_argument, 0, 'r'}, > + {"verbose", no_argument, 0, 'v'}, > + {0, 0, 0, 0}, > + }; > + > + int c = getopt_long(argc, argv, short_options, > + long_options, &option_index); > + if (c == EOF) { > + break; > + } > + > + switch (c) { > + case 0: > + switch (option_index) { > + case 0: > + display_help(); > + break; > + case 1: > + display_version(); > + break; > + } > + break; > + case 'v': > + verbose = 1; > + break; > + case 'b': > + target_endian = __BIG_ENDIAN; > + break; > + case 'l': > + target_endian = __LITTLE_ENDIAN; > + break; > + case 'c': > + dumpcontent = 1; > + break; > + case 'd': > + datsize = atoi(optarg); > + break; > + case 'o': > + oobsize = atoi(optarg); > + break; > + case 'e': > + convertendian = 1; > + strcpy (cnvfile, optarg); > + break; > + case 'r': > + recalccrc = 1; > + break; > + case '?': > + error = 1; > + break; > + } > + } > + > + if ((argc - optind) != 1 || error) > + display_help (); > + > + img = argv[optind]; > +} > + > + > +/* > + * Dump image contents > + */ > +void do_dumpcontent (void) > +{ > + char *p = data, *p_free_begin; > + union jffs2_node_union *node; > + int empty = 0, dirty = 0; > + char name[256]; > + uint32_t crc; > + uint16_t type; > + int bitchbitmask = 0; > + int obsolete; > + > + p_free_begin = NULL; > + while ( p < (data + imglen)) { > + node = (union jffs2_node_union*) p; > + > + /* Skip empty space */ > + if (!p_free_begin) > + p_free_begin = p; > + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { > + p += 4; > + empty += 4; > + continue; > + } > + > + if (p != p_free_begin) > + printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data); > + p_free_begin = NULL; > + > + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { > + if (!bitchbitmask++) > + printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); > + p += 4; > + dirty += 4; > + continue; > + } > + bitchbitmask = 0; > + > + type = je16_to_cpu(node->u.nodetype); > + if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { > + obsolete = 1; > + type |= JFFS2_NODE_ACCURATE; > + } else > + obsolete = 0; > + /* Set accurate for CRC check */ > + node->u.nodetype = cpu_to_je16(type); > + > + crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); > + if (crc != je32_to_cpu (node->u.hdr_crc)) { > + printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); > + p += 4; > + dirty += 4; > + continue; > + } > + > + switch(je16_to_cpu(node->u.nodetype)) { > + > + case JFFS2_NODETYPE_INODE: > + printf ("%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", > + obsolete ? "Obsolete" : "", > + p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), > + je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), > + je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); > + > + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); > + if (crc != je32_to_cpu (node->i.node_crc)) { > + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); > + p += PAD(je32_to_cpu (node->i.totlen)); > + dirty += PAD(je32_to_cpu (node->i.totlen));; > + continue; > + } > + > + crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); > + if (crc != je32_to_cpu(node->i.data_crc)) { > + printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); > + p += PAD(je32_to_cpu (node->i.totlen)); > + dirty += PAD(je32_to_cpu (node->i.totlen));; > + continue; > + } > + > + p += PAD(je32_to_cpu (node->i.totlen)); > + break; > + > + case JFFS2_NODETYPE_DIRENT: > + memcpy (name, node->d.name, node->d.nsize); > + name [node->d.nsize] = 0x0; > + printf ("%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", > + obsolete ? "Obsolete" : "", > + p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), > + je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), > + node->d.nsize, name); > + > + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); > + if (crc != je32_to_cpu (node->d.node_crc)) { > + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); > + p += PAD(je32_to_cpu (node->d.totlen)); > + dirty += PAD(je32_to_cpu (node->d.totlen));; > + continue; > + } > + > + crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); > + if (crc != je32_to_cpu(node->d.name_crc)) { > + printf ("Wrong name_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); > + p += PAD(je32_to_cpu (node->d.totlen)); > + dirty += PAD(je32_to_cpu (node->d.totlen));; > + continue; > + } > + > + p += PAD(je32_to_cpu (node->d.totlen)); > + break; > + > + case JFFS2_NODETYPE_XATTR: > + memcpy(name, node->x.data, node->x.name_len); > + name[node->x.name_len] = '\x00'; > + printf ("%8s Xattr node at 0x%08zx, totlen 0x%08x, xid %5d, version %5d, name_len %3d, name %s\n", > + obsolete ? "Obsolete" : "", > + p - data, > + je32_to_cpu (node->x.totlen), > + je32_to_cpu (node->x.xid), > + je32_to_cpu (node->x.version), > + node->x.name_len, > + name); > + > + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_xattr) - sizeof (node->x.node_crc)); > + if (crc != je32_to_cpu (node->x.node_crc)) { > + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.node_crc), crc); > + p += PAD(je32_to_cpu (node->x.totlen)); > + dirty += PAD(je32_to_cpu (node->x.totlen)); > + continue; > + } > + > + crc = mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1); > + if (crc != je32_to_cpu (node->x.data_crc)) { > + printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.data_crc), crc); > + p += PAD(je32_to_cpu (node->x.totlen)); > + dirty += PAD(je32_to_cpu (node->x.totlen)); > + continue; > + } > + p += PAD(je32_to_cpu (node->x.totlen)); > + break; > + > + case JFFS2_NODETYPE_XREF: > + printf ("%8s Xref node at 0x%08zx, totlen 0x%08x, xid %5d, xseqno %5d, #ino %8d\n", > + obsolete ? "Obsolete" : "", > + p - data, > + je32_to_cpu (node->r.totlen), > + je32_to_cpu (node->r.xid), > + je32_to_cpu (node->r.xseqno), > + je32_to_cpu (node->r.ino)); > + p += PAD(je32_to_cpu (node->r.totlen)); > + break; > + > + case JFFS2_NODETYPE_SUMMARY: { > + > + int i; > + struct jffs2_sum_marker * sm; > + > + printf("%8s Inode Sum node at 0x%08zx, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", > + obsolete ? "Obsolete" : "", > + p - data, > + je32_to_cpu (node->s.totlen), > + je32_to_cpu (node->s.sum_num), > + je32_to_cpu (node->s.cln_mkr)); > + > + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); > + if (crc != je32_to_cpu (node->s.node_crc)) { > + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); > + p += PAD(je32_to_cpu (node->s.totlen)); > + dirty += PAD(je32_to_cpu (node->s.totlen));; > + continue; > + } > + > + crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); > + if (crc != je32_to_cpu(node->s.sum_crc)) { > + printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); > + p += PAD(je32_to_cpu (node->s.totlen)); > + dirty += PAD(je32_to_cpu (node->s.totlen));; > + continue; > + } > + > + if (verbose) { > + void *sp; > + sp = (p + sizeof(struct jffs2_raw_summary)); > + > + for(i=0; i<je32_to_cpu(node->s.sum_num); i++) { > + > + switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { > + case JFFS2_NODETYPE_INODE : { > + > + struct jffs2_sum_inode_flash *spi; > + spi = sp; > + > + printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", > + "", > + je32_to_cpu (spi->inode), > + je32_to_cpu (spi->version), > + je32_to_cpu (spi->offset), > + je32_to_cpu (spi->totlen)); > + > + sp += JFFS2_SUMMARY_INODE_SIZE; > + break; > + } > + > + case JFFS2_NODETYPE_DIRENT : { > + > + char name[255]; > + struct jffs2_sum_dirent_flash *spd; > + spd = sp; > + > + memcpy(name,spd->name,spd->nsize); > + name [spd->nsize] = 0x0; > + > + printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", > + "", > + je32_to_cpu (spd->offset), > + je32_to_cpu (spd->totlen), > + je32_to_cpu (spd->pino), > + je32_to_cpu (spd->version), > + je32_to_cpu (spd->ino), > + spd->nsize, > + name); > + > + sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); > + break; > + } > + > + case JFFS2_NODETYPE_XATTR : { > + struct jffs2_sum_xattr_flash *spx; > + spx = sp; > + printf ("%14s Xattr offset 0x%08x, totlen 0x%08x, version %5d, #xid %8d\n", > + "", > + je32_to_cpu (spx->offset), > + je32_to_cpu (spx->totlen), > + je32_to_cpu (spx->version), > + je32_to_cpu (spx->xid)); > + sp += JFFS2_SUMMARY_XATTR_SIZE; > + break; > + } > + > + case JFFS2_NODETYPE_XREF : { > + struct jffs2_sum_xref_flash *spr; > + spr = sp; > + printf ("%14s Xref offset 0x%08x\n", > + "", > + je32_to_cpu (spr->offset)); > + sp += JFFS2_SUMMARY_XREF_SIZE; > + break; > + } > + > + default : > + printf("Unknown summary node!\n"); > + break; > + } > + } > + > + sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); > + > + printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", > + "", > + je32_to_cpu(sm->offset), > + je32_to_cpu(sm->magic), > + je32_to_cpu(node->s.padded)); > + } > + > + p += PAD(je32_to_cpu (node->s.totlen)); > + break; > + } > + > + case JFFS2_NODETYPE_CLEANMARKER: > + if (verbose) { > + printf ("%8s Cleanmarker at 0x%08zx, totlen 0x%08x\n", > + obsolete ? "Obsolete" : "", > + p - data, je32_to_cpu (node->u.totlen)); > + } > + p += PAD(je32_to_cpu (node->u.totlen)); > + break; > + > + case JFFS2_NODETYPE_PADDING: > + if (verbose) { > + printf ("%8s Padding node at 0x%08zx, totlen 0x%08x\n", > + obsolete ? "Obsolete" : "", > + p - data, je32_to_cpu (node->u.totlen)); > + } > + p += PAD(je32_to_cpu (node->u.totlen)); > + break; > + > + case 0xffff: > + p += 4; > + empty += 4; > + break; > + > + default: > + if (verbose) { > + printf ("%8s Unknown node at 0x%08zx, totlen 0x%08x\n", > + obsolete ? "Obsolete" : "", > + p - data, je32_to_cpu (node->u.totlen)); > + } > + p += PAD(je32_to_cpu (node->u.totlen)); > + dirty += PAD(je32_to_cpu (node->u.totlen)); > + > + } > + } > + > + if (verbose) > + printf ("Empty space: %d, dirty space: %d\n", empty, dirty); > +} > + > +/* > + * Convert endianess > + */ > +void do_endianconvert (void) > +{ > + char *p = data; > + union jffs2_node_union *node, newnode; > + int fd, len; > + jint32_t mode; > + uint32_t crc; > + > + fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); > + if (fd < 0) { > + fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); > + return; > + } > + > + while ( p < (data + imglen)) { > + node = (union jffs2_node_union*) p; > + > + /* Skip empty space */ > + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { > + write (fd, p, 4); > + p += 4; > + continue; > + } > + > + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { > + printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); > + newnode.u.magic = cnv_e16 (node->u.magic); > + newnode.u.nodetype = cnv_e16 (node->u.nodetype); > + write (fd, &newnode, 4); > + p += 4; > + continue; > + } > + > + crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); > + if (crc != je32_to_cpu (node->u.hdr_crc)) { > + printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); > + } > + > + switch(je16_to_cpu(node->u.nodetype)) { > + > + case JFFS2_NODETYPE_INODE: > + > + newnode.i.magic = cnv_e16 (node->i.magic); > + newnode.i.nodetype = cnv_e16 (node->i.nodetype); > + newnode.i.totlen = cnv_e32 (node->i.totlen); > + newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > + newnode.i.ino = cnv_e32 (node->i.ino); > + newnode.i.version = cnv_e32 (node->i.version); > + mode.v32 = node->i.mode.m; > + mode = cnv_e32 (mode); > + newnode.i.mode.m = mode.v32; > + newnode.i.uid = cnv_e16 (node->i.uid); > + newnode.i.gid = cnv_e16 (node->i.gid); > + newnode.i.isize = cnv_e32 (node->i.isize); > + newnode.i.atime = cnv_e32 (node->i.atime); > + newnode.i.mtime = cnv_e32 (node->i.mtime); > + newnode.i.ctime = cnv_e32 (node->i.ctime); > + newnode.i.offset = cnv_e32 (node->i.offset); > + newnode.i.csize = cnv_e32 (node->i.csize); > + newnode.i.dsize = cnv_e32 (node->i.dsize); > + newnode.i.compr = node->i.compr; > + newnode.i.usercompr = node->i.usercompr; > + newnode.i.flags = cnv_e16 (node->i.flags); > + if (recalccrc) { > + len = je32_to_cpu(node->i.csize); > + newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len)); > + } else > + newnode.i.data_crc = cnv_e32 (node->i.data_crc); > + > + newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); > + > + write (fd, &newnode, sizeof (struct jffs2_raw_inode)); > + write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); > + > + p += PAD(je32_to_cpu (node->i.totlen)); > + break; > + > + case JFFS2_NODETYPE_DIRENT: > + newnode.d.magic = cnv_e16 (node->d.magic); > + newnode.d.nodetype = cnv_e16 (node->d.nodetype); > + newnode.d.totlen = cnv_e32 (node->d.totlen); > + newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > + newnode.d.pino = cnv_e32 (node->d.pino); > + newnode.d.version = cnv_e32 (node->d.version); > + newnode.d.ino = cnv_e32 (node->d.ino); > + newnode.d.mctime = cnv_e32 (node->d.mctime); > + newnode.d.nsize = node->d.nsize; > + newnode.d.type = node->d.type; > + newnode.d.unused[0] = node->d.unused[0]; > + newnode.d.unused[1] = node->d.unused[1]; > + newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); > + if (recalccrc) > + newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); > + else > + newnode.d.name_crc = cnv_e32 (node->d.name_crc); > + > + write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); > + write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); > + p += PAD(je32_to_cpu (node->d.totlen)); > + break; > + > + case JFFS2_NODETYPE_XATTR: > + newnode.x.magic = cnv_e16 (node->x.magic); > + newnode.x.nodetype = cnv_e16 (node->x.nodetype); > + newnode.x.totlen = cnv_e32 (node->x.totlen); > + newnode.x.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > + newnode.x.xid = cnv_e32 (node->x.xid); > + newnode.x.version = cnv_e32 (node->x.version); > + newnode.x.xprefix = node->x.xprefix; > + newnode.x.name_len = node->x.name_len; > + newnode.x.value_len = cnv_e16 (node->x.value_len); > + if (recalccrc) > + newnode.x.data_crc = cpu_to_e32 (mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1)); > + else > + newnode.x.data_crc = cnv_e32 (node->x.data_crc); > + newnode.x.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xattr) - sizeof (newnode.x.node_crc))); > + > + write (fd, &newnode, sizeof (struct jffs2_raw_xattr)); > + write (fd, p + sizeof (struct jffs2_raw_xattr), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_xattr))); > + p += PAD(je32_to_cpu (node->x.totlen)); > + break; > + > + case JFFS2_NODETYPE_XREF: > + newnode.r.magic = cnv_e16 (node->r.magic); > + newnode.r.nodetype = cnv_e16 (node->r.nodetype); > + newnode.r.totlen = cnv_e32 (node->r.totlen); > + newnode.r.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - sizeof (newnode.r.hdr_crc))); > + newnode.r.ino = cnv_e32 (node->r.ino); > + newnode.r.xid = cnv_e32 (node->r.xid); > + newnode.r.xseqno = cnv_e32 (node->r.xseqno); > + newnode.r.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xref) - sizeof (newnode.r.node_crc))); > + p += PAD(je32_to_cpu (node->x.totlen)); > + break; > + > + case JFFS2_NODETYPE_CLEANMARKER: > + case JFFS2_NODETYPE_PADDING: > + newnode.u.magic = cnv_e16 (node->u.magic); > + newnode.u.nodetype = cnv_e16 (node->u.nodetype); > + newnode.u.totlen = cnv_e32 (node->u.totlen); > + newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > + > + write (fd, &newnode, sizeof (struct jffs2_unknown_node)); > + len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); > + if (len > 0) > + write (fd, p + sizeof (struct jffs2_unknown_node), len); > + > + p += PAD(je32_to_cpu (node->u.totlen)); > + break; > + > + case JFFS2_NODETYPE_SUMMARY : { > + struct jffs2_sum_marker *sm_ptr; > + int i,sum_len; > + int counter = 0; > + > + newnode.s.magic = cnv_e16 (node->s.magic); > + newnode.s.nodetype = cnv_e16 (node->s.nodetype); > + newnode.s.totlen = cnv_e32 (node->s.totlen); > + newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); > + newnode.s.sum_num = cnv_e32 (node->s.sum_num); > + newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); > + newnode.s.padded = cnv_e32 (node->s.padded); > + > + newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); > + > + // summary header > + p += sizeof (struct jffs2_raw_summary); > + > + // summary data > + sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); > + > + for (i=0; i<je32_to_cpu (node->s.sum_num); i++) { > + union jffs2_sum_flash *fl_ptr; > + > + fl_ptr = (union jffs2_sum_flash *) p; > + > + switch (je16_to_cpu (fl_ptr->u.nodetype)) { > + case JFFS2_NODETYPE_INODE: > + > + fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); > + fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); > + fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); > + fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); > + fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); > + p += sizeof (struct jffs2_sum_inode_flash); > + counter += sizeof (struct jffs2_sum_inode_flash); > + break; > + > + case JFFS2_NODETYPE_DIRENT: > + fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); > + fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); > + fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); > + fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); > + fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); > + fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); > + p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; > + counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; > + break; > + > + case JFFS2_NODETYPE_XATTR: > + fl_ptr->x.nodetype = cnv_e16 (fl_ptr->x.nodetype); > + fl_ptr->x.xid = cnv_e32 (fl_ptr->x.xid); > + fl_ptr->x.version = cnv_e32 (fl_ptr->x.version); > + fl_ptr->x.offset = cnv_e32 (fl_ptr->x.offset); > + fl_ptr->x.totlen = cnv_e32 (fl_ptr->x.totlen); > + p += sizeof (struct jffs2_sum_xattr_flash); > + counter += sizeof (struct jffs2_sum_xattr_flash); > + break; > + > + case JFFS2_NODETYPE_XREF: > + fl_ptr->r.nodetype = cnv_e16 (fl_ptr->r.nodetype); > + fl_ptr->r.offset = cnv_e32 (fl_ptr->r.offset); > + p += sizeof (struct jffs2_sum_xref_flash); > + counter += sizeof (struct jffs2_sum_xref_flash); > + break; > + > + default : > + printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); > + exit(EXIT_FAILURE); > + break; > + } > + > + } > + > + //pad > + p += sum_len - counter; > + > + // summary marker > + sm_ptr = (struct jffs2_sum_marker *) p; > + sm_ptr->offset = cnv_e32 (sm_ptr->offset); > + sm_ptr->magic = cnv_e32 (sm_ptr->magic); > + p += sizeof (struct jffs2_sum_marker); > + > + // generate new crc on sum data > + newnode.s.sum_crc = cpu_to_e32 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), > + je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); > + > + // write out new node header > + write(fd, &newnode, sizeof (struct jffs2_raw_summary)); > + // write out new summary data > + write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); > + > + break; > + } > + > + case 0xffff: > + write (fd, p, 4); > + p += 4; > + break; > + > + default: > + printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); > + p += PAD(je32_to_cpu (node->u.totlen)); > + > + } > + } > + > + close (fd); > + > +} > + > +/* > + * Main program > + */ > +int main(int argc, char **argv) > +{ > + int fd; > + > + process_options(argc, argv); > + > + /* Open the input file */ > + if ((fd = open(img, O_RDONLY)) == -1) { > + perror("open input file"); > + exit(1); > + } > + > + // get image length > + imglen = lseek(fd, 0, SEEK_END); > + lseek (fd, 0, SEEK_SET); > + > + data = malloc (imglen); > + if (!data) { > + perror("out of memory"); > + close (fd); > + exit(1); > + } > + > + if (datsize && oobsize) { > + int idx = 0; > + long len = imglen; > + uint8_t oob[oobsize]; > + printf ("Peeling data out of combined data/oob image\n"); > + while (len) { > + // read image data > + read (fd, &data[idx], datsize); > + read (fd, oob, oobsize); > + idx += datsize; > + imglen -= oobsize; > + len -= datsize + oobsize; > + } > + > + } else { > + // read image data > + read (fd, data, imglen); > + } > + // Close the input file > + close(fd); > + > + if (dumpcontent) > + do_dumpcontent (); > + > + if (convertendian) > + do_endianconvert (); > + > + // free memory > + free (data); > + > + // Return happy > + exit (0); > +} > diff --git a/jffsX-utils/jffs2reader.c b/jffsX-utils/jffs2reader.c > new file mode 100644 > index 0000000..a62da9a > --- /dev/null > +++ b/jffsX-utils/jffs2reader.c > @@ -0,0 +1,918 @@ > +/* 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 > + */ > + > + > +/* > +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. > + */ > + > +#define PROGRAM_NAME "jffs2reader" > + > +#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 <dirent.h> > +#include <zlib.h> > + > +#include "mtd/jffs2-user.h" > +#include "common.h" > + > +#define SCRATCH_SIZE (5*1024*1024) > + > +/* macro to avoid "lvalue required as left operand of assignment" error */ > +#define ADD_BYTES(p, n) ((p) = (typeof(p))((char *)(p) + (n))) > + > +#define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0) > +#define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0) > + > +struct dir { > + struct dir *next; > + uint8_t type; > + uint8_t nsize; > + uint32_t ino; > + char name[256]; > +}; > + > +int target_endian = __BYTE_ORDER; > + > +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, const char *path, > + int recurse, int want_ctime); > +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, const char *, > + uint32_t *, int); > +struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *, > + uint32_t *); > + > +void lsdir(char *, size_t, const char *, int, 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 = je32_to_cpu(n->dsize); > + > + if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize) > + errmsg_die("File does not fit into buffer!"); > + > + if (*rsize < je32_to_cpu(n->isize)) > + bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize); > + > + switch (n->compr) { > + case JFFS2_COMPR_ZLIB: > + uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen, > + (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), > + (uLongf) je32_to_cpu(n->csize)); > + break; > + > + case JFFS2_COMPR_NONE: > + memcpy(b + je32_to_cpu(n->offset), > + ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); > + break; > + > + case JFFS2_COMPR_ZERO: > + bzero(b + je32_to_cpu(n->offset), dlen); > + break; > + > + /* [DYN]RUBIN support required! */ > + > + default: > + errmsg_die("Unsupported compression method!"); > + } > + > + *rsize = je32_to_cpu(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 (je32_to_cpu(n->ino)) { > + if (dd == NULL) { > + d = xmalloc(sizeof(struct dir)); > + d->type = n->type; > + memcpy(d->name, n->name, n->nsize); > + d->nsize = n->nsize; > + d->ino = je32_to_cpu(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 = je32_to_cpu(n->ino); > + > + return o; > + } > + > + if (dd->next == NULL) { > + dd->next = xmalloc(sizeof(struct dir)); > + dd->next->type = n->type; > + memcpy(dd->next->name, n->name, n->nsize); > + dd->next->nsize = n->nsize; > + dd->next->ino = je32_to_cpu(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, const char *path, int recurse, > + int want_ctime) > +{ > + char m; > + char *filetime; > + time_t age; > + struct jffs2_raw_inode *ri; > + jint32_t mode; > + > + 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) { > + warnmsg("bug: raw_inode missing!"); > + d = d->next; > + continue; > + } > + > + filetime = ctime((const time_t *) &(ri->ctime)); > + age = time(NULL) - je32_to_cpu(ri->ctime); > + mode.v32 = ri->mode.m; > + printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)), > + 1, je16_to_cpu(ri->uid), je16_to_cpu(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 ", major(rdev), minor(rdev)); > + } else { > + printf("%9ld ", (long)je32_to_cpu(ri->dsize)); > + } > + d->name[d->nsize]='\0'; > + if (want_ctime) { > + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) > + /* hh:mm if less than 6 months old */ > + printf("%6.6s %5.5s ", filetime + 4, filetime + 11); > + else > + printf("%6.6s %4.4s ", filetime + 4, filetime + 20); > + } > + printf("%s/%s%c", 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 = xmalloc(BUFSIZ); > + sprintf(tmp, "%s/%s", path, d->name); > + lsdir(o, size, tmp, recurse, want_ctime); /* 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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) > + ADD_BYTES(n, 4); > + > + if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { > + if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE && > + je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(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)); > + } > + > + ADD_BYTES(n, ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) > + ADD_BYTES(n, 4); > + > + if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { > + if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && > + je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(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); > + } > + } > + > + ADD_BYTES(n, ((je32_to_cpu(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) + > + ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) > + ADD_BYTES(n, 4); > + > + if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { > + if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && > + (!ino || je32_to_cpu(n->d.ino) == ino) && > + (v = je32_to_cpu(n->d.version)) > vmax && > + (!pino || (je32_to_cpu(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); > + } > + } > + > + ADD_BYTES(n, ((je32_to_cpu(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, > + const 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 = xstrdup(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, > + const 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, const char *path, int recurse, int want_ctime) > +{ > + 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)) > + errmsg_die("%s: No such file or directory", path); > + > + d = collectdir(o, size, ino, d); > + printdir(o, size, d, path, recurse, want_ctime); > + 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) > + errmsg_die("%s: No such file or directory", path); > + > + if (dd == NULL || dd->type != DT_REG) > + errmsg_die("%s: Not a regular file", path); > + > + 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, want_ctime = 0; > + struct stat st; > + > + char *scratch, *dir = NULL, *file = NULL; > + size_t ssize = 0; > + > + char *buf; > + > + while ((opt = getopt(argc, argv, "rd:f:t")) > 0) { > + switch (opt) { > + case 'd': > + dir = optarg; > + break; > + case 'f': > + file = optarg; > + break; > + case 'r': > + recurse++; > + break; > + case 't': > + want_ctime++; > + break; > + default: > + fprintf(stderr, > + "Usage: %s <image> [-d|-f] < path >\n", > + PROGRAM_NAME); > + exit(EXIT_FAILURE); > + } > + } > + > + fd = open(argv[optind], O_RDONLY); > + if (fd == -1) > + sys_errmsg_die("%s", argv[optind]); > + > + if (fstat(fd, &st)) > + sys_errmsg_die("%s", argv[optind]); > + > + buf = xmalloc((size_t) st.st_size); > + > + if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) > + sys_errmsg_die("%s", argv[optind]); > + > + if (dir) > + lsdir(buf, st.st_size, dir, recurse, want_ctime); > + > + if (file) { > + scratch = xmalloc(SCRATCH_SIZE); > + > + catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); > + free(scratch); > + } > + > + if (!dir && !file) > + lsdir(buf, st.st_size, "/", 1, want_ctime); > + > + > + free(buf); > + exit(EXIT_SUCCESS); > +} > diff --git a/jffsX-utils/mkfs.jffs2.1 b/jffsX-utils/mkfs.jffs2.1 > new file mode 100644 > index 0000000..7c57ddc > --- /dev/null > +++ b/jffsX-utils/mkfs.jffs2.1 > @@ -0,0 +1,268 @@ > +.TH MKFS.JFFS2 1 > +.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 --with-xattr > +] > +[ > +.B --with-selinux > +] > +[ > +.B --with-posix-acl > +] > +[ > +.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 available 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. Set according to target system's memory > +management page size (NOTE: this is NOT related to NAND page size). > +.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 --with-xattr > +Enables xattr, stuff all xattr entries into jffs2 image file. > +.TP > +.B --with-selinux > +Enables xattr, stuff only SELinux Labels into jffs2 image file. > +.TP > +.B --with-posix-acl > +Enable xattr, stuff only POSIX ACL entries into jffs2 image file. > +.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 available compressors and their default states. > +.TP > +.B -X, --enable-compressor=NAME > +Enable a compressor. Use > +.B -L > +to see the list of the available 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 available compressors and their default priority. > +Priorities are used by priority compression mode. > +.TP > +.B -L, --list-compressors > +Show the list of the available 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 LIMITATIONS > +The format and grammar of the device table file does not allow it to > +create symbolic links when the symbolic links are not already present > +in the root working directory. > + > +However, symbolic links may be specified in the device table file > +using the \fIl\fR type for the purposes of setting their permissions > +and ownership. > +.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/jffsX-utils/mkfs.jffs2.c b/jffsX-utils/mkfs.jffs2.c > new file mode 100644 > index 0000000..f09c0b2 > --- /dev/null > +++ b/jffsX-utils/mkfs.jffs2.c > @@ -0,0 +1,1805 @@ > +/* 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 > + * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> > + * > + * 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 PROGRAM_NAME "mkfs.jffs2" > + > +#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> > +#ifndef WITHOUT_XATTR > +#include <sys/xattr.h> > +#include <sys/acl.h> > +#endif > +#include <byteswap.h> > +#include <crc32.h> > +#include <inttypes.h> > + > +#include "rbtree.h" > +#include "common.h" > + > +/* Do not use the weird XPG version of basename */ > +#undef basename > + > +//#define DMALLOC > +//#define mkfs_debug_msg errmsg > +#define mkfs_debug_msg(a...) { } > + > +#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 */ > + uint32_t ino; /* Inode number of this file in JFFS2 */ > + 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 */ > + struct rb_node hardlink_rb; > +}; > + > +struct rb_root hardlinks; > +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; > + > +uint32_t find_hardlink(struct filesystem_entry *e) > +{ > + struct filesystem_entry *f; > + struct rb_node **n = &hardlinks.rb_node; > + struct rb_node *parent = NULL; > + > + while (*n) { > + parent = *n; > + f = rb_entry(parent, struct filesystem_entry, hardlink_rb); > + > + if ((f->sb.st_dev < e->sb.st_dev) || > + (f->sb.st_dev == e->sb.st_dev && > + f->sb.st_ino < e->sb.st_ino)) > + n = &parent->rb_left; > + else if ((f->sb.st_dev > e->sb.st_dev) || > + (f->sb.st_dev == e->sb.st_dev && > + f->sb.st_ino > e->sb.st_ino)) { > + n = &parent->rb_right; > + } else > + return f->ino; > + } > + > + rb_link_node(&e->hardlink_rb, parent, n); > + rb_insert_color(&e->hardlink_rb, &hardlinks); > + return 0; > +} > + > +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) { > + sys_errmsg("%s:%s", PROGRAM_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) > + sys_errmsg_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)) { > + /* If this is the first call, and we actually want this > + * directory, then return it now */ > + if (strcmp(fullname, e->fullname) == 0) > + return e; > + > + e = dir->files; > + } > + while (e) { > + 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(const char *name, > + const 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)) { > + errmsg_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) { > + errmsg_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_ino = sb.st_ino; > + entry->sb.st_dev = sb.st_dev; > + entry->sb.st_nlink = sb.st_nlink; > + > + 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, const char *targetpath, > + const char *hostpath) > +{ > + int i, n; > + struct stat sb; > + char *hpath, *tpath; > + struct dirent *dp, **namelist; > + struct filesystem_entry *entry; > + > + > + if (lstat(hostpath, &sb)) { > + sys_errmsg_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) { > + sys_errmsg_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; > + } > + > + xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); > + if (lstat(hpath, &sb)) { > + sys_errmsg_die("%s", hpath); > + } > + if (strcmp(targetpath, "/") == 0) { > + xasprintf(&tpath, "%s%s", targetpath, dp->d_name); > + } else { > + xasprintf(&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: > + errmsg("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 = xmalloc(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, "/")) { > + errmsg_die("Device table entries require absolute paths"); > + } > + > + xasprintf(&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; > + case 'l': > + mode |= S_IFLNK; > + break; > + default: > + errmsg_die("Unsupported file type '%c'", type); > + } > + entry = find_filesystem_entry(root, name, mode); > + if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { > + /* 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 = xstrdup(name); > + dir = dirname(tmp); > + parent = find_filesystem_entry(root, dir, S_IFDIR); > + free(tmp); > + if (parent == NULL) { > + errmsg ("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 < (start + count); i++) { > + xasprintf(&dname, "%s%lu", name, i); > + xasprintf(&hpath, "%s/%s%lu", rootdir, name, i); > + rdev = makedev(major, minor + (i - start) * increment); > + 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: > + errmsg_die("Unsupported file type '%c'", 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_cleanmarkers = 1; > +static struct jffs2_unknown_node cleanmarker; > +static int cleanmarker_size = sizeof(cleanmarker); > +static unsigned char ffbuf[16] = > +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, > + 0xff, 0xff, 0xff, 0xff, 0xff > +}; > + > +/* We set this at start of main() using sysconf(), -1 means we don't know */ > +/* When building an fs for non-native systems, use --pagesize=SIZE option */ > +int page_size = -1; > + > +#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) > + sys_errmsg_die("write"); > + > + if (ret == 0) > + sys_errmsg_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_cleanmarkers) { > + if ((out_ofs % erase_block_size) == 0) { > + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); > + pad(cleanmarker_size - sizeof(cleanmarker)); > + padword(); > + } > + } > + if ((out_ofs % erase_block_size) + req > erase_block_size) { > + padblock(); > + } > + if (add_cleanmarkers) { > + if ((out_ofs % erase_block_size) == 0) { > + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); > + pad(cleanmarker_size - sizeof(cleanmarker)); > + 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(mtd_crc32(0, &rd, > + sizeof(struct jffs2_unknown_node) - 4)); > + rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); > + rd.version = cpu_to_je32(version++); > + rd.ino = cpu_to_je32(e->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(mtd_crc32(0, &rd, sizeof(rd) - 8)); > + rd.name_crc = cpu_to_je32(mtd_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 unsigned int 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; > + unsigned int totcomp = 0; > + > + statbuf = &(e->sb); > + if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { > + errmsg("Skipping file \"%s\" too large.", e->path); > + return -1; > + } > + fd = open(e->hostname, O_RDONLY); > + if (fd == -1) { > + sys_errmsg_die("%s: open file", e->hostname); > + } > + > + e->ino = ++ino; > + mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", > + e->name, (unsigned long) e->ino, > + (unsigned long) e->parent->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(e->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) { > + sys_errmsg_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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > + ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); > + > + full_write(out_fd, &ri, sizeof(ri)); > + totcomp += sizeof(ri); > + full_write(out_fd, wbuf, space); > + totcomp += 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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > + > + full_write(out_fd, &ri, sizeof(ri)); > + padword(); > + } > + free(buf); > + close(fd); > + return totcomp; > +} > + > +static void write_symlink(struct filesystem_entry *e) > +{ > + int len; > + struct stat *statbuf; > + struct jffs2_raw_inode ri; > + > + statbuf = &(e->sb); > + e->ino = ++ino; > + mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", > + e->name, (unsigned long) e->ino, > + (unsigned long) e->parent->ino); > + write_dirent(e); > + > + len = strlen(e->link); > + if (len > JFFS2_MAX_SYMLINK_LEN) { > + errmsg("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(mtd_crc32(0, > + &ri, sizeof(struct jffs2_unknown_node) - 4)); > + > + ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > + ri.data_crc = cpu_to_je32(mtd_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); > + e->ino = ++ino; > + if (S_ISDIR(statbuf->st_mode)) { > + mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", > + e->name, (unsigned long) e->ino, > + (unsigned long) (e->parent) ? e->parent->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(mtd_crc32(0, > + &ri, sizeof(struct jffs2_unknown_node) - 4)); > + > + ri.ino = cpu_to_je32(e->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(mtd_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); > + e->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(mtd_crc32(0, > + &ri, sizeof(struct jffs2_unknown_node) - 4)); > + > + ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > + ri.data_crc = cpu_to_je32(mtd_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(); > +} > + > +#ifndef WITHOUT_XATTR > +typedef struct xattr_entry { > + struct xattr_entry *next; > + uint32_t xid; > + int xprefix; > + char *xname; > + char *xvalue; > + int name_len; > + int value_len; > +} xattr_entry_t; > + > +#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ > +static uint32_t enable_xattr = 0; > +static uint32_t highest_xid = 0; > +static uint32_t highest_xseqno = 0; > + > +static struct { > + int xprefix; > + const char *string; > + int length; > +} xprefix_tbl[] = { > + { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, > + { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, > + { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, > + { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, > + { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, > + { 0, NULL, 0 } > +}; > + > +static void formalize_posix_acl(void *xvalue, int *value_len) > +{ > + struct posix_acl_xattr_header *pacl_header; > + struct posix_acl_xattr_entry *pent, *plim; > + struct jffs2_acl_header *jacl_header; > + struct jffs2_acl_entry *jent; > + struct jffs2_acl_entry_short *jent_s; > + char buffer[XATTR_BUFFER_SIZE]; > + int offset = 0; > + > + pacl_header = xvalue;; > + pent = pacl_header->a_entries; > + plim = xvalue + *value_len; > + > + jacl_header = (struct jffs2_acl_header *)buffer; > + offset += sizeof(struct jffs2_acl_header); > + jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); > + > + while (pent < plim) { > + switch(le16_to_cpu(pent->e_tag)) { > + case ACL_USER_OBJ: > + case ACL_GROUP_OBJ: > + case ACL_MASK: > + case ACL_OTHER: > + jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); > + offset += sizeof(struct jffs2_acl_entry_short); > + jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); > + jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); > + break; > + case ACL_USER: > + case ACL_GROUP: > + jent = (struct jffs2_acl_entry *)(buffer + offset); > + offset += sizeof(struct jffs2_acl_entry); > + jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); > + jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); > + jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); > + break; > + default: > + printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); > + exit(1); > + } > + pent++; > + } > + if (offset > *value_len) { > + printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", > + offset, *value_len); > + exit(1); > + } > + memcpy(xvalue, buffer, offset); > + *value_len = offset; > +} > + > +static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) > +{ > + xattr_entry_t *xe; > + struct jffs2_raw_xattr rx; > + int name_len; > + > + /* create xattr entry */ > + name_len = strlen(xname); > + xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); > + xe->next = NULL; > + xe->xid = ++highest_xid; > + xe->xprefix = xprefix; > + xe->xname = ((char *)xe) + sizeof(xattr_entry_t); > + xe->xvalue = xe->xname + name_len + 1; > + xe->name_len = name_len; > + xe->value_len = value_len; > + strcpy(xe->xname, xname); > + memcpy(xe->xvalue, xvalue, value_len); > + > + /* write xattr node */ > + memset(&rx, 0, sizeof(rx)); > + rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); > + rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); > + rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); > + rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); > + > + rx.xid = cpu_to_je32(xe->xid); > + rx.version = cpu_to_je32(1); /* initial version */ > + rx.xprefix = xprefix; > + rx.name_len = xe->name_len; > + rx.value_len = cpu_to_je16(xe->value_len); > + rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); > + rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4)); > + > + pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); > + full_write(out_fd, &rx, sizeof(rx)); > + full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); > + padword(); > + > + return xe; > +} > + > +#define XATTRENTRY_HASHSIZE 57 > +static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) > +{ > + static xattr_entry_t **xentry_hash = NULL; > + xattr_entry_t *xe; > + int index, name_len; > + > + /* create hash table */ > + if (!xentry_hash) > + xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); > + > + if (xprefix == JFFS2_XPREFIX_ACL_ACCESS > + || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) > + formalize_posix_acl(xvalue, &value_len); > + > + name_len = strlen(xname); > + index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; > + for (xe = xentry_hash[index]; xe; xe = xe->next) { > + if (xe->xprefix == xprefix > + && xe->value_len == value_len > + && !strcmp(xe->xname, xname) > + && !memcmp(xe->xvalue, xvalue, value_len)) > + break; > + } > + if (!xe) { > + xe = create_xattr_entry(xprefix, xname, xvalue, value_len); > + xe->next = xentry_hash[index]; > + xentry_hash[index] = xe; > + } > + return xe; > +} > + > +static void write_xattr_entry(struct filesystem_entry *e) > +{ > + struct jffs2_raw_xref ref; > + struct xattr_entry *xe; > + char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; > + char *xname; > + const char *prefix_str; > + int i, xprefix, prefix_len; > + int list_sz, offset, name_len, value_len; > + > + if (!enable_xattr) > + return; > + > + list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); > + if (list_sz < 0) { > + if (verbose) > + printf("llistxattr('%s') = %d : %s\n", > + e->hostname, errno, strerror(errno)); > + return; > + } > + > + for (offset = 0; offset < list_sz; offset += name_len) { > + xname = xlist + offset; > + name_len = strlen(xname) + 1; > + > + for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { > + prefix_str = xprefix_tbl[i].string; > + prefix_len = xprefix_tbl[i].length; > + if (prefix_str[prefix_len - 1] == '.') { > + if (!strncmp(xname, prefix_str, prefix_len - 1)) > + break; > + } else { > + if (!strcmp(xname, prefix_str)) > + break; > + } > + } > + if (!xprefix) { > + if (verbose) > + printf("%s: xattr '%s' is not supported.\n", > + e->hostname, xname); > + continue; > + } > + if ((enable_xattr & (1 << xprefix)) == 0) > + continue; > + > + value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); > + if (value_len < 0) { > + if (verbose) > + printf("lgetxattr('%s', '%s') = %d : %s\n", > + e->hostname, xname, errno, strerror(errno)); > + continue; > + } > + xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); > + if (!xe) { > + if (verbose) > + printf("%s : xattr '%s' was ignored.\n", > + e->hostname, xname); > + continue; > + } > + > + memset(&ref, 0, sizeof(ref)); > + ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); > + ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); > + ref.totlen = cpu_to_je32(sizeof(ref)); > + ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); > + ref.ino = cpu_to_je32(e->ino); > + ref.xid = cpu_to_je32(xe->xid); > + ref.xseqno = cpu_to_je32(highest_xseqno += 2); > + ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4)); > + > + pad_block_if_less_than(sizeof(ref)); > + full_write(out_fd, &ref, sizeof(ref)); > + padword(); > + } > +} > + > +#else /* WITHOUT_XATTR */ > +#define write_xattr_entry(x) > +#endif > + > +static void recursive_populate_directory(struct filesystem_entry *dir) > +{ > + struct filesystem_entry *e; > + unsigned int wrote; > + > + if (verbose) { > + printf("%s\n", dir->fullname); > + } > + write_xattr_entry(dir); /* for '/' */ > + > + e = dir->files; > + while (e) { > + if (e->sb.st_nlink >= 1 && > + (e->ino = find_hardlink(e))) { > + > + write_dirent(e); > + if (verbose) { > + printf("\tL %04o %9lu %5d:%-3d %s\n", > + e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, > + (int) (e->sb.st_uid), (int) (e->sb.st_gid), > + e->name); > + } > + } else switch (e->sb.st_mode & S_IFMT) { > + case S_IFDIR: > + if (verbose) { > + printf("\td %04o %9" PRIdoff_t " %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); > + write_xattr_entry(e); > + break; > + case S_IFSOCK: > + if (verbose) { > + printf("\ts %04o %9" PRIdoff_t " %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); > + write_xattr_entry(e); > + break; > + case S_IFIFO: > + if (verbose) { > + printf("\tp %04o %9" PRIdoff_t " %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); > + write_xattr_entry(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); > + write_xattr_entry(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); > + write_xattr_entry(e); > + break; > + case S_IFLNK: > + if (verbose) { > + printf("\tl %04o %9" PRIdoff_t " %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); > + write_xattr_entry(e); > + break; > + case S_IFREG: > + wrote = write_regular_file(e); > + write_xattr_entry(e); > + if (verbose) { > + printf("\tf %04o %9" PRIdoff_t " (%9u) %5d:%-3d %s\n", > + e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, > + (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); > + } > + break; > + default: > + errmsg("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) > +{ > + 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(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); > + > + if (ino == 0) > + ino = 1; > + > + root->ino = 1; > + recursive_populate_directory(root); > + > + if (pad_fs_size == -1) { > + padblock(); > + } else { > + if (pad_fs_size && add_cleanmarkers){ > + padblock(); > + while (out_ofs < pad_fs_size) { > + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); > + pad(cleanmarker_size - sizeof(cleanmarker)); > + 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-cleanmarkers", 0, NULL, 'n'}, > + {"cleanmarker", 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'}, > + {"enable-compressor", 1, NULL, 'X'}, > + {"test-compression", 0, NULL, 't'}, > + {"compressor-priority", 1, NULL, 'y'}, > + {"incremental", 1, NULL, 'i'}, > +#ifndef WITHOUT_XATTR > + {"with-xattr", 0, NULL, 1000 }, > + {"with-selinux", 0, NULL, 1001 }, > + {"with-posix-acl", 0, NULL, 1002 }, > +#endif > + {NULL, 0, NULL, 0} > +}; > + > +static const 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.\n" > +" Set according to target system's memory management\n" > +" page size (default: 4KiB)\n" > +" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" > +" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" > +" -m, --compr-mode=MODE Select compression mode (default: priority)\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 available compressors\n" > +" -t, --test-compression Call decompress and compare with the original (for test)\n" > +" -n, --no-cleanmarkers Don't add a cleanmarker 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" > +#ifndef WITHOUT_XATTR > +" --with-xattr stuff all xattr entries into image\n" > +" --with-selinux stuff only SELinux Labels into jffs2 image\n" > +" --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" > +#endif > +" -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 const char revtext[] = "1.60"; > + > +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%08zx, 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%08zx, 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%08zx, 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%08zx, 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_PADDING: > + if (verbose) { > + printf ("%8s Padding node at 0x%08zx, 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%08zx, 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 = xmalloc(erase_block_size); > + > + 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; > + int warn_page_size = 0; > + > + page_size = sysconf(_SC_PAGESIZE); > + if (page_size < 0) /* System doesn't know so ... */ > + page_size = 4096; /* ... we make an educated guess */ > + if (page_size != 4096) > + warn_page_size = 1; /* warn user if page size not 4096 */ > + > + 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) > + sys_errmsg_die("%s", optarg); > + if (sb.st_size < 10) > + errmsg_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) { > + errmsg_die("root directory specified more than once"); > + } > + rootdir = xstrdup(optarg); > + break; > + > + case 's': > + page_size = strtol(optarg, NULL, 0); > + warn_page_size = 0; /* set by user, so don't need to warn */ > + break; > + > + case 'o': > + if (out_fd != -1) { > + errmsg_die("output filename specified more than once"); > + } > + out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); > + if (out_fd == -1) { > + sys_errmsg_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 '?': > + errmsg_die("%s", helptext); > + > + case 'v': > + verbose = 1; > + break; > + > + case 'V': > + errmsg_die("revision %s\n", revtext); > + > + case 'e': { > + char *next; > + unsigned units = 0; > + erase_block_size = strtol(optarg, &next, 0); > + if (!erase_block_size) > + errmsg_die("Unrecognisable erase size\n"); > + > + if (*next) { > + if (!strcmp(next, "KiB")) { > + units = 1024; > + } else if (!strcmp(next, "MiB")) { > + units = 1024 * 1024; > + } else { > + errmsg_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_cleanmarkers = 0; > + break; > + case 'c': > + cleanmarker_size = strtol(optarg, NULL, 0); > + if (cleanmarker_size < sizeof(cleanmarker)) { > + errmsg_die("cleanmarker size must be >= 12"); > + } > + if (cleanmarker_size >= erase_block_size) { > + errmsg_die("cleanmarker size must be < eraseblock size"); > + } > + break; > + case 'm': > + if (jffs2_set_compression_mode_name(optarg)) { > + errmsg_die("Unknown compression mode %s", optarg); > + } > + break; > + case 'x': > + if (jffs2_disable_compressor_name(optarg)) { > + errmsg_die("Unknown compressor name %s",optarg); > + } > + break; > + case 'X': > + if (jffs2_enable_compressor_name(optarg)) { > + errmsg_die("Unknown compressor name %s",optarg); > + } > + break; > + case 'L': > + errmsg_die("\n%s",jffs2_list_compressors()); > + break; > + case 't': > + jffs2_compression_check_set(1); > + break; > + case 'y': > + compr_name = xmalloc(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 { > + errmsg_die("Cannot parse %s",optarg); > + } > + free(compr_name); > + break; > + case 'i': > + if (in_fd != -1) { > + errmsg_die("(incremental) filename specified more than once"); > + } > + in_fd = open(optarg, O_RDONLY); > + if (in_fd == -1) { > + sys_errmsg_die("cannot open (incremental) file"); > + } > + break; > +#ifndef WITHOUT_XATTR > + case 1000: /* --with-xattr */ > + enable_xattr |= (1 << JFFS2_XPREFIX_USER) > + | (1 << JFFS2_XPREFIX_SECURITY) > + | (1 << JFFS2_XPREFIX_ACL_ACCESS) > + | (1 << JFFS2_XPREFIX_ACL_DEFAULT) > + | (1 << JFFS2_XPREFIX_TRUSTED); > + break; > + case 1001: /* --with-selinux */ > + enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); > + break; > + case 1002: /* --with-posix-acl */ > + enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) > + | (1 << JFFS2_XPREFIX_ACL_DEFAULT); > + break; > +#endif > + } > + } > + if (warn_page_size) { > + errmsg("Page size for this system is by default %d", page_size); > + errmsg("Use the --pagesize=SIZE option if this is not what you want"); > + } > + if (out_fd == -1) { > + if (isatty(1)) { > + errmsg_die("%s", helptext); > + } > + out_fd = 1; > + } > + if (lstat(rootdir, &sb)) { > + sys_errmsg_die("%s", rootdir); > + } > + if (chdir(rootdir)) > + sys_errmsg_die("%s", rootdir); > + > + if (!(cwd = getcwd(0, GETCWD_SIZE))) > + sys_errmsg_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/jffsX-utils/rbtree.c b/jffsX-utils/rbtree.c > new file mode 100644 > index 0000000..329e098 > --- /dev/null > +++ b/jffsX-utils/rbtree.c > @@ -0,0 +1,390 @@ > +/* > + Red Black Trees > + (C) 1999 Andrea Arcangeli <andrea@suse.de> > + (C) 2002 David Woodhouse <dwmw2@infradead.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. > + > + 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 > + > + linux/lib/rbtree.c > +*/ > + > +#include <stdlib.h> > +#include "rbtree.h" > + > +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) > +{ > + struct rb_node *right = node->rb_right; > + struct rb_node *parent = rb_parent(node); > + > + if ((node->rb_right = right->rb_left)) > + rb_set_parent(right->rb_left, node); > + right->rb_left = node; > + > + rb_set_parent(right, parent); > + > + if (parent) > + { > + if (node == parent->rb_left) > + parent->rb_left = right; > + else > + parent->rb_right = right; > + } > + else > + root->rb_node = right; > + rb_set_parent(node, right); > +} > + > +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) > +{ > + struct rb_node *left = node->rb_left; > + struct rb_node *parent = rb_parent(node); > + > + if ((node->rb_left = left->rb_right)) > + rb_set_parent(left->rb_right, node); > + left->rb_right = node; > + > + rb_set_parent(left, parent); > + > + if (parent) > + { > + if (node == parent->rb_right) > + parent->rb_right = left; > + else > + parent->rb_left = left; > + } > + else > + root->rb_node = left; > + rb_set_parent(node, left); > +} > + > +void rb_insert_color(struct rb_node *node, struct rb_root *root) > +{ > + struct rb_node *parent, *gparent; > + > + while ((parent = rb_parent(node)) && rb_is_red(parent)) > + { > + gparent = rb_parent(parent); > + > + if (parent == gparent->rb_left) > + { > + { > + register struct rb_node *uncle = gparent->rb_right; > + if (uncle && rb_is_red(uncle)) > + { > + rb_set_black(uncle); > + rb_set_black(parent); > + rb_set_red(gparent); > + node = gparent; > + continue; > + } > + } > + > + if (parent->rb_right == node) > + { > + register struct rb_node *tmp; > + __rb_rotate_left(parent, root); > + tmp = parent; > + parent = node; > + node = tmp; > + } > + > + rb_set_black(parent); > + rb_set_red(gparent); > + __rb_rotate_right(gparent, root); > + } else { > + { > + register struct rb_node *uncle = gparent->rb_left; > + if (uncle && rb_is_red(uncle)) > + { > + rb_set_black(uncle); > + rb_set_black(parent); > + rb_set_red(gparent); > + node = gparent; > + continue; > + } > + } > + > + if (parent->rb_left == node) > + { > + register struct rb_node *tmp; > + __rb_rotate_right(parent, root); > + tmp = parent; > + parent = node; > + node = tmp; > + } > + > + rb_set_black(parent); > + rb_set_red(gparent); > + __rb_rotate_left(gparent, root); > + } > + } > + > + rb_set_black(root->rb_node); > +} > + > +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, > + struct rb_root *root) > +{ > + struct rb_node *other; > + > + while ((!node || rb_is_black(node)) && node != root->rb_node) > + { > + if (parent->rb_left == node) > + { > + other = parent->rb_right; > + if (rb_is_red(other)) > + { > + rb_set_black(other); > + rb_set_red(parent); > + __rb_rotate_left(parent, root); > + other = parent->rb_right; > + } > + if ((!other->rb_left || rb_is_black(other->rb_left)) && > + (!other->rb_right || rb_is_black(other->rb_right))) > + { > + rb_set_red(other); > + node = parent; > + parent = rb_parent(node); > + } > + else > + { > + if (!other->rb_right || rb_is_black(other->rb_right)) > + { > + struct rb_node *o_left; > + if ((o_left = other->rb_left)) > + rb_set_black(o_left); > + rb_set_red(other); > + __rb_rotate_right(other, root); > + other = parent->rb_right; > + } > + rb_set_color(other, rb_color(parent)); > + rb_set_black(parent); > + if (other->rb_right) > + rb_set_black(other->rb_right); > + __rb_rotate_left(parent, root); > + node = root->rb_node; > + break; > + } > + } > + else > + { > + other = parent->rb_left; > + if (rb_is_red(other)) > + { > + rb_set_black(other); > + rb_set_red(parent); > + __rb_rotate_right(parent, root); > + other = parent->rb_left; > + } > + if ((!other->rb_left || rb_is_black(other->rb_left)) && > + (!other->rb_right || rb_is_black(other->rb_right))) > + { > + rb_set_red(other); > + node = parent; > + parent = rb_parent(node); > + } > + else > + { > + if (!other->rb_left || rb_is_black(other->rb_left)) > + { > + register struct rb_node *o_right; > + if ((o_right = other->rb_right)) > + rb_set_black(o_right); > + rb_set_red(other); > + __rb_rotate_left(other, root); > + other = parent->rb_left; > + } > + rb_set_color(other, rb_color(parent)); > + rb_set_black(parent); > + if (other->rb_left) > + rb_set_black(other->rb_left); > + __rb_rotate_right(parent, root); > + node = root->rb_node; > + break; > + } > + } > + } > + if (node) > + rb_set_black(node); > +} > + > +void rb_erase(struct rb_node *node, struct rb_root *root) > +{ > + struct rb_node *child, *parent; > + int color; > + > + if (!node->rb_left) > + child = node->rb_right; > + else if (!node->rb_right) > + child = node->rb_left; > + else > + { > + struct rb_node *old = node, *left; > + > + node = node->rb_right; > + while ((left = node->rb_left) != NULL) > + node = left; > + child = node->rb_right; > + parent = rb_parent(node); > + color = rb_color(node); > + > + if (child) > + rb_set_parent(child, parent); > + if (parent == old) { > + parent->rb_right = child; > + parent = node; > + } else > + parent->rb_left = child; > + > + node->rb_parent_color = old->rb_parent_color; > + node->rb_right = old->rb_right; > + node->rb_left = old->rb_left; > + > + if (rb_parent(old)) > + { > + if (rb_parent(old)->rb_left == old) > + rb_parent(old)->rb_left = node; > + else > + rb_parent(old)->rb_right = node; > + } else > + root->rb_node = node; > + > + rb_set_parent(old->rb_left, node); > + if (old->rb_right) > + rb_set_parent(old->rb_right, node); > + goto color; > + } > + > + parent = rb_parent(node); > + color = rb_color(node); > + > + if (child) > + rb_set_parent(child, parent); > + if (parent) > + { > + if (parent->rb_left == node) > + parent->rb_left = child; > + else > + parent->rb_right = child; > + } > + else > + root->rb_node = child; > + > + color: > + if (color == RB_BLACK) > + __rb_erase_color(child, parent, root); > +} > + > +/* > + * This function returns the first node (in sort order) of the tree. > + */ > +struct rb_node *rb_first(struct rb_root *root) > +{ > + struct rb_node *n; > + > + n = root->rb_node; > + if (!n) > + return NULL; > + while (n->rb_left) > + n = n->rb_left; > + return n; > +} > + > +struct rb_node *rb_last(struct rb_root *root) > +{ > + struct rb_node *n; > + > + n = root->rb_node; > + if (!n) > + return NULL; > + while (n->rb_right) > + n = n->rb_right; > + return n; > +} > + > +struct rb_node *rb_next(struct rb_node *node) > +{ > + struct rb_node *parent; > + > + if (rb_parent(node) == node) > + return NULL; > + > + /* If we have a right-hand child, go down and then left as far > + as we can. */ > + if (node->rb_right) { > + node = node->rb_right; > + while (node->rb_left) > + node=node->rb_left; > + return node; > + } > + > + /* No right-hand children. Everything down and left is > + smaller than us, so any 'next' node must be in the general > + direction of our parent. Go up the tree; any time the > + ancestor is a right-hand child of its parent, keep going > + up. First time it's a left-hand child of its parent, said > + parent is our 'next' node. */ > + while ((parent = rb_parent(node)) && node == parent->rb_right) > + node = parent; > + > + return parent; > +} > + > +struct rb_node *rb_prev(struct rb_node *node) > +{ > + struct rb_node *parent; > + > + if (rb_parent(node) == node) > + return NULL; > + > + /* If we have a left-hand child, go down and then right as far > + as we can. */ > + if (node->rb_left) { > + node = node->rb_left; > + while (node->rb_right) > + node=node->rb_right; > + return node; > + } > + > + /* No left-hand children. Go up till we find an ancestor which > + is a right-hand child of its parent */ > + while ((parent = rb_parent(node)) && node == parent->rb_left) > + node = parent; > + > + return parent; > +} > + > +void rb_replace_node(struct rb_node *victim, struct rb_node *new, > + struct rb_root *root) > +{ > + struct rb_node *parent = rb_parent(victim); > + > + /* Set the surrounding nodes to point to the replacement */ > + if (parent) { > + if (victim == parent->rb_left) > + parent->rb_left = new; > + else > + parent->rb_right = new; > + } else { > + root->rb_node = new; > + } > + if (victim->rb_left) > + rb_set_parent(victim->rb_left, new); > + if (victim->rb_right) > + rb_set_parent(victim->rb_right, new); > + > + /* Copy the pointers/colour from the victim to the replacement */ > + *new = *victim; > +} > diff --git a/jffsX-utils/rbtree.h b/jffsX-utils/rbtree.h > new file mode 100644 > index 0000000..0d77b65 > --- /dev/null > +++ b/jffsX-utils/rbtree.h > @@ -0,0 +1,171 @@ > +/* > + Red Black Trees > + (C) 1999 Andrea Arcangeli <andrea@suse.de> > + > + 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 > + > + linux/include/linux/rbtree.h > + > + To use rbtrees you'll have to implement your own insert and search cores. > + This will avoid us to use callbacks and to drop drammatically performances. > + I know it's not the cleaner way, but in C (not in C++) to get > + performances and genericity... > + > + Some example of insert and search follows here. The search is a plain > + normal search over an ordered tree. The insert instead must be implemented > + int two steps: as first thing the code must insert the element in > + order as a red leaf in the tree, then the support library function > + rb_insert_color() must be called. Such function will do the > + not trivial work to rebalance the rbtree if necessary. > + > +----------------------------------------------------------------------- > +static inline struct page * rb_search_page_cache(struct inode * inode, > + unsigned long offset) > +{ > + struct rb_node * n = inode->i_rb_page_cache.rb_node; > + struct page * page; > + > + while (n) > + { > + page = rb_entry(n, struct page, rb_page_cache); > + > + if (offset < page->offset) > + n = n->rb_left; > + else if (offset > page->offset) > + n = n->rb_right; > + else > + return page; > + } > + return NULL; > +} > + > +static inline struct page * __rb_insert_page_cache(struct inode * inode, > + unsigned long offset, > + struct rb_node * node) > +{ > + struct rb_node ** p = &inode->i_rb_page_cache.rb_node; > + struct rb_node * parent = NULL; > + struct page * page; > + > + while (*p) > + { > + parent = *p; > + page = rb_entry(parent, struct page, rb_page_cache); > + > + if (offset < page->offset) > + p = &(*p)->rb_left; > + else if (offset > page->offset) > + p = &(*p)->rb_right; > + else > + return page; > + } > + > + rb_link_node(node, parent, p); > + > + return NULL; > +} > + > +static inline struct page * rb_insert_page_cache(struct inode * inode, > + unsigned long offset, > + struct rb_node * node) > +{ > + struct page * ret; > + if ((ret = __rb_insert_page_cache(inode, offset, node))) > + goto out; > + rb_insert_color(node, &inode->i_rb_page_cache); > + out: > + return ret; > +} > +----------------------------------------------------------------------- > +*/ > + > +#ifndef _LINUX_RBTREE_H > +#define _LINUX_RBTREE_H > + > +#include <linux/kernel.h> > +#include <linux/stddef.h> > + > +struct rb_node > +{ > + unsigned long rb_parent_color; > +#define RB_RED 0 > +#define RB_BLACK 1 > + struct rb_node *rb_right; > + struct rb_node *rb_left; > +} __attribute__((aligned(sizeof(long)))); > + /* The alignment might seem pointless, but allegedly CRIS needs it */ > + > +struct rb_root > +{ > + struct rb_node *rb_node; > +}; > + > + > +#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) > +#define rb_color(r) ((r)->rb_parent_color & 1) > +#define rb_is_red(r) (!rb_color(r)) > +#define rb_is_black(r) rb_color(r) > +#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) > +#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) > + > +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) > +{ > + rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; > +} > +static inline void rb_set_color(struct rb_node *rb, int color) > +{ > + rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; > +} > + > +#define RB_ROOT (struct rb_root) { NULL, } > + > +/* Newer gcc versions take care of exporting this */ > +#ifndef offsetof > +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) > +#endif > + > +#define container_of(ptr, type, member) ({ \ > + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ > + (type *)( (char *)__mptr - offsetof(type,member) );}) > + > +#define rb_entry(ptr, type, member) container_of(ptr, type, member) > + > +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) > +#define RB_EMPTY_NODE(node) (rb_parent(node) == node) > +#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) > + > +extern void rb_insert_color(struct rb_node *, struct rb_root *); > +extern void rb_erase(struct rb_node *, struct rb_root *); > + > +/* Find logical next and previous nodes in a tree */ > +extern struct rb_node *rb_next(struct rb_node *); > +extern struct rb_node *rb_prev(struct rb_node *); > +extern struct rb_node *rb_first(struct rb_root *); > +extern struct rb_node *rb_last(struct rb_root *); > + > +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ > +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, > + struct rb_root *root); > + > +static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, > + struct rb_node ** rb_link) > +{ > + node->rb_parent_color = (unsigned long )parent; > + node->rb_left = node->rb_right = NULL; > + > + *rb_link = node; > +} > + > +#endif /* _LINUX_RBTREE_H */ > diff --git a/jffsX-utils/summary.h b/jffsX-utils/summary.h > new file mode 100644 > index 0000000..e9d95a5 > --- /dev/null > +++ b/jffsX-utils/summary.h > @@ -0,0 +1,177 @@ > +/* > + * 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. > + */ > + > +#ifndef JFFS2_SUMMARY_H > +#define JFFS2_SUMMARY_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)) > +#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) > +#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) > + > +/* 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)); > + > +struct jffs2_sum_xattr_flash > +{ > + jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ > + jint32_t xid; /* xattr identifier */ > + jint32_t version; /* version number */ > + jint32_t offset; /* offset on jeb */ > + jint32_t totlen; /* node length */ > +} __attribute__((packed)); > + > +struct jffs2_sum_xref_flash > +{ > + jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ > + jint32_t offset; /* offset on jeb */ > +} __attribute__((packed)); > + > +union jffs2_sum_flash > +{ > + struct jffs2_sum_unknown_flash u; > + struct jffs2_sum_inode_flash i; > + struct jffs2_sum_dirent_flash d; > + struct jffs2_sum_xattr_flash x; > + struct jffs2_sum_xref_flash r; > +}; > + > +/* 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)); > + > +struct jffs2_sum_xattr_mem > +{ > + union jffs2_sum_mem *next; > + jint16_t nodetype; > + jint32_t xid; > + jint32_t version; > + jint32_t offset; > + jint32_t totlen; > +} __attribute__((packed)); > + > +struct jffs2_sum_xref_mem > +{ > + union jffs2_sum_mem *next; > + jint16_t nodetype; > + jint32_t offset; > +} __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_sum_xattr_mem x; > + struct jffs2_sum_xref_mem r; > +}; > + > +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/jffsX-utils/sumtool.c b/jffsX-utils/sumtool.c > new file mode 100644 > index 0000000..886b545 > --- /dev/null > +++ b/jffsX-utils/sumtool.c > @@ -0,0 +1,872 @@ > +/* > + * sumtool.c > + * > + * Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>, > + * Ferenc Havasi <havasi@inf.u-szeged.hu> > + * University of Szeged, Hungary > + * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> > + * > + * 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 > + * > + */ > + > +#define PROGRAM_NAME "sumtool" > + > +#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" > +#include "common.h" > + > +#define PAD(x) (((x)+3)&~3) > + > +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 const 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 const 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 full_write(void *target_buff, const void *buf, int len); > + > +void setup_cleanmarker(void) > +{ > + 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(mtd_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) > + errmsg_die("output filename specified more than once"); > + out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); > + if (out_fd == -1) > + sys_errmsg_die("open output file"); > + break; > + > + case 'i': > + if (in_fd != -1) > + errmsg_die("input filename specified more than once"); > + in_fd = open(optarg, O_RDONLY); > + if (in_fd == -1) > + sys_errmsg_die("open input file"); > + break; > + case 'b': > + target_endian = __BIG_ENDIAN; > + break; > + case 'l': > + target_endian = __LITTLE_ENDIAN; > + break; > + case 'h': > + case '?': > + errmsg_die("%s", helptext); > + case 'v': > + verbose = 1; > + break; > + > + case 'V': > + errmsg_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) > + errmsg_die("Unrecognisable erase size\n"); > + > + if (*next) { > + if (!strcmp(next, "KiB")) { > + units = 1024; > + } else if (!strcmp(next, "MiB")) { > + units = 1024 * 1024; > + } else { > + errmsg_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) { > + warnmsg("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)) { > + errmsg_die("cleanmarker size must be >= 12"); > + } > + if (cleanmarker_size >= erase_block_size) { > + errmsg_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(void) > +{ > + data_buffer = xmalloc(erase_block_size); > + file_buffer = xmalloc(erase_block_size); > +} > + > +void init_sumlist(void) > +{ > + sum_collected = xzalloc(sizeof(*sum_collected)); > +} > + > +void clean_buffers(void) > +{ > + free(data_buffer); > + free(file_buffer); > +} > + > +void clean_sumlist(void) > +{ > + 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) > + warnmsg("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); > + > + free(sum_collected); > + } > +} > + > +int load_next_block(void) > +{ > + int ret; > + ret = read(in_fd, file_buffer, erase_block_size); > + file_ofs = 0; > + > + bareverbose(verbose, "Load next block : %d bytes read\n", ret); > + > + return ret; > +} > + > +void write_buff_to_file(void) > +{ > + 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) > + sys_errmsg_die("write"); > + > + if (ret == 0) > + sys_errmsg_die("write returned zero"); > + > + len -= ret; > + buf += ret; > + } > + > + data_ofs = 0; > +} > + > +void dump_sum_records(void) > +{ > + > + 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 = xmalloc(datasize); > + > + 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(mtd_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; > + } > + > + case JFFS2_NODETYPE_XATTR: { > + struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; > + > + sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; > + sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; > + sxattr_ptr->version = sum_collected->sum_list_head->x.version; > + sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; > + sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; > + > + wpage += JFFS2_SUMMARY_XATTR_SIZE; > + break; > + } > + > + case JFFS2_NODETYPE_XREF: { > + struct jffs2_sum_xref_flash *sxref_ptr = wpage; > + > + sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; > + sxref_ptr->offset = sum_collected->sum_list_head->r.offset; > + > + wpage += JFFS2_SUMMARY_XREF_SIZE; > + break; > + } > + > + default : { > + warnmsg("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(mtd_crc32(0, tpage, datasize)); > + isum.node_crc = cpu_to_je32(mtd_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(void) > +{ > + 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(void) > +{ > + > + 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; > + > + case JFFS2_NODETYPE_XATTR: > + sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; > + sum_collected->sum_num++; > + break; > + > + case JFFS2_NODETYPE_XREF: > + sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; > + sum_collected->sum_num++; > + break; > + > + default: > + errmsg_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 = xmalloc(sizeof(*temp)); > + > + 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 = xmalloc(sizeof(*temp) + node->d.nsize); > + > + 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 add_sum_xattr_mem(union jffs2_node_union *node) > +{ > + struct jffs2_sum_xattr_mem *temp = xmalloc(sizeof(*temp)); > + > + temp->nodetype = node->x.nodetype; > + temp->xid = node->x.xid; > + temp->version = node->x.version; > + temp->offset = cpu_to_je32(data_ofs); > + temp->totlen = node->x.totlen; > + temp->next = NULL; > + > + add_sum_mem((union jffs2_sum_mem *) temp); > +} > + > +void add_sum_xref_mem(union jffs2_node_union *node) > +{ > + struct jffs2_sum_xref_mem *temp = xmalloc(sizeof(*temp)); > + > + temp->nodetype = node->r.nodetype; > + temp->offset = cpu_to_je32(data_ofs); > + temp->next = NULL; > + > + 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 write_xattr_to_buff(union jffs2_node_union *node) > +{ > + pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); > + add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ > + full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); > + padword(); > +} > + > +void write_xref_to_buff(union jffs2_node_union *node) > +{ > + pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); > + add_sum_xref_mem(node); /* Add xref summary mem to summary list */ > + full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); > + padword(); > +} > + > +void create_summed_image(int inp_size) > +{ > + uint8_t *p = file_buffer; > + union jffs2_node_union *node; > + uint32_t crc, length; > + 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++) > + warnmsg("Wrong bitmask at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); > + if (crc != je32_to_cpu (node->u.hdr_crc)) { > + warnmsg("Wrong hdr_crc at 0x%08zx, 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: > + bareverbose(verbose, > + "%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", > + obsolete ? "Obsolete" : "", > + p - 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); > + if (crc != je32_to_cpu (node->i.node_crc)) { > + warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); > + if (crc != je32_to_cpu(node->i.data_crc)) { > + warnmsg("Wrong data_crc at 0x%08zx, 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; > + > + bareverbose(verbose, > + "%8s Dirent node at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); > + if (crc != je32_to_cpu (node->d.node_crc)) { > + warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); > + if (crc != je32_to_cpu(node->d.name_crc)) { > + warnmsg("Wrong name_crc at 0x%08zx, 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_XATTR: > + if (je32_to_cpu(node->x.node_crc) == 0xffffffff) > + obsolete = 1; > + bareverbose(verbose, > + "%8s Xdatum node at 0x%08zx, totlen 0x%08x, #xid %5u, version %5u\n", > + obsolete ? "Obsolete" : "", > + p - file_buffer, je32_to_cpu (node->x.totlen), > + je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); > + crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); > + if (crc != je32_to_cpu(node->x.node_crc)) { > + warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", > + p - file_buffer, je32_to_cpu(node->x.node_crc), crc); > + p += PAD(je32_to_cpu (node->x.totlen)); > + continue; > + } > + length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); > + crc = mtd_crc32(0, node->x.data, length); > + if (crc != je32_to_cpu(node->x.data_crc)) { > + warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", > + p - file_buffer, je32_to_cpu(node->x.data_crc), crc); > + p += PAD(je32_to_cpu (node->x.totlen)); > + continue; > + } > + > + write_xattr_to_buff(node); > + p += PAD(je32_to_cpu (node->x.totlen)); > + break; > + > + case JFFS2_NODETYPE_XREF: > + if (je32_to_cpu(node->r.node_crc) == 0xffffffff) > + obsolete = 1; > + bareverbose(verbose, > + "%8s Xref node at 0x%08zx, totlen 0x%08x, #ino %5u, xid %5u\n", > + obsolete ? "Obsolete" : "", > + p - file_buffer, je32_to_cpu(node->r.totlen), > + je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); > + crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); > + if (crc != je32_to_cpu(node->r.node_crc)) { > + warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", > + p - file_buffer, je32_to_cpu(node->r.node_crc), crc); > + p += PAD(je32_to_cpu (node->r.totlen)); > + continue; > + } > + > + write_xref_to_buff(node); > + p += PAD(je32_to_cpu (node->r.totlen)); > + break; > + > + case JFFS2_NODETYPE_CLEANMARKER: > + bareverbose(verbose, > + "%8s Cleanmarker at 0x%08zx, 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: > + bareverbose(verbose, > + "%8s Padding node at 0x%08zx, 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: > + bareverbose(verbose, > + "%8s Unknown node at 0x%08zx, 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, "%s", helptext); > + errmsg_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; > +} > diff --git a/load_nandsim.sh b/load_nandsim.sh > deleted file mode 100755 > index 4d9f0cb..0000000 > --- a/load_nandsim.sh > +++ /dev/null > @@ -1,127 +0,0 @@ > -#!/bin/sh -euf > - > -# > -# This script inserts NAND simulator module to emulate NAND flash of specified > -# size. > -# > -# Author: Artem Bityutskiy > -# > - > -fatal() > -{ > - echo "Error: $1" 1>&2 > - exit 1 > -} > - > -usage() > -{ > - cat 1>&2 <<EOF > -Load NAND simulator to simulate flash of a specified size. > - > -Usage: ${0##*/} <size in MiB> <eraseblock size in KiB> \\ > - <page size (512 or 2048)> > - > -Only the first parameter is mandatory. Default eraseblock size > -is 16KiB, default NAND page size is 512 bytes. > - > -Only the following combinations are supported: > --------------------------------------------------- > -| size (MiB) | EB size (KiB) | Page size (bytes) | > --------------------------------------------------- > -| 16 | 16 | 512 | > -| 32 | 16 | 512 | > -| 64 | 16 | 512 | > -| 128 | 16 | 512 | > -| 256 | 16 | 512 | > -| 64 | 64 | 2048 | > -| 64 | 128 | 2048 | > -| 64 | 256 | 2048 | > -| 64 | 512 | 2048 | > -| 128 | 64 | 2048 | > -| 128 | 128 | 2048 | > -| 128 | 256 | 2048 | > -| 128 | 512 | 2048 | > -| 256 | 64 | 2048 | > -| 256 | 128 | 2048 | > -| 256 | 256 | 2048 | > -| 256 | 512 | 2048 | > -| 512 | 64 | 2048 | > -| 512 | 128 | 2048 | > -| 512 | 256 | 2048 | > -| 512 | 512 | 2048 | > -| 1024 | 64 | 2048 | > -| 1024 | 128 | 2048 | > -| 1024 | 256 | 2048 | > -| 1024 | 512 | 2048 | > --------------------------------------------------- > -EOF > -} > - > -if grep -q "NAND simulator" /proc/mtd; then > - fatal "nandsim is already loaded" > -fi > - > -if [ "$#" -lt "1" ]; then > - usage > - exit 1 > -fi > - > -size="$1" > -eb_size="$2" > -page_size="$3" > -if [ "$#" = "1" ]; then > - eb_size="16" > - page_size="512" > -elif [ "$#" = "2" ]; then > - page_size="512" > -fi > - > -if [ "$page_size" -eq 512 ] && [ "$eb_size" -ne "16" ]; then > - fatal "only 16KiB eraseblocks are possible in case of 512 bytes page" > -fi > - > -first= > -second= > -third= > -fourth= > - > -if [ "$page_size" -eq "512" ]; then > - first="0x20" > - case "$size" in > - 16) second=0x33 ;; > - 32) second=0x35 ;; > - 64) second=0x36 ;; > - 128) second=0x78 ;; > - 256) second=0x71 ;; > - *) fatal "flash size ${size}MiB is not supported, try 16, 32, 64 or 256" > - esac > -elif [ "$page_size" -eq "2048" ]; then > - case "$eb_size" in > - 64) fourth="0x05" ;; > - 128) fourth="0x15" ;; > - 256) fourth="0x25" ;; > - 512) fourth="0x35" ;; > - *) fatal "eraseblock ${eb_size}KiB is not supported" > - esac > - > - > - case "$size" in > - 64) first="0x20"; second="0xa2"; third="0x00 ";; > - 128) first="0xec"; second="0xa1"; third="0x00 ";; > - 256) first="0x20"; second="0xaa"; third="0x00 ";; > - 512) first="0x20"; second="0xac"; third="0x00 ";; > - 1024) first="0xec"; second="0xd3"; third="0x51 ";; > - *) fatal "unable to emulate ${size}MiB flash with ${eb_size}KiB eraseblock" > - esac > -else > - fatal "bad NAND page size ${page_size}KiB, it has to be either 512 or 2048" > -fi > - > -first="first_id_byte=$first" > -second="second_id_byte=$second" > -[ -z "$third" ] || third="third_id_byte=$third" > -[ -z "$fourth" ] || fourth="fourth_id_byte=$fourth" > - > -modprobe nandsim "$first" "$second" $third $fourth > - > -echo "Loaded NAND simulator (${size}MiB, ${eb_size}KiB eraseblock, $page_size bytes NAND page)" > diff --git a/mcast_image.h b/mcast_image.h > deleted file mode 100644 > index 8e94ffa..0000000 > --- a/mcast_image.h > +++ /dev/null > @@ -1,54 +0,0 @@ > -#include <stdint.h> > - > -#define PKT_SIZE 2820 > - > -struct image_pkt_hdr { > - uint32_t resend; > - uint32_t totcrc; > - uint32_t nr_blocks; > - uint32_t blocksize; > - uint32_t block_crc; > - uint32_t block_nr; > - uint32_t pkt_sequence; > - uint16_t pkt_nr; > - uint16_t nr_pkts; > - uint32_t thislen; > - uint32_t thiscrc; > -}; > - > -struct image_pkt { > - struct image_pkt_hdr hdr; > - unsigned char data[PKT_SIZE]; > -}; > - > -struct fec_parms; > - > -/* k - number of actual data packets > - * n - total number of packets including data and redundant packets > - * (actual packet size isn't relevant here) */ > -struct fec_parms *fec_new(int k, int n); > -void fec_free(struct fec_parms *p); > - > -/* src - array of (n) pointers to data packets > - * fec - buffer for packet to be generated > - * index - index of packet to be generated (0 <= index < n) > - * sz - data packet size > - * > - * _linear version just takes a pointer to the raw data; no > - * mucking about with packet pointers. > - */ > -void fec_encode(struct fec_parms *code, unsigned char *src[], > - unsigned char *fec, int index, int sz); > -void fec_encode_linear(struct fec_parms *code, unsigned char *src, > - unsigned char *fec, int index, int sz); > - > -/* data - array of (k) pointers to data packets, in arbitrary order (see i) > - * i - indices of (data) packets > - * sz - data packet size > - * > - * Will never fail as long as you give it (k) individual data packets. > - * Will re-order the (data) pointers but not the indices -- data packets > - * are ordered on return. > - */ > -int fec_decode(struct fec_parms *code, unsigned char *data[], > - int i[], int sz); > diff --git a/misc-utils/MAKEDEV b/misc-utils/MAKEDEV > new file mode 100755 > index 0000000..b59e90e > --- /dev/null > +++ b/misc-utils/MAKEDEV > @@ -0,0 +1,41 @@ > +#!/bin/bash > + > +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/misc-utils/doc_loadbios.c b/misc-utils/doc_loadbios.c > new file mode 100644 > index 0000000..b999c73 > --- /dev/null > +++ b/misc-utils/doc_loadbios.c > @@ -0,0 +1,150 @@ > +#define PROGRAM_NAME "doc_loadbios" > + > +#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> > + > +#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/misc-utils/docfdisk.c b/misc-utils/docfdisk.c > new file mode 100644 > index 0000000..9956de5 > --- /dev/null > +++ b/misc-utils/docfdisk.c > @@ -0,0 +1,318 @@ > +/* > + * docfdisk.c: Modify INFTL partition tables > + * > + * 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 PROGRAM_NAME "docfdisk" > + > +#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> > +#include <mtd_swab.h> > + > +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", > + PROGRAM_NAME); > + 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.writesize) * 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.writesize) { > + if (ioctl(fd, MEMREADOOB, &oob)) { > + perror("ioctl(MEMREADOOB)"); > + return 1; > + } > + oob.start += meminfo.writesize; > + 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.writesize) { > + 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.writesize, oob.start)) < 0) { > + perror("Write page"); > + printf("Your MediaHeader may be hosed. UHOH!\n"); > + return 1; > + } > + if (ret != meminfo.writesize) { > + printf("Short write!\n"); > + printf("Your MediaHeader may be hosed. UHOH!\n"); > + return 1; > + } > + > + oob.start += meminfo.writesize; > + oob.ptr += meminfo.oobsize; > + buf += meminfo.writesize; > + } > + > + printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); > + return 0; > +} > diff --git a/misc-utils/fectest.c b/misc-utils/fectest.c > new file mode 100644 > index 0000000..fd577f3 > --- /dev/null > +++ b/misc-utils/fectest.c > @@ -0,0 +1,91 @@ > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <stdio.h> > +#include <sys/time.h> > +#include <sys/types.h> > +#include <sys/stat.h> > + > +#include "mcast_image.h" > +#include <crc32.h> > + > +#define ERASE_SIZE 131072 > +#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE) > +#define DROPS 8 > + > +int main(void) > +{ > + int i, j; > + unsigned char buf[NR_PKTS * PKT_SIZE]; > + unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE]; > + struct fec_parms *fec; > + unsigned char *srcs[NR_PKTS]; > + unsigned char *pkt[NR_PKTS + DROPS]; > + int pktnr[NR_PKTS + DROPS]; > + struct timeval then, now; > + > + srand(3453); > + for (i=0; i < sizeof(buf); i++) > + if (i < ERASE_SIZE) > + buf[i] = rand(); > + else > + buf[i] = 0; > + > + for (i=0; i < NR_PKTS + DROPS; i++) > + srcs[i] = buf + (i * PKT_SIZE); > + > + for (i=0; i < NR_PKTS + DROPS; i++) { > + pkt[i] = malloc(PKT_SIZE); > + pktnr[i] = -1; > + } > + fec = fec_new(NR_PKTS, NR_PKTS + DROPS); > + if (!fec) { > + printf("fec_init() failed\n"); > + exit(1); > + } > + j = 0; > + for (i=0; i < NR_PKTS + DROPS; i++) { > +#if 1 > + if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 ) > + continue; > +#endif > + if (i == 69 || i == 93 || i == 103) > + continue; > + fec_encode(fec, srcs, pkt[j], i, PKT_SIZE); > + pktnr[j] = i; > + j++; > + } > + gettimeofday(&then, NULL); > + if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) { > + printf("Decode failed\n"); > + exit(1); > + } > + > + for (i=0; i < NR_PKTS; i++) > + memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE); > + gettimeofday(&now, NULL); > + now.tv_sec -= then.tv_sec; > + now.tv_usec -= then.tv_usec; > + if (now.tv_usec < 0) { > + now.tv_usec += 1000000; > + now.tv_sec--; > + } > + > + if (memcmp(pktbuf, buf, ERASE_SIZE)) { > + int fd; > + printf("Compare failed\n"); > + fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644); > + if (fd >= 0) > + write(fd, buf, ERASE_SIZE); > + close(fd); > + fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644); > + if (fd >= 0) > + write(fd, pktbuf, ERASE_SIZE); > + > + exit(1); > + } > + > + printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec); > + return 0; > +} > diff --git a/misc-utils/ftl_check.c b/misc-utils/ftl_check.c > new file mode 100644 > index 0000000..0eada8f > --- /dev/null > +++ b/misc-utils/ftl_check.c > @@ -0,0 +1,217 @@ > +/* Ported to MTD system. > + * 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. > + > + ======================================================================*/ > + > +#define PROGRAM_NAME "ftl_check" > + > +#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 <mtd/ftl-user.h> > +#include <mtd_swab.h> > + > +#include "common.h" > + > +/*====================================================================*/ > + > +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) > +{ > + mtd_info_t mtd; > + erase_unit_header_t hdr, hdr2; > + off_t i; > + u_int 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 ((le32_to_cpu(hdr.FormattedSize) > 0) && > + (le32_to_cpu(hdr.FormattedSize) <= mtd.size) && > + (le16_to_cpu(hdr.NumEraseUnits) > 0) && > + (le16_to_cpu(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(le32_to_cpu(hdr.FormattedSize)); > + printf(", erase units = %d, transfer units = %d\n", > + le16_to_cpu(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 < le16_to_cpu(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 %"PRIdoff_t":\n", i); > + if ((hdr2.FormattedSize != hdr.FormattedSize) || > + (hdr2.NumEraseUnits != hdr.NumEraseUnits) || > + (hdr2.SerialNumber != hdr.SerialNumber)) > + printf(" Erase unit header is corrupt.\n"); > + else if (le16_to_cpu(hdr2.LogicalEUN) == 0xffff) > + printf(" Transfer unit, erase count = %d\n", le32_to_cpu(hdr2.EraseCount)); > + else { > + printf(" Logical unit %d, erase count = %d\n", > + le16_to_cpu(hdr2.LogicalEUN), le32_to_cpu(hdr2.EraseCount)); > + if (lseek(fd, (i << hdr.EraseUnitSize)+le32_to_cpu(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(le32_to_cpu(bam[j]))) > + free++; > + else if (BLOCK_DELETED(le32_to_cpu(bam[j]))) > + deleted++; > + else switch (BLOCK_TYPE(le32_to_cpu(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(void) > +{ > + fprintf(stderr, "usage: %s device\n", PROGRAM_NAME); > +} > + > +/*====================================================================*/ > + > +int main(int argc, char *argv[]) > +{ > + int optch, errflg, fd; > + struct stat buf; > + > + errflg = 0; > + while ((optch = getopt(argc, argv, "h")) != -1) { > + switch (optch) { > + case 'h': > + errflg = 1; break; > + default: > + errflg = -1; break; > + } > + } > + if (errflg || (optind != argc-1)) { > + showusage(); > + 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); > + close(fd); > + > + exit(EXIT_SUCCESS); > + return 0; > +} > diff --git a/misc-utils/ftl_format.c b/misc-utils/ftl_format.c > new file mode 100644 > index 0000000..b58677f > --- /dev/null > +++ b/misc-utils/ftl_format.c > @@ -0,0 +1,324 @@ > +/* Ported to MTD system. > + * 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. > + > + ======================================================================*/ > + > +#define PROGRAM_NAME "ftl_format" > + > +#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 <mtd/ftl-user.h> > +#include <mtd_swab.h> > +#include "common.h" > + > +/*====================================================================*/ > + > +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 = cpu_to_le32(0); > + hdr->FirstPhysicalEUN = cpu_to_le16(BootUnits); > + hdr->NumEraseUnits = cpu_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 = cpu_to_le32(0x80); > + /* Adjust size to account for BAM space */ > + nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int) > + + le32_to_cpu(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize; > + > + __FormattedSize -= > + (le16_to_cpu(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize); > + __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff); > + > + hdr->FormattedSize = cpu_to_le32(__FormattedSize); > + > + /* hdr->FirstVMAddress defaults to erased state */ > + hdr->NumVMPages = cpu_to_le16(0); > + hdr->Flags = 0; > + /* hdr->Code defaults to erased state */ > + hdr->SerialNumber = cpu_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(le16_to_cpu(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize); > + printf(" allocated for boot image\n"); > + } > + printf("Reserved %d%%, formatted size = ", reserve); > + print_size(le32_to_cpu(hdr.FormattedSize)); > + printf("\n"); > + fflush(stdout); > + } > + > + if (interrogate) > + if (!prompt("This will destroy all data on the target device. Confirm?", false)) > + return -1; > + > + /* Create basic block allocation table for control blocks */ > + nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int) > + + le32_to_cpu(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize; > + bam = malloc(nbam * sizeof(u_int)); > + for (i = 0; i < nbam; i++) > + bam[i] = cpu_to_le32(BLOCK_CONTROL); > + > + /* Erase partition */ > + if (!quiet) { > + printf("Erasing all blocks...\n"); > + fflush(stdout); > + } > + erase.length = mtd.erasesize; > + erase.start = mtd.erasesize * le16_to_cpu(hdr.FirstPhysicalEUN); > + for (i = 0; i < le16_to_cpu(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 ? (le16_to_cpu(hdr.NumEraseUnits) / spare) : (le16_to_cpu(hdr.NumEraseUnits) + 1); > + for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) { > + off_t ofs = (off_t) (i + le16_to_cpu(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 = cpu_to_le16(0xffff); > + else { > + hdr.LogicalEUN = cpu_to_le16(lun); > + lun++; > + } > + if (write(fd, &hdr, sizeof(hdr)) == -1) { > + perror("write failed"); > + break; > + } > + if (lseek(fd, ofs + le32_to_cpu(hdr.BAMOffset), SEEK_SET) == -1) { > + perror("seek failed"); > + break; > + } > + if (write(fd, bam, nbam * sizeof(u_int)) == -1) { > + perror("write failed"); > + break; > + } > + } > + if (i < le16_to_cpu(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", PROGRAM_NAME); > + 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/misc-utils/mcast_image.h b/misc-utils/mcast_image.h > new file mode 100644 > index 0000000..8e94ffa > --- /dev/null > +++ b/misc-utils/mcast_image.h > @@ -0,0 +1,54 @@ > +#include <stdint.h> > + > +#define PKT_SIZE 2820 > + > +struct image_pkt_hdr { > + uint32_t resend; > + uint32_t totcrc; > + uint32_t nr_blocks; > + uint32_t blocksize; > + uint32_t block_crc; > + uint32_t block_nr; > + uint32_t pkt_sequence; > + uint16_t pkt_nr; > + uint16_t nr_pkts; > + uint32_t thislen; > + uint32_t thiscrc; > +}; > + > +struct image_pkt { > + struct image_pkt_hdr hdr; > + unsigned char data[PKT_SIZE]; > +}; > + > +struct fec_parms; > + > +/* k - number of actual data packets > + * n - total number of packets including data and redundant packets > + * (actual packet size isn't relevant here) */ > +struct fec_parms *fec_new(int k, int n); > +void fec_free(struct fec_parms *p); > + > +/* src - array of (n) pointers to data packets > + * fec - buffer for packet to be generated > + * index - index of packet to be generated (0 <= index < n) > + * sz - data packet size > + * > + * _linear version just takes a pointer to the raw data; no > + * mucking about with packet pointers. > + */ > +void fec_encode(struct fec_parms *code, unsigned char *src[], > + unsigned char *fec, int index, int sz); > +void fec_encode_linear(struct fec_parms *code, unsigned char *src, > + unsigned char *fec, int index, int sz); > + > +/* data - array of (k) pointers to data packets, in arbitrary order (see i) > + * i - indices of (data) packets > + * sz - data packet size > + * > + * Will never fail as long as you give it (k) individual data packets. > + * Will re-order the (data) pointers but not the indices -- data packets > + * are ordered on return. > + */ > +int fec_decode(struct fec_parms *code, unsigned char *data[], > + int i[], int sz); > diff --git a/misc-utils/mtd_debug.c b/misc-utils/mtd_debug.c > new file mode 100644 > index 0000000..d6993ce > --- /dev/null > +++ b/misc-utils/mtd_debug.c > @@ -0,0 +1,397 @@ > +/* > + * Copyright (c) 2d3D, Inc. > + * Written by Abraham vd Merwe <abraham@2d3d.co.za> > + * All rights reserved. > + * > + * 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. > + */ > + > +#define PROGRAM_NAME "mtd_debug" > + > +#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> > +#include "common.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, off_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, 0666); > + 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", __func__, size); > + if (size != BUF_SIZE) { > + size = BUF_SIZE; > + fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, size, n); > + perror("read()"); > + goto err2; > + } > + err = write(outfd, buf, size); > + if (err < 0) { > + fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, 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 %zu bytes from address 0x%.8"PRIxoff_t" 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, off_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", __func__, size); > + if (size != BUF_SIZE) { > + size = BUF_SIZE; > + fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, 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", __func__, 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%.8"PRIxoff_t" 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_MLCNANDFLASH: > + printf("MTD_MLCNANDFLASH"); > + break; > + case MTD_DATAFLASH: > + printf("MTD_DATAFLASH"); > + break; > + case MTD_UBIVOLUME: > + printf("MTD_UBIVOLUME"); > + 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_WRITEABLE", MTD_WRITEABLE }, > + { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, > + { "MTD_NO_ERASE", MTD_NO_ERASE }, > + { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK }, > + { NULL, -1 } > + }; > + for (i = 0; flags[i].name != NULL; i++) { > + if (mtd.flags & flags[i].value) { > + if (first) { > + printf("%s", 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.writesize = "); > + printsize(mtd.writesize); > + > + printf("\nmtd.oobsize = "); > + printsize(mtd.oobsize); > + > + printf("\nregions = %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(void) > +{ > + fprintf(stderr, "usage: %1$s info <device>\n" > + " %1$s read <device> <offset> <len> <dest-filename>\n" > + " %1$s write <device> <offset> <len> <source-filename>\n" > + " %1$s erase <device> <offset> <len>\n", > + PROGRAM_NAME); > + exit(EXIT_FAILURE); > +} > + > +int main(int argc, char *argv[]) > +{ > + int err = 0, fd; > + int open_flag; > + > + enum { > + OPT_INFO, > + OPT_READ, > + OPT_WRITE, > + OPT_ERASE > + } option = OPT_INFO; > + > + /* 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(); > + > + /* open device */ > + open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR; > + if ((fd = open(argv[2], O_SYNC | open_flag)) < 0) > + errmsg_die("open()"); > + > + switch (option) { > + case OPT_INFO: > + showinfo(fd); > + break; > + case OPT_READ: > + err = flash_to_file(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); > + break; > + case OPT_WRITE: > + err = file_to_flash(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); > + break; > + case OPT_ERASE: > + err = erase_flash(fd, strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0)); > + break; > + } > + > + /* close device */ > + if (close(fd) < 0) > + errmsg_die("close()"); > + > + return err; > +} > diff --git a/misc-utils/mtdpart.c b/misc-utils/mtdpart.c > new file mode 100644 > index 0000000..0016e34 > --- /dev/null > +++ b/misc-utils/mtdpart.c > @@ -0,0 +1,194 @@ > +/* > + * mtdpart.c > + * > + * Copyright 2015 The Chromium OS Authors. > + * > + * 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 adds or removes a partition from an MTD device. > + */ > + > +#define PROGRAM_NAME "mtdpart" > + > +#include <fcntl.h> > +#include <getopt.h> > +#include <limits.h> > +#include <linux/blkpg.h> > +#include <stdio.h> > +#include <string.h> > +#include <sys/ioctl.h> > +#include <sys/stat.h> > +#include <sys/types.h> > +#include <unistd.h> > + > +#include "common.h" > + > +static void display_help(int status) > +{ > + fprintf(status == EXIT_SUCCESS ? stdout : stderr, > +"Usage: %1$s add [OPTION] <MTD_DEVICE> <PART_NAME> <START> <SIZE>\n" > +" %1$s del [OPTION] <MTD_DEVICE> <PART_NUMBER>\n" > +"Adds a partition to an MTD device, or remove an existing partition from it.\n" > +"\n" > +" -h, --help Display this help and exit\n" > +" --version Output version information and exit\n" > +"\n" > +"START location and SIZE of the partition are in bytes. They should align on\n" > +"eraseblock size.\n", > + PROGRAM_NAME > + ); > + exit(status); > +} > + > +static void display_version(void) > +{ > + printf("%1$s " VERSION "\n" > + "\n" > + "%1$s comes with NO WARRANTY\n" > + "to the extent permitted by law.\n" > + "\n" > + "You may redistribute copies of %1$s\n" > + "under the terms of the GNU General Public Licence.\n" > + "See the file `COPYING' for more information.\n", > + PROGRAM_NAME); > + exit(EXIT_SUCCESS); > +} > + > +/* Command arguments */ > + > +typedef enum { > + COMMAND_ADD, > + COMMAND_DEL > +} command_type; > + > +static command_type command; /* add or del */ > +static const char *mtddev; /* mtd device name */ > +static const char *part_name; /* partition name */ > +static int part_no; /* partition number */ > +static long long start_addr; /* start address */ > +static long long length; /* partition size */ > + > +static void process_options(int argc, char * const argv[]) > +{ > + int error = 0; > + > + for (;;) { > + int option_index = 0; > + static const char short_options[] = "h"; > + static const struct option long_options[] = { > + {"version", no_argument, 0, 0}, > + {"help", no_argument, 0, 'h'}, > + {0, 0, 0, 0}, > + }; > + > + int c = getopt_long(argc, argv, short_options, > + long_options, &option_index); > + if (c == EOF) { > + break; > + } > + > + switch (c) { > + case 0: > + display_version(); > + break; > + case 'h': > + display_help(EXIT_SUCCESS); > + break; > + case '?': > + error++; > + break; > + } > + } > + > + if ((argc - optind) < 3 || error) > + display_help(EXIT_FAILURE); > + > + const char *s_command = argv[optind++]; > + mtddev = argv[optind++]; > + > + if (strcmp(s_command, "del") == 0 && (argc - optind) == 1) { > + const char *s_part_no = argv[optind++]; > + > + long tmp = simple_strtol(s_part_no, &error); > + if (tmp < 0) > + errmsg_die("Can't specify negative partition number: %ld", > + tmp); > + if (tmp > INT_MAX) > + errmsg_die("Partition number exceeds INT_MAX: %ld", > + tmp); > + > + part_no = tmp; > + command = COMMAND_DEL; > + } else if (strcmp(s_command, "add") == 0 && (argc - optind) == 3) { > + const char *s_start; > + const char *s_length; > + > + part_name = argv[optind++]; > + s_start = argv[optind++]; > + s_length = argv[optind++]; > + > + if (strlen(part_name) >= BLKPG_DEVNAMELTH) > + errmsg_die("Partition name (%s) should be less than %d characters", > + part_name, BLKPG_DEVNAMELTH); > + > + start_addr = simple_strtoll(s_start, &error); > + if (start_addr < 0) > + errmsg_die("Can't specify negative start offset: %lld", > + start_addr); > + > + length = simple_strtoll(s_length, &error); > + if (length < 0) > + errmsg_die("Can't specify negative length: %lld", > + length); > + > + command = COMMAND_ADD; > + } else > + display_help(EXIT_FAILURE); > + > + if (error) > + display_help(EXIT_FAILURE); > +} > + > + > +int main(int argc, char * const argv[]) > +{ > + int fd; > + struct blkpg_partition part; > + struct blkpg_ioctl_arg arg; > + > + process_options(argc, argv); > + > + fd = open(mtddev, O_RDWR | O_CLOEXEC); > + if (fd == -1) > + sys_errmsg_die("Cannot open %s", mtddev); > + > + memset(&part, 0, sizeof(part)); > + > + memset(&arg, 0, sizeof(arg)); > + arg.datalen = sizeof(part); > + arg.data = ∂ > + > + switch (command) { > + case COMMAND_ADD: > + part.start = start_addr; > + part.length = length; > + strncpy(part.devname, part_name, sizeof(part.devname)); > + arg.op = BLKPG_ADD_PARTITION; > + break; > + case COMMAND_DEL: > + part.pno = part_no; > + arg.op = BLKPG_DEL_PARTITION; > + break; > + } > + > + if (ioctl(fd, BLKPG, &arg)) > + sys_errmsg_die("Failed to issue BLKPG ioctl"); > + > + close(fd); > + > + /* Exit happy */ > + return EXIT_SUCCESS; > +} > diff --git a/misc-utils/recv_image.c b/misc-utils/recv_image.c > new file mode 100644 > index 0000000..0093831 > --- /dev/null > +++ b/misc-utils/recv_image.c > @@ -0,0 +1,484 @@ > + > +#define PROGRAM_NAME "recv_image" > +#define _XOPEN_SOURCE 500 > +#define _BSD_SOURCE /* struct ip_mreq */ > + > +#include <errno.h> > +#include <stdio.h> > +#include <netdb.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <sys/ioctl.h> > +#include <sys/time.h> > +#include <crc32.h> > +#include "mtd/mtd-user.h" > +#include "mcast_image.h" > + > +#include "common.h" > + > +#define WBUF_SIZE 4096 > +struct eraseblock { > + uint32_t flash_offset; > + unsigned char wbuf[WBUF_SIZE]; > + int wbuf_ofs; > + int nr_pkts; > + int *pkt_indices; > + uint32_t crc; > +}; > + > +int main(int argc, char **argv) > +{ > + struct addrinfo *ai; > + struct addrinfo hints; > + struct addrinfo *runp; > + int ret; > + int sock; > + ssize_t len; > + int flfd; > + struct mtd_info_user meminfo; > + unsigned char *eb_buf, *decode_buf, **src_pkts; > + int nr_blocks = 0; > + int pkts_per_block; > + int block_nr = -1; > + uint32_t image_crc = 0; > + int total_pkts = 0; > + int ignored_pkts = 0; > + loff_t mtdoffset = 0; > + int badcrcs = 0; > + int duplicates = 0; > + int file_mode = 0; > + struct fec_parms *fec = NULL; > + int i; > + struct eraseblock *eraseblocks = NULL; > + uint32_t start_seq = 0; > + struct timeval start, now; > + unsigned long fec_time = 0, flash_time = 0, crc_time = 0, > + rflash_time = 0, erase_time = 0, net_time = 0; > + > + if (argc != 4) { > + fprintf(stderr, "usage: %s <host> <port> <mtddev>\n", > + PROGRAM_NAME); > + exit(1); > + } > + /* Open the device */ > + flfd = open(argv[3], O_RDWR); > + > + if (flfd >= 0) { > + /* Fill in MTD device capability structure */ > + if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { > + perror("MEMGETINFO"); > + close(flfd); > + flfd = -1; > + } else { > + printf("Receive to MTD device %s with erasesize %d\n", > + argv[3], meminfo.erasesize); > + } > + } > + if (flfd == -1) { > + /* Try again, as if it's a file */ > + flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); > + if (flfd < 0) { > + perror("open"); > + exit(1); > + } > + meminfo.erasesize = 131072; > + file_mode = 1; > + printf("Receive to file %s with (assumed) erasesize %d\n", > + argv[3], meminfo.erasesize); > + } > + > + pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; > + > + eb_buf = malloc(pkts_per_block * PKT_SIZE); > + decode_buf = malloc(pkts_per_block * PKT_SIZE); > + if (!eb_buf && !decode_buf) { > + fprintf(stderr, "No memory for eraseblock buffer\n"); > + exit(1); > + } > + src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); > + if (!src_pkts) { > + fprintf(stderr, "No memory for decode packet pointers\n"); > + exit(1); > + } > + > + memset(&hints, 0, sizeof(hints)); > + hints.ai_flags = AI_ADDRCONFIG; > + hints.ai_socktype = SOCK_DGRAM; > + > + ret = getaddrinfo(argv[1], argv[2], &hints, &ai); > + if (ret) { > + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); > + exit(1); > + } > + runp = ai; > + for (runp = ai; runp; runp = runp->ai_next) { > + sock = socket(runp->ai_family, runp->ai_socktype, > + runp->ai_protocol); > + if (sock == -1) { > + perror("socket"); > + continue; > + } > + if (runp->ai_family == AF_INET && > + IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { > + struct ip_mreq rq; > + rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; > + rq.imr_interface.s_addr = INADDR_ANY; > + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { > + perror("IP_ADD_MEMBERSHIP"); > + close(sock); > + continue; > + } > + > + } else if (runp->ai_family == AF_INET6 && > + ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { > + struct ipv6_mreq rq; > + rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; > + rq.ipv6mr_interface = 0; > + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { > + perror("IPV6_ADD_MEMBERSHIP"); > + close(sock); > + continue; > + } > + } > + if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { > + perror("bind"); > + close(sock); > + continue; > + } > + break; > + } > + if (!runp) > + exit(1); > + > + while (1) { > + struct image_pkt thispkt; > + > + len = read(sock, &thispkt, sizeof(thispkt)); > + > + if (len < 0) { > + perror("read socket"); > + break; > + } > + if (len < sizeof(thispkt)) { > + fprintf(stderr, "Wrong length %zd bytes (expected %zu)\n", > + len, sizeof(thispkt)); > + continue; > + } > + if (!eraseblocks) { > + image_crc = thispkt.hdr.totcrc; > + start_seq = ntohl(thispkt.hdr.pkt_sequence); > + > + if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { > + fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", > + ntohl(thispkt.hdr.blocksize), meminfo.erasesize); > + exit(1); > + } > + nr_blocks = ntohl(thispkt.hdr.nr_blocks); > + > + fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); > + > + eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); > + if (!eraseblocks) { > + fprintf(stderr, "No memory for block map\n"); > + exit(1); > + } > + for (i = 0; i < nr_blocks; i++) { > + eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); > + if (!eraseblocks[i].pkt_indices) { > + fprintf(stderr, "Failed to allocate packet indices\n"); > + exit(1); > + } > + eraseblocks[i].nr_pkts = 0; > + if (!file_mode) { > + if (mtdoffset >= meminfo.size) { > + fprintf(stderr, "Run out of space on flash\n"); > + exit(1); > + } > +#if 1 /* Deliberately use bad blocks... test write failures */ > + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { > + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); > + mtdoffset += meminfo.erasesize; > + } > +#endif > + } > + eraseblocks[i].flash_offset = mtdoffset; > + mtdoffset += meminfo.erasesize; > + eraseblocks[i].wbuf_ofs = 0; > + } > + gettimeofday(&start, NULL); > + } > + if (image_crc != thispkt.hdr.totcrc) { > + fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", > + ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); > + exit(1); > + } > + > + block_nr = ntohl(thispkt.hdr.block_nr); > + if (block_nr >= nr_blocks) { > + fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", > + block_nr, nr_blocks); > + exit(1); > + } > + for (i=0; i<eraseblocks[block_nr].nr_pkts; i++) { > + if (eraseblocks[block_nr].pkt_indices[i] == ntohs(thispkt.hdr.pkt_nr)) { > +// printf("Discarding duplicate packet at %08x pkt %d\n", > +// block_nr * meminfo.erasesize, eraseblocks[block_nr].pkt_indices[i]); > + duplicates++; > + break; > + } > + } > + if (i < eraseblocks[block_nr].nr_pkts) { > + continue; > + } > + > + if (eraseblocks[block_nr].nr_pkts >= pkts_per_block) { > + /* We have a block which we didn't really need */ > + eraseblocks[block_nr].nr_pkts++; > + ignored_pkts++; > + continue; > + } > + > + if (mtd_crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { > + printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", > + block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), > + mtd_crc32(-1, thispkt.data, PKT_SIZE), > + ntohl(thispkt.hdr.thiscrc)); > + badcrcs++; > + continue; > + } > + pkt_again: > + eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = > + ntohs(thispkt.hdr.pkt_nr); > + total_pkts++; > + if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { > + uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; > + long time_msec; > + gettimeofday(&now, NULL); > + > + time_msec = ((now.tv_usec - start.tv_usec) / 1000) + > + (now.tv_sec - start.tv_sec) * 1000; > + > + printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", > + total_pkts, nr_blocks * pkts_per_block, > + total_pkts * 100 / nr_blocks / pkts_per_block, > + time_msec / 1000, > + total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, > + pkts_sent - total_pkts - duplicates - ignored_pkts, > + (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, > + duplicates + ignored_pkts); > + fflush(stdout); > + } > + > + if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { > + /* New packet doesn't full the wbuf */ > + memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, > + thispkt.data, PKT_SIZE); > + eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; > + } else { > + int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; > + ssize_t wrotelen; > + static int faked = 1; > + > + memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, > + thispkt.data, fits); > + wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, > + eraseblocks[block_nr].flash_offset); > + > + if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { > + faked = 1; > + if (wrotelen < 0) > + perror("\npacket write"); > + else > + fprintf(stderr, "\nshort write of packet wbuf\n"); > + > + if (!file_mode) { > + struct erase_info_user erase; > + /* FIXME: Perhaps we should store pkt crcs and try > + to recover data from the offending eraseblock */ > + > + /* We have increased nr_pkts but not yet flash_offset */ > + erase.start = eraseblocks[block_nr].flash_offset & > + ~(meminfo.erasesize - 1); > + erase.length = meminfo.erasesize; > + > + printf("Will erase at %08x len %08x (bad write was at %08x)\n", > + erase.start, erase.length, eraseblocks[block_nr].flash_offset); > + if (ioctl(flfd, MEMERASE, &erase)) { > + perror("MEMERASE"); > + exit(1); > + } > + if (mtdoffset >= meminfo.size) { > + fprintf(stderr, "Run out of space on flash\n"); > + exit(1); > + } > + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { > + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); > + mtdoffset += meminfo.erasesize; > + if (mtdoffset >= meminfo.size) { > + fprintf(stderr, "Run out of space on flash\n"); > + exit(1); > + } > + } > + eraseblocks[block_nr].flash_offset = mtdoffset; > + printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); > + total_pkts -= eraseblocks[block_nr].nr_pkts; > + eraseblocks[block_nr].nr_pkts = 0; > + eraseblocks[block_nr].wbuf_ofs = 0; > + mtdoffset += meminfo.erasesize; > + goto pkt_again; > + } > + else /* Usually nothing we can do in file mode */ > + exit(1); > + } > + eraseblocks[block_nr].flash_offset += WBUF_SIZE; > + /* Copy the remainder into the wbuf */ > + memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); > + eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; > + } > + > + if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { > + eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); > + > + if (total_pkts == nr_blocks * pkts_per_block) > + break; > + } > + } > + printf("\n"); > + gettimeofday(&now, NULL); > + net_time = (now.tv_usec - start.tv_usec) / 1000; > + net_time += (now.tv_sec - start.tv_sec) * 1000; > + close(sock); > + for (block_nr = 0; block_nr < nr_blocks; block_nr++) { > + ssize_t rwlen; > + gettimeofday(&start, NULL); > + eraseblocks[block_nr].flash_offset -= meminfo.erasesize; > + rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); > + > + gettimeofday(&now, NULL); > + rflash_time += (now.tv_usec - start.tv_usec) / 1000; > + rflash_time += (now.tv_sec - start.tv_sec) * 1000; > + if (rwlen < 0) { > + perror("read"); > + /* Argh. Perhaps we could go back and try again, but if the flash is > + going to fail to read back what we write to it, and the whole point > + in this program is to write to it, what's the point? */ > + fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); > + exit(1); > + } > + > + memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, > + eraseblocks[block_nr].wbuf_ofs); > + > + for (i=0; i < pkts_per_block; i++) > + src_pkts[i] = &eb_buf[i * PKT_SIZE]; > + > + gettimeofday(&start, NULL); > + if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { > + /* Eep. This cannot happen */ > + printf("The world is broken. fec_decode() returned error\n"); > + exit(1); > + } > + gettimeofday(&now, NULL); > + fec_time += (now.tv_usec - start.tv_usec) / 1000; > + fec_time += (now.tv_sec - start.tv_sec) * 1000; > + > + for (i=0; i < pkts_per_block; i++) > + memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); > + > + /* Paranoia */ > + gettimeofday(&start, NULL); > + if (mtd_crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { > + printf("\nCRC mismatch for block #%d: want %08x got %08x\n", > + block_nr, eraseblocks[block_nr].crc, > + mtd_crc32(-1, decode_buf, meminfo.erasesize)); > + exit(1); > + } > + gettimeofday(&now, NULL); > + crc_time += (now.tv_usec - start.tv_usec) / 1000; > + crc_time += (now.tv_sec - start.tv_sec) * 1000; > + start = now; > + > + if (!file_mode) { > + struct erase_info_user erase; > + > + erase.start = eraseblocks[block_nr].flash_offset; > + erase.length = meminfo.erasesize; > + > + printf("\rErasing block at %08x...", erase.start); > + > + if (ioctl(flfd, MEMERASE, &erase)) { > + perror("MEMERASE"); > + /* This block has dirty data on it. If the erase failed, we're screwed */ > + fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); > + exit(1); > + } > + gettimeofday(&now, NULL); > + erase_time += (now.tv_usec - start.tv_usec) / 1000; > + erase_time += (now.tv_sec - start.tv_sec) * 1000; > + start = now; > + } > + else printf("\r"); > + write_again: > + rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); > + if (rwlen < meminfo.erasesize) { > + if (rwlen < 0) { > + perror("\ndecoded data write"); > + } else > + fprintf(stderr, "\nshort write of decoded data\n"); > + > + if (!file_mode) { > + struct erase_info_user erase; > + erase.start = eraseblocks[block_nr].flash_offset; > + erase.length = meminfo.erasesize; > + > + printf("Erasing failed block at %08x\n", > + eraseblocks[block_nr].flash_offset); > + > + if (ioctl(flfd, MEMERASE, &erase)) { > + perror("MEMERASE"); > + exit(1); > + } > + if (mtdoffset >= meminfo.size) { > + fprintf(stderr, "Run out of space on flash\n"); > + exit(1); > + } > + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { > + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); > + mtdoffset += meminfo.erasesize; > + if (mtdoffset >= meminfo.size) { > + fprintf(stderr, "Run out of space on flash\n"); > + exit(1); > + } > + } > + printf("Will try again at %08lx...", (long)mtdoffset); > + eraseblocks[block_nr].flash_offset = mtdoffset; > + > + goto write_again; > + } > + else /* Usually nothing we can do in file mode */ > + exit(1); > + } > + gettimeofday(&now, NULL); > + flash_time += (now.tv_usec - start.tv_usec) / 1000; > + flash_time += (now.tv_sec - start.tv_sec) * 1000; > + > + printf("wrote image block %08x (%d pkts) ", > + block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); > + fflush(stdout); > + } > + close(flfd); > + printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); > + printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); > + printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); > + printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); > + printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); > + printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); > + > + return 0; > +} > diff --git a/misc-utils/serve_image.c b/misc-utils/serve_image.c > new file mode 100644 > index 0000000..d3794ec > --- /dev/null > +++ b/misc-utils/serve_image.c > @@ -0,0 +1,300 @@ > +#define PROGRAM_NAME "serve_image" > +#define _POSIX_C_SOURCE 199309 > + > +#include <time.h> > +#include <errno.h> > +#include <netdb.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/socket.h> > +#include <sys/mman.h> > +#include <netinet/in.h> > +#include <sys/time.h> > +#include <crc32.h> > +#include <inttypes.h> > + > +#include "mcast_image.h" > + > +int tx_rate = 80000; > +int pkt_delay; > + > +#undef RANDOMDROP > + > +int main(int argc, char **argv) > +{ > + struct addrinfo *ai; > + struct addrinfo hints; > + struct addrinfo *runp; > + int ret; > + int sock; > + struct image_pkt pktbuf; > + int rfd; > + struct stat st; > + int writeerrors = 0; > + uint32_t erasesize; > + unsigned char *image, *blockptr = NULL; > + uint32_t block_nr, pkt_nr; > + int nr_blocks; > + struct timeval then, now, nextpkt; > + long time_msecs; > + int pkts_per_block; > + int total_pkts_per_block; > + struct fec_parms *fec; > + unsigned char *last_block; > + uint32_t *block_crcs; > + long tosleep; > + uint32_t sequence = 0; > + > + if (argc == 6) { > + tx_rate = atol(argv[5]) * 1024; > + if (tx_rate < PKT_SIZE || tx_rate > 20000000) { > + fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); > + exit(1); > + } > + argc = 5; > + } > + if (argc != 5) { > + fprintf(stderr, "usage: %s <host> <port> <image> <erasesize> [<tx_rate>]\n", > + PROGRAM_NAME); > + exit(1); > + } > + pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; > + printf("Inter-packet delay (avg): %dµs\n", pkt_delay); > + printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); > + > + erasesize = atol(argv[4]); > + if (!erasesize) { > + fprintf(stderr, "erasesize cannot be zero\n"); > + exit(1); > + } > + > + pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; > + total_pkts_per_block = pkts_per_block * 3 / 2; > + > + /* We have to pad it with zeroes, so can't use it in-place */ > + last_block = malloc(pkts_per_block * PKT_SIZE); > + if (!last_block) { > + fprintf(stderr, "Failed to allocate last-block buffer\n"); > + exit(1); > + } > + > + fec = fec_new(pkts_per_block, total_pkts_per_block); > + if (!fec) { > + fprintf(stderr, "Error initialising FEC\n"); > + exit(1); > + } > + > + memset(&hints, 0, sizeof(hints)); > + hints.ai_flags = AI_ADDRCONFIG; > + hints.ai_socktype = SOCK_DGRAM; > + > + ret = getaddrinfo(argv[1], argv[2], &hints, &ai); > + if (ret) { > + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); > + exit(1); > + } > + runp = ai; > + for (runp = ai; runp; runp = runp->ai_next) { > + sock = socket(runp->ai_family, runp->ai_socktype, > + runp->ai_protocol); > + if (sock == -1) { > + perror("socket"); > + continue; > + } > + if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) > + break; > + perror("connect"); > + close(sock); > + } > + if (!runp) > + exit(1); > + > + rfd = open(argv[3], O_RDONLY); > + if (rfd < 0) { > + perror("open"); > + exit(1); > + } > + > + if (fstat(rfd, &st)) { > + perror("fstat"); > + exit(1); > + } > + > + if (st.st_size % erasesize) { > + fprintf(stderr, "Image size %" PRIu64 " bytes is not a multiple of erasesize %d bytes\n", > + st.st_size, erasesize); > + exit(1); > + } > + image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); > + if (image == MAP_FAILED) { > + perror("mmap"); > + exit(1); > + } > + > + nr_blocks = st.st_size / erasesize; > + > + block_crcs = malloc(nr_blocks * sizeof(uint32_t)); > + if (!block_crcs) { > + fprintf(stderr, "Failed to allocate memory for CRCs\n"); > + exit(1); > + } > + > + memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); > + memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); > + > + printf("Checking CRC...."); > + fflush(stdout); > + > + pktbuf.hdr.resend = 0; > + pktbuf.hdr.totcrc = htonl(mtd_crc32(-1, image, st.st_size)); > + pktbuf.hdr.nr_blocks = htonl(nr_blocks); > + pktbuf.hdr.blocksize = htonl(erasesize); > + pktbuf.hdr.thislen = htonl(PKT_SIZE); > + pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); > + > + printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); > + printf("Checking block CRCs...."); > + fflush(stdout); > + for (block_nr=0; block_nr < nr_blocks; block_nr++) { > + printf("\rChecking block CRCS.... %d/%d", > + block_nr + 1, nr_blocks); > + fflush(stdout); > + block_crcs[block_nr] = mtd_crc32(-1, image + (block_nr * erasesize), erasesize); > + } > + > + printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" > + "Estimated transmit time per cycle: %ds\n", > + (long)st.st_size / 1024, (long) st.st_size, > + nr_blocks, pkts_per_block, > + nr_blocks * pkts_per_block * pkt_delay / 1000000); > + gettimeofday(&then, NULL); > + nextpkt = then; > + > +#ifdef RANDOMDROP > + srand((unsigned)then.tv_usec); > + printf("Random seed %u\n", (unsigned)then.tv_usec); > +#endif > + while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { > + > + if (blockptr && pkt_nr == 0) { > + unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); > + gettimeofday(&now, NULL); > + > + time_msecs = (now.tv_sec - then.tv_sec) * 1000; > + time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; > + printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", > + amt_sent / 1024, time_msecs, > + amt_sent / 1024 * 1000 / time_msecs); > + then = now; > + } > + > + for (block_nr = 0; block_nr < nr_blocks; block_nr++) { > + > + int actualpkt; > + > + /* Calculating the redundant FEC blocks is expensive; > + the first $pkts_per_block are cheap enough though > + because they're just copies. So alternate between > + simple and complex stuff, so that we don't start > + to choke and fail to keep up with the expected > + bitrate in the second half of the sequence */ > + if (block_nr & 1) > + actualpkt = pkt_nr; > + else > + actualpkt = total_pkts_per_block - 1 - pkt_nr; > + > + blockptr = image + (erasesize * block_nr); > + if (block_nr == nr_blocks - 1) > + blockptr = last_block; > + > + fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); > + > + pktbuf.hdr.thiscrc = htonl(mtd_crc32(-1, pktbuf.data, PKT_SIZE)); > + pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); > + pktbuf.hdr.block_nr = htonl(block_nr); > + pktbuf.hdr.pkt_nr = htons(actualpkt); > + pktbuf.hdr.pkt_sequence = htonl(sequence++); > + > + printf("\rSending data block %08x packet %3d/%d", > + block_nr * erasesize, > + pkt_nr, total_pkts_per_block); > + > + if (pkt_nr && !block_nr) { > + unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); > + > + gettimeofday(&now, NULL); > + > + time_msecs = (now.tv_sec - then.tv_sec) * 1000; > + time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; > + printf(" (%ld KiB/s) ", > + amt_sent / 1024 * 1000 / time_msecs); > + } > + > + fflush(stdout); > + > +#ifdef RANDOMDROP > + if ((rand() % 1000) < 20) { > + printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); > + continue; > + } > +#endif > + gettimeofday(&now, NULL); > +#if 1 > + tosleep = nextpkt.tv_usec - now.tv_usec + > + (1000000 * (nextpkt.tv_sec - now.tv_sec)); > + > + /* We need hrtimers for this to actually work */ > + if (tosleep > 0) { > + struct timespec req; > + > + req.tv_nsec = (tosleep % 1000000) * 1000; > + req.tv_sec = tosleep / 1000000; > + > + nanosleep(&req, NULL); > + } > +#else > + while (now.tv_sec < nextpkt.tv_sec || > + (now.tv_sec == nextpkt.tv_sec && > + now.tv_usec < nextpkt.tv_usec)) { > + gettimeofday(&now, NULL); > + } > +#endif > + nextpkt.tv_usec += pkt_delay; > + if (nextpkt.tv_usec >= 1000000) { > + nextpkt.tv_sec += nextpkt.tv_usec / 1000000; > + nextpkt.tv_usec %= 1000000; > + } > + > + /* If the time for the next packet has already > + passed (by some margin), then we've lost time > + Adjust our expected timings accordingly. If > + we're only a little way behind, don't slip yet */ > + if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + > + 1000000 * (nextpkt.tv_sec - now.tv_sec))) { > + nextpkt = now; > + } > + > + if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { > + perror("write"); > + writeerrors++; > + if (writeerrors > 10) { > + fprintf(stderr, "Too many consecutive write errors\n"); > + exit(1); > + } > + } else > + writeerrors = 0; > + > + > + > + } > + } > + munmap(image, st.st_size); > + close(rfd); > + close(sock); > + return 0; > +} > diff --git a/mkfs.jffs2.1 b/mkfs.jffs2.1 > deleted file mode 100644 > index 7c57ddc..0000000 > --- a/mkfs.jffs2.1 > +++ /dev/null > @@ -1,268 +0,0 @@ > -.TH MKFS.JFFS2 1 > -.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 --with-xattr > -] > -[ > -.B --with-selinux > -] > -[ > -.B --with-posix-acl > -] > -[ > -.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 available 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. Set according to target system's memory > -management page size (NOTE: this is NOT related to NAND page size). > -.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 --with-xattr > -Enables xattr, stuff all xattr entries into jffs2 image file. > -.TP > -.B --with-selinux > -Enables xattr, stuff only SELinux Labels into jffs2 image file. > -.TP > -.B --with-posix-acl > -Enable xattr, stuff only POSIX ACL entries into jffs2 image file. > -.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 available compressors and their default states. > -.TP > -.B -X, --enable-compressor=NAME > -Enable a compressor. Use > -.B -L > -to see the list of the available 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 available compressors and their default priority. > -Priorities are used by priority compression mode. > -.TP > -.B -L, --list-compressors > -Show the list of the available 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 LIMITATIONS > -The format and grammar of the device table file does not allow it to > -create symbolic links when the symbolic links are not already present > -in the root working directory. > - > -However, symbolic links may be specified in the device table file > -using the \fIl\fR type for the purposes of setting their permissions > -and ownership. > -.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 > deleted file mode 100644 > index f09c0b2..0000000 > --- a/mkfs.jffs2.c > +++ /dev/null > @@ -1,1805 +0,0 @@ > -/* 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 > - * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> > - * > - * 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 PROGRAM_NAME "mkfs.jffs2" > - > -#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> > -#ifndef WITHOUT_XATTR > -#include <sys/xattr.h> > -#include <sys/acl.h> > -#endif > -#include <byteswap.h> > -#include <crc32.h> > -#include <inttypes.h> > - > -#include "rbtree.h" > -#include "common.h" > - > -/* Do not use the weird XPG version of basename */ > -#undef basename > - > -//#define DMALLOC > -//#define mkfs_debug_msg errmsg > -#define mkfs_debug_msg(a...) { } > - > -#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 */ > - uint32_t ino; /* Inode number of this file in JFFS2 */ > - 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 */ > - struct rb_node hardlink_rb; > -}; > - > -struct rb_root hardlinks; > -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; > - > -uint32_t find_hardlink(struct filesystem_entry *e) > -{ > - struct filesystem_entry *f; > - struct rb_node **n = &hardlinks.rb_node; > - struct rb_node *parent = NULL; > - > - while (*n) { > - parent = *n; > - f = rb_entry(parent, struct filesystem_entry, hardlink_rb); > - > - if ((f->sb.st_dev < e->sb.st_dev) || > - (f->sb.st_dev == e->sb.st_dev && > - f->sb.st_ino < e->sb.st_ino)) > - n = &parent->rb_left; > - else if ((f->sb.st_dev > e->sb.st_dev) || > - (f->sb.st_dev == e->sb.st_dev && > - f->sb.st_ino > e->sb.st_ino)) { > - n = &parent->rb_right; > - } else > - return f->ino; > - } > - > - rb_link_node(&e->hardlink_rb, parent, n); > - rb_insert_color(&e->hardlink_rb, &hardlinks); > - return 0; > -} > - > -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) { > - sys_errmsg("%s:%s", PROGRAM_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) > - sys_errmsg_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)) { > - /* If this is the first call, and we actually want this > - * directory, then return it now */ > - if (strcmp(fullname, e->fullname) == 0) > - return e; > - > - e = dir->files; > - } > - while (e) { > - 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(const char *name, > - const 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)) { > - errmsg_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) { > - errmsg_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_ino = sb.st_ino; > - entry->sb.st_dev = sb.st_dev; > - entry->sb.st_nlink = sb.st_nlink; > - > - 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, const char *targetpath, > - const char *hostpath) > -{ > - int i, n; > - struct stat sb; > - char *hpath, *tpath; > - struct dirent *dp, **namelist; > - struct filesystem_entry *entry; > - > - > - if (lstat(hostpath, &sb)) { > - sys_errmsg_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) { > - sys_errmsg_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; > - } > - > - xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); > - if (lstat(hpath, &sb)) { > - sys_errmsg_die("%s", hpath); > - } > - if (strcmp(targetpath, "/") == 0) { > - xasprintf(&tpath, "%s%s", targetpath, dp->d_name); > - } else { > - xasprintf(&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: > - errmsg("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 = xmalloc(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, "/")) { > - errmsg_die("Device table entries require absolute paths"); > - } > - > - xasprintf(&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; > - case 'l': > - mode |= S_IFLNK; > - break; > - default: > - errmsg_die("Unsupported file type '%c'", type); > - } > - entry = find_filesystem_entry(root, name, mode); > - if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { > - /* 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 = xstrdup(name); > - dir = dirname(tmp); > - parent = find_filesystem_entry(root, dir, S_IFDIR); > - free(tmp); > - if (parent == NULL) { > - errmsg ("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 < (start + count); i++) { > - xasprintf(&dname, "%s%lu", name, i); > - xasprintf(&hpath, "%s/%s%lu", rootdir, name, i); > - rdev = makedev(major, minor + (i - start) * increment); > - 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: > - errmsg_die("Unsupported file type '%c'", 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_cleanmarkers = 1; > -static struct jffs2_unknown_node cleanmarker; > -static int cleanmarker_size = sizeof(cleanmarker); > -static unsigned char ffbuf[16] = > -{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, > - 0xff, 0xff, 0xff, 0xff, 0xff > -}; > - > -/* We set this at start of main() using sysconf(), -1 means we don't know */ > -/* When building an fs for non-native systems, use --pagesize=SIZE option */ > -int page_size = -1; > - > -#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) > - sys_errmsg_die("write"); > - > - if (ret == 0) > - sys_errmsg_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_cleanmarkers) { > - if ((out_ofs % erase_block_size) == 0) { > - full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); > - pad(cleanmarker_size - sizeof(cleanmarker)); > - padword(); > - } > - } > - if ((out_ofs % erase_block_size) + req > erase_block_size) { > - padblock(); > - } > - if (add_cleanmarkers) { > - if ((out_ofs % erase_block_size) == 0) { > - full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); > - pad(cleanmarker_size - sizeof(cleanmarker)); > - 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(mtd_crc32(0, &rd, > - sizeof(struct jffs2_unknown_node) - 4)); > - rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); > - rd.version = cpu_to_je32(version++); > - rd.ino = cpu_to_je32(e->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(mtd_crc32(0, &rd, sizeof(rd) - 8)); > - rd.name_crc = cpu_to_je32(mtd_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 unsigned int 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; > - unsigned int totcomp = 0; > - > - statbuf = &(e->sb); > - if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { > - errmsg("Skipping file \"%s\" too large.", e->path); > - return -1; > - } > - fd = open(e->hostname, O_RDONLY); > - if (fd == -1) { > - sys_errmsg_die("%s: open file", e->hostname); > - } > - > - e->ino = ++ino; > - mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", > - e->name, (unsigned long) e->ino, > - (unsigned long) e->parent->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(e->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) { > - sys_errmsg_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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > - ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); > - > - full_write(out_fd, &ri, sizeof(ri)); > - totcomp += sizeof(ri); > - full_write(out_fd, wbuf, space); > - totcomp += 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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > - > - full_write(out_fd, &ri, sizeof(ri)); > - padword(); > - } > - free(buf); > - close(fd); > - return totcomp; > -} > - > -static void write_symlink(struct filesystem_entry *e) > -{ > - int len; > - struct stat *statbuf; > - struct jffs2_raw_inode ri; > - > - statbuf = &(e->sb); > - e->ino = ++ino; > - mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", > - e->name, (unsigned long) e->ino, > - (unsigned long) e->parent->ino); > - write_dirent(e); > - > - len = strlen(e->link); > - if (len > JFFS2_MAX_SYMLINK_LEN) { > - errmsg("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(mtd_crc32(0, > - &ri, sizeof(struct jffs2_unknown_node) - 4)); > - > - ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > - ri.data_crc = cpu_to_je32(mtd_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); > - e->ino = ++ino; > - if (S_ISDIR(statbuf->st_mode)) { > - mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", > - e->name, (unsigned long) e->ino, > - (unsigned long) (e->parent) ? e->parent->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(mtd_crc32(0, > - &ri, sizeof(struct jffs2_unknown_node) - 4)); > - > - ri.ino = cpu_to_je32(e->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(mtd_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); > - e->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(mtd_crc32(0, > - &ri, sizeof(struct jffs2_unknown_node) - 4)); > - > - ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); > - ri.data_crc = cpu_to_je32(mtd_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(); > -} > - > -#ifndef WITHOUT_XATTR > -typedef struct xattr_entry { > - struct xattr_entry *next; > - uint32_t xid; > - int xprefix; > - char *xname; > - char *xvalue; > - int name_len; > - int value_len; > -} xattr_entry_t; > - > -#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ > -static uint32_t enable_xattr = 0; > -static uint32_t highest_xid = 0; > -static uint32_t highest_xseqno = 0; > - > -static struct { > - int xprefix; > - const char *string; > - int length; > -} xprefix_tbl[] = { > - { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, > - { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, > - { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, > - { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, > - { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, > - { 0, NULL, 0 } > -}; > - > -static void formalize_posix_acl(void *xvalue, int *value_len) > -{ > - struct posix_acl_xattr_header *pacl_header; > - struct posix_acl_xattr_entry *pent, *plim; > - struct jffs2_acl_header *jacl_header; > - struct jffs2_acl_entry *jent; > - struct jffs2_acl_entry_short *jent_s; > - char buffer[XATTR_BUFFER_SIZE]; > - int offset = 0; > - > - pacl_header = xvalue;; > - pent = pacl_header->a_entries; > - plim = xvalue + *value_len; > - > - jacl_header = (struct jffs2_acl_header *)buffer; > - offset += sizeof(struct jffs2_acl_header); > - jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); > - > - while (pent < plim) { > - switch(le16_to_cpu(pent->e_tag)) { > - case ACL_USER_OBJ: > - case ACL_GROUP_OBJ: > - case ACL_MASK: > - case ACL_OTHER: > - jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); > - offset += sizeof(struct jffs2_acl_entry_short); > - jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); > - jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); > - break; > - case ACL_USER: > - case ACL_GROUP: > - jent = (struct jffs2_acl_entry *)(buffer + offset); > - offset += sizeof(struct jffs2_acl_entry); > - jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); > - jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); > - jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); > - break; > - default: > - printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); > - exit(1); > - } > - pent++; > - } > - if (offset > *value_len) { > - printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", > - offset, *value_len); > - exit(1); > - } > - memcpy(xvalue, buffer, offset); > - *value_len = offset; > -} > - > -static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) > -{ > - xattr_entry_t *xe; > - struct jffs2_raw_xattr rx; > - int name_len; > - > - /* create xattr entry */ > - name_len = strlen(xname); > - xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); > - xe->next = NULL; > - xe->xid = ++highest_xid; > - xe->xprefix = xprefix; > - xe->xname = ((char *)xe) + sizeof(xattr_entry_t); > - xe->xvalue = xe->xname + name_len + 1; > - xe->name_len = name_len; > - xe->value_len = value_len; > - strcpy(xe->xname, xname); > - memcpy(xe->xvalue, xvalue, value_len); > - > - /* write xattr node */ > - memset(&rx, 0, sizeof(rx)); > - rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); > - rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); > - rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); > - rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); > - > - rx.xid = cpu_to_je32(xe->xid); > - rx.version = cpu_to_je32(1); /* initial version */ > - rx.xprefix = xprefix; > - rx.name_len = xe->name_len; > - rx.value_len = cpu_to_je16(xe->value_len); > - rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); > - rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4)); > - > - pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); > - full_write(out_fd, &rx, sizeof(rx)); > - full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); > - padword(); > - > - return xe; > -} > - > -#define XATTRENTRY_HASHSIZE 57 > -static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) > -{ > - static xattr_entry_t **xentry_hash = NULL; > - xattr_entry_t *xe; > - int index, name_len; > - > - /* create hash table */ > - if (!xentry_hash) > - xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); > - > - if (xprefix == JFFS2_XPREFIX_ACL_ACCESS > - || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) > - formalize_posix_acl(xvalue, &value_len); > - > - name_len = strlen(xname); > - index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; > - for (xe = xentry_hash[index]; xe; xe = xe->next) { > - if (xe->xprefix == xprefix > - && xe->value_len == value_len > - && !strcmp(xe->xname, xname) > - && !memcmp(xe->xvalue, xvalue, value_len)) > - break; > - } > - if (!xe) { > - xe = create_xattr_entry(xprefix, xname, xvalue, value_len); > - xe->next = xentry_hash[index]; > - xentry_hash[index] = xe; > - } > - return xe; > -} > - > -static void write_xattr_entry(struct filesystem_entry *e) > -{ > - struct jffs2_raw_xref ref; > - struct xattr_entry *xe; > - char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; > - char *xname; > - const char *prefix_str; > - int i, xprefix, prefix_len; > - int list_sz, offset, name_len, value_len; > - > - if (!enable_xattr) > - return; > - > - list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); > - if (list_sz < 0) { > - if (verbose) > - printf("llistxattr('%s') = %d : %s\n", > - e->hostname, errno, strerror(errno)); > - return; > - } > - > - for (offset = 0; offset < list_sz; offset += name_len) { > - xname = xlist + offset; > - name_len = strlen(xname) + 1; > - > - for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { > - prefix_str = xprefix_tbl[i].string; > - prefix_len = xprefix_tbl[i].length; > - if (prefix_str[prefix_len - 1] == '.') { > - if (!strncmp(xname, prefix_str, prefix_len - 1)) > - break; > - } else { > - if (!strcmp(xname, prefix_str)) > - break; > - } > - } > - if (!xprefix) { > - if (verbose) > - printf("%s: xattr '%s' is not supported.\n", > - e->hostname, xname); > - continue; > - } > - if ((enable_xattr & (1 << xprefix)) == 0) > - continue; > - > - value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); > - if (value_len < 0) { > - if (verbose) > - printf("lgetxattr('%s', '%s') = %d : %s\n", > - e->hostname, xname, errno, strerror(errno)); > - continue; > - } > - xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); > - if (!xe) { > - if (verbose) > - printf("%s : xattr '%s' was ignored.\n", > - e->hostname, xname); > - continue; > - } > - > - memset(&ref, 0, sizeof(ref)); > - ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); > - ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); > - ref.totlen = cpu_to_je32(sizeof(ref)); > - ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); > - ref.ino = cpu_to_je32(e->ino); > - ref.xid = cpu_to_je32(xe->xid); > - ref.xseqno = cpu_to_je32(highest_xseqno += 2); > - ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4)); > - > - pad_block_if_less_than(sizeof(ref)); > - full_write(out_fd, &ref, sizeof(ref)); > - padword(); > - } > -} > - > -#else /* WITHOUT_XATTR */ > -#define write_xattr_entry(x) > -#endif > - > -static void recursive_populate_directory(struct filesystem_entry *dir) > -{ > - struct filesystem_entry *e; > - unsigned int wrote; > - > - if (verbose) { > - printf("%s\n", dir->fullname); > - } > - write_xattr_entry(dir); /* for '/' */ > - > - e = dir->files; > - while (e) { > - if (e->sb.st_nlink >= 1 && > - (e->ino = find_hardlink(e))) { > - > - write_dirent(e); > - if (verbose) { > - printf("\tL %04o %9lu %5d:%-3d %s\n", > - e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, > - (int) (e->sb.st_uid), (int) (e->sb.st_gid), > - e->name); > - } > - } else switch (e->sb.st_mode & S_IFMT) { > - case S_IFDIR: > - if (verbose) { > - printf("\td %04o %9" PRIdoff_t " %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); > - write_xattr_entry(e); > - break; > - case S_IFSOCK: > - if (verbose) { > - printf("\ts %04o %9" PRIdoff_t " %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); > - write_xattr_entry(e); > - break; > - case S_IFIFO: > - if (verbose) { > - printf("\tp %04o %9" PRIdoff_t " %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); > - write_xattr_entry(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); > - write_xattr_entry(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); > - write_xattr_entry(e); > - break; > - case S_IFLNK: > - if (verbose) { > - printf("\tl %04o %9" PRIdoff_t " %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); > - write_xattr_entry(e); > - break; > - case S_IFREG: > - wrote = write_regular_file(e); > - write_xattr_entry(e); > - if (verbose) { > - printf("\tf %04o %9" PRIdoff_t " (%9u) %5d:%-3d %s\n", > - e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, > - (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); > - } > - break; > - default: > - errmsg("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) > -{ > - 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(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); > - > - if (ino == 0) > - ino = 1; > - > - root->ino = 1; > - recursive_populate_directory(root); > - > - if (pad_fs_size == -1) { > - padblock(); > - } else { > - if (pad_fs_size && add_cleanmarkers){ > - padblock(); > - while (out_ofs < pad_fs_size) { > - full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); > - pad(cleanmarker_size - sizeof(cleanmarker)); > - 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-cleanmarkers", 0, NULL, 'n'}, > - {"cleanmarker", 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'}, > - {"enable-compressor", 1, NULL, 'X'}, > - {"test-compression", 0, NULL, 't'}, > - {"compressor-priority", 1, NULL, 'y'}, > - {"incremental", 1, NULL, 'i'}, > -#ifndef WITHOUT_XATTR > - {"with-xattr", 0, NULL, 1000 }, > - {"with-selinux", 0, NULL, 1001 }, > - {"with-posix-acl", 0, NULL, 1002 }, > -#endif > - {NULL, 0, NULL, 0} > -}; > - > -static const 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.\n" > -" Set according to target system's memory management\n" > -" page size (default: 4KiB)\n" > -" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" > -" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" > -" -m, --compr-mode=MODE Select compression mode (default: priority)\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 available compressors\n" > -" -t, --test-compression Call decompress and compare with the original (for test)\n" > -" -n, --no-cleanmarkers Don't add a cleanmarker 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" > -#ifndef WITHOUT_XATTR > -" --with-xattr stuff all xattr entries into image\n" > -" --with-selinux stuff only SELinux Labels into jffs2 image\n" > -" --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" > -#endif > -" -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 const char revtext[] = "1.60"; > - > -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%08zx, 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%08zx, 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%08zx, 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%08zx, 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_PADDING: > - if (verbose) { > - printf ("%8s Padding node at 0x%08zx, 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%08zx, 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 = xmalloc(erase_block_size); > - > - 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; > - int warn_page_size = 0; > - > - page_size = sysconf(_SC_PAGESIZE); > - if (page_size < 0) /* System doesn't know so ... */ > - page_size = 4096; /* ... we make an educated guess */ > - if (page_size != 4096) > - warn_page_size = 1; /* warn user if page size not 4096 */ > - > - 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) > - sys_errmsg_die("%s", optarg); > - if (sb.st_size < 10) > - errmsg_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) { > - errmsg_die("root directory specified more than once"); > - } > - rootdir = xstrdup(optarg); > - break; > - > - case 's': > - page_size = strtol(optarg, NULL, 0); > - warn_page_size = 0; /* set by user, so don't need to warn */ > - break; > - > - case 'o': > - if (out_fd != -1) { > - errmsg_die("output filename specified more than once"); > - } > - out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); > - if (out_fd == -1) { > - sys_errmsg_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 '?': > - errmsg_die("%s", helptext); > - > - case 'v': > - verbose = 1; > - break; > - > - case 'V': > - errmsg_die("revision %s\n", revtext); > - > - case 'e': { > - char *next; > - unsigned units = 0; > - erase_block_size = strtol(optarg, &next, 0); > - if (!erase_block_size) > - errmsg_die("Unrecognisable erase size\n"); > - > - if (*next) { > - if (!strcmp(next, "KiB")) { > - units = 1024; > - } else if (!strcmp(next, "MiB")) { > - units = 1024 * 1024; > - } else { > - errmsg_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_cleanmarkers = 0; > - break; > - case 'c': > - cleanmarker_size = strtol(optarg, NULL, 0); > - if (cleanmarker_size < sizeof(cleanmarker)) { > - errmsg_die("cleanmarker size must be >= 12"); > - } > - if (cleanmarker_size >= erase_block_size) { > - errmsg_die("cleanmarker size must be < eraseblock size"); > - } > - break; > - case 'm': > - if (jffs2_set_compression_mode_name(optarg)) { > - errmsg_die("Unknown compression mode %s", optarg); > - } > - break; > - case 'x': > - if (jffs2_disable_compressor_name(optarg)) { > - errmsg_die("Unknown compressor name %s",optarg); > - } > - break; > - case 'X': > - if (jffs2_enable_compressor_name(optarg)) { > - errmsg_die("Unknown compressor name %s",optarg); > - } > - break; > - case 'L': > - errmsg_die("\n%s",jffs2_list_compressors()); > - break; > - case 't': > - jffs2_compression_check_set(1); > - break; > - case 'y': > - compr_name = xmalloc(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 { > - errmsg_die("Cannot parse %s",optarg); > - } > - free(compr_name); > - break; > - case 'i': > - if (in_fd != -1) { > - errmsg_die("(incremental) filename specified more than once"); > - } > - in_fd = open(optarg, O_RDONLY); > - if (in_fd == -1) { > - sys_errmsg_die("cannot open (incremental) file"); > - } > - break; > -#ifndef WITHOUT_XATTR > - case 1000: /* --with-xattr */ > - enable_xattr |= (1 << JFFS2_XPREFIX_USER) > - | (1 << JFFS2_XPREFIX_SECURITY) > - | (1 << JFFS2_XPREFIX_ACL_ACCESS) > - | (1 << JFFS2_XPREFIX_ACL_DEFAULT) > - | (1 << JFFS2_XPREFIX_TRUSTED); > - break; > - case 1001: /* --with-selinux */ > - enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); > - break; > - case 1002: /* --with-posix-acl */ > - enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) > - | (1 << JFFS2_XPREFIX_ACL_DEFAULT); > - break; > -#endif > - } > - } > - if (warn_page_size) { > - errmsg("Page size for this system is by default %d", page_size); > - errmsg("Use the --pagesize=SIZE option if this is not what you want"); > - } > - if (out_fd == -1) { > - if (isatty(1)) { > - errmsg_die("%s", helptext); > - } > - out_fd = 1; > - } > - if (lstat(rootdir, &sb)) { > - sys_errmsg_die("%s", rootdir); > - } > - if (chdir(rootdir)) > - sys_errmsg_die("%s", rootdir); > - > - if (!(cwd = getcwd(0, GETCWD_SIZE))) > - sys_errmsg_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/mkfs.ubifs/.gitignore b/mkfs.ubifs/.gitignore > deleted file mode 100644 > index 6b0e85c..0000000 > --- a/mkfs.ubifs/.gitignore > +++ /dev/null > @@ -1 +0,0 @@ > -/mkfs.ubifs > diff --git a/mkfs.ubifs/COPYING b/mkfs.ubifs/COPYING > deleted file mode 100644 > index 60549be..0000000 > --- a/mkfs.ubifs/COPYING > +++ /dev/null > @@ -1,340 +0,0 @@ > - 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/mkfs.ubifs/README b/mkfs.ubifs/README > deleted file mode 100644 > index 7e19939..0000000 > --- a/mkfs.ubifs/README > +++ /dev/null > @@ -1,9 +0,0 @@ > -UBIFS File System - Make File System program > - > -* crc16.h and crc16.c were copied from the linux kernel. > -* crc32.h and crc32.c were copied from mtd-utils and amended. > -* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. > -* key.h is copied from fs/ubifs/key.h from the linux kernel. > -* defs.h is a bunch of definitions to smooth things over. > -* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. > -* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ > diff --git a/mkfs.ubifs/compr.c b/mkfs.ubifs/compr.c > deleted file mode 100644 > index 34b2f60..0000000 > --- a/mkfs.ubifs/compr.c > +++ /dev/null > @@ -1,219 +0,0 @@ > -/* > - * Copyright (C) 2008 Nokia Corporation. > - * Copyright (C) 2008 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 version 2 as published by > - * the Free Software Foundation. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Artem Bityutskiy > - * Adrian Hunter > - * Zoltan Sogor > - */ > - > -#include <stdlib.h> > -#include <stdio.h> > -#include <stdint.h> > -#include <string.h> > -#include <lzo/lzo1x.h> > -#include <linux/types.h> > - > -#define crc32 __zlib_crc32 > -#include <zlib.h> > -#undef crc32 > - > -#include "compr.h" > -#include "mkfs.ubifs.h" > - > -static void *lzo_mem; > -static unsigned long long errcnt = 0; > -static struct ubifs_info *c = &info_; > - > -#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION > -#define DEFLATE_DEF_WINBITS 11 > -#define DEFLATE_DEF_MEMLEVEL 8 > - > -static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, > - size_t *out_len) > -{ > - z_stream strm; > - > - strm.zalloc = NULL; > - strm.zfree = NULL; > - > - /* > - * Match exactly the zlib parameters used by the Linux kernel crypto > - * API. > - */ > - if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, > - -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, > - Z_DEFAULT_STRATEGY)) { > - errcnt += 1; > - return -1; > - } > - > - strm.next_in = in_buf; > - strm.avail_in = in_len; > - strm.total_in = 0; > - > - strm.next_out = out_buf; > - strm.avail_out = *out_len; > - strm.total_out = 0; > - > - if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { > - deflateEnd(&strm); > - errcnt += 1; > - return -1; > - } > - > - if (deflateEnd(&strm) != Z_OK) { > - errcnt += 1; > - return -1; > - } > - > - *out_len = strm.total_out; > - > - return 0; > -} > - > -static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, > - size_t *out_len) > -{ > - lzo_uint len; > - int ret; > - > - len = *out_len; > - ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); > - *out_len = len; > - > - if (ret != LZO_E_OK) { > - errcnt += 1; > - return -1; > - } > - > - return 0; > -} > - > -static int no_compress(void *in_buf, size_t in_len, void *out_buf, > - size_t *out_len) > -{ > - memcpy(out_buf, in_buf, in_len); > - *out_len = in_len; > - return 0; > -} > - > -static char *zlib_buf; > - > -static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, > - size_t *out_len, int *type) > -{ > - int lzo_ret, zlib_ret; > - size_t lzo_len, zlib_len; > - > - lzo_len = zlib_len = *out_len; > - lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); > - zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); > - > - if (lzo_ret && zlib_ret) > - /* Both compressors failed */ > - return -1; > - > - if (!lzo_ret && !zlib_ret) { > - double percent; > - > - /* Both compressors succeeded */ > - if (lzo_len <= zlib_len ) > - goto select_lzo; > - > - percent = (double)zlib_len / (double)lzo_len; > - percent *= 100; > - if (percent > 100 - c->favor_percent) > - goto select_lzo; > - goto select_zlib; > - } > - > - if (lzo_ret) > - /* Only zlib compressor succeeded */ > - goto select_zlib; > - > - /* Only LZO compressor succeeded */ > - > -select_lzo: > - *out_len = lzo_len; > - *type = MKFS_UBIFS_COMPR_LZO; > - return 0; > - > -select_zlib: > - *out_len = zlib_len; > - *type = MKFS_UBIFS_COMPR_ZLIB; > - memcpy(out_buf, zlib_buf, zlib_len); > - return 0; > -} > - > -int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, > - int type) > -{ > - int ret; > - > - if (in_len < UBIFS_MIN_COMPR_LEN) { > - no_compress(in_buf, in_len, out_buf, out_len); > - return MKFS_UBIFS_COMPR_NONE; > - } > - > - if (c->favor_lzo) > - ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); > - else { > - switch (type) { > - case MKFS_UBIFS_COMPR_LZO: > - ret = lzo_compress(in_buf, in_len, out_buf, out_len); > - break; > - case MKFS_UBIFS_COMPR_ZLIB: > - ret = zlib_deflate(in_buf, in_len, out_buf, out_len); > - break; > - case MKFS_UBIFS_COMPR_NONE: > - ret = 1; > - break; > - default: > - errcnt += 1; > - ret = 1; > - break; > - } > - } > - if (ret || *out_len >= in_len) { > - no_compress(in_buf, in_len, out_buf, out_len); > - return MKFS_UBIFS_COMPR_NONE; > - } > - return type; > -} > - > -int init_compression(void) > -{ > - lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); > - if (!lzo_mem) > - return -1; > - > - zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); > - if (!zlib_buf) { > - free(lzo_mem); > - return -1; > - } > - > - return 0; > -} > - > -void destroy_compression(void) > -{ > - free(zlib_buf); > - free(lzo_mem); > - if (errcnt) > - fprintf(stderr, "%llu compression errors occurred\n", errcnt); > -} > diff --git a/mkfs.ubifs/compr.h b/mkfs.ubifs/compr.h > deleted file mode 100644 > index e3dd95c..0000000 > --- a/mkfs.ubifs/compr.h > +++ /dev/null > @@ -1,46 +0,0 @@ > -/* > - * Copyright (C) 2008 Nokia Corporation. > - * Copyright (C) 2008 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 version 2 as published by > - * the Free Software Foundation. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Artem Bityutskiy > - * Adrian Hunter > - * Zoltan Sogor > - */ > - > -#ifndef __UBIFS_COMPRESS_H__ > -#define __UBIFS_COMPRESS_H__ > - > -/* > - * Compressors may end-up with more data in the output buffer than in the input > - * buffer. This constant defined the worst case factor, i.e. we assume that the > - * output buffer may be at max. WORST_COMPR_FACTOR times larger than input > - * buffer. > - */ > -#define WORST_COMPR_FACTOR 4 > - > -enum compression_type > -{ > - MKFS_UBIFS_COMPR_NONE, > - MKFS_UBIFS_COMPR_LZO, > - MKFS_UBIFS_COMPR_ZLIB, > -}; > - > -int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, > - int type); > -int init_compression(void); > -void destroy_compression(void); > - > -#endif > diff --git a/mkfs.ubifs/crc16.c b/mkfs.ubifs/crc16.c > deleted file mode 100644 > index a19512e..0000000 > --- a/mkfs.ubifs/crc16.c > +++ /dev/null > @@ -1,56 +0,0 @@ > -/* > - * This code was taken from the linux kernel. The license is GPL Version 2. > - */ > - > -#include "crc16.h" > - > -/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ > -uint16_t const crc16_table[256] = { > - 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, > - 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, > - 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, > - 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, > - 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, > - 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, > - 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, > - 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, > - 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, > - 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, > - 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, > - 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, > - 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, > - 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, > - 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, > - 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, > - 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, > - 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, > - 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, > - 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, > - 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, > - 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, > - 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, > - 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, > - 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, > - 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, > - 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, > - 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, > - 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, > - 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, > - 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, > - 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 > -}; > - > -/** > - * crc16 - compute the CRC-16 for the data buffer > - * @crc: previous CRC value > - * @buffer: data pointer > - * @len: number of bytes in the buffer > - * > - * Returns the updated CRC value. > - */ > -uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) > -{ > - while (len--) > - crc = crc16_byte(crc, *buffer++); > - return crc; > -} > diff --git a/mkfs.ubifs/crc16.h b/mkfs.ubifs/crc16.h > deleted file mode 100644 > index 539d21a..0000000 > --- a/mkfs.ubifs/crc16.h > +++ /dev/null > @@ -1,27 +0,0 @@ > -/* > - * Implements the standard CRC-16: > - * Width 16 > - * Poly 0x8005 (x^16 + x^15 + x^2 + 1) > - * Init 0 > - * > - * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> > - * > - * This code was taken from the linux kernel. The license is GPL Version 2. > - */ > - > -#ifndef __CRC16_H__ > -#define __CRC16_H__ > - > -#include <stdlib.h> > -#include <stdint.h> > - > -extern uint16_t const crc16_table[256]; > - > -extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len); > - > -static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data) > -{ > - return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; > -} > - > -#endif /* __CRC16_H__ */ > diff --git a/mkfs.ubifs/defs.h b/mkfs.ubifs/defs.h > deleted file mode 100644 > index 1fa3316..0000000 > --- a/mkfs.ubifs/defs.h > +++ /dev/null > @@ -1,92 +0,0 @@ > -/* > - * Greate deal of the code was taken from the kernel UBIFS implementation, and > - * this file contains some "glue" definitions. > - */ > - > -#ifndef __UBIFS_DEFS_H__ > -#define __UBIFS_DEFS_H__ > - > -#define t16(x) ({ \ > - uint16_t __b = (x); \ > - (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ > -}) > - > -#define t32(x) ({ \ > - uint32_t __b = (x); \ > - (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ > -}) > - > -#define t64(x) ({ \ > - uint64_t __b = (x); \ > - (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ > -}) > - > -#define cpu_to_le16(x) ((__le16){t16(x)}) > -#define cpu_to_le32(x) ((__le32){t32(x)}) > -#define cpu_to_le64(x) ((__le64){t64(x)}) > - > -#define le16_to_cpu(x) (t16((x))) > -#define le32_to_cpu(x) (t32((x))) > -#define le64_to_cpu(x) (t64((x))) > - > -#define unlikely(x) (x) > - > -#define ubifs_assert(x) ({}) > - > -struct qstr > -{ > - char *name; > - size_t len; > -}; > - > -/** > - * fls - find last (most-significant) bit set > - * @x: the word to search > - * > - * This is defined the same way as ffs. > - * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. > - */ > -static inline int fls(int x) > -{ > - int r = 32; > - > - if (!x) > - return 0; > - if (!(x & 0xffff0000u)) { > - x <<= 16; > - r -= 16; > - } > - if (!(x & 0xff000000u)) { > - x <<= 8; > - r -= 8; > - } > - if (!(x & 0xf0000000u)) { > - x <<= 4; > - r -= 4; > - } > - if (!(x & 0xc0000000u)) { > - x <<= 2; > - r -= 2; > - } > - if (!(x & 0x80000000u)) { > - x <<= 1; > - r -= 1; > - } > - return r; > -} > - > -#define do_div(n,base) ({ \ > -int __res; \ > -__res = ((unsigned long) n) % (unsigned) base; \ > -n = ((unsigned long) n) / (unsigned) base; \ > -__res; }) > - > -#if INT_MAX != 0x7fffffff > -#error : sizeof(int) must be 4 for this program > -#endif > - > -#if (~0ULL) != 0xffffffffffffffffULL > -#error : sizeof(long long) must be 8 for this program > -#endif > - > -#endif > diff --git a/mkfs.ubifs/devtable.c b/mkfs.ubifs/devtable.c > deleted file mode 100644 > index dee035d..0000000 > --- a/mkfs.ubifs/devtable.c > +++ /dev/null > @@ -1,524 +0,0 @@ > -/* > - * Copyright (C) 2008 Nokia Corporation. > - * > - * 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. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Author: Artem Bityutskiy > - * > - * Part of the device table parsing code was taken from the mkfs.jffs2 utility. > - * The original author of that code is Erik Andersen, hence: > - * Copyright (C) 2001, 2002 Erik Andersen <andersen@codepoet.org> > - */ > - > -/* > - * This file implemented device table support. 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) > - * > - * 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. > - * > - * Please, refer the device_table.txt file which can be found at MTD utilities > - * for more information about what the device table is. > - */ > - > -#include "mkfs.ubifs.h" > -#include "hashtable/hashtable.h" > -#include "hashtable/hashtable_itr.h" > - > -/* > - * The hash table which contains paths to files/directories/device nodes > - * referred to in the device table. For example, if the device table refers > - * "/dev/loop0", the @path_htbl will contain "/dev" element. > - */ > -static struct hashtable *path_htbl; > - > -/* Hash function used for hash tables */ > -static unsigned int r5_hash(void *s) > -{ > - unsigned int a = 0; > - const signed char *str = s; > - > - while (*str) { > - a += *str << 4; > - a += *str >> 4; > - a *= 11; > - str++; > - } > - > - return a; > -} > - > -/* > - * Check whether 2 keys of a hash table are equivalent. The keys are path/file > - * names, so we simply use 'strcmp()'. > - */ > -static int is_equivalent(void *k1, void *k2) > -{ > - return !strcmp(k1, k2); > -} > - > -/** > - * separate_last - separate out the last path component > - * @buf: the path to split > - * @len: length of the @buf string > - * @path: the beginning of path is returned here > - * @name: the last path component is returned here > - * > - * This helper function separates out the the last component of the full path > - * string. For example, "/dev/loop" would be split on "/dev" and "loop". This > - * function allocates memory for @path and @name and return the result there. > - * Returns zero in case of success and a negative error code in case of > - * failure. > - */ > -static int separate_last(const char *buf, int len, char **path, char **name) > -{ > - int path_len = len, name_len; > - const char *p = buf + len, *n; > - > - while (*--p != '/') > - path_len -= 1; > - > - /* Drop the final '/' unless this is the root directory */ > - name_len = len - path_len; > - n = buf + path_len; > - if (path_len > 1) > - path_len -= 1; > - > - *path = malloc(path_len + 1); > - if (!*path) > - return err_msg("cannot allocate %d bytes of memory", > - path_len + 1); > - memcpy(*path, buf, path_len); > - (*path)[path_len] = '\0'; > - > - *name = malloc(name_len + 1); > - if (!*name) { > - free(*path); > - return err_msg("cannot allocate %d bytes of memory", > - name_len + 1); > - } > - memcpy(*name, n, name_len + 1); > - > - return 0; > -} > - > -static int interpret_table_entry(const char *line) > -{ > - char buf[1024], type, *path = NULL, *name = NULL; > - int len; > - struct path_htbl_element *ph_elt = NULL; > - struct name_htbl_element *nh_elt = NULL; > - unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; > - unsigned int start = 0, increment = 0, count = 0; > - > - if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u", > - buf, &type, &mode, &uid, &gid, &major, &minor, > - &start, &increment, &count) < 0) > - return sys_err_msg("sscanf failed"); > - > - dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, " > - "minor %u, start %u, inc %u, cnt %u", > - buf, type, mode, uid, gid, major, minor, start, > - increment, count); > - > - len = strnlen(buf, 1024); > - if (len == 1024) > - return err_msg("too long path"); > - > - if (!strcmp(buf, "/")) > - return err_msg("device table entries require absolute paths"); > - if (buf[1] == '\0') > - return err_msg("root directory cannot be created"); > - if (strstr(buf, "//")) > - return err_msg("'//' cannot be used in the path"); > - if (buf[len - 1] == '/') > - return err_msg("do not put '/' at the end"); > - > - if (strstr(buf, "/./") || strstr(buf, "/../") || > - !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/..")) > - return err_msg("'.' and '..' cannot be used in the path"); > - > - 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: > - return err_msg("unsupported file type '%c'", type); > - } > - > - if (separate_last(buf, len, &path, &name)) > - return -1; > - > - /* > - * Check if this path already exist in the path hash table and add it > - * if it is not. > - */ > - ph_elt = hashtable_search(path_htbl, path); > - if (!ph_elt) { > - dbg_msg(3, "inserting '%s' into path hash table", path); > - ph_elt = malloc(sizeof(struct path_htbl_element)); > - if (!ph_elt) { > - err_msg("cannot allocate %zd bytes of memory", > - sizeof(struct path_htbl_element)); > - goto out_free; > - } > - > - if (!hashtable_insert(path_htbl, path, ph_elt)) { > - err_msg("cannot insert into path hash table"); > - goto out_free; > - } > - > - ph_elt->path = path; > - path = NULL; > - ph_elt->name_htbl = create_hashtable(128, &r5_hash, > - &is_equivalent); > - if (!ph_elt->name_htbl) { > - err_msg("cannot create name hash table"); > - goto out_free; > - } > - } > - > - if (increment != 0 && count == 0) > - return err_msg("count cannot be zero if increment is non-zero"); > - > - /* > - * Add the file/directory/device node (last component of the path) to > - * the name hashtable. The name hashtable resides in the corresponding > - * path hashtable element. > - */ > - > - if (count == 0) { > - /* This entry does not require any iterating */ > - nh_elt = malloc(sizeof(struct name_htbl_element)); > - if (!nh_elt) { > - err_msg("cannot allocate %zd bytes of memory", > - sizeof(struct name_htbl_element)); > - goto out_free; > - } > - > - nh_elt->mode = mode; > - nh_elt->uid = uid; > - nh_elt->gid = gid; > - nh_elt->dev = makedev(major, minor); > - > - dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", > - name, major(nh_elt->dev), minor(nh_elt->dev)); > - > - if (hashtable_search(ph_elt->name_htbl, name)) > - return err_msg("'%s' is referred twice", buf); > - > - nh_elt->name = name; > - if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) { > - err_msg("cannot insert into name hash table"); > - goto out_free; > - } > - } else { > - int i, num = start + count, len = strlen(name) + 20; > - char *nm; > - > - for (i = start; i < num; i++) { > - nh_elt = malloc(sizeof(struct name_htbl_element)); > - if (!nh_elt) { > - err_msg("cannot allocate %zd bytes of memory", > - sizeof(struct name_htbl_element)); > - goto out_free; > - } > - > - nh_elt->mode = mode; > - nh_elt->uid = uid; > - nh_elt->gid = gid; > - nh_elt->dev = makedev(major, minor + (i - start) * increment); > - > - nm = malloc(len); > - if (!nm) { > - err_msg("cannot allocate %d bytes of memory", len); > - goto out_free; > - } > - > - sprintf(nm, "%s%d", name, i); > - nh_elt->name = nm; > - > - dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", > - nm, major(nh_elt->dev), minor(nh_elt->dev)); > - > - if (hashtable_search(ph_elt->name_htbl, nm)) { > - err_msg("'%s' is referred twice", buf); > - free (nm); > - goto out_free; > - } > - > - if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { > - err_msg("cannot insert into name hash table"); > - free (nm); > - goto out_free; > - } > - } > - free(name); > - name = NULL; > - } > - > - return 0; > - > -out_free: > - free(ph_elt); > - free(nh_elt); > - free(path); > - free(name); > - return -1; > -} > - > -/** > - * parse_devtable - parse the device table. > - * @tbl_file: device table file name > - * > - * This function parses the device table and prepare the hash table which will > - * later be used by mkfs.ubifs to create the specified files/device nodes. > - * Returns zero in case of success and a negative error code in case of > - * failure. > - */ > -int parse_devtable(const char *tbl_file) > -{ > - FILE *f; > - char *line = NULL; > - struct stat st; > - size_t len; > - > - dbg_msg(1, "parsing device table file '%s'", tbl_file); > - > - path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); > - if (!path_htbl) > - return err_msg("cannot create path hash table"); > - > - f = fopen(tbl_file, "r"); > - if (!f) > - return sys_err_msg("cannot open '%s'", tbl_file); > - > - if (fstat(fileno(f), &st) < 0) { > - sys_err_msg("cannot stat '%s'", tbl_file); > - goto out_close; > - } > - > - if (st.st_size < 10) { > - sys_err_msg("'%s' is too short", tbl_file); > - goto out_close; > - } > - > - /* > - * 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 > - */ > - while (getline(&line, &len, f) != -1) { > - /* First trim off any white-space */ > - len = strlen(line); > - > - /* Trim trailing white-space */ > - while (len > 0 && isspace(line[len - 1])) > - line[--len] = '\0'; > - /* Trim leading white-space */ > - 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(line)) { > - err_msg("cannot parse '%s'", line); > - goto out_close; > - } > - } > - > - free(line); > - line = NULL; > - } > - > - dbg_msg(1, "finished parsing"); > - fclose(f); > - return 0; > - > -out_close: > - fclose(f); > - free_devtable_info(); > - return -1; > -} > - > -/** > - * devtbl_find_path - find a path in the path hash table. > - * @path: UBIFS path to find. > - * > - * This looks up the path hash table. Returns the path hash table element > - * reference if @path was found and %NULL if not. > - */ > -struct path_htbl_element *devtbl_find_path(const char *path) > -{ > - if (!path_htbl) > - return NULL; > - > - return hashtable_search(path_htbl, (void *)path); > -} > - > -/** > - * devtbl_find_name - find a name in the name hash table. > - * @ph_etl: path hash table element to find at > - * @name: name to find > - * > - * This looks up the name hash table. Returns the name hash table element > - * reference if @name found and %NULL if not. > - */ > -struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, > - const char *name) > -{ > - if (!path_htbl) > - return NULL; > - > - return hashtable_search(ph_elt->name_htbl, (void *)name); > -} > - > -/** > - * override_attributes - override inode attributes. > - * @st: struct stat object to containing the attributes to override > - * @ph_elt: path hash table element object > - * @nh_elt: name hash table element object containing the new values > - * > - * The device table file may override attributes like UID of files. For > - * example, the device table may contain a "/dev" entry, and the UBIFS FS on > - * the host may contain "/dev" directory. In this case the attributes of the > - * "/dev" directory inode has to be as the device table specifies. > - * > - * Note, the hash element is removed by this function as well. > - */ > -int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, > - struct name_htbl_element *nh_elt) > -{ > - if (!path_htbl) > - return 0; > - > - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || > - S_ISFIFO(st->st_mode)) > - return err_msg("%s/%s both exists at UBIFS root at host, " > - "and is referred from the device table", > - strcmp(ph_elt->path, "/") ? ph_elt->path : "", > - nh_elt->name); > - > - if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) > - return err_msg("%s/%s is referred from the device table also exists in " > - "the UBIFS root directory at host, but the file type is " > - "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", > - nh_elt->name); > - > - dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", > - nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); > - > - st->st_uid = nh_elt->uid; > - st->st_gid = nh_elt->gid; > - st->st_mode = nh_elt->mode; > - > - hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); > - return 0; > -} > - > -/** > - * first_name_htbl_element - return first element of the name hash table. > - * @ph_elt: the path hash table the name hash table belongs to > - * @itr: double pointer to a 'struct hashtable_itr' object where the > - * information about further iterations is stored > - * > - * This function implements name hash table iteration together with > - * 'next_name_htbl_element()'. Returns the first name hash table element or > - * %NULL if the hash table is empty. > - */ > -struct name_htbl_element * > -first_name_htbl_element(struct path_htbl_element *ph_elt, > - struct hashtable_itr **itr) > -{ > - if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) > - return NULL; > - > - *itr = hashtable_iterator(ph_elt->name_htbl); > - return hashtable_iterator_value(*itr); > -} > - > -/** > - * first_name_htbl_element - return next element of the name hash table. > - * @ph_elt: the path hash table the name hash table belongs to > - * @itr: double pointer to a 'struct hashtable_itr' object where the > - * information about further iterations is stored > - * > - * This function implements name hash table iteration together with > - * 'first_name_htbl_element()'. Returns the next name hash table element or > - * %NULL if there are no more elements. > - */ > -struct name_htbl_element * > -next_name_htbl_element(struct path_htbl_element *ph_elt, > - struct hashtable_itr **itr) > -{ > - if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) > - return NULL; > - > - return hashtable_iterator_value(*itr); > -} > - > -/** > - * free_devtable_info - free device table information. > - * > - * This function frees the path hash table and the name hash tables. > - */ > -void free_devtable_info(void) > -{ > - struct hashtable_itr *ph_itr; > - struct path_htbl_element *ph_elt; > - > - if (!path_htbl) > - return; > - > - if (hashtable_count(path_htbl) > 0) { > - ph_itr = hashtable_iterator(path_htbl); > - do { > - ph_elt = hashtable_iterator_value(ph_itr); > - /* > - * Note, since we use the same string for the key and > - * @name in the name hash table elements, we do not > - * have to iterate name hash table because @name memory > - * will be freed when freeing the key. > - */ > - hashtable_destroy(ph_elt->name_htbl, 1); > - } while (hashtable_iterator_advance(ph_itr)); > - } > - hashtable_destroy(path_htbl, 1); > -} > diff --git a/mkfs.ubifs/hashtable/hashtable.c b/mkfs.ubifs/hashtable/hashtable.c > deleted file mode 100644 > index c1f99ed..0000000 > --- a/mkfs.ubifs/hashtable/hashtable.c > +++ /dev/null > @@ -1,277 +0,0 @@ > -/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > - > -#define PROGRAM_NAME "hashtable" > - > -#include "common.h" > -#include "hashtable.h" > -#include "hashtable_private.h" > -#include <stdlib.h> > -#include <stdio.h> > -#include <string.h> > -#include <math.h> > - > -/* > -Credit for primes table: Aaron Krowne > - http://br.endernet.org/~akrowne/ > - http://planetmath.org/encyclopedia/GoodHashTablePrimes.html > -*/ > -static const unsigned int primes[] = { > -53, 97, 193, 389, > -769, 1543, 3079, 6151, > -12289, 24593, 49157, 98317, > -196613, 393241, 786433, 1572869, > -3145739, 6291469, 12582917, 25165843, > -50331653, 100663319, 201326611, 402653189, > -805306457, 1610612741 > -}; > -const unsigned int prime_table_length = ARRAY_SIZE(primes); > -const float max_load_factor = 0.65; > - > -/*****************************************************************************/ > -struct hashtable * > -create_hashtable(unsigned int minsize, > - unsigned int (*hashf) (void*), > - int (*eqf) (void*,void*)) > -{ > - struct hashtable *h; > - unsigned int pindex, size = primes[0]; > - /* Check requested hashtable isn't too large */ > - if (minsize > (1u << 30)) return NULL; > - /* Enforce size as prime */ > - for (pindex=0; pindex < prime_table_length; pindex++) { > - if (primes[pindex] > minsize) { size = primes[pindex]; break; } > - } > - h = (struct hashtable *)malloc(sizeof(struct hashtable)); > - if (NULL == h) return NULL; /*oom*/ > - h->table = (struct entry **)malloc(sizeof(struct entry*) * size); > - if (NULL == h->table) { free(h); return NULL; } /*oom*/ > - memset(h->table, 0, size * sizeof(struct entry *)); > - h->tablelength = size; > - h->primeindex = pindex; > - h->entrycount = 0; > - h->hashfn = hashf; > - h->eqfn = eqf; > - h->loadlimit = (unsigned int) ceil(size * max_load_factor); > - return h; > -} > - > -/*****************************************************************************/ > -unsigned int > -hash(struct hashtable *h, void *k) > -{ > - /* Aim to protect against poor hash functions by adding logic here > - * - logic taken from java 1.4 hashtable source */ > - unsigned int i = h->hashfn(k); > - i += ~(i << 9); > - i ^= ((i >> 14) | (i << 18)); /* >>> */ > - i += (i << 4); > - i ^= ((i >> 10) | (i << 22)); /* >>> */ > - return i; > -} > - > -/*****************************************************************************/ > -static int > -hashtable_expand(struct hashtable *h) > -{ > - /* Double the size of the table to accomodate more entries */ > - struct entry **newtable; > - struct entry *e; > - struct entry **pE; > - unsigned int newsize, i, index; > - /* Check we're not hitting max capacity */ > - if (h->primeindex == (prime_table_length - 1)) return 0; > - newsize = primes[++(h->primeindex)]; > - > - newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); > - if (NULL != newtable) > - { > - memset(newtable, 0, newsize * sizeof(struct entry *)); > - /* This algorithm is not 'stable'. ie. it reverses the list > - * when it transfers entries between the tables */ > - for (i = 0; i < h->tablelength; i++) { > - while (NULL != (e = h->table[i])) { > - h->table[i] = e->next; > - index = indexFor(newsize,e->h); > - e->next = newtable[index]; > - newtable[index] = e; > - } > - } > - free(h->table); > - h->table = newtable; > - } > - /* Plan B: realloc instead */ > - else > - { > - newtable = (struct entry **) > - realloc(h->table, newsize * sizeof(struct entry *)); > - if (NULL == newtable) { (h->primeindex)--; return 0; } > - h->table = newtable; > - memset(newtable[h->tablelength], 0, newsize - h->tablelength); > - for (i = 0; i < h->tablelength; i++) { > - for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { > - index = indexFor(newsize,e->h); > - if (index == i) > - { > - pE = &(e->next); > - } > - else > - { > - *pE = e->next; > - e->next = newtable[index]; > - newtable[index] = e; > - } > - } > - } > - } > - h->tablelength = newsize; > - h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); > - return -1; > -} > - > -/*****************************************************************************/ > -unsigned int > -hashtable_count(struct hashtable *h) > -{ > - return h->entrycount; > -} > - > -/*****************************************************************************/ > -int > -hashtable_insert(struct hashtable *h, void *k, void *v) > -{ > - /* This method allows duplicate keys - but they shouldn't be used */ > - unsigned int index; > - struct entry *e; > - if (++(h->entrycount) > h->loadlimit) > - { > - /* Ignore the return value. If expand fails, we should > - * still try cramming just this value into the existing table > - * -- we may not have memory for a larger table, but one more > - * element may be ok. Next time we insert, we'll try expanding again.*/ > - hashtable_expand(h); > - } > - e = (struct entry *)malloc(sizeof(struct entry)); > - if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ > - e->h = hash(h,k); > - index = indexFor(h->tablelength,e->h); > - e->k = k; > - e->v = v; > - e->next = h->table[index]; > - h->table[index] = e; > - return -1; > -} > - > -/*****************************************************************************/ > -void * /* returns value associated with key */ > -hashtable_search(struct hashtable *h, void *k) > -{ > - struct entry *e; > - unsigned int hashvalue, index; > - hashvalue = hash(h,k); > - index = indexFor(h->tablelength,hashvalue); > - e = h->table[index]; > - while (NULL != e) > - { > - /* Check hash value to short circuit heavier comparison */ > - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; > - e = e->next; > - } > - return NULL; > -} > - > -/*****************************************************************************/ > -void * /* returns value associated with key */ > -hashtable_remove(struct hashtable *h, void *k) > -{ > - /* TODO: consider compacting the table when the load factor drops enough, > - * or provide a 'compact' method. */ > - > - struct entry *e; > - struct entry **pE; > - void *v; > - unsigned int hashvalue, index; > - > - hashvalue = hash(h,k); > - index = indexFor(h->tablelength,hash(h,k)); > - pE = &(h->table[index]); > - e = *pE; > - while (NULL != e) > - { > - /* Check hash value to short circuit heavier comparison */ > - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) > - { > - *pE = e->next; > - h->entrycount--; > - v = e->v; > - freekey(e->k); > - free(e); > - return v; > - } > - pE = &(e->next); > - e = e->next; > - } > - return NULL; > -} > - > -/*****************************************************************************/ > -/* destroy */ > -void > -hashtable_destroy(struct hashtable *h, int free_values) > -{ > - unsigned int i; > - struct entry *e, *f; > - struct entry **table = h->table; > - if (free_values) > - { > - for (i = 0; i < h->tablelength; i++) > - { > - e = table[i]; > - while (NULL != e) > - { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } > - } > - } > - else > - { > - for (i = 0; i < h->tablelength; i++) > - { > - e = table[i]; > - while (NULL != e) > - { f = e; e = e->next; freekey(f->k); free(f); } > - } > - } > - free(h->table); > - free(h); > -} > - > -/* > - * Copyright (c) 2002, Christopher Clark > - * All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * > - * * Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * > - * * 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. > - * > - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > - * 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. > -*/ > diff --git a/mkfs.ubifs/hashtable/hashtable.h b/mkfs.ubifs/hashtable/hashtable.h > deleted file mode 100644 > index c0b0acd..0000000 > --- a/mkfs.ubifs/hashtable/hashtable.h > +++ /dev/null > @@ -1,199 +0,0 @@ > -/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > - > -#ifndef __HASHTABLE_CWC22_H__ > -#define __HASHTABLE_CWC22_H__ > - > -struct hashtable; > - > -/* Example of use: > - * > - * struct hashtable *h; > - * struct some_key *k; > - * struct some_value *v; > - * > - * static unsigned int hash_from_key_fn( void *k ); > - * static int keys_equal_fn ( void *key1, void *key2 ); > - * > - * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); > - * k = (struct some_key *) malloc(sizeof(struct some_key)); > - * v = (struct some_value *) malloc(sizeof(struct some_value)); > - * > - * (initialise k and v to suitable values) > - * > - * if (! hashtable_insert(h,k,v) ) > - * { exit(-1); } > - * > - * if (NULL == (found = hashtable_search(h,k) )) > - * { printf("not found!"); } > - * > - * if (NULL == (found = hashtable_remove(h,k) )) > - * { printf("Not found\n"); } > - * > - */ > - > -/* Macros may be used to define type-safe(r) hashtable access functions, with > - * methods specialized to take known key and value types as parameters. > - * > - * Example: > - * > - * Insert this at the start of your file: > - * > - * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); > - * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); > - * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); > - * > - * This defines the functions 'insert_some', 'search_some' and 'remove_some'. > - * These operate just like hashtable_insert etc., with the same parameters, > - * but their function signatures have 'struct some_key *' rather than > - * 'void *', and hence can generate compile time errors if your program is > - * supplying incorrect data as a key (and similarly for value). > - * > - * Note that the hash and key equality functions passed to create_hashtable > - * still take 'void *' parameters instead of 'some key *'. This shouldn't be > - * a difficult issue as they're only defined and passed once, and the other > - * functions will ensure that only valid keys are supplied to them. > - * > - * The cost for this checking is increased code size and runtime overhead > - * - if performance is important, it may be worth switching back to the > - * unsafe methods once your program has been debugged with the safe methods. > - * This just requires switching to some simple alternative defines - eg: > - * #define insert_some hashtable_insert > - * > - */ > - > -/***************************************************************************** > - * create_hashtable > - > - * @name create_hashtable > - * @param minsize minimum initial size of hashtable > - * @param hashfunction function for hashing keys > - * @param key_eq_fn function for determining key equality > - * @return newly created hashtable or NULL on failure > - */ > - > -struct hashtable * > -create_hashtable(unsigned int minsize, > - unsigned int (*hashfunction) (void*), > - int (*key_eq_fn) (void*,void*)); > - > -/***************************************************************************** > - * hashtable_insert > - > - * @name hashtable_insert > - * @param h the hashtable to insert into > - * @param k the key - hashtable claims ownership and will free on removal > - * @param v the value - does not claim ownership > - * @return non-zero for successful insertion > - * > - * This function will cause the table to expand if the insertion would take > - * the ratio of entries to table size over the maximum load factor. > - * > - * This function does not check for repeated insertions with a duplicate key. > - * The value returned when using a duplicate key is undefined -- when > - * the hashtable changes size, the order of retrieval of duplicate key > - * entries is reversed. > - * If in doubt, remove before insert. > - */ > - > -int > -hashtable_insert(struct hashtable *h, void *k, void *v); > - > -#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ > -int fnname (struct hashtable *h, keytype *k, valuetype *v) \ > -{ \ > - return hashtable_insert(h,k,v); \ > -} > - > -/***************************************************************************** > - * hashtable_search > - > - * @name hashtable_search > - * @param h the hashtable to search > - * @param k the key to search for - does not claim ownership > - * @return the value associated with the key, or NULL if none found > - */ > - > -void * > -hashtable_search(struct hashtable *h, void *k); > - > -#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ > -valuetype * fnname (struct hashtable *h, keytype *k) \ > -{ \ > - return (valuetype *) (hashtable_search(h,k)); \ > -} > - > -/***************************************************************************** > - * hashtable_remove > - > - * @name hashtable_remove > - * @param h the hashtable to remove the item from > - * @param k the key to search for - does not claim ownership > - * @return the value associated with the key, or NULL if none found > - */ > - > -void * /* returns value */ > -hashtable_remove(struct hashtable *h, void *k); > - > -#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ > -valuetype * fnname (struct hashtable *h, keytype *k) \ > -{ \ > - return (valuetype *) (hashtable_remove(h,k)); \ > -} > - > - > -/***************************************************************************** > - * hashtable_count > - > - * @name hashtable_count > - * @param h the hashtable > - * @return the number of items stored in the hashtable > - */ > -unsigned int > -hashtable_count(struct hashtable *h); > - > - > -/***************************************************************************** > - * hashtable_destroy > - > - * @name hashtable_destroy > - * @param h the hashtable > - * @param free_values whether to call 'free' on the remaining values > - */ > - > -void > -hashtable_destroy(struct hashtable *h, int free_values); > - > -#endif /* __HASHTABLE_CWC22_H__ */ > - > -/* > - * Copyright (c) 2002, Christopher Clark > - * All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * > - * * Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * > - * * 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. > - * > - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > - * 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. > -*/ > diff --git a/mkfs.ubifs/hashtable/hashtable_itr.c b/mkfs.ubifs/hashtable/hashtable_itr.c > deleted file mode 100644 > index d102453..0000000 > --- a/mkfs.ubifs/hashtable/hashtable_itr.c > +++ /dev/null > @@ -1,176 +0,0 @@ > -/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > - > -#include "hashtable.h" > -#include "hashtable_private.h" > -#include "hashtable_itr.h" > -#include <stdlib.h> /* defines NULL */ > - > -/*****************************************************************************/ > -/* hashtable_iterator - iterator constructor */ > - > -struct hashtable_itr * > -hashtable_iterator(struct hashtable *h) > -{ > - unsigned int i, tablelength; > - struct hashtable_itr *itr = (struct hashtable_itr *) > - malloc(sizeof(struct hashtable_itr)); > - if (NULL == itr) return NULL; > - itr->h = h; > - itr->e = NULL; > - itr->parent = NULL; > - tablelength = h->tablelength; > - itr->index = tablelength; > - if (0 == h->entrycount) return itr; > - > - for (i = 0; i < tablelength; i++) > - { > - if (NULL != h->table[i]) > - { > - itr->e = h->table[i]; > - itr->index = i; > - break; > - } > - } > - return itr; > -} > - > -/*****************************************************************************/ > -/* advance - advance the iterator to the next element > - * returns zero if advanced to end of table */ > - > -int > -hashtable_iterator_advance(struct hashtable_itr *itr) > -{ > - unsigned int j,tablelength; > - struct entry **table; > - struct entry *next; > - if (NULL == itr->e) return 0; /* stupidity check */ > - > - next = itr->e->next; > - if (NULL != next) > - { > - itr->parent = itr->e; > - itr->e = next; > - return -1; > - } > - tablelength = itr->h->tablelength; > - itr->parent = NULL; > - if (tablelength <= (j = ++(itr->index))) > - { > - itr->e = NULL; > - return 0; > - } > - table = itr->h->table; > - while (NULL == (next = table[j])) > - { > - if (++j >= tablelength) > - { > - itr->index = tablelength; > - itr->e = NULL; > - return 0; > - } > - } > - itr->index = j; > - itr->e = next; > - return -1; > -} > - > -/*****************************************************************************/ > -/* remove - remove the entry at the current iterator position > - * and advance the iterator, if there is a successive > - * element. > - * If you want the value, read it before you remove: > - * beware memory leaks if you don't. > - * Returns zero if end of iteration. */ > - > -int > -hashtable_iterator_remove(struct hashtable_itr *itr) > -{ > - struct entry *remember_e, *remember_parent; > - int ret; > - > - /* Do the removal */ > - if (NULL == (itr->parent)) > - { > - /* element is head of a chain */ > - itr->h->table[itr->index] = itr->e->next; > - } else { > - /* element is mid-chain */ > - itr->parent->next = itr->e->next; > - } > - /* itr->e is now outside the hashtable */ > - remember_e = itr->e; > - itr->h->entrycount--; > - freekey(remember_e->k); > - > - /* Advance the iterator, correcting the parent */ > - remember_parent = itr->parent; > - ret = hashtable_iterator_advance(itr); > - if (itr->parent == remember_e) { itr->parent = remember_parent; } > - free(remember_e); > - return ret; > -} > - > -/*****************************************************************************/ > -int /* returns zero if not found */ > -hashtable_iterator_search(struct hashtable_itr *itr, > - struct hashtable *h, void *k) > -{ > - struct entry *e, *parent; > - unsigned int hashvalue, index; > - > - hashvalue = hash(h,k); > - index = indexFor(h->tablelength,hashvalue); > - > - e = h->table[index]; > - parent = NULL; > - while (NULL != e) > - { > - /* Check hash value to short circuit heavier comparison */ > - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) > - { > - itr->index = index; > - itr->e = e; > - itr->parent = parent; > - itr->h = h; > - return -1; > - } > - parent = e; > - e = e->next; > - } > - return 0; > -} > - > - > -/* > - * Copyright (c) 2002, 2004, Christopher Clark > - * All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * > - * * Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * > - * * 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. > - * > - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > - * 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. > -*/ > diff --git a/mkfs.ubifs/hashtable/hashtable_itr.h b/mkfs.ubifs/hashtable/hashtable_itr.h > deleted file mode 100644 > index 5c94a04..0000000 > --- a/mkfs.ubifs/hashtable/hashtable_itr.h > +++ /dev/null > @@ -1,112 +0,0 @@ > -/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > - > -#ifndef __HASHTABLE_ITR_CWC22__ > -#define __HASHTABLE_ITR_CWC22__ > -#include "hashtable.h" > -#include "hashtable_private.h" /* needed to enable inlining */ > - > -/*****************************************************************************/ > -/* This struct is only concrete here to allow the inlining of two of the > - * accessor functions. */ > -struct hashtable_itr > -{ > - struct hashtable *h; > - struct entry *e; > - struct entry *parent; > - unsigned int index; > -}; > - > - > -/*****************************************************************************/ > -/* hashtable_iterator > - */ > - > -struct hashtable_itr * > -hashtable_iterator(struct hashtable *h); > - > -/*****************************************************************************/ > -/* hashtable_iterator_key > - * - return the value of the (key,value) pair at the current position */ > - > -static inline void * > -hashtable_iterator_key(struct hashtable_itr *i) > -{ > - return i->e->k; > -} > - > -/*****************************************************************************/ > -/* value - return the value of the (key,value) pair at the current position */ > - > -static inline void * > -hashtable_iterator_value(struct hashtable_itr *i) > -{ > - return i->e->v; > -} > - > -/*****************************************************************************/ > -/* advance - advance the iterator to the next element > - * returns zero if advanced to end of table */ > - > -int > -hashtable_iterator_advance(struct hashtable_itr *itr); > - > -/*****************************************************************************/ > -/* remove - remove current element and advance the iterator to the next element > - * NB: if you need the value to free it, read it before > - * removing. ie: beware memory leaks! > - * returns zero if advanced to end of table */ > - > -int > -hashtable_iterator_remove(struct hashtable_itr *itr); > - > -/*****************************************************************************/ > -/* search - overwrite the supplied iterator, to point to the entry > - * matching the supplied key. > - h points to the hashtable to be searched. > - * returns zero if not found. */ > -int > -hashtable_iterator_search(struct hashtable_itr *itr, > - struct hashtable *h, void *k); > - > -#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ > -int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ > -{ \ > - return (hashtable_iterator_search(i,h,k)); \ > -} > - > - > - > -#endif /* __HASHTABLE_ITR_CWC22__*/ > - > -/* > - * Copyright (c) 2002, 2004, Christopher Clark > - * All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * > - * * Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * > - * * 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. > - * > - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > - * 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. > -*/ > diff --git a/mkfs.ubifs/hashtable/hashtable_private.h b/mkfs.ubifs/hashtable/hashtable_private.h > deleted file mode 100644 > index 3a558e6..0000000 > --- a/mkfs.ubifs/hashtable/hashtable_private.h > +++ /dev/null > @@ -1,85 +0,0 @@ > -/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > - > -#ifndef __HASHTABLE_PRIVATE_CWC22_H__ > -#define __HASHTABLE_PRIVATE_CWC22_H__ > - > -#include "hashtable.h" > - > -/*****************************************************************************/ > -struct entry > -{ > - void *k, *v; > - unsigned int h; > - struct entry *next; > -}; > - > -struct hashtable { > - unsigned int tablelength; > - struct entry **table; > - unsigned int entrycount; > - unsigned int loadlimit; > - unsigned int primeindex; > - unsigned int (*hashfn) (void *k); > - int (*eqfn) (void *k1, void *k2); > -}; > - > -/*****************************************************************************/ > -unsigned int > -hash(struct hashtable *h, void *k); > - > -/*****************************************************************************/ > -/* indexFor */ > -static inline unsigned int > -indexFor(unsigned int tablelength, unsigned int hashvalue) { > - return (hashvalue % tablelength); > -}; > - > -/* Only works if tablelength == 2^N */ > -/*static inline unsigned int > -indexFor(unsigned int tablelength, unsigned int hashvalue) > -{ > - return (hashvalue & (tablelength - 1u)); > -} > -*/ > - > -/*****************************************************************************/ > -#define freekey(X) free(X) > -/*define freekey(X) ; */ > - > - > -/*****************************************************************************/ > - > -#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ > - > -/* > - * Copyright (c) 2002, Christopher Clark > - * All rights reserved. > - * > - * Redistribution and use in source and binary forms, with or without > - * modification, are permitted provided that the following conditions > - * are met: > - * > - * * Redistributions of source code must retain the above copyright > - * notice, this list of conditions and the following disclaimer. > - * > - * * 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. > - * > - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > - * 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. > -*/ > diff --git a/mkfs.ubifs/key.h b/mkfs.ubifs/key.h > deleted file mode 100644 > index d3a02d4..0000000 > --- a/mkfs.ubifs/key.h > +++ /dev/null > @@ -1,189 +0,0 @@ > -/* > - * This file is part of UBIFS. > - * > - * Copyright (C) 2006-2008 Nokia Corporation. > - * > - * 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. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Artem Bityutskiy (Битюцкий Артём) > - * Adrian Hunter > - */ > - > -/* > - * This header contains various key-related definitions and helper function. > - * UBIFS allows several key schemes, so we access key fields only via these > - * helpers. At the moment only one key scheme is supported. > - * > - * Simple key scheme > - * ~~~~~~~~~~~~~~~~~ > - * > - * Keys are 64-bits long. First 32-bits are inode number (parent inode number > - * in case of direntry key). Next 3 bits are node type. The last 29 bits are > - * 4KiB offset in case of inode node, and direntry hash in case of a direntry > - * node. We use "r5" hash borrowed from reiserfs. > - */ > - > -#ifndef __UBIFS_KEY_H__ > -#define __UBIFS_KEY_H__ > - > -/** > - * key_mask_hash - mask a valid hash value. > - * @val: value to be masked > - * > - * We use hash values as offset in directories, so values %0 and %1 are > - * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This > - * function makes sure the reserved values are not used. > - */ > -static inline uint32_t key_mask_hash(uint32_t hash) > -{ > - hash &= UBIFS_S_KEY_HASH_MASK; > - if (unlikely(hash <= 2)) > - hash += 3; > - return hash; > -} > - > -/** > - * key_r5_hash - R5 hash function (borrowed from reiserfs). > - * @s: direntry name > - * @len: name length > - */ > -static inline uint32_t key_r5_hash(const char *s, int len) > -{ > - uint32_t a = 0; > - const signed char *str = (const signed char *)s; > - > - len = len; > - while (*str) { > - a += *str << 4; > - a += *str >> 4; > - a *= 11; > - str++; > - } > - > - return key_mask_hash(a); > -} > - > -/** > - * key_test_hash - testing hash function. > - * @str: direntry name > - * @len: name length > - */ > -static inline uint32_t key_test_hash(const char *str, int len) > -{ > - uint32_t a = 0; > - > - len = min_t(uint32_t, len, 4); > - memcpy(&a, str, len); > - return key_mask_hash(a); > -} > - > -/** > - * ino_key_init - initialize inode key. > - * @c: UBIFS file-system description object > - * @key: key to initialize > - * @inum: inode number > - */ > -static inline void ino_key_init(union ubifs_key *key, ino_t inum) > -{ > - key->u32[0] = inum; > - key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; > -} > - > -/** > - * dent_key_init - initialize directory entry key. > - * @c: UBIFS file-system description object > - * @key: key to initialize > - * @inum: parent inode number > - * @nm: direntry name and length > - */ > -static inline void dent_key_init(const struct ubifs_info *c, > - union ubifs_key *key, ino_t inum, > - const struct qstr *nm) > -{ > - uint32_t hash = c->key_hash(nm->name, nm->len); > - > - ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); > - key->u32[0] = inum; > - key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); > -} > - > -/** > - * data_key_init - initialize data key. > - * @c: UBIFS file-system description object > - * @key: key to initialize > - * @inum: inode number > - * @block: block number > - */ > -static inline void data_key_init(union ubifs_key *key, ino_t inum, > - unsigned int block) > -{ > - ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); > - key->u32[0] = inum; > - key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); > -} > - > -/** > - * key_write - transform a key from in-memory format. > - * @c: UBIFS file-system description object > - * @from: the key to transform > - * @to: the key to store the result > - */ > -static inline void key_write(const union ubifs_key *from, void *to) > -{ > - union ubifs_key *t = to; > - > - t->j32[0] = cpu_to_le32(from->u32[0]); > - t->j32[1] = cpu_to_le32(from->u32[1]); > - memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); > -} > - > -/** > - * key_write_idx - transform a key from in-memory format for the index. > - * @c: UBIFS file-system description object > - * @from: the key to transform > - * @to: the key to store the result > - */ > -static inline void key_write_idx(const union ubifs_key *from, void *to) > -{ > - union ubifs_key *t = to; > - > - t->j32[0] = cpu_to_le32(from->u32[0]); > - t->j32[1] = cpu_to_le32(from->u32[1]); > -} > - > -/** > - * keys_cmp - compare keys. > - * @c: UBIFS file-system description object > - * @key1: the first key to compare > - * @key2: the second key to compare > - * > - * This function compares 2 keys and returns %-1 if @key1 is less than > - * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. > - */ > -static inline int keys_cmp(const union ubifs_key *key1, > - const union ubifs_key *key2) > -{ > - if (key1->u32[0] < key2->u32[0]) > - return -1; > - if (key1->u32[0] > key2->u32[0]) > - return 1; > - if (key1->u32[1] < key2->u32[1]) > - return -1; > - if (key1->u32[1] > key2->u32[1]) > - return 1; > - > - return 0; > -} > - > -#endif /* !__UBIFS_KEY_H__ */ > diff --git a/mkfs.ubifs/lpt.c b/mkfs.ubifs/lpt.c > deleted file mode 100644 > index 6aa0b88..0000000 > --- a/mkfs.ubifs/lpt.c > +++ /dev/null > @@ -1,578 +0,0 @@ > -/* > - * This file is part of UBIFS. > - * > - * Copyright (C) 2006, 2007 Nokia Corporation. > - * > - * 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. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Adrian Hunter > - * Artem Bityutskiy > - */ > - > -#include "mkfs.ubifs.h" > - > -/** > - * do_calc_lpt_geom - calculate sizes for the LPT area. > - * @c: the UBIFS file-system description object > - * > - * Calculate the sizes of LPT bit fields, nodes, and tree, based on the > - * properties of the flash and whether LPT is "big" (c->big_lpt). > - */ > -static void do_calc_lpt_geom(struct ubifs_info *c) > -{ > - int n, bits, per_leb_wastage; > - long long sz, tot_wastage; > - > - c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > - > - n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > - c->nnode_cnt = n; > - while (n > 1) { > - n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > - c->nnode_cnt += n; > - } > - > - c->lpt_hght = 1; > - n = UBIFS_LPT_FANOUT; > - while (n < c->pnode_cnt) { > - c->lpt_hght += 1; > - n <<= UBIFS_LPT_FANOUT_SHIFT; > - } > - > - c->space_bits = fls(c->leb_size) - 3; > - c->lpt_lnum_bits = fls(c->lpt_lebs); > - c->lpt_offs_bits = fls(c->leb_size - 1); > - c->lpt_spc_bits = fls(c->leb_size); > - > - n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > - c->pcnt_bits = fls(n - 1); > - > - c->lnum_bits = fls(c->max_leb_cnt - 1); > - > - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > - (c->big_lpt ? c->pcnt_bits : 0) + > - (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; > - c->pnode_sz = (bits + 7) / 8; > - > - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > - (c->big_lpt ? c->pcnt_bits : 0) + > - (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; > - c->nnode_sz = (bits + 7) / 8; > - > - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > - c->lpt_lebs * c->lpt_spc_bits * 2; > - c->ltab_sz = (bits + 7) / 8; > - > - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > - c->lnum_bits * c->lsave_cnt; > - c->lsave_sz = (bits + 7) / 8; > - > - /* Calculate the minimum LPT size */ > - c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; > - c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; > - c->lpt_sz += c->ltab_sz; > - c->lpt_sz += c->lsave_sz; > - > - /* Add wastage */ > - sz = c->lpt_sz; > - per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); > - sz += per_leb_wastage; > - tot_wastage = per_leb_wastage; > - while (sz > c->leb_size) { > - sz += per_leb_wastage; > - sz -= c->leb_size; > - tot_wastage += per_leb_wastage; > - } > - tot_wastage += ALIGN(sz, c->min_io_size) - sz; > - c->lpt_sz += tot_wastage; > -} > - > -/** > - * calc_dflt_lpt_geom - calculate default LPT geometry. > - * @c: the UBIFS file-system description object > - * @main_lebs: number of main area LEBs is passed and returned here > - * @big_lpt: whether the LPT area is "big" is returned here > - * > - * The size of the LPT area depends on parameters that themselves are dependent > - * on the size of the LPT area. This function, successively recalculates the LPT > - * area geometry until the parameters and resultant geometry are consistent. > - * > - * This function returns %0 on success and a negative error code on failure. > - */ > -int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) > -{ > - int i, lebs_needed; > - long long sz; > - > - /* Start by assuming the minimum number of LPT LEBs */ > - c->lpt_lebs = UBIFS_MIN_LPT_LEBS; > - c->main_lebs = *main_lebs - c->lpt_lebs; > - if (c->main_lebs <= 0) > - return -EINVAL; > - > - /* And assume we will use the small LPT model */ > - c->big_lpt = 0; > - > - /* > - * Calculate the geometry based on assumptions above and then see if it > - * makes sense > - */ > - do_calc_lpt_geom(c); > - > - /* Small LPT model must have lpt_sz < leb_size */ > - if (c->lpt_sz > c->leb_size) { > - /* Nope, so try again using big LPT model */ > - c->big_lpt = 1; > - do_calc_lpt_geom(c); > - } > - > - /* Now check there are enough LPT LEBs */ > - for (i = 0; i < 64 ; i++) { > - sz = c->lpt_sz * 4; /* Allow 4 times the size */ > - sz += c->leb_size - 1; > - do_div(sz, c->leb_size); > - lebs_needed = sz; > - if (lebs_needed > c->lpt_lebs) { > - /* Not enough LPT LEBs so try again with more */ > - c->lpt_lebs = lebs_needed; > - c->main_lebs = *main_lebs - c->lpt_lebs; > - if (c->main_lebs <= 0) > - return -EINVAL; > - do_calc_lpt_geom(c); > - continue; > - } > - if (c->ltab_sz > c->leb_size) { > - err_msg("LPT ltab too big"); > - return -EINVAL; > - } > - *main_lebs = c->main_lebs; > - *big_lpt = c->big_lpt; > - return 0; > - } > - return -EINVAL; > -} > - > -/** > - * pack_bits - pack bit fields end-to-end. > - * @addr: address at which to pack (passed and next address returned) > - * @pos: bit position at which to pack (passed and next position returned) > - * @val: value to pack > - * @nrbits: number of bits of value to pack (1-32) > - */ > -static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) > -{ > - uint8_t *p = *addr; > - int b = *pos; > - > - if (b) { > - *p |= ((uint8_t)val) << b; > - nrbits += b; > - if (nrbits > 8) { > - *++p = (uint8_t)(val >>= (8 - b)); > - if (nrbits > 16) { > - *++p = (uint8_t)(val >>= 8); > - if (nrbits > 24) { > - *++p = (uint8_t)(val >>= 8); > - if (nrbits > 32) > - *++p = (uint8_t)(val >>= 8); > - } > - } > - } > - } else { > - *p = (uint8_t)val; > - if (nrbits > 8) { > - *++p = (uint8_t)(val >>= 8); > - if (nrbits > 16) { > - *++p = (uint8_t)(val >>= 8); > - if (nrbits > 24) > - *++p = (uint8_t)(val >>= 8); > - } > - } > - } > - b = nrbits & 7; > - if (b == 0) > - p++; > - *addr = p; > - *pos = b; > -} > - > -/** > - * pack_pnode - pack all the bit fields of a pnode. > - * @c: UBIFS file-system description object > - * @buf: buffer into which to pack > - * @pnode: pnode to pack > - */ > -static void pack_pnode(struct ubifs_info *c, void *buf, > - struct ubifs_pnode *pnode) > -{ > - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > - int i, pos = 0; > - uint16_t crc; > - > - pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); > - if (c->big_lpt) > - pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); > - for (i = 0; i < UBIFS_LPT_FANOUT; i++) { > - pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, > - c->space_bits); > - pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, > - c->space_bits); > - if (pnode->lprops[i].flags & LPROPS_INDEX) > - pack_bits(&addr, &pos, 1, 1); > - else > - pack_bits(&addr, &pos, 0, 1); > - } > - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > - c->pnode_sz - UBIFS_LPT_CRC_BYTES); > - addr = buf; > - pos = 0; > - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > -} > - > -/** > - * pack_nnode - pack all the bit fields of a nnode. > - * @c: UBIFS file-system description object > - * @buf: buffer into which to pack > - * @nnode: nnode to pack > - */ > -static void pack_nnode(struct ubifs_info *c, void *buf, > - struct ubifs_nnode *nnode) > -{ > - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > - int i, pos = 0; > - uint16_t crc; > - > - pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); > - if (c->big_lpt) > - pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); > - for (i = 0; i < UBIFS_LPT_FANOUT; i++) { > - int lnum = nnode->nbranch[i].lnum; > - > - if (lnum == 0) > - lnum = c->lpt_last + 1; > - pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); > - pack_bits(&addr, &pos, nnode->nbranch[i].offs, > - c->lpt_offs_bits); > - } > - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > - c->nnode_sz - UBIFS_LPT_CRC_BYTES); > - addr = buf; > - pos = 0; > - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > -} > - > -/** > - * pack_ltab - pack the LPT's own lprops table. > - * @c: UBIFS file-system description object > - * @buf: buffer into which to pack > - * @ltab: LPT's own lprops table to pack > - */ > -static void pack_ltab(struct ubifs_info *c, void *buf, > - struct ubifs_lpt_lprops *ltab) > -{ > - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > - int i, pos = 0; > - uint16_t crc; > - > - pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); > - for (i = 0; i < c->lpt_lebs; i++) { > - pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); > - pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); > - } > - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > - c->ltab_sz - UBIFS_LPT_CRC_BYTES); > - addr = buf; > - pos = 0; > - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > -} > - > -/** > - * pack_lsave - pack the LPT's save table. > - * @c: UBIFS file-system description object > - * @buf: buffer into which to pack > - * @lsave: LPT's save table to pack > - */ > -static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) > -{ > - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > - int i, pos = 0; > - uint16_t crc; > - > - pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); > - for (i = 0; i < c->lsave_cnt; i++) > - pack_bits(&addr, &pos, lsave[i], c->lnum_bits); > - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > - c->lsave_sz - UBIFS_LPT_CRC_BYTES); > - addr = buf; > - pos = 0; > - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > -} > - > -/** > - * set_ltab - set LPT LEB properties. > - * @c: UBIFS file-system description object > - * @lnum: LEB number > - * @free: amount of free space > - * @dirty: amount of dirty space > - */ > -static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) > -{ > - dbg_msg(3, "LEB %d free %d dirty %d to %d %d", > - lnum, c->ltab[lnum - c->lpt_first].free, > - c->ltab[lnum - c->lpt_first].dirty, free, dirty); > - c->ltab[lnum - c->lpt_first].free = free; > - c->ltab[lnum - c->lpt_first].dirty = dirty; > -} > - > -/** > - * calc_nnode_num - calculate nnode number. > - * @row: the row in the tree (root is zero) > - * @col: the column in the row (leftmost is zero) > - * > - * The nnode number is a number that uniquely identifies a nnode and can be used > - * easily to traverse the tree from the root to that nnode. > - * > - * This function calculates and returns the nnode number for the nnode at @row > - * and @col. > - */ > -static int calc_nnode_num(int row, int col) > -{ > - int num, bits; > - > - num = 1; > - while (row--) { > - bits = (col & (UBIFS_LPT_FANOUT - 1)); > - col >>= UBIFS_LPT_FANOUT_SHIFT; > - num <<= UBIFS_LPT_FANOUT_SHIFT; > - num |= bits; > - } > - return num; > -} > - > -/** > - * create_lpt - create LPT. > - * @c: UBIFS file-system description object > - * > - * This function returns %0 on success and a negative error code on failure. > - */ > -int create_lpt(struct ubifs_info *c) > -{ > - int lnum, err = 0, i, j, cnt, len, alen, row; > - int blnum, boffs, bsz, bcnt; > - struct ubifs_pnode *pnode = NULL; > - struct ubifs_nnode *nnode = NULL; > - void *buf = NULL, *p; > - int *lsave = NULL; > - > - pnode = malloc(sizeof(struct ubifs_pnode)); > - nnode = malloc(sizeof(struct ubifs_nnode)); > - buf = malloc(c->leb_size); > - lsave = malloc(sizeof(int) * c->lsave_cnt); > - if (!pnode || !nnode || !buf || !lsave) { > - err = -ENOMEM; > - goto out; > - } > - memset(pnode, 0 , sizeof(struct ubifs_pnode)); > - memset(nnode, 0 , sizeof(struct ubifs_nnode)); > - > - c->lscan_lnum = c->main_first; > - > - lnum = c->lpt_first; > - p = buf; > - len = 0; > - /* Number of leaf nodes (pnodes) */ > - cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; > - //printf("pnode_cnt=%d\n",cnt); > - > - /* > - * To calculate the internal node branches, we keep information about > - * the level below. > - */ > - blnum = lnum; /* LEB number of level below */ > - boffs = 0; /* Offset of level below */ > - bcnt = cnt; /* Number of nodes in level below */ > - bsz = c->pnode_sz; /* Size of nodes in level below */ > - > - /* Add pnodes */ > - for (i = 0; i < cnt; i++) { > - if (len + c->pnode_sz > c->leb_size) { > - alen = ALIGN(len, c->min_io_size); > - set_ltab(c, lnum, c->leb_size - alen, alen - len); > - memset(p, 0xff, alen - len); > - err = write_leb(lnum++, alen, buf); > - if (err) > - goto out; > - p = buf; > - len = 0; > - } > - /* Fill in the pnode */ > - for (j = 0; j < UBIFS_LPT_FANOUT; j++) { > - int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; > - > - if (k < c->main_lebs) > - pnode->lprops[j] = c->lpt[k]; > - else { > - pnode->lprops[j].free = c->leb_size; > - pnode->lprops[j].dirty = 0; > - pnode->lprops[j].flags = 0; > - } > - } > - pack_pnode(c, p, pnode); > - p += c->pnode_sz; > - len += c->pnode_sz; > - /* > - * pnodes are simply numbered left to right starting at zero, > - * which means the pnode number can be used easily to traverse > - * down the tree to the corresponding pnode. > - */ > - pnode->num += 1; > - } > - > - row = c->lpt_hght - 1; > - /* Add all nnodes, one level at a time */ > - while (1) { > - /* Number of internal nodes (nnodes) at next level */ > - cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > - if (cnt == 0) > - cnt = 1; > - for (i = 0; i < cnt; i++) { > - if (len + c->nnode_sz > c->leb_size) { > - alen = ALIGN(len, c->min_io_size); > - set_ltab(c, lnum, c->leb_size - alen, > - alen - len); > - memset(p, 0xff, alen - len); > - err = write_leb(lnum++, alen, buf); > - if (err) > - goto out; > - p = buf; > - len = 0; > - } > - /* The root is on row zero */ > - if (row == 0) { > - c->lpt_lnum = lnum; > - c->lpt_offs = len; > - } > - /* Set branches to the level below */ > - for (j = 0; j < UBIFS_LPT_FANOUT; j++) { > - if (bcnt) { > - if (boffs + bsz > c->leb_size) { > - blnum += 1; > - boffs = 0; > - } > - nnode->nbranch[j].lnum = blnum; > - nnode->nbranch[j].offs = boffs; > - boffs += bsz; > - bcnt--; > - } else { > - nnode->nbranch[j].lnum = 0; > - nnode->nbranch[j].offs = 0; > - } > - } > - nnode->num = calc_nnode_num(row, i); > - pack_nnode(c, p, nnode); > - p += c->nnode_sz; > - len += c->nnode_sz; > - } > - /* Row zero is the top row */ > - if (row == 0) > - break; > - /* Update the information about the level below */ > - bcnt = cnt; > - bsz = c->nnode_sz; > - row -= 1; > - } > - > - if (c->big_lpt) { > - /* Need to add LPT's save table */ > - if (len + c->lsave_sz > c->leb_size) { > - alen = ALIGN(len, c->min_io_size); > - set_ltab(c, lnum, c->leb_size - alen, alen - len); > - memset(p, 0xff, alen - len); > - err = write_leb(lnum++, alen, buf); > - if (err) > - goto out; > - p = buf; > - len = 0; > - } > - > - c->lsave_lnum = lnum; > - c->lsave_offs = len; > - > - for (i = 0; i < c->lsave_cnt; i++) > - lsave[i] = c->main_first + i; > - > - pack_lsave(c, p, lsave); > - p += c->lsave_sz; > - len += c->lsave_sz; > - } > - > - /* Need to add LPT's own LEB properties table */ > - if (len + c->ltab_sz > c->leb_size) { > - alen = ALIGN(len, c->min_io_size); > - set_ltab(c, lnum, c->leb_size - alen, alen - len); > - memset(p, 0xff, alen - len); > - err = write_leb(lnum++, alen, buf); > - if (err) > - goto out; > - p = buf; > - len = 0; > - } > - > - c->ltab_lnum = lnum; > - c->ltab_offs = len; > - > - /* Update ltab before packing it */ > - len += c->ltab_sz; > - alen = ALIGN(len, c->min_io_size); > - set_ltab(c, lnum, c->leb_size - alen, alen - len); > - > - pack_ltab(c, p, c->ltab); > - p += c->ltab_sz; > - > - /* Write remaining buffer */ > - memset(p, 0xff, alen - len); > - err = write_leb(lnum, alen, buf); > - if (err) > - goto out; > - > - c->nhead_lnum = lnum; > - c->nhead_offs = ALIGN(len, c->min_io_size); > - > - dbg_msg(1, "lpt_sz: %lld", c->lpt_sz); > - dbg_msg(1, "space_bits: %d", c->space_bits); > - dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); > - dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); > - dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); > - dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); > - dbg_msg(1, "lnum_bits: %d", c->lnum_bits); > - dbg_msg(1, "pnode_sz: %d", c->pnode_sz); > - dbg_msg(1, "nnode_sz: %d", c->nnode_sz); > - dbg_msg(1, "ltab_sz: %d", c->ltab_sz); > - dbg_msg(1, "lsave_sz: %d", c->lsave_sz); > - dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); > - dbg_msg(1, "lpt_hght: %d", c->lpt_hght); > - dbg_msg(1, "big_lpt: %d", c->big_lpt); > - dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); > - dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); > - dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); > - if (c->big_lpt) > - dbg_msg(1, "LPT lsave is at %d:%d", > - c->lsave_lnum, c->lsave_offs); > -out: > - free(lsave); > - free(buf); > - free(nnode); > - free(pnode); > - return err; > -} > diff --git a/mkfs.ubifs/lpt.h b/mkfs.ubifs/lpt.h > deleted file mode 100644 > index 4cde59d..0000000 > --- a/mkfs.ubifs/lpt.h > +++ /dev/null > @@ -1,28 +0,0 @@ > -/* > - * Copyright (C) 2008 Nokia Corporation. > - * Copyright (C) 2008 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 version 2 as published by > - * the Free Software Foundation. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Artem Bityutskiy > - * Adrian Hunter > - */ > - > -#ifndef __UBIFS_LPT_H__ > -#define __UBIFS_LPT_H__ > - > -int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); > -int create_lpt(struct ubifs_info *c); > - > -#endif > diff --git a/mkfs.ubifs/mkfs.ubifs.c b/mkfs.ubifs/mkfs.ubifs.c > deleted file mode 100644 > index ca17e2b..0000000 > --- a/mkfs.ubifs/mkfs.ubifs.c > +++ /dev/null > @@ -1,2324 +0,0 @@ > -/* > - * Copyright (C) 2008 Nokia Corporation. > - * Copyright (C) 2008 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 version 2 as published by > - * the Free Software Foundation. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Adrian Hunter > - * Artem Bityutskiy > - * Zoltan Sogor > - */ > - > -#define _XOPEN_SOURCE 500 /* For realpath() */ > - > -#include "mkfs.ubifs.h" > -#include <crc32.h> > -#include "common.h" > - > -/* Size (prime number) of hash table for link counting */ > -#define HASH_TABLE_SIZE 10099 > - > -/* The node buffer must allow for worst case compression */ > -#define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + \ > - UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) > - > -/* Default time granularity in nanoseconds */ > -#define DEFAULT_TIME_GRAN 1000000000 > - > -/** > - * struct idx_entry - index entry. > - * @next: next index entry (NULL at end of list) > - * @prev: previous index entry (NULL at beginning of list) > - * @key: key > - * @name: directory entry name used for sorting colliding keys by name > - * @lnum: LEB number > - * @offs: offset > - * @len: length > - * > - * The index is recorded as a linked list which is sorted and used to create > - * the bottom level of the on-flash index tree. The remaining levels of the > - * index tree are each built from the level below. > - */ > -struct idx_entry { > - struct idx_entry *next; > - struct idx_entry *prev; > - union ubifs_key key; > - char *name; > - int lnum; > - int offs; > - int len; > -}; > - > -/** > - * struct inum_mapping - inode number mapping for link counting. > - * @next: next inum_mapping (NULL at end of list) > - * @prev: previous inum_mapping (NULL at beginning of list) > - * @dev: source device on which the source inode number resides > - * @inum: source inode number of the file > - * @use_inum: target inode number of the file > - * @use_nlink: number of links > - * @path_name: a path name of the file > - * @st: struct stat object containing inode attributes which have to be used > - * when the inode is being created (actually only UID, GID, access > - * mode, major and minor device numbers) > - * > - * If a file has more than one hard link, then the number of hard links that > - * exist in the source directory hierarchy must be counted to exclude the > - * possibility that the file is linked from outside the source directory > - * hierarchy. > - * > - * The inum_mappings are stored in a hash_table of linked lists. > - */ > -struct inum_mapping { > - struct inum_mapping *next; > - struct inum_mapping *prev; > - dev_t dev; > - ino_t inum; > - ino_t use_inum; > - unsigned int use_nlink; > - char *path_name; > - struct stat st; > -}; > - > -/* > - * Because we copy functions from the kernel, we use a subset of the UBIFS > - * file-system description object struct ubifs_info. > - */ > -struct ubifs_info info_; > -static struct ubifs_info *c = &info_; > -static libubi_t ubi; > - > -/* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ > -int debug_level; > -int verbose; > -int yes; > - > -static char *root; > -static int root_len; > -static struct stat root_st; > -static char *output; > -static int out_fd; > -static int out_ubi; > -static int squash_owner; > - > -/* The 'head' (position) which nodes are written */ > -static int head_lnum; > -static int head_offs; > -static int head_flags; > - > -/* The index list */ > -static struct idx_entry *idx_list_first; > -static struct idx_entry *idx_list_last; > -static size_t idx_cnt; > - > -/* Global buffers */ > -static void *leb_buf; > -static void *node_buf; > -static void *block_buf; > - > -/* Hash table for inode link counting */ > -static struct inum_mapping **hash_table; > - > -/* Inode creation sequence number */ > -static unsigned long long creat_sqnum; > - > -static const char *optstring = "d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq"; > - > -static const struct option longopts[] = { > - {"root", 1, NULL, 'r'}, > - {"min-io-size", 1, NULL, 'm'}, > - {"leb-size", 1, NULL, 'e'}, > - {"max-leb-cnt", 1, NULL, 'c'}, > - {"output", 1, NULL, 'o'}, > - {"devtable", 1, NULL, 'D'}, > - {"yes", 0, NULL, 'y'}, > - {"help", 0, NULL, 'h'}, > - {"verbose", 0, NULL, 'v'}, > - {"version", 0, NULL, 'V'}, > - {"debug-level", 1, NULL, 'g'}, > - {"jrn-size", 1, NULL, 'j'}, > - {"reserved", 1, NULL, 'R'}, > - {"compr", 1, NULL, 'x'}, > - {"favor-percent", 1, NULL, 'X'}, > - {"fanout", 1, NULL, 'f'}, > - {"space-fixup", 0, NULL, 'F'}, > - {"keyhash", 1, NULL, 'k'}, > - {"log-lebs", 1, NULL, 'l'}, > - {"orph-lebs", 1, NULL, 'p'}, > - {"squash-uids" , 0, NULL, 'U'}, > - {NULL, 0, NULL, 0} > -}; > - > -static const char *helptext = > -"Usage: mkfs.ubifs [OPTIONS] target\n" > -"Make a UBIFS file system image from an existing directory tree\n\n" > -"Examples:\n" > -"Build file system from directory /opt/img, writting the result in the ubifs.img file\n" > -"\tmkfs.ubifs -m 512 -e 128KiB -c 100 -r /opt/img ubifs.img\n" > -"The same, but writting directly to an UBI volume\n" > -"\tmkfs.ubifs -r /opt/img /dev/ubi0_0\n" > -"Creating an empty UBIFS filesystem on an UBI volume\n" > -"\tmkfs.ubifs /dev/ubi0_0\n\n" > -"Options:\n" > -"-r, -d, --root=DIR build file system from directory DIR\n" > -"-m, --min-io-size=SIZE minimum I/O unit size\n" > -"-e, --leb-size=SIZE logical erase block size\n" > -"-c, --max-leb-cnt=COUNT maximum logical erase block count\n" > -"-o, --output=FILE output to FILE\n" > -"-j, --jrn-size=SIZE journal size\n" > -"-R, --reserved=SIZE how much space should be reserved for the super-user\n" > -"-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" > -" \"none\" (default: \"lzo\")\n" > -"-X, --favor-percent may only be used with favor LZO compression and defines\n" > -" how many percent better zlib should compress to make\n" > -" mkfs.ubifs use zlib instead of LZO (default 20%)\n" > -"-f, --fanout=NUM fanout NUM (default: 8)\n" > -"-F, --space-fixup file-system free space has to be fixed up on first mount\n" > -" (requires kernel version 3.0 or greater)\n" > -"-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" > -"-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" > -"-D, --devtable=FILE use device table FILE\n" > -"-U, --squash-uids squash owners making all files owned by root\n" > -"-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" > -" debugging)\n" > -"-y, --yes assume the answer is \"yes\" for all questions\n" > -"-v, --verbose verbose operation\n" > -"-V, --version display version information\n" > -"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" > -" 2 - files, 3 - more details)\n" > -"-h, --help display this help text\n\n" > -"Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" > -"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" > -"If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" > -"for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" > -"really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" > -"compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" > -"compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" > -"or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" > -"\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" > -"default 20%.\n\n" > -"The -F parameter is used to set the \"fix up free space\" flag in the superblock,\n" > -"which forces UBIFS to \"fixup\" all the free space which it is going to use. This\n" > -"option is useful to work-around the problem of double free space programming: if the\n" > -"flasher program which flashes the UBI image is unable to skip NAND pages containing\n" > -"only 0xFF bytes, the effect is that some NAND pages are written to twice - first time\n" > -"when flashing the image and the second time when UBIFS is mounted and writes useful\n" > -"data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n" > -"flag may make the first mount very slow, because the \"free space fixup\" procedure\n" > -"takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"; > - > -/** > - * make_path - make a path name from a directory and a name. > - * @dir: directory path name > - * @name: name > - */ > -static char *make_path(const char *dir, const char *name) > -{ > - char *s; > - > - s = malloc(strlen(dir) + strlen(name) + 2); > - if (!s) > - return NULL; > - strcpy(s, dir); > - if (dir[strlen(dir) - 1] != '/') > - strcat(s, "/"); > - strcat(s, name); > - return s; > -} > - > -/** > - * is_contained - determine if a file is beneath a directory. > - * @file: file path name > - * @dir: directory path name > - * > - * This function returns %1 if @file is accessible from the @dir directory and > - * %0 otherwise. In case of error, returns %-1. > - */ > -static int is_contained(const char *file, const char *dir) > -{ > - char *real_file = NULL; > - char *real_dir = NULL; > - char *file_base, *copy; > - int ret = -1; > - > - /* Make a copy of the file path because 'dirname()' can modify it */ > - copy = strdup(file); > - if (!copy) > - return -1; > - file_base = dirname(copy); > - > - /* Turn the paths into the canonical form */ > - real_file = malloc(PATH_MAX); > - if (!real_file) > - goto out_free; > - > - real_dir = malloc(PATH_MAX); > - if (!real_dir) > - goto out_free; > - > - if (!realpath(file_base, real_file)) { > - perror("Could not canonicalize file path"); > - goto out_free; > - } > - if (!realpath(dir, real_dir)) { > - perror("Could not canonicalize directory"); > - goto out_free; > - } > - > - ret = !!strstr(real_file, real_dir); > - > -out_free: > - free(copy); > - free(real_file); > - free(real_dir); > - return ret; > -} > - > -/** > - * calc_min_log_lebs - calculate the minimum number of log LEBs needed. > - * @max_bud_bytes: journal size (buds only) > - */ > -static int calc_min_log_lebs(unsigned long long max_bud_bytes) > -{ > - int buds, log_lebs; > - unsigned long long log_size; > - > - buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; > - log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); > - log_size *= buds; > - log_size += ALIGN(UBIFS_CS_NODE_SZ + > - UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), > - c->min_io_size); > - log_lebs = (log_size + c->leb_size - 1) / c->leb_size; > - log_lebs += 1; > - return log_lebs; > -} > - > -/** > - * add_space_overhead - add UBIFS overhead. > - * @size: flash space which should be visible to the user > - * > - * UBIFS has overhead, and if we need to reserve @size bytes for the user data, > - * we have to reserve more flash space, to compensate the overhead. This > - * function calculates and returns the amount of physical flash space which > - * should be reserved to provide @size bytes for the user. > - */ > -static long long add_space_overhead(long long size) > -{ > - int divisor, factor, f, max_idx_node_sz; > - > - /* > - * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS > - * function does. > - */ > - max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); > - f = c->fanout > 3 ? c->fanout >> 1 : 2; > - divisor = UBIFS_BLOCK_SIZE; > - factor = UBIFS_MAX_DATA_NODE_SZ; > - factor += (max_idx_node_sz * 3) / (f - 1); > - size *= factor; > - return size / divisor; > -} > - > -static int validate_options(void) > -{ > - int tmp; > - > - if (!output) > - return err_msg("no output file or UBI volume specified"); > - if (root) { > - tmp = is_contained(output, root); > - if (tmp < 0) > - return err_msg("failed to perform output file root check"); > - else if (tmp) > - return err_msg("output file cannot be in the UBIFS root " > - "directory"); > - } > - if (!is_power_of_2(c->min_io_size)) > - return err_msg("min. I/O unit size should be power of 2"); > - if (c->leb_size < c->min_io_size) > - return err_msg("min. I/O unit cannot be larger than LEB size"); > - if (c->leb_size < UBIFS_MIN_LEB_SZ) > - return err_msg("too small LEB size %d, minimum is %d", > - c->leb_size, UBIFS_MIN_LEB_SZ); > - if (c->leb_size % c->min_io_size) > - return err_msg("LEB should be multiple of min. I/O units"); > - if (c->leb_size % 8) > - return err_msg("LEB size has to be multiple of 8"); > - if (c->leb_size > UBIFS_MAX_LEB_SZ) > - return err_msg("too large LEB size %d, maximum is %d", > - c->leb_size, UBIFS_MAX_LEB_SZ); > - if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) > - return err_msg("too low max. count of LEBs, minimum is %d", > - UBIFS_MIN_LEB_CNT); > - if (c->fanout < UBIFS_MIN_FANOUT) > - return err_msg("too low fanout, minimum is %d", > - UBIFS_MIN_FANOUT); > - tmp = c->leb_size - UBIFS_IDX_NODE_SZ; > - tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; > - if (c->fanout > tmp) > - return err_msg("too high fanout, maximum is %d", tmp); > - if (c->log_lebs < UBIFS_MIN_LOG_LEBS) > - return err_msg("too few log LEBs, minimum is %d", > - UBIFS_MIN_LOG_LEBS); > - if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) > - return err_msg("too many log LEBs, maximum is %d", > - c->max_leb_cnt - UBIFS_MIN_LEB_CNT); > - if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) > - return err_msg("too few orphan LEBs, minimum is %d", > - UBIFS_MIN_ORPH_LEBS); > - if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) > - return err_msg("too many orphan LEBs, maximum is %d", > - c->max_leb_cnt - UBIFS_MIN_LEB_CNT); > - tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; > - tmp += c->orph_lebs + 4; > - if (tmp > c->max_leb_cnt) > - return err_msg("too low max. count of LEBs, expected at " > - "least %d", tmp); > - tmp = calc_min_log_lebs(c->max_bud_bytes); > - if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) > - return err_msg("too few log LEBs, expected at least %d", tmp); > - if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) > - return err_msg("too much reserved space %lld", c->rp_size); > - return 0; > -} > - > -/** > - * get_multiplier - convert size specifier to an integer multiplier. > - * @str: the size specifier string > - * > - * This function parses the @str size specifier, which may be one of > - * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive > - * size multiplier in case of success and %-1 in case of failure. > - */ > -static int get_multiplier(const char *str) > -{ > - if (!str) > - return 1; > - > - /* Remove spaces before the specifier */ > - while (*str == ' ' || *str == '\t') > - str += 1; > - > - if (!strcmp(str, "KiB")) > - return 1024; > - if (!strcmp(str, "MiB")) > - return 1024 * 1024; > - if (!strcmp(str, "GiB")) > - return 1024 * 1024 * 1024; > - > - return -1; > -} > - > -/** > - * get_bytes - convert a string containing amount of bytes into an > - * integer. > - * @str: string to convert > - * > - * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size > - * specifiers. Returns positive amount of bytes in case of success and %-1 in > - * case of failure. > - */ > -static long long get_bytes(const char *str) > -{ > - char *endp; > - long long bytes = strtoull(str, &endp, 0); > - > - if (endp == str || bytes < 0) > - return err_msg("incorrect amount of bytes: \"%s\"", str); > - > - if (*endp != '\0') { > - int mult = get_multiplier(endp); > - > - if (mult == -1) > - return err_msg("bad size specifier: \"%s\" - " > - "should be 'KiB', 'MiB' or 'GiB'", endp); > - bytes *= mult; > - } > - > - return bytes; > -} > -/** > - * open_ubi - open the UBI volume. > - * @node: name of the UBI volume character device to fetch information about > - * > - * Returns %0 in case of success and %-1 in case of failure > - */ > -static int open_ubi(const char *node) > -{ > - struct stat st; > - > - if (stat(node, &st) || !S_ISCHR(st.st_mode)) > - return -1; > - > - ubi = libubi_open(); > - if (!ubi) > - return -1; > - if (ubi_get_vol_info(ubi, node, &c->vi)) > - return -1; > - if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) > - return -1; > - return 0; > -} > - > -static int get_options(int argc, char**argv) > -{ > - int opt, i; > - const char *tbl_file = NULL; > - struct stat st; > - char *endp; > - > - c->fanout = 8; > - c->orph_lebs = 1; > - c->key_hash = key_r5_hash; > - c->key_len = UBIFS_SK_LEN; > - c->default_compr = UBIFS_COMPR_LZO; > - c->favor_percent = 20; > - c->lsave_cnt = 256; > - c->leb_size = -1; > - c->min_io_size = -1; > - c->max_leb_cnt = -1; > - c->max_bud_bytes = -1; > - c->log_lebs = -1; > - > - while (1) { > - opt = getopt_long(argc, argv, optstring, longopts, &i); > - if (opt == -1) > - break; > - switch (opt) { > - case 'r': > - case 'd': > - root_len = strlen(optarg); > - root = malloc(root_len + 2); > - if (!root) > - return err_msg("cannot allocate memory"); > - > - /* > - * The further code expects '/' at the end of the root > - * UBIFS directory on the host. > - */ > - memcpy(root, optarg, root_len); > - if (root[root_len - 1] != '/') > - root[root_len++] = '/'; > - root[root_len] = 0; > - > - /* Make sure the root directory exists */ > - if (stat(root, &st)) > - return sys_err_msg("bad root directory '%s'", > - root); > - break; > - case 'm': > - c->min_io_size = get_bytes(optarg); > - if (c->min_io_size <= 0) > - return err_msg("bad min. I/O size"); > - break; > - case 'e': > - c->leb_size = get_bytes(optarg); > - if (c->leb_size <= 0) > - return err_msg("bad LEB size"); > - break; > - case 'c': > - c->max_leb_cnt = get_bytes(optarg); > - if (c->max_leb_cnt <= 0) > - return err_msg("bad maximum LEB count"); > - break; > - case 'o': > - output = xstrdup(optarg); > - break; > - case 'D': > - tbl_file = optarg; > - if (stat(tbl_file, &st) < 0) > - return sys_err_msg("bad device table file '%s'", > - tbl_file); > - break; > - case 'y': > - yes = 1; > - break; > - case 'h': > - case '?': > - printf("%s", helptext); > - exit(0); > - case 'v': > - verbose = 1; > - break; > - case 'V': > - common_print_version(); > - exit(0); > - case 'g': > - debug_level = strtol(optarg, &endp, 0); > - if (*endp != '\0' || endp == optarg || > - debug_level < 0 || debug_level > 3) > - return err_msg("bad debugging level '%s'", > - optarg); > - break; > - case 'f': > - c->fanout = strtol(optarg, &endp, 0); > - if (*endp != '\0' || endp == optarg || c->fanout <= 0) > - return err_msg("bad fanout %s", optarg); > - break; > - case 'F': > - c->space_fixup = 1; > - break; > - case 'l': > - c->log_lebs = strtol(optarg, &endp, 0); > - if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) > - return err_msg("bad count of log LEBs '%s'", > - optarg); > - break; > - case 'p': > - c->orph_lebs = strtol(optarg, &endp, 0); > - if (*endp != '\0' || endp == optarg || > - c->orph_lebs <= 0) > - return err_msg("bad orphan LEB count '%s'", > - optarg); > - break; > - case 'k': > - if (strcmp(optarg, "r5") == 0) { > - c->key_hash = key_r5_hash; > - c->key_hash_type = UBIFS_KEY_HASH_R5; > - } else if (strcmp(optarg, "test") == 0) { > - c->key_hash = key_test_hash; > - c->key_hash_type = UBIFS_KEY_HASH_TEST; > - } else > - return err_msg("bad key hash"); > - break; > - case 'x': > - if (strcmp(optarg, "favor_lzo") == 0) > - c->favor_lzo = 1; > - else if (strcmp(optarg, "zlib") == 0) > - c->default_compr = UBIFS_COMPR_ZLIB; > - else if (strcmp(optarg, "none") == 0) > - c->default_compr = UBIFS_COMPR_NONE; > - else if (strcmp(optarg, "lzo") != 0) > - return err_msg("bad compressor name"); > - break; > - case 'X': > - c->favor_percent = strtol(optarg, &endp, 0); > - if (*endp != '\0' || endp == optarg || > - c->favor_percent <= 0 || c->favor_percent >= 100) > - return err_msg("bad favor LZO percent '%s'", > - optarg); > - break; > - case 'j': > - c->max_bud_bytes = get_bytes(optarg); > - if (c->max_bud_bytes <= 0) > - return err_msg("bad maximum amount of buds"); > - break; > - case 'R': > - c->rp_size = get_bytes(optarg); > - if (c->rp_size < 0) > - return err_msg("bad reserved bytes count"); > - break; > - case 'U': > - squash_owner = 1; > - break; > - } > - } > - > - if (optind != argc && !output) > - output = xstrdup(argv[optind]); > - > - if (!output) > - return err_msg("not output device or file specified"); > - > - out_ubi = !open_ubi(output); > - > - if (out_ubi) { > - c->min_io_size = c->di.min_io_size; > - c->leb_size = c->vi.leb_size; > - if (c->max_leb_cnt == -1) > - c->max_leb_cnt = c->vi.rsvd_lebs; > - } > - > - if (c->min_io_size == -1) > - return err_msg("min. I/O unit was not specified " > - "(use -h for help)"); > - > - if (c->leb_size == -1) > - return err_msg("LEB size was not specified (use -h for help)"); > - > - if (c->max_leb_cnt == -1) > - return err_msg("Maximum count of LEBs was not specified " > - "(use -h for help)"); > - > - if (c->max_bud_bytes == -1) { > - int lebs; > - > - lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; > - lebs -= c->orph_lebs; > - if (c->log_lebs != -1) > - lebs -= c->log_lebs; > - else > - lebs -= UBIFS_MIN_LOG_LEBS; > - /* > - * We do not know lprops geometry so far, so assume minimum > - * count of lprops LEBs. > - */ > - lebs -= UBIFS_MIN_LPT_LEBS; > - /* Make the journal about 12.5% of main area lebs */ > - c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; > - /* Make the max journal size 8MiB */ > - if (c->max_bud_bytes > 8 * 1024 * 1024) > - c->max_bud_bytes = 8 * 1024 * 1024; > - if (c->max_bud_bytes < 4 * c->leb_size) > - c->max_bud_bytes = 4 * c->leb_size; > - } > - > - if (c->log_lebs == -1) { > - c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); > - c->log_lebs += 2; > - } > - > - if (c->min_io_size < 8) > - c->min_io_size = 8; > - c->rp_size = add_space_overhead(c->rp_size); > - > - if (verbose) { > - printf("mkfs.ubifs\n"); > - printf("\troot: %s\n", root); > - printf("\tmin_io_size: %d\n", c->min_io_size); > - printf("\tleb_size: %d\n", c->leb_size); > - printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); > - printf("\toutput: %s\n", output); > - printf("\tjrn_size: %llu\n", c->max_bud_bytes); > - printf("\treserved: %llu\n", c->rp_size); > - switch (c->default_compr) { > - case UBIFS_COMPR_LZO: > - printf("\tcompr: lzo\n"); > - break; > - case UBIFS_COMPR_ZLIB: > - printf("\tcompr: zlib\n"); > - break; > - case UBIFS_COMPR_NONE: > - printf("\tcompr: none\n"); > - break; > - } > - printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? > - "r5" : "test"); > - printf("\tfanout: %d\n", c->fanout); > - printf("\torph_lebs: %d\n", c->orph_lebs); > - printf("\tspace_fixup: %d\n", c->space_fixup); > - } > - > - if (validate_options()) > - return -1; > - > - if (tbl_file && parse_devtable(tbl_file)) > - return err_msg("cannot parse device table file '%s'", tbl_file); > - > - return 0; > -} > - > -/** > - * prepare_node - fill in the common header. > - * @node: node > - * @len: node length > - */ > -static void prepare_node(void *node, int len) > -{ > - uint32_t crc; > - struct ubifs_ch *ch = node; > - > - ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); > - ch->len = cpu_to_le32(len); > - ch->group_type = UBIFS_NO_NODE_GROUP; > - ch->sqnum = cpu_to_le64(++c->max_sqnum); > - ch->padding[0] = ch->padding[1] = 0; > - crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); > - ch->crc = cpu_to_le32(crc); > -} > - > -/** > - * write_leb - copy the image of a LEB to the output target. > - * @lnum: LEB number > - * @len: length of data in the buffer > - * @buf: buffer (must be at least c->leb_size bytes) > - */ > -int write_leb(int lnum, int len, void *buf) > -{ > - off_t pos = (off_t)lnum * c->leb_size; > - > - dbg_msg(3, "LEB %d len %d", lnum, len); > - memset(buf + len, 0xff, c->leb_size - len); > - if (out_ubi) > - if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size)) > - return sys_err_msg("ubi_leb_change_start failed"); > - > - if (lseek(out_fd, pos, SEEK_SET) != pos) > - return sys_err_msg("lseek failed seeking %"PRIdoff_t, pos); > - > - if (write(out_fd, buf, c->leb_size) != c->leb_size) > - return sys_err_msg("write failed writing %d bytes at pos %"PRIdoff_t, > - c->leb_size, pos); > - > - return 0; > -} > - > -/** > - * write_empty_leb - copy the image of an empty LEB to the output target. > - * @lnum: LEB number > - */ > -static int write_empty_leb(int lnum) > -{ > - return write_leb(lnum, 0, leb_buf); > -} > - > -/** > - * do_pad - pad a buffer to the minimum I/O size. > - * @buf: buffer > - * @len: buffer length > - */ > -static int do_pad(void *buf, int len) > -{ > - int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); > - uint32_t crc; > - > - memset(buf + len, 0xff, alen - len); > - pad_len = wlen - alen; > - dbg_msg(3, "len %d pad_len %d", len, pad_len); > - buf += alen; > - if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { > - struct ubifs_ch *ch = buf; > - struct ubifs_pad_node *pad_node = buf; > - > - ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); > - ch->node_type = UBIFS_PAD_NODE; > - ch->group_type = UBIFS_NO_NODE_GROUP; > - ch->padding[0] = ch->padding[1] = 0; > - ch->sqnum = cpu_to_le64(0); > - ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); > - > - pad_len -= UBIFS_PAD_NODE_SZ; > - pad_node->pad_len = cpu_to_le32(pad_len); > - > - crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8, > - UBIFS_PAD_NODE_SZ - 8); > - ch->crc = cpu_to_le32(crc); > - > - memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); > - } else if (pad_len > 0) > - memset(buf, UBIFS_PADDING_BYTE, pad_len); > - > - return wlen; > -} > - > -/** > - * write_node - write a node to a LEB. > - * @node: node > - * @len: node length > - * @lnum: LEB number > - */ > -static int write_node(void *node, int len, int lnum) > -{ > - prepare_node(node, len); > - > - memcpy(leb_buf, node, len); > - > - len = do_pad(leb_buf, len); > - > - return write_leb(lnum, len, leb_buf); > -} > - > -/** > - * calc_dark - calculate LEB dark space size. > - * @c: the UBIFS file-system description object > - * @spc: amount of free and dirty space in the LEB > - * > - * This function calculates amount of dark space in an LEB which has @spc bytes > - * of free and dirty space. Returns the calculations result. > - * > - * Dark space is the space which is not always usable - it depends on which > - * nodes are written in which order. E.g., if an LEB has only 512 free bytes, > - * it is dark space, because it cannot fit a large data node. So UBIFS cannot > - * count on this LEB and treat these 512 bytes as usable because it is not true > - * if, for example, only big chunks of uncompressible data will be written to > - * the FS. > - */ > -static int calc_dark(struct ubifs_info *c, int spc) > -{ > - if (spc < c->dark_wm) > - return spc; > - > - /* > - * If we have slightly more space then the dark space watermark, we can > - * anyway safely assume it we'll be able to write a node of the > - * smallest size there. > - */ > - if (spc - c->dark_wm < (int)MIN_WRITE_SZ) > - return spc - MIN_WRITE_SZ; > - > - return c->dark_wm; > -} > - > -/** > - * set_lprops - set the LEB property values for a LEB. > - * @lnum: LEB number > - * @offs: end offset of data in the LEB > - * @flags: LEB property flags > - */ > -static void set_lprops(int lnum, int offs, int flags) > -{ > - int i = lnum - c->main_first, free, dirty; > - int a = max_t(int, c->min_io_size, 8); > - > - free = c->leb_size - ALIGN(offs, a); > - dirty = c->leb_size - free - ALIGN(offs, 8); > - dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, > - flags); > - if (i < c->main_lebs) { > - c->lpt[i].free = free; > - c->lpt[i].dirty = dirty; > - c->lpt[i].flags = flags; > - } > - c->lst.total_free += free; > - c->lst.total_dirty += dirty; > - if (flags & LPROPS_INDEX) > - c->lst.idx_lebs += 1; > - else { > - int spc; > - > - spc = free + dirty; > - if (spc < c->dead_wm) > - c->lst.total_dead += spc; > - else > - c->lst.total_dark += calc_dark(c, spc); > - c->lst.total_used += c->leb_size - spc; > - } > -} > - > -/** > - * add_to_index - add a node key and position to the index. > - * @key: node key > - * @lnum: node LEB number > - * @offs: node offset > - * @len: node length > - */ > -static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, > - int len) > -{ > - struct idx_entry *e; > - > - dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); > - e = malloc(sizeof(struct idx_entry)); > - if (!e) > - return err_msg("out of memory"); > - e->next = NULL; > - e->prev = idx_list_last; > - e->key = *key; > - e->name = name; > - e->lnum = lnum; > - e->offs = offs; > - e->len = len; > - if (!idx_list_first) > - idx_list_first = e; > - if (idx_list_last) > - idx_list_last->next = e; > - idx_list_last = e; > - idx_cnt += 1; > - return 0; > -} > - > -/** > - * flush_nodes - write the current head and move the head to the next LEB. > - */ > -static int flush_nodes(void) > -{ > - int len, err; > - > - if (!head_offs) > - return 0; > - len = do_pad(leb_buf, head_offs); > - err = write_leb(head_lnum, len, leb_buf); > - if (err) > - return err; > - set_lprops(head_lnum, head_offs, head_flags); > - head_lnum += 1; > - head_offs = 0; > - return 0; > -} > - > -/** > - * reserve_space - reserve space for a node on the head. > - * @len: node length > - * @lnum: LEB number is returned here > - * @offs: offset is returned here > - */ > -static int reserve_space(int len, int *lnum, int *offs) > -{ > - int err; > - > - if (len > c->leb_size - head_offs) { > - err = flush_nodes(); > - if (err) > - return err; > - } > - *lnum = head_lnum; > - *offs = head_offs; > - head_offs += ALIGN(len, 8); > - return 0; > -} > - > -/** > - * add_node - write a node to the head. > - * @key: node key > - * @node: node > - * @len: node length > - */ > -static int add_node(union ubifs_key *key, char *name, void *node, int len) > -{ > - int err, lnum, offs; > - > - prepare_node(node, len); > - > - err = reserve_space(len, &lnum, &offs); > - if (err) > - return err; > - > - memcpy(leb_buf + offs, node, len); > - memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); > - > - add_to_index(key, name, lnum, offs, len); > - > - return 0; > -} > - > -/** > - * add_inode_with_data - write an inode. > - * @st: stat information of source inode > - * @inum: target inode number > - * @data: inode data (for special inodes e.g. symlink path etc) > - * @data_len: inode data length > - * @flags: source inode flags > - */ > -static int add_inode_with_data(struct stat *st, ino_t inum, void *data, > - unsigned int data_len, int flags) > -{ > - struct ubifs_ino_node *ino = node_buf; > - union ubifs_key key; > - int len, use_flags = 0; > - > - if (c->default_compr != UBIFS_COMPR_NONE) > - use_flags |= UBIFS_COMPR_FL; > - if (flags & FS_COMPR_FL) > - use_flags |= UBIFS_COMPR_FL; > - if (flags & FS_SYNC_FL) > - use_flags |= UBIFS_SYNC_FL; > - if (flags & FS_IMMUTABLE_FL) > - use_flags |= UBIFS_IMMUTABLE_FL; > - if (flags & FS_APPEND_FL) > - use_flags |= UBIFS_APPEND_FL; > - if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) > - use_flags |= UBIFS_DIRSYNC_FL; > - > - memset(ino, 0, UBIFS_INO_NODE_SZ); > - > - ino_key_init(&key, inum); > - ino->ch.node_type = UBIFS_INO_NODE; > - key_write(&key, &ino->key); > - ino->creat_sqnum = cpu_to_le64(creat_sqnum); > - ino->size = cpu_to_le64(st->st_size); > - ino->nlink = cpu_to_le32(st->st_nlink); > - /* > - * The time fields are updated assuming the default time granularity > - * of 1 second. To support finer granularities, utime() would be needed. > - */ > - ino->atime_sec = cpu_to_le64(st->st_atime); > - ino->ctime_sec = cpu_to_le64(st->st_ctime); > - ino->mtime_sec = cpu_to_le64(st->st_mtime); > - ino->atime_nsec = 0; > - ino->ctime_nsec = 0; > - ino->mtime_nsec = 0; > - ino->uid = cpu_to_le32(st->st_uid); > - ino->gid = cpu_to_le32(st->st_gid); > - ino->mode = cpu_to_le32(st->st_mode); > - ino->flags = cpu_to_le32(use_flags); > - ino->data_len = cpu_to_le32(data_len); > - ino->compr_type = cpu_to_le16(c->default_compr); > - if (data_len) > - memcpy(&ino->data, data, data_len); > - > - len = UBIFS_INO_NODE_SZ + data_len; > - > - return add_node(&key, NULL, ino, len); > -} > - > -/** > - * add_inode - write an inode. > - * @st: stat information of source inode > - * @inum: target inode number > - * @flags: source inode flags > - */ > -static int add_inode(struct stat *st, ino_t inum, int flags) > -{ > - return add_inode_with_data(st, inum, NULL, 0, flags); > -} > - > -/** > - * add_dir_inode - write an inode for a directory. > - * @dir: source directory > - * @inum: target inode number > - * @size: target directory size > - * @nlink: target directory link count > - * @st: struct stat object describing attributes (except size and nlink) of the > - * target inode to create > - * > - * Note, this function may be called with %NULL @dir, when the directory which > - * is being created does not exist at the host file system, but is defined by > - * the device table. > - */ > -static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, > - struct stat *st) > -{ > - int fd, flags = 0; > - > - st->st_size = size; > - st->st_nlink = nlink; > - > - if (dir) { > - fd = dirfd(dir); > - if (fd == -1) > - return sys_err_msg("dirfd failed"); > - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) > - flags = 0; > - } > - > - return add_inode(st, inum, flags); > -} > - > -/** > - * add_dev_inode - write an inode for a character or block device. > - * @st: stat information of source inode > - * @inum: target inode number > - * @flags: source inode flags > - */ > -static int add_dev_inode(struct stat *st, ino_t inum, int flags) > -{ > - union ubifs_dev_desc dev; > - > - dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); > - return add_inode_with_data(st, inum, &dev, 8, flags); > -} > - > -/** > - * add_symlink_inode - write an inode for a symbolic link. > - * @path_name: path name of symbolic link inode itself (not the link target) > - * @st: stat information of source inode > - * @inum: target inode number > - * @flags: source inode flags > - */ > -static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, > - int flags) > -{ > - char buf[UBIFS_MAX_INO_DATA + 2]; > - ssize_t len; > - > - /* Take the symlink as is */ > - len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); > - if (len <= 0) > - return sys_err_msg("readlink failed for %s", path_name); > - if (len > UBIFS_MAX_INO_DATA) > - return err_msg("symlink too long for %s", path_name); > - > - return add_inode_with_data(st, inum, buf, len, flags); > -} > - > -/** > - * add_dent_node - write a directory entry node. > - * @dir_inum: target inode number of directory > - * @name: directory entry name > - * @inum: target inode number of the directory entry > - * @type: type of the target inode > - */ > -static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, > - unsigned char type) > -{ > - struct ubifs_dent_node *dent = node_buf; > - union ubifs_key key; > - struct qstr dname; > - char *kname; > - int len; > - > - dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum, > - (unsigned int)type, (unsigned long)dir_inum); > - memset(dent, 0, UBIFS_DENT_NODE_SZ); > - > - dname.name = (void *)name; > - dname.len = strlen(name); > - > - dent->ch.node_type = UBIFS_DENT_NODE; > - > - dent_key_init(c, &key, dir_inum, &dname); > - key_write(&key, dent->key); > - dent->inum = cpu_to_le64(inum); > - dent->padding1 = 0; > - dent->type = type; > - dent->nlen = cpu_to_le16(dname.len); > - memcpy(dent->name, dname.name, dname.len); > - dent->name[dname.len] = '\0'; > - > - len = UBIFS_DENT_NODE_SZ + dname.len + 1; > - > - kname = strdup(name); > - if (!kname) > - return err_msg("cannot allocate memory"); > - > - return add_node(&key, kname, dent, len); > -} > - > -/** > - * lookup_inum_mapping - add an inode mapping for link counting. > - * @dev: source device on which source inode number resides > - * @inum: source inode number > - */ > -static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) > -{ > - struct inum_mapping *im; > - unsigned int k; > - > - k = inum % HASH_TABLE_SIZE; > - im = hash_table[k]; > - while (im) { > - if (im->dev == dev && im->inum == inum) > - return im; > - im = im->next; > - } > - im = malloc(sizeof(struct inum_mapping)); > - if (!im) > - return NULL; > - im->next = hash_table[k]; > - im->prev = NULL; > - im->dev = dev; > - im->inum = inum; > - im->use_inum = 0; > - im->use_nlink = 0; > - if (hash_table[k]) > - hash_table[k]->prev = im; > - hash_table[k] = im; > - return im; > -} > - > -/** > - * all_zero - does a buffer contain only zero bytes. > - * @buf: buffer > - * @len: buffer length > - */ > -static int all_zero(void *buf, int len) > -{ > - unsigned char *p = buf; > - > - while (len--) > - if (*p++ != 0) > - return 0; > - return 1; > -} > - > -/** > - * add_file - write the data of a file and its inode to the output file. > - * @path_name: source path name > - * @st: source inode stat information > - * @inum: target inode number > - * @flags: source inode flags > - */ > -static int add_file(const char *path_name, struct stat *st, ino_t inum, > - int flags) > -{ > - struct ubifs_data_node *dn = node_buf; > - void *buf = block_buf; > - loff_t file_size = 0; > - ssize_t ret, bytes_read; > - union ubifs_key key; > - int fd, dn_len, err, compr_type, use_compr; > - unsigned int block_no = 0; > - size_t out_len; > - > - fd = open(path_name, O_RDONLY | O_LARGEFILE); > - if (fd == -1) > - return sys_err_msg("failed to open file '%s'", path_name); > - do { > - /* Read next block */ > - bytes_read = 0; > - do { > - ret = read(fd, buf + bytes_read, > - UBIFS_BLOCK_SIZE - bytes_read); > - if (ret == -1) { > - sys_err_msg("failed to read file '%s'", > - path_name); > - close(fd); > - return 1; > - } > - bytes_read += ret; > - } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); > - if (bytes_read == 0) > - break; > - file_size += bytes_read; > - /* Skip holes */ > - if (all_zero(buf, bytes_read)) { > - block_no += 1; > - continue; > - } > - /* Make data node */ > - memset(dn, 0, UBIFS_DATA_NODE_SZ); > - data_key_init(&key, inum, block_no++); > - dn->ch.node_type = UBIFS_DATA_NODE; > - key_write(&key, &dn->key); > - dn->size = cpu_to_le32(bytes_read); > - out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; > - if (c->default_compr == UBIFS_COMPR_NONE && > - (flags & FS_COMPR_FL)) > - use_compr = UBIFS_COMPR_LZO; > - else > - use_compr = c->default_compr; > - compr_type = compress_data(buf, bytes_read, &dn->data, > - &out_len, use_compr); > - dn->compr_type = cpu_to_le16(compr_type); > - dn_len = UBIFS_DATA_NODE_SZ + out_len; > - /* Add data node to file system */ > - err = add_node(&key, NULL, dn, dn_len); > - if (err) { > - close(fd); > - return err; > - } > - } while (ret != 0); > - if (close(fd) == -1) > - return sys_err_msg("failed to close file '%s'", path_name); > - if (file_size != st->st_size) > - return err_msg("file size changed during writing file '%s'", > - path_name); > - return add_inode(st, inum, flags); > -} > - > -/** > - * add_non_dir - write a non-directory to the output file. > - * @path_name: source path name > - * @inum: target inode number is passed and returned here (due to link counting) > - * @nlink: number of links if known otherwise zero > - * @type: UBIFS inode type is returned here > - * @st: struct stat object containing inode attributes which should be use when > - * creating the UBIFS inode > - */ > -static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, > - unsigned char *type, struct stat *st) > -{ > - int fd, flags = 0; > - > - dbg_msg(2, "%s", path_name); > - > - if (S_ISREG(st->st_mode)) { > - fd = open(path_name, O_RDONLY); > - if (fd == -1) > - return sys_err_msg("failed to open file '%s'", > - path_name); > - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) > - flags = 0; > - if (close(fd) == -1) > - return sys_err_msg("failed to close file '%s'", > - path_name); > - *type = UBIFS_ITYPE_REG; > - } else if (S_ISCHR(st->st_mode)) > - *type = UBIFS_ITYPE_CHR; > - else if (S_ISBLK(st->st_mode)) > - *type = UBIFS_ITYPE_BLK; > - else if (S_ISLNK(st->st_mode)) > - *type = UBIFS_ITYPE_LNK; > - else if (S_ISSOCK(st->st_mode)) > - *type = UBIFS_ITYPE_SOCK; > - else if (S_ISFIFO(st->st_mode)) > - *type = UBIFS_ITYPE_FIFO; > - else > - return err_msg("file '%s' has unknown inode type", path_name); > - > - if (nlink) > - st->st_nlink = nlink; > - else if (st->st_nlink > 1) { > - /* > - * If the number of links is greater than 1, then add this file > - * later when we know the number of links that we actually have. > - * For now, we just put the inode mapping in the hash table. > - */ > - struct inum_mapping *im; > - > - im = lookup_inum_mapping(st->st_dev, st->st_ino); > - if (!im) > - return err_msg("out of memory"); > - if (im->use_nlink == 0) { > - /* New entry */ > - im->use_inum = *inum; > - im->use_nlink = 1; > - im->path_name = malloc(strlen(path_name) + 1); > - if (!im->path_name) > - return err_msg("out of memory"); > - strcpy(im->path_name, path_name); > - } else { > - /* Existing entry */ > - *inum = im->use_inum; > - im->use_nlink += 1; > - /* Return unused inode number */ > - c->highest_inum -= 1; > - } > - > - memcpy(&im->st, st, sizeof(struct stat)); > - return 0; > - } else > - st->st_nlink = 1; > - > - creat_sqnum = ++c->max_sqnum; > - > - if (S_ISREG(st->st_mode)) > - return add_file(path_name, st, *inum, flags); > - if (S_ISCHR(st->st_mode)) > - return add_dev_inode(st, *inum, flags); > - if (S_ISBLK(st->st_mode)) > - return add_dev_inode(st, *inum, flags); > - if (S_ISLNK(st->st_mode)) > - return add_symlink_inode(path_name, st, *inum, flags); > - if (S_ISSOCK(st->st_mode)) > - return add_inode(st, *inum, flags); > - if (S_ISFIFO(st->st_mode)) > - return add_inode(st, *inum, flags); > - > - return err_msg("file '%s' has unknown inode type", path_name); > -} > - > -/** > - * add_directory - write a directory tree to the output file. > - * @dir_name: directory path name > - * @dir_inum: UBIFS inode number of directory > - * @st: directory inode statistics > - * @non_existing: non-zero if this function is called for a directory which > - * does not exist on the host file-system and it is being > - * created because it is defined in the device table file. > - */ > -static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, > - int non_existing) > -{ > - struct dirent *entry; > - DIR *dir = NULL; > - int err = 0; > - loff_t size = UBIFS_INO_NODE_SZ; > - char *name = NULL; > - unsigned int nlink = 2; > - struct path_htbl_element *ph_elt; > - struct name_htbl_element *nh_elt = NULL; > - struct hashtable_itr *itr; > - ino_t inum; > - unsigned char type; > - unsigned long long dir_creat_sqnum = ++c->max_sqnum; > - > - dbg_msg(2, "%s", dir_name); > - if (!non_existing) { > - dir = opendir(dir_name); > - if (dir == NULL) > - return sys_err_msg("cannot open directory '%s'", > - dir_name); > - } > - > - /* > - * Check whether this directory contains files which should be > - * added/changed because they were specified in the device table. > - * @ph_elt will be non-zero if yes. > - */ > - ph_elt = devtbl_find_path(dir_name + root_len - 1); > - > - /* > - * Before adding the directory itself, we have to iterate over all the > - * entries the device table adds to this directory and create them. > - */ > - for (; !non_existing;) { > - struct stat dent_st; > - > - errno = 0; > - entry = readdir(dir); > - if (!entry) { > - if (errno == 0) > - break; > - sys_err_msg("error reading directory '%s'", dir_name); > - err = -1; > - break; > - } > - > - if (strcmp(".", entry->d_name) == 0) > - continue; > - if (strcmp("..", entry->d_name) == 0) > - continue; > - > - if (ph_elt) > - /* > - * This directory was referred to at the device table > - * file. Check if this directory entry is referred at > - * too. > - */ > - nh_elt = devtbl_find_name(ph_elt, entry->d_name); > - > - /* > - * We are going to create the file corresponding to this > - * directory entry (@entry->d_name). We use 'struct stat' > - * object to pass information about file attributes (actually > - * only about UID, GID, mode, major, and minor). Get attributes > - * for this file from the UBIFS rootfs on the host. > - */ > - free(name); > - name = make_path(dir_name, entry->d_name); > - if (lstat(name, &dent_st) == -1) { > - sys_err_msg("lstat failed for file '%s'", name); > - goto out_free; > - } > - > - if (squash_owner) > - /* > - * Squash UID/GID. But the device table may override > - * this. > - */ > - dent_st.st_uid = dent_st.st_gid = 0; > - > - /* > - * And if the device table describes the same file, override > - * the attributes. However, this is not allowed for device node > - * files. > - */ > - if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) > - goto out_free; > - > - inum = ++c->highest_inum; > - > - if (S_ISDIR(dent_st.st_mode)) { > - err = add_directory(name, inum, &dent_st, 0); > - if (err) > - goto out_free; > - nlink += 1; > - type = UBIFS_ITYPE_DIR; > - } else { > - err = add_non_dir(name, &inum, 0, &type, &dent_st); > - if (err) > - goto out_free; > - } > - > - err = add_dent_node(dir_inum, entry->d_name, inum, type); > - if (err) > - goto out_free; > - size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, > - 8); > - } > - > - /* > - * OK, we have created all files in this directory (recursively), let's > - * also create all files described in the device table. All t > - */ > - nh_elt = first_name_htbl_element(ph_elt, &itr); > - while (nh_elt) { > - struct stat fake_st; > - > - /* > - * We prohibit creating regular files using the device table, > - * the device table may only re-define attributes of regular > - * files. > - */ > - if (S_ISREG(nh_elt->mode)) { > - err_msg("Bad device table entry %s/%s - it is " > - "prohibited to create regular files " > - "via device table", > - strcmp(ph_elt->path, "/") ? ph_elt->path : "", > - nh_elt->name); > - goto out_free; > - } > - > - memcpy(&fake_st, &root_st, sizeof(struct stat)); > - fake_st.st_uid = nh_elt->uid; > - fake_st.st_uid = nh_elt->uid; > - fake_st.st_mode = nh_elt->mode; > - fake_st.st_rdev = nh_elt->dev; > - fake_st.st_nlink = 1; > - > - free(name); > - name = make_path(dir_name, nh_elt->name); > - inum = ++c->highest_inum; > - > - if (S_ISDIR(nh_elt->mode)) { > - err = add_directory(name, inum, &fake_st, 1); > - if (err) > - goto out_free; > - nlink += 1; > - type = UBIFS_ITYPE_DIR; > - } else { > - err = add_non_dir(name, &inum, 0, &type, &fake_st); > - if (err) > - goto out_free; > - } > - > - err = add_dent_node(dir_inum, nh_elt->name, inum, type); > - if (err) > - goto out_free; > - size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); > - > - nh_elt = next_name_htbl_element(ph_elt, &itr); > - } > - > - creat_sqnum = dir_creat_sqnum; > - > - err = add_dir_inode(dir, dir_inum, size, nlink, st); > - if (err) > - goto out_free; > - > - free(name); > - if (!non_existing && closedir(dir) == -1) > - return sys_err_msg("error closing directory '%s'", dir_name); > - > - return 0; > - > -out_free: > - free(name); > - if (!non_existing) > - closedir(dir); > - return -1; > -} > - > -/** > - * add_multi_linked_files - write all the files for which we counted links. > - */ > -static int add_multi_linked_files(void) > -{ > - int i, err; > - > - for (i = 0; i < HASH_TABLE_SIZE; i++) { > - struct inum_mapping *im; > - unsigned char type = 0; > - > - for (im = hash_table[i]; im; im = im->next) { > - dbg_msg(2, "%s", im->path_name); > - err = add_non_dir(im->path_name, &im->use_inum, > - im->use_nlink, &type, &im->st); > - if (err) > - return err; > - } > - } > - return 0; > -} > - > -/** > - * write_data - write the files and directories. > - */ > -static int write_data(void) > -{ > - int err; > - mode_t mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; > - > - if (root) { > - err = stat(root, &root_st); > - if (err) > - return sys_err_msg("bad root file-system directory '%s'", > - root); > - } else { > - root_st.st_mtime = time(NULL); > - root_st.st_atime = root_st.st_ctime = root_st.st_mtime; > - root_st.st_mode = mode; > - } > - > - head_flags = 0; > - err = add_directory(root, UBIFS_ROOT_INO, &root_st, !root); > - if (err) > - return err; > - err = add_multi_linked_files(); > - if (err) > - return err; > - return flush_nodes(); > -} > - > -static int namecmp(const char *name1, const char *name2) > -{ > - size_t len1 = strlen(name1), len2 = strlen(name2); > - size_t clen = (len1 < len2) ? len1 : len2; > - int cmp; > - > - cmp = memcmp(name1, name2, clen); > - if (cmp) > - return cmp; > - return (len1 < len2) ? -1 : 1; > -} > - > -static int cmp_idx(const void *a, const void *b) > -{ > - const struct idx_entry *e1 = *(const struct idx_entry **)a; > - const struct idx_entry *e2 = *(const struct idx_entry **)b; > - int cmp; > - > - cmp = keys_cmp(&e1->key, &e2->key); > - if (cmp) > - return cmp; > - return namecmp(e1->name, e2->name); > -} > - > -/** > - * add_idx_node - write an index node to the head. > - * @node: index node > - * @child_cnt: number of children of this index node > - */ > -static int add_idx_node(void *node, int child_cnt) > -{ > - int err, lnum, offs, len; > - > - len = ubifs_idx_node_sz(c, child_cnt); > - > - prepare_node(node, len); > - > - err = reserve_space(len, &lnum, &offs); > - if (err) > - return err; > - > - memcpy(leb_buf + offs, node, len); > - memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); > - > - c->old_idx_sz += ALIGN(len, 8); > - > - dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, > - c->old_idx_sz); > - > - /* The last index node written will be the root */ > - c->zroot.lnum = lnum; > - c->zroot.offs = offs; > - c->zroot.len = len; > - > - return 0; > -} > - > -/** > - * write_index - write out the index. > - */ > -static int write_index(void) > -{ > - size_t sz, i, cnt, idx_sz, pstep, bcnt; > - struct idx_entry **idx_ptr, **p; > - struct ubifs_idx_node *idx; > - struct ubifs_branch *br; > - int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err; > - > - dbg_msg(1, "leaf node count: %zd", idx_cnt); > - > - /* Reset the head for the index */ > - head_flags = LPROPS_INDEX; > - /* Allocate index node */ > - idx_sz = ubifs_idx_node_sz(c, c->fanout); > - idx = malloc(idx_sz); > - if (!idx) > - return err_msg("out of memory"); > - /* Make an array of pointers to sort the index list */ > - sz = idx_cnt * sizeof(struct idx_entry *); > - if (sz / sizeof(struct idx_entry *) != idx_cnt) { > - free(idx); > - return err_msg("index is too big (%zu entries)", idx_cnt); > - } > - idx_ptr = malloc(sz); > - if (!idx_ptr) { > - free(idx); > - return err_msg("out of memory - needed %zu bytes for index", > - sz); > - } > - idx_ptr[0] = idx_list_first; > - for (i = 1; i < idx_cnt; i++) > - idx_ptr[i] = idx_ptr[i - 1]->next; > - qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); > - /* Write level 0 index nodes */ > - cnt = idx_cnt / c->fanout; > - if (idx_cnt % c->fanout) > - cnt += 1; > - p = idx_ptr; > - blnum = head_lnum; > - boffs = head_offs; > - for (i = 0; i < cnt; i++) { > - /* > - * Calculate the child count. All index nodes are created full > - * except for the last index node on each row. > - */ > - if (i == cnt - 1) { > - child_cnt = idx_cnt % c->fanout; > - if (child_cnt == 0) > - child_cnt = c->fanout; > - } else > - child_cnt = c->fanout; > - memset(idx, 0, idx_sz); > - idx->ch.node_type = UBIFS_IDX_NODE; > - idx->child_cnt = cpu_to_le16(child_cnt); > - idx->level = cpu_to_le16(0); > - for (j = 0; j < child_cnt; j++, p++) { > - br = ubifs_idx_branch(c, idx, j); > - key_write_idx(&(*p)->key, &br->key); > - br->lnum = cpu_to_le32((*p)->lnum); > - br->offs = cpu_to_le32((*p)->offs); > - br->len = cpu_to_le32((*p)->len); > - } > - add_idx_node(idx, child_cnt); > - } > - /* Write level 1 index nodes and above */ > - level = 0; > - pstep = 1; > - while (cnt > 1) { > - /* > - * 'blast_len' is the length of the last index node in the level > - * below. > - */ > - blast_len = ubifs_idx_node_sz(c, child_cnt); > - /* 'bcnt' is the number of index nodes in the level below */ > - bcnt = cnt; > - /* 'cnt' is the number of index nodes in this level */ > - cnt = (cnt + c->fanout - 1) / c->fanout; > - if (cnt == 0) > - cnt = 1; > - level += 1; > - /* > - * The key of an index node is the same as the key of its first > - * child. Thus we can get the key by stepping along the bottom > - * level 'p' with an increasing large step 'pstep'. > - */ > - p = idx_ptr; > - pstep *= c->fanout; > - for (i = 0; i < cnt; i++) { > - /* > - * Calculate the child count. All index nodes are > - * created full except for the last index node on each > - * row. > - */ > - if (i == cnt - 1) { > - child_cnt = bcnt % c->fanout; > - if (child_cnt == 0) > - child_cnt = c->fanout; > - } else > - child_cnt = c->fanout; > - memset(idx, 0, idx_sz); > - idx->ch.node_type = UBIFS_IDX_NODE; > - idx->child_cnt = cpu_to_le16(child_cnt); > - idx->level = cpu_to_le16(level); > - for (j = 0; j < child_cnt; j++) { > - size_t bn = i * c->fanout + j; > - > - /* > - * The length of the index node in the level > - * below is 'idx_sz' except when it is the last > - * node on the row. i.e. all the others on the > - * row are full. > - */ > - if (bn == bcnt - 1) > - blen = blast_len; > - else > - blen = idx_sz; > - /* > - * 'blnum' and 'boffs' hold the position of the > - * index node on the level below. > - */ > - if (boffs + blen > c->leb_size) { > - blnum += 1; > - boffs = 0; > - } > - /* > - * Fill in the branch with the key and position > - * of the index node from the level below. > - */ > - br = ubifs_idx_branch(c, idx, j); > - key_write_idx(&(*p)->key, &br->key); > - br->lnum = cpu_to_le32(blnum); > - br->offs = cpu_to_le32(boffs); > - br->len = cpu_to_le32(blen); > - /* > - * Step to the next index node on the level > - * below. > - */ > - boffs += ALIGN(blen, 8); > - p += pstep; > - } > - add_idx_node(idx, child_cnt); > - } > - } > - > - /* Free stuff */ > - for (i = 0; i < idx_cnt; i++) > - free(idx_ptr[i]); > - free(idx_ptr); > - free(idx); > - > - dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, > - c->zroot.len); > - > - /* Set the index head */ > - c->ihead_lnum = head_lnum; > - c->ihead_offs = ALIGN(head_offs, c->min_io_size); > - dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); > - > - /* Flush the last index LEB */ > - err = flush_nodes(); > - if (err) > - return err; > - > - return 0; > -} > - > -/** > - * set_gc_lnum - set the LEB number reserved for the garbage collector. > - */ > -static int set_gc_lnum(void) > -{ > - int err; > - > - c->gc_lnum = head_lnum++; > - err = write_empty_leb(c->gc_lnum); > - if (err) > - return err; > - set_lprops(c->gc_lnum, 0, 0); > - c->lst.empty_lebs += 1; > - return 0; > -} > - > -/** > - * finalize_leb_cnt - now that we know how many LEBs we used. > - */ > -static int finalize_leb_cnt(void) > -{ > - c->leb_cnt = head_lnum; > - if (c->leb_cnt > c->max_leb_cnt) > - return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); > - c->main_lebs = c->leb_cnt - c->main_first; > - if (verbose) { > - printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); > - printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); > - printf("\tlog_lebs: %d\n", c->log_lebs); > - printf("\tlpt_lebs: %d\n", c->lpt_lebs); > - printf("\torph_lebs: %d\n", c->orph_lebs); > - printf("\tmain_lebs: %d\n", c->main_lebs); > - printf("\tgc lebs: %d\n", 1); > - printf("\tindex lebs: %d\n", c->lst.idx_lebs); > - printf("\tleb_cnt: %d\n", c->leb_cnt); > - } > - dbg_msg(1, "total_free: %llu", c->lst.total_free); > - dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); > - dbg_msg(1, "total_used: %llu", c->lst.total_used); > - dbg_msg(1, "total_dead: %llu", c->lst.total_dead); > - dbg_msg(1, "total_dark: %llu", c->lst.total_dark); > - dbg_msg(1, "index size: %llu", c->old_idx_sz); > - dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); > - return 0; > -} > - > -/** > - * write_super - write the super block. > - */ > -static int write_super(void) > -{ > - struct ubifs_sb_node sup; > - > - memset(&sup, 0, UBIFS_SB_NODE_SZ); > - > - sup.ch.node_type = UBIFS_SB_NODE; > - sup.key_hash = c->key_hash_type; > - sup.min_io_size = cpu_to_le32(c->min_io_size); > - sup.leb_size = cpu_to_le32(c->leb_size); > - sup.leb_cnt = cpu_to_le32(c->leb_cnt); > - sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); > - sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); > - sup.log_lebs = cpu_to_le32(c->log_lebs); > - sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); > - sup.orph_lebs = cpu_to_le32(c->orph_lebs); > - sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); > - sup.fanout = cpu_to_le32(c->fanout); > - sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); > - sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); > - sup.default_compr = cpu_to_le16(c->default_compr); > - sup.rp_size = cpu_to_le64(c->rp_size); > - sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); > - uuid_generate_random(sup.uuid); > - if (verbose) { > - char s[40]; > - > - uuid_unparse_upper(sup.uuid, s); > - printf("\tUUID: %s\n", s); > - } > - if (c->big_lpt) > - sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); > - if (c->space_fixup) > - sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP); > - > - return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); > -} > - > -/** > - * write_master - write the master node. > - */ > -static int write_master(void) > -{ > - struct ubifs_mst_node mst; > - int err; > - > - memset(&mst, 0, UBIFS_MST_NODE_SZ); > - > - mst.ch.node_type = UBIFS_MST_NODE; > - mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); > - mst.highest_inum = cpu_to_le64(c->highest_inum); > - mst.cmt_no = cpu_to_le64(0); > - mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); > - mst.root_lnum = cpu_to_le32(c->zroot.lnum); > - mst.root_offs = cpu_to_le32(c->zroot.offs); > - mst.root_len = cpu_to_le32(c->zroot.len); > - mst.gc_lnum = cpu_to_le32(c->gc_lnum); > - mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); > - mst.ihead_offs = cpu_to_le32(c->ihead_offs); > - mst.index_size = cpu_to_le64(c->old_idx_sz); > - mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); > - mst.lpt_offs = cpu_to_le32(c->lpt_offs); > - mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); > - mst.nhead_offs = cpu_to_le32(c->nhead_offs); > - mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); > - mst.ltab_offs = cpu_to_le32(c->ltab_offs); > - mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); > - mst.lsave_offs = cpu_to_le32(c->lsave_offs); > - mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); > - mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); > - mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); > - mst.total_free = cpu_to_le64(c->lst.total_free); > - mst.total_dirty = cpu_to_le64(c->lst.total_dirty); > - mst.total_used = cpu_to_le64(c->lst.total_used); > - mst.total_dead = cpu_to_le64(c->lst.total_dead); > - mst.total_dark = cpu_to_le64(c->lst.total_dark); > - mst.leb_cnt = cpu_to_le32(c->leb_cnt); > - > - err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); > - if (err) > - return err; > - > - err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); > - if (err) > - return err; > - > - return 0; > -} > - > -/** > - * write_log - write an empty log. > - */ > -static int write_log(void) > -{ > - struct ubifs_cs_node cs; > - int err, i, lnum; > - > - lnum = UBIFS_LOG_LNUM; > - > - cs.ch.node_type = UBIFS_CS_NODE; > - cs.cmt_no = cpu_to_le64(0); > - > - err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); > - if (err) > - return err; > - > - lnum += 1; > - > - for (i = 1; i < c->log_lebs; i++, lnum++) { > - err = write_empty_leb(lnum); > - if (err) > - return err; > - } > - > - return 0; > -} > - > -/** > - * write_lpt - write the LEB properties tree. > - */ > -static int write_lpt(void) > -{ > - int err, lnum; > - > - err = create_lpt(c); > - if (err) > - return err; > - > - lnum = c->nhead_lnum + 1; > - while (lnum <= c->lpt_last) { > - err = write_empty_leb(lnum++); > - if (err) > - return err; > - } > - > - return 0; > -} > - > -/** > - * write_orphan_area - write an empty orphan area. > - */ > -static int write_orphan_area(void) > -{ > - int err, i, lnum; > - > - lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; > - for (i = 0; i < c->orph_lebs; i++, lnum++) { > - err = write_empty_leb(lnum); > - if (err) > - return err; > - } > - return 0; > -} > - > -/** > - * check_volume_empty - check if the UBI volume is empty. > - * > - * This function checks if the UBI volume is empty by looking if its LEBs are > - * mapped or not. > - * > - * Returns %0 in case of success, %1 is the volume is not empty, > - * and a negative error code in case of failure. > - */ > -static int check_volume_empty(void) > -{ > - int lnum, err; > - > - for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) { > - err = ubi_is_mapped(out_fd, lnum); > - if (err < 0) > - return err; > - if (err == 1) > - return 1; > - } > - return 0; > -} > - > -/** > - * open_target - open the output target. > - * > - * Open the output target. The target can be an UBI volume > - * or a file. > - * > - * Returns %0 in case of success and %-1 in case of failure. > - */ > -static int open_target(void) > -{ > - if (out_ubi) { > - out_fd = open(output, O_RDWR | O_EXCL); > - > - if (out_fd == -1) > - return sys_err_msg("cannot open the UBI volume '%s'", > - output); > - if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) > - return sys_err_msg("ubi_set_property failed"); > - > - if (!yes && check_volume_empty()) { > - if (!prompt("UBI volume is not empty. Format anyways?", false)) > - return err_msg("UBI volume is not empty"); > - } > - } else { > - out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, > - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); > - if (out_fd == -1) > - return sys_err_msg("cannot create output file '%s'", > - output); > - } > - return 0; > -} > - > - > -/** > - * close_target - close the output target. > - * > - * Close the output target. If the target was an UBI > - * volume, also close libubi. > - * > - * Returns %0 in case of success and %-1 in case of failure. > - */ > -static int close_target(void) > -{ > - if (ubi) > - libubi_close(ubi); > - if (out_fd >= 0 && close(out_fd) == -1) > - return sys_err_msg("cannot close the target '%s'", output); > - if (output) > - free(output); > - return 0; > -} > - > -/** > - * init - initialize things. > - */ > -static int init(void) > -{ > - int err, i, main_lebs, big_lpt = 0, sz; > - > - c->highest_inum = UBIFS_FIRST_INO; > - > - c->jhead_cnt = 1; > - > - main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; > - main_lebs -= c->log_lebs + c->orph_lebs; > - > - err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); > - if (err) > - return err; > - > - c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + > - c->orph_lebs; > - head_lnum = c->main_first; > - head_offs = 0; > - > - c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; > - c->lpt_last = c->lpt_first + c->lpt_lebs - 1; > - > - c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); > - if (!c->lpt) > - return err_msg("unable to allocate LPT"); > - > - c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); > - if (!c->ltab) > - return err_msg("unable to allocate LPT ltab"); > - > - /* Initialize LPT's own lprops */ > - for (i = 0; i < c->lpt_lebs; i++) { > - c->ltab[i].free = c->leb_size; > - c->ltab[i].dirty = 0; > - } > - > - c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); > - c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); > - dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); > - > - leb_buf = malloc(c->leb_size); > - if (!leb_buf) > - return err_msg("out of memory"); > - > - node_buf = malloc(NODE_BUFFER_SIZE); > - if (!node_buf) > - return err_msg("out of memory"); > - > - block_buf = malloc(UBIFS_BLOCK_SIZE); > - if (!block_buf) > - return err_msg("out of memory"); > - > - sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; > - hash_table = malloc(sz); > - if (!hash_table) > - return err_msg("out of memory"); > - memset(hash_table, 0, sz); > - > - err = init_compression(); > - if (err) > - return err; > - > - return 0; > -} > - > -static void destroy_hash_table(void) > -{ > - int i; > - > - for (i = 0; i < HASH_TABLE_SIZE; i++) { > - struct inum_mapping *im, *q; > - > - for (im = hash_table[i]; im; ) { > - q = im; > - im = im->next; > - free(q->path_name); > - free(q); > - } > - } > -} > - > -/** > - * deinit - deinitialize things. > - */ > -static void deinit(void) > -{ > - free(c->lpt); > - free(c->ltab); > - free(leb_buf); > - free(node_buf); > - free(block_buf); > - destroy_hash_table(); > - free(hash_table); > - destroy_compression(); > - free_devtable_info(); > -} > - > -/** > - * mkfs - make the file system. > - * > - * Each on-flash area has a corresponding function to create it. The order of > - * the functions reflects what information must be known to complete each stage. > - * As a consequence the output file is not written sequentially. No effort has > - * been made to make efficient use of memory or to allow for the possibility of > - * incremental updates to the output file. > - */ > -static int mkfs(void) > -{ > - int err = 0; > - > - err = init(); > - if (err) > - goto out; > - > - err = write_data(); > - if (err) > - goto out; > - > - err = set_gc_lnum(); > - if (err) > - goto out; > - > - err = write_index(); > - if (err) > - goto out; > - > - err = finalize_leb_cnt(); > - if (err) > - goto out; > - > - err = write_lpt(); > - if (err) > - goto out; > - > - err = write_super(); > - if (err) > - goto out; > - > - err = write_master(); > - if (err) > - goto out; > - > - err = write_log(); > - if (err) > - goto out; > - > - err = write_orphan_area(); > - > -out: > - deinit(); > - return err; > -} > - > -int main(int argc, char *argv[]) > -{ > - int err; > - > - err = get_options(argc, argv); > - if (err) > - return err; > - > - err = open_target(); > - if (err) > - return err; > - > - err = mkfs(); > - if (err) { > - close_target(); > - return err; > - } > - > - err = close_target(); > - if (err) > - return err; > - > - if (verbose) > - printf("Success!\n"); > - > - return 0; > -} > diff --git a/mkfs.ubifs/mkfs.ubifs.h b/mkfs.ubifs/mkfs.ubifs.h > deleted file mode 100644 > index 944a159..0000000 > --- a/mkfs.ubifs/mkfs.ubifs.h > +++ /dev/null > @@ -1,150 +0,0 @@ > -/* > - * Copyright (C) 2008 Nokia Corporation. > - * Copyright (C) 2008 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 version 2 as published by > - * the Free Software Foundation. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Artem Bityutskiy > - * Adrian Hunter > - * Zoltan Sogor > - */ > - > -#ifndef __MKFS_UBIFS_H__ > -#define __MKFS_UBIFS_H__ > - > -#include <unistd.h> > -#include <stdlib.h> > -#include <stdio.h> > -#include <limits.h> > -#include <string.h> > -#include <stdint.h> > -#include <endian.h> > -#include <byteswap.h> > -#include <linux/types.h> > -#include <linux/fs.h> > - > -#include <getopt.h> > -#include <sys/types.h> > -#include <sys/stat.h> > -#include <sys/ioctl.h> > -#include <fcntl.h> > -#include <dirent.h> > -#include <errno.h> > -#include <libgen.h> > -#include <ctype.h> > -#include <uuid/uuid.h> > -#include <sys/file.h> > - > -#include <mtd/ubifs-media.h> > - > -/* common.h requires the PROGRAM_NAME macro */ > -#define PROGRAM_NAME "mkfs.ubifs" > -#include "common.h" > - > -#include "libubi.h" > -#include "defs.h" > -#include "crc16.h" > -#include "ubifs.h" > -#include "key.h" > -#include "lpt.h" > -#include "compr.h" > - > -/* > - * Compression flags are duplicated so that compr.c can compile without ubifs.h. > - * Here we make sure they are the same. > - */ > -#if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE > -#error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE > -#endif > -#if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO > -#error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO > -#endif > -#if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB > -#error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB > -#endif > - > -extern int verbose; > -extern int debug_level; > - > -#define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ > - printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ > -} while(0) > - > -#define err_msg(fmt, ...) ({ \ > - fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ > - -1; \ > -}) > - > -#define sys_err_msg(fmt, ...) ({ \ > - int err_ = errno; \ > - fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ > - fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ > - -1; \ > -}) > - > -/** > - * struct path_htbl_element - an element of the path hash table. > - * @path: the UBIFS path the element describes (the key of the element) > - * @name_htbl: one more (nested) hash table containing names of all > - * files/directories/device nodes which should be created at this > - * path > - * > - * See device table handling for more information. > - */ > -struct path_htbl_element { > - const char *path; > - struct hashtable *name_htbl; > -}; > - > -/** > - * struct name_htbl_element - an element in the name hash table > - * @name: name of the file/directory/device node (the key of the element) > - * @mode: accsess rights and file type > - * @uid: user ID > - * @gid: group ID > - * @major: device node major number > - * @minor: device node minor number > - * > - * This is an element of the name hash table. Name hash table sits in the path > - * hash table elements and describes file names which should be created/changed > - * at this path. > - */ > -struct name_htbl_element { > - const char *name; > - unsigned int mode; > - unsigned int uid; > - unsigned int gid; > - dev_t dev; > -}; > - > -extern struct ubifs_info info_; > - > -struct hashtable_itr; > - > -int write_leb(int lnum, int len, void *buf); > -int parse_devtable(const char *tbl_file); > -struct path_htbl_element *devtbl_find_path(const char *path); > -struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, > - const char *name); > -int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, > - struct name_htbl_element *nh_elt); > -struct name_htbl_element * > -first_name_htbl_element(struct path_htbl_element *ph_elt, > - struct hashtable_itr **itr); > -struct name_htbl_element * > -next_name_htbl_element(struct path_htbl_element *ph_elt, > - struct hashtable_itr **itr); > -void free_devtable_info(void); > - > -#endif > diff --git a/mkfs.ubifs/ubifs.h b/mkfs.ubifs/ubifs.h > deleted file mode 100644 > index 434b651..0000000 > --- a/mkfs.ubifs/ubifs.h > +++ /dev/null > @@ -1,441 +0,0 @@ > -/* > - * This file is part of UBIFS. > - * > - * Copyright (C) 2008 Nokia Corporation. > - * Copyright (C) 2008 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 version 2 as published by > - * the Free Software Foundation. > - * > - * 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., 51 > - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > - * > - * Authors: Artem Bityutskiy > - * Adrian Hunter > - * Zoltan Sogor > - */ > - > -#ifndef __UBIFS_H__ > -#define __UBIFS_H__ > - > -/* Maximum logical eraseblock size in bytes */ > -#define UBIFS_MAX_LEB_SZ (2*1024*1024) > - > -/* Minimum amount of data UBIFS writes to the flash */ > -#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) > - > -/* Largest key size supported in this implementation */ > -#define CUR_MAX_KEY_LEN UBIFS_SK_LEN > - > -/* > - * There is no notion of truncation key because truncation nodes do not exist > - * in TNC. However, when replaying, it is handy to introduce fake "truncation" > - * keys for truncation nodes because the code becomes simpler. So we define > - * %UBIFS_TRUN_KEY type. > - */ > -#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT > - > -/* The below union makes it easier to deal with keys */ > -union ubifs_key > -{ > - uint8_t u8[CUR_MAX_KEY_LEN]; > - uint32_t u32[CUR_MAX_KEY_LEN/4]; > - uint64_t u64[CUR_MAX_KEY_LEN/8]; > - __le32 j32[CUR_MAX_KEY_LEN/4]; > -}; > - > -/* > - * LEB properties flags. > - * > - * LPROPS_UNCAT: not categorized > - * LPROPS_DIRTY: dirty > 0, not index > - * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index > - * LPROPS_FREE: free > 0, not empty, not index > - * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs > - * LPROPS_EMPTY: LEB is empty, not taken > - * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken > - * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken > - * LPROPS_CAT_MASK: mask for the LEB categories above > - * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) > - * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) > - */ > -enum { > - LPROPS_UNCAT = 0, > - LPROPS_DIRTY = 1, > - LPROPS_DIRTY_IDX = 2, > - LPROPS_FREE = 3, > - LPROPS_HEAP_CNT = 3, > - LPROPS_EMPTY = 4, > - LPROPS_FREEABLE = 5, > - LPROPS_FRDI_IDX = 6, > - LPROPS_CAT_MASK = 15, > - LPROPS_TAKEN = 16, > - LPROPS_INDEX = 32, > -}; > - > -/** > - * struct ubifs_lprops - logical eraseblock properties. > - * @free: amount of free space in bytes > - * @dirty: amount of dirty space in bytes > - * @flags: LEB properties flags (see above) > - */ > -struct ubifs_lprops > -{ > - int free; > - int dirty; > - int flags; > -}; > - > -/** > - * struct ubifs_lpt_lprops - LPT logical eraseblock properties. > - * @free: amount of free space in bytes > - * @dirty: amount of dirty space in bytes > - */ > -struct ubifs_lpt_lprops > -{ > - int free; > - int dirty; > -}; > - > -struct ubifs_nnode; > - > -/** > - * struct ubifs_cnode - LEB Properties Tree common node. > - * @parent: parent nnode > - * @cnext: next cnode to commit > - * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) > - * @iip: index in parent > - * @level: level in the tree (zero for pnodes, greater than zero for nnodes) > - * @num: node number > - */ > -struct ubifs_cnode > -{ > - struct ubifs_nnode *parent; > - struct ubifs_cnode *cnext; > - unsigned long flags; > - int iip; > - int level; > - int num; > -}; > - > -/** > - * struct ubifs_pnode - LEB Properties Tree leaf node. > - * @parent: parent nnode > - * @cnext: next cnode to commit > - * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) > - * @iip: index in parent > - * @level: level in the tree (always zero for pnodes) > - * @num: node number > - * @lprops: LEB properties array > - */ > -struct ubifs_pnode > -{ > - struct ubifs_nnode *parent; > - struct ubifs_cnode *cnext; > - unsigned long flags; > - int iip; > - int level; > - int num; > - struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; > -}; > - > -/** > - * struct ubifs_nbranch - LEB Properties Tree internal node branch. > - * @lnum: LEB number of child > - * @offs: offset of child > - * @nnode: nnode child > - * @pnode: pnode child > - * @cnode: cnode child > - */ > -struct ubifs_nbranch > -{ > - int lnum; > - int offs; > - union > - { > - struct ubifs_nnode *nnode; > - struct ubifs_pnode *pnode; > - struct ubifs_cnode *cnode; > - }; > -}; > - > -/** > - * struct ubifs_nnode - LEB Properties Tree internal node. > - * @parent: parent nnode > - * @cnext: next cnode to commit > - * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) > - * @iip: index in parent > - * @level: level in the tree (always greater than zero for nnodes) > - * @num: node number > - * @nbranch: branches to child nodes > - */ > -struct ubifs_nnode > -{ > - struct ubifs_nnode *parent; > - struct ubifs_cnode *cnext; > - unsigned long flags; > - int iip; > - int level; > - int num; > - struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; > -}; > - > -/** > - * struct ubifs_lp_stats - statistics of eraseblocks in the main area. > - * @empty_lebs: number of empty LEBs > - * @taken_empty_lebs: number of taken LEBs > - * @idx_lebs: number of indexing LEBs > - * @total_free: total free space in bytes > - * @total_dirty: total dirty space in bytes > - * @total_used: total used space in bytes (includes only data LEBs) > - * @total_dead: total dead space in bytes (includes only data LEBs) > - * @total_dark: total dark space in bytes (includes only data LEBs) > - */ > -struct ubifs_lp_stats { > - int empty_lebs; > - int taken_empty_lebs; > - int idx_lebs; > - long long total_free; > - long long total_dirty; > - long long total_used; > - long long total_dead; > - long long total_dark; > -}; > - > -/** > - * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. > - * @key: key > - * @znode: znode address in memory > - * @lnum: LEB number of the indexing node > - * @offs: offset of the indexing node within @lnum > - * @len: target node length > - */ > -struct ubifs_zbranch > -{ > - union ubifs_key key; > - struct ubifs_znode *znode; > - int lnum; > - int offs; > - int len; > -}; > - > -/** > - * struct ubifs_znode - in-memory representation of an indexing node. > - * @parent: parent znode or NULL if it is the root > - * @cnext: next znode to commit > - * @flags: flags > - * @time: last access time (seconds) > - * @level: level of the entry in the TNC tree > - * @child_cnt: count of child znodes > - * @iip: index in parent's zbranch array > - * @alt: lower bound of key range has altered i.e. child inserted at slot 0 > - * @zbranch: array of znode branches (@c->fanout elements) > - */ > -struct ubifs_znode > -{ > - struct ubifs_znode *parent; > - struct ubifs_znode *cnext; > - unsigned long flags; > - unsigned long time; > - int level; > - int child_cnt; > - int iip; > - int alt; > -#ifdef CONFIG_UBIFS_FS_DEBUG > - int lnum, offs, len; > -#endif > - struct ubifs_zbranch zbranch[]; > -}; > - > -/** > - * struct ubifs_info - UBIFS file-system description data structure > - * (per-superblock). > - * > - * @highest_inum: highest used inode number > - * @max_sqnum: current global sequence number > - * > - * @jhead_cnt: count of journal heads > - * @max_bud_bytes: maximum number of bytes allowed in buds > - * > - * @zroot: zbranch which points to the root index node and znode > - * @ihead_lnum: LEB number of index head > - * @ihead_offs: offset of index head > - * > - * @log_lebs: number of logical eraseblocks in the log > - * @lpt_lebs: number of LEBs used for lprops table > - * @lpt_first: first LEB of the lprops table area > - * @lpt_last: last LEB of the lprops table area > - * @main_lebs: count of LEBs in the main area > - * @main_first: first LEB of the main area > - * @default_compr: default compression type > - * @favor_lzo: favor LZO compression method > - * @favor_percent: lzo vs. zlib threshold used in case favor LZO > - * > - * @key_hash_type: type of the key hash > - * @key_hash: direntry key hash function > - * @key_fmt: key format > - * @key_len: key length > - * @fanout: fanout of the index tree (number of links per indexing node) > - * > - * @min_io_size: minimal input/output unit size > - * @leb_size: logical eraseblock size in bytes > - * @leb_cnt: count of logical eraseblocks > - * @max_leb_cnt: maximum count of logical eraseblocks > - * > - * @old_idx_sz: size of index on flash > - * @lst: lprops statistics > - * > - * @dead_wm: LEB dead space watermark > - * @dark_wm: LEB dark space watermark > - * > - * @di: UBI device information > - * @vi: UBI volume information > - * > - * @gc_lnum: LEB number used for garbage collection > - * @rp_size: reserved pool size > - * > - * @space_bits: number of bits needed to record free or dirty space > - * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT > - * @lpt_offs_bits: number of bits needed to record an offset in the LPT > - * @lpt_spc_bits: number of bits needed to space in the LPT > - * @pcnt_bits: number of bits needed to record pnode or nnode number > - * @lnum_bits: number of bits needed to record LEB number > - * @nnode_sz: size of on-flash nnode > - * @pnode_sz: size of on-flash pnode > - * @ltab_sz: size of on-flash LPT lprops table > - * @lsave_sz: size of on-flash LPT save table > - * @pnode_cnt: number of pnodes > - * @nnode_cnt: number of nnodes > - * @lpt_hght: height of the LPT > - * > - * @lpt_lnum: LEB number of the root nnode of the LPT > - * @lpt_offs: offset of the root nnode of the LPT > - * @nhead_lnum: LEB number of LPT head > - * @nhead_offs: offset of LPT head > - * @big_lpt: flag that LPT is too big to write whole during commit > - * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up > - * @lpt_sz: LPT size > - * > - * @ltab_lnum: LEB number of LPT's own lprops table > - * @ltab_offs: offset of LPT's own lprops table > - * @lpt: lprops table > - * @ltab: LPT's own lprops table > - * @lsave_cnt: number of LEB numbers in LPT's save table > - * @lsave_lnum: LEB number of LPT's save table > - * @lsave_offs: offset of LPT's save table > - * @lsave: LPT's save table > - * @lscan_lnum: LEB number of last LPT scan > - */ > -struct ubifs_info > -{ > - ino_t highest_inum; > - unsigned long long max_sqnum; > - > - int jhead_cnt; > - long long max_bud_bytes; > - > - struct ubifs_zbranch zroot; > - int ihead_lnum; > - int ihead_offs; > - > - int log_lebs; > - int lpt_lebs; > - int lpt_first; > - int lpt_last; > - int orph_lebs; > - int main_lebs; > - int main_first; > - int default_compr; > - int favor_lzo; > - int favor_percent; > - > - uint8_t key_hash_type; > - uint32_t (*key_hash)(const char *str, int len); > - int key_fmt; > - int key_len; > - int fanout; > - > - int min_io_size; > - int leb_size; > - int leb_cnt; > - int max_leb_cnt; > - > - unsigned long long old_idx_sz; > - struct ubifs_lp_stats lst; > - > - int dead_wm; > - int dark_wm; > - > - struct ubi_dev_info di; > - struct ubi_vol_info vi; > - > - int gc_lnum; > - long long rp_size; > - > - int space_bits; > - int lpt_lnum_bits; > - int lpt_offs_bits; > - int lpt_spc_bits; > - int pcnt_bits; > - int lnum_bits; > - int nnode_sz; > - int pnode_sz; > - int ltab_sz; > - int lsave_sz; > - int pnode_cnt; > - int nnode_cnt; > - int lpt_hght; > - > - int lpt_lnum; > - int lpt_offs; > - int nhead_lnum; > - int nhead_offs; > - int big_lpt; > - int space_fixup; > - long long lpt_sz; > - > - int ltab_lnum; > - int ltab_offs; > - struct ubifs_lprops *lpt; > - struct ubifs_lpt_lprops *ltab; > - int lsave_cnt; > - int lsave_lnum; > - int lsave_offs; > - int *lsave; > - int lscan_lnum; > - > -}; > - > -/** > - * ubifs_idx_node_sz - return index node size. > - * @c: the UBIFS file-system description object > - * @child_cnt: number of children of this index node > - */ > -static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) > -{ > - return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; > -} > - > -/** > - * ubifs_idx_branch - return pointer to an index branch. > - * @c: the UBIFS file-system description object > - * @idx: index node > - * @bnum: branch number > - */ > -static inline > -struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, > - const struct ubifs_idx_node *idx, > - int bnum) > -{ > - return (struct ubifs_branch *)((void *)idx->branches + > - (UBIFS_BRANCH_SZ + c->key_len) * bnum); > -} > - > -#endif /* __UBIFS_H__ */ > diff --git a/mtd_debug.c b/mtd_debug.c > deleted file mode 100644 > index d6993ce..0000000 > --- a/mtd_debug.c > +++ /dev/null > @@ -1,397 +0,0 @@ > -/* > - * Copyright (c) 2d3D, Inc. > - * Written by Abraham vd Merwe <abraham@2d3d.co.za> > - * All rights reserved. > - * > - * 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. > - */ > - > -#define PROGRAM_NAME "mtd_debug" > - > -#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> > -#include "common.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, off_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, 0666); > - 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", __func__, size); > - if (size != BUF_SIZE) { > - size = BUF_SIZE; > - fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, size, n); > - perror("read()"); > - goto err2; > - } > - err = write(outfd, buf, size); > - if (err < 0) { > - fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, 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 %zu bytes from address 0x%.8"PRIxoff_t" 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, off_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", __func__, size); > - if (size != BUF_SIZE) { > - size = BUF_SIZE; > - fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, 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", __func__, 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%.8"PRIxoff_t" 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_MLCNANDFLASH: > - printf("MTD_MLCNANDFLASH"); > - break; > - case MTD_DATAFLASH: > - printf("MTD_DATAFLASH"); > - break; > - case MTD_UBIVOLUME: > - printf("MTD_UBIVOLUME"); > - 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_WRITEABLE", MTD_WRITEABLE }, > - { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, > - { "MTD_NO_ERASE", MTD_NO_ERASE }, > - { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK }, > - { NULL, -1 } > - }; > - for (i = 0; flags[i].name != NULL; i++) { > - if (mtd.flags & flags[i].value) { > - if (first) { > - printf("%s", 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.writesize = "); > - printsize(mtd.writesize); > - > - printf("\nmtd.oobsize = "); > - printsize(mtd.oobsize); > - > - printf("\nregions = %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(void) > -{ > - fprintf(stderr, "usage: %1$s info <device>\n" > - " %1$s read <device> <offset> <len> <dest-filename>\n" > - " %1$s write <device> <offset> <len> <source-filename>\n" > - " %1$s erase <device> <offset> <len>\n", > - PROGRAM_NAME); > - exit(EXIT_FAILURE); > -} > - > -int main(int argc, char *argv[]) > -{ > - int err = 0, fd; > - int open_flag; > - > - enum { > - OPT_INFO, > - OPT_READ, > - OPT_WRITE, > - OPT_ERASE > - } option = OPT_INFO; > - > - /* 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(); > - > - /* open device */ > - open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR; > - if ((fd = open(argv[2], O_SYNC | open_flag)) < 0) > - errmsg_die("open()"); > - > - switch (option) { > - case OPT_INFO: > - showinfo(fd); > - break; > - case OPT_READ: > - err = flash_to_file(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); > - break; > - case OPT_WRITE: > - err = file_to_flash(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); > - break; > - case OPT_ERASE: > - err = erase_flash(fd, strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0)); > - break; > - } > - > - /* close device */ > - if (close(fd) < 0) > - errmsg_die("close()"); > - > - return err; > -} > diff --git a/mtdpart.c b/mtdpart.c > deleted file mode 100644 > index 0016e34..0000000 > --- a/mtdpart.c > +++ /dev/null > @@ -1,194 +0,0 @@ > -/* > - * mtdpart.c > - * > - * Copyright 2015 The Chromium OS Authors. > - * > - * 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 adds or removes a partition from an MTD device. > - */ > - > -#define PROGRAM_NAME "mtdpart" > - > -#include <fcntl.h> > -#include <getopt.h> > -#include <limits.h> > -#include <linux/blkpg.h> > -#include <stdio.h> > -#include <string.h> > -#include <sys/ioctl.h> > -#include <sys/stat.h> > -#include <sys/types.h> > -#include <unistd.h> > - > -#include "common.h" > - > -static void display_help(int status) > -{ > - fprintf(status == EXIT_SUCCESS ? stdout : stderr, > -"Usage: %1$s add [OPTION] <MTD_DEVICE> <PART_NAME> <START> <SIZE>\n" > -" %1$s del [OPTION] <MTD_DEVICE> <PART_NUMBER>\n" > -"Adds a partition to an MTD device, or remove an existing partition from it.\n" > -"\n" > -" -h, --help Display this help and exit\n" > -" --version Output version information and exit\n" > -"\n" > -"START location and SIZE of the partition are in bytes. They should align on\n" > -"eraseblock size.\n", > - PROGRAM_NAME > - ); > - exit(status); > -} > - > -static void display_version(void) > -{ > - printf("%1$s " VERSION "\n" > - "\n" > - "%1$s comes with NO WARRANTY\n" > - "to the extent permitted by law.\n" > - "\n" > - "You may redistribute copies of %1$s\n" > - "under the terms of the GNU General Public Licence.\n" > - "See the file `COPYING' for more information.\n", > - PROGRAM_NAME); > - exit(EXIT_SUCCESS); > -} > - > -/* Command arguments */ > - > -typedef enum { > - COMMAND_ADD, > - COMMAND_DEL > -} command_type; > - > -static command_type command; /* add or del */ > -static const char *mtddev; /* mtd device name */ > -static const char *part_name; /* partition name */ > -static int part_no; /* partition number */ > -static long long start_addr; /* start address */ > -static long long length; /* partition size */ > - > -static void process_options(int argc, char * const argv[]) > -{ > - int error = 0; > - > - for (;;) { > - int option_index = 0; > - static const char short_options[] = "h"; > - static const struct option long_options[] = { > - {"version", no_argument, 0, 0}, > - {"help", no_argument, 0, 'h'}, > - {0, 0, 0, 0}, > - }; > - > - int c = getopt_long(argc, argv, short_options, > - long_options, &option_index); > - if (c == EOF) { > - break; > - } > - > - switch (c) { > - case 0: > - display_version(); > - break; > - case 'h': > - display_help(EXIT_SUCCESS); > - break; > - case '?': > - error++; > - break; > - } > - } > - > - if ((argc - optind) < 3 || error) > - display_help(EXIT_FAILURE); > - > - const char *s_command = argv[optind++]; > - mtddev = argv[optind++]; > - > - if (strcmp(s_command, "del") == 0 && (argc - optind) == 1) { > - const char *s_part_no = argv[optind++]; > - > - long tmp = simple_strtol(s_part_no, &error); > - if (tmp < 0) > - errmsg_die("Can't specify negative partition number: %ld", > - tmp); > - if (tmp > INT_MAX) > - errmsg_die("Partition number exceeds INT_MAX: %ld", > - tmp); > - > - part_no = tmp; > - command = COMMAND_DEL; > - } else if (strcmp(s_command, "add") == 0 && (argc - optind) == 3) { > - const char *s_start; > - const char *s_length; > - > - part_name = argv[optind++]; > - s_start = argv[optind++]; > - s_length = argv[optind++]; > - > - if (strlen(part_name) >= BLKPG_DEVNAMELTH) > - errmsg_die("Partition name (%s) should be less than %d characters", > - part_name, BLKPG_DEVNAMELTH); > - > - start_addr = simple_strtoll(s_start, &error); > - if (start_addr < 0) > - errmsg_die("Can't specify negative start offset: %lld", > - start_addr); > - > - length = simple_strtoll(s_length, &error); > - if (length < 0) > - errmsg_die("Can't specify negative length: %lld", > - length); > - > - command = COMMAND_ADD; > - } else > - display_help(EXIT_FAILURE); > - > - if (error) > - display_help(EXIT_FAILURE); > -} > - > - > -int main(int argc, char * const argv[]) > -{ > - int fd; > - struct blkpg_partition part; > - struct blkpg_ioctl_arg arg; > - > - process_options(argc, argv); > - > - fd = open(mtddev, O_RDWR | O_CLOEXEC); > - if (fd == -1) > - sys_errmsg_die("Cannot open %s", mtddev); > - > - memset(&part, 0, sizeof(part)); > - > - memset(&arg, 0, sizeof(arg)); > - arg.datalen = sizeof(part); > - arg.data = ∂ > - > - switch (command) { > - case COMMAND_ADD: > - part.start = start_addr; > - part.length = length; > - strncpy(part.devname, part_name, sizeof(part.devname)); > - arg.op = BLKPG_ADD_PARTITION; > - break; > - case COMMAND_DEL: > - part.pno = part_no; > - arg.op = BLKPG_DEL_PARTITION; > - break; > - } > - > - if (ioctl(fd, BLKPG, &arg)) > - sys_errmsg_die("Failed to issue BLKPG ioctl"); > - > - close(fd); > - > - /* Exit happy */ > - return EXIT_SUCCESS; > -} > diff --git a/nand-utils/load_nandsim.sh b/nand-utils/load_nandsim.sh > new file mode 100755 > index 0000000..4d9f0cb > --- /dev/null > +++ b/nand-utils/load_nandsim.sh > @@ -0,0 +1,127 @@ > +#!/bin/sh -euf > + > +# > +# This script inserts NAND simulator module to emulate NAND flash of specified > +# size. > +# > +# Author: Artem Bityutskiy > +# > + > +fatal() > +{ > + echo "Error: $1" 1>&2 > + exit 1 > +} > + > +usage() > +{ > + cat 1>&2 <<EOF > +Load NAND simulator to simulate flash of a specified size. > + > +Usage: ${0##*/} <size in MiB> <eraseblock size in KiB> \\ > + <page size (512 or 2048)> > + > +Only the first parameter is mandatory. Default eraseblock size > +is 16KiB, default NAND page size is 512 bytes. > + > +Only the following combinations are supported: > +-------------------------------------------------- > +| size (MiB) | EB size (KiB) | Page size (bytes) | > +-------------------------------------------------- > +| 16 | 16 | 512 | > +| 32 | 16 | 512 | > +| 64 | 16 | 512 | > +| 128 | 16 | 512 | > +| 256 | 16 | 512 | > +| 64 | 64 | 2048 | > +| 64 | 128 | 2048 | > +| 64 | 256 | 2048 | > +| 64 | 512 | 2048 | > +| 128 | 64 | 2048 | > +| 128 | 128 | 2048 | > +| 128 | 256 | 2048 | > +| 128 | 512 | 2048 | > +| 256 | 64 | 2048 | > +| 256 | 128 | 2048 | > +| 256 | 256 | 2048 | > +| 256 | 512 | 2048 | > +| 512 | 64 | 2048 | > +| 512 | 128 | 2048 | > +| 512 | 256 | 2048 | > +| 512 | 512 | 2048 | > +| 1024 | 64 | 2048 | > +| 1024 | 128 | 2048 | > +| 1024 | 256 | 2048 | > +| 1024 | 512 | 2048 | > +-------------------------------------------------- > +EOF > +} > + > +if grep -q "NAND simulator" /proc/mtd; then > + fatal "nandsim is already loaded" > +fi > + > +if [ "$#" -lt "1" ]; then > + usage > + exit 1 > +fi > + > +size="$1" > +eb_size="$2" > +page_size="$3" > +if [ "$#" = "1" ]; then > + eb_size="16" > + page_size="512" > +elif [ "$#" = "2" ]; then > + page_size="512" > +fi > + > +if [ "$page_size" -eq 512 ] && [ "$eb_size" -ne "16" ]; then > + fatal "only 16KiB eraseblocks are possible in case of 512 bytes page" > +fi > + > +first= > +second= > +third= > +fourth= > + > +if [ "$page_size" -eq "512" ]; then > + first="0x20" > + case "$size" in > + 16) second=0x33 ;; > + 32) second=0x35 ;; > + 64) second=0x36 ;; > + 128) second=0x78 ;; > + 256) second=0x71 ;; > + *) fatal "flash size ${size}MiB is not supported, try 16, 32, 64 or 256" > + esac > +elif [ "$page_size" -eq "2048" ]; then > + case "$eb_size" in > + 64) fourth="0x05" ;; > + 128) fourth="0x15" ;; > + 256) fourth="0x25" ;; > + 512) fourth="0x35" ;; > + *) fatal "eraseblock ${eb_size}KiB is not supported" > + esac > + > + > + case "$size" in > + 64) first="0x20"; second="0xa2"; third="0x00 ";; > + 128) first="0xec"; second="0xa1"; third="0x00 ";; > + 256) first="0x20"; second="0xaa"; third="0x00 ";; > + 512) first="0x20"; second="0xac"; third="0x00 ";; > + 1024) first="0xec"; second="0xd3"; third="0x51 ";; > + *) fatal "unable to emulate ${size}MiB flash with ${eb_size}KiB eraseblock" > + esac > +else > + fatal "bad NAND page size ${page_size}KiB, it has to be either 512 or 2048" > +fi > + > +first="first_id_byte=$first" > +second="second_id_byte=$second" > +[ -z "$third" ] || third="third_id_byte=$third" > +[ -z "$fourth" ] || fourth="fourth_id_byte=$fourth" > + > +modprobe nandsim "$first" "$second" $third $fourth > + > +echo "Loaded NAND simulator (${size}MiB, ${eb_size}KiB eraseblock, $page_size bytes NAND page)" > diff --git a/nand-utils/nanddump.c b/nand-utils/nanddump.c > new file mode 100644 > index 0000000..4ee7ed4 > --- /dev/null > +++ b/nand-utils/nanddump.c > @@ -0,0 +1,490 @@ > +/* > + * nanddump.c > + * > + * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) > + * Steven J. Hill (sjhill@realitydiluted.com) > + * > + * 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 PROGRAM_NAME "nanddump" > + > +#include <ctype.h> > +#include <errno.h> > +#include <fcntl.h> > +#include <stdbool.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> > +#include "common.h" > +#include <libmtd.h> > + > +static void display_help(int status) > +{ > + fprintf(status == EXIT_SUCCESS ? stdout : stderr, > +"Usage: %s [OPTIONS] MTD-device\n" > +"Dumps the contents of a nand mtd partition.\n" > +"\n" > +"-h --help Display this help and exit\n" > +" --version Output version information and exit\n" > +" --bb=METHOD Choose bad block handling method (see below).\n" > +"-a --forcebinary Force printing of binary data to tty\n" > +"-c --canonicalprint Print canonical Hex+ASCII dump\n" > +"-f file --file=file Dump to file\n" > +"-l length --length=length Length\n" > +"-n --noecc Read without error correction\n" > +" --omitoob Omit OOB data (default)\n" > +"-o --oob Dump OOB data\n" > +"-p --prettyprint Print nice (hexdump)\n" > +"-q --quiet Don't display progress and status messages\n" > +"-s addr --startaddress=addr Start address\n" > +"\n" > +"--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':\n" > +" padbad: dump flash data, substituting 0xFF for any bad blocks\n" > +" dumpbad: dump flash data, including any bad blocks\n" > +" skipbad: dump good data, completely skipping any bad blocks (default)\n", > + PROGRAM_NAME); > + exit(status); > +} > + > +static void display_version(void) > +{ > + printf("%1$s " VERSION "\n" > + "\n" > + "%1$s comes with NO WARRANTY\n" > + "to the extent permitted by law.\n" > + "\n" > + "You may redistribute copies of %1$s\n" > + "under the terms of the GNU General Public Licence.\n" > + "See the file `COPYING' for more information.\n", > + PROGRAM_NAME); > + exit(EXIT_SUCCESS); > +} > + > +// Option variables > + > +static bool pretty_print = false; // print nice > +static bool noecc = false; // don't error correct > +static bool omitoob = true; // omit oob data > +static long long start_addr; // start address > +static long long length; // dump length > +static const char *mtddev; // mtd device name > +static const char *dumpfile; // dump file name > +static bool quiet = false; // suppress diagnostic output > +static bool canonical = false; // print nice + ascii > +static bool forcebinary = false; // force printing binary to tty > + > +static enum { > + padbad, // dump flash data, substituting 0xFF for any bad blocks > + dumpbad, // dump flash data, including any bad blocks > + skipbad, // dump good data, completely skipping any bad blocks > +} bb_method = skipbad; > + > +static void process_options(int argc, char * const argv[]) > +{ > + int error = 0; > + bool oob_default = true; > + > + for (;;) { > + int option_index = 0; > + static const char short_options[] = "hs:f:l:opqnca"; > + static const struct option long_options[] = { > + {"version", no_argument, 0, 0}, > + {"bb", required_argument, 0, 0}, > + {"omitoob", no_argument, 0, 0}, > + {"help", no_argument, 0, 'h'}, > + {"forcebinary", no_argument, 0, 'a'}, > + {"canonicalprint", no_argument, 0, 'c'}, > + {"file", required_argument, 0, 'f'}, > + {"oob", no_argument, 0, 'o'}, > + {"prettyprint", no_argument, 0, 'p'}, > + {"startaddress", required_argument, 0, 's'}, > + {"length", required_argument, 0, 'l'}, > + {"noecc", no_argument, 0, 'n'}, > + {"quiet", 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_version(); > + break; > + case 1: > + /* Handle --bb=METHOD */ > + if (!strcmp(optarg, "padbad")) > + bb_method = padbad; > + else if (!strcmp(optarg, "dumpbad")) > + bb_method = dumpbad; > + else if (!strcmp(optarg, "skipbad")) > + bb_method = skipbad; > + else > + error++; > + break; > + case 2: /* --omitoob */ > + if (oob_default) { > + oob_default = false; > + omitoob = true; > + } else { > + errmsg_die("--oob and --oomitoob are mutually exclusive"); > + } > + break; > + } > + break; > + case 's': > + start_addr = simple_strtoll(optarg, &error); > + break; > + case 'f': > + dumpfile = xstrdup(optarg); > + break; > + case 'l': > + length = simple_strtoll(optarg, &error); > + break; > + case 'o': > + if (oob_default) { > + oob_default = false; > + omitoob = false; > + } else { > + errmsg_die("--oob and --oomitoob are mutually exclusive"); > + } > + break; > + case 'a': > + forcebinary = true; > + break; > + case 'c': > + canonical = true; > + case 'p': > + pretty_print = true; > + break; > + case 'q': > + quiet = true; > + break; > + case 'n': > + noecc = true; > + break; > + case 'h': > + display_help(EXIT_SUCCESS); > + break; > + case '?': > + error++; > + break; > + } > + } > + > + if (start_addr < 0) > + errmsg_die("Can't specify negative offset with option -s: %lld", > + start_addr); > + > + if (length < 0) > + errmsg_die("Can't specify negative length with option -l: %lld", length); > + > + if (quiet && pretty_print) { > + fprintf(stderr, "The quiet and pretty print options are mutually-\n" > + "exclusive. Choose one or the other.\n"); > + exit(EXIT_FAILURE); > + } > + > + if (forcebinary && pretty_print) { > + fprintf(stderr, "The forcebinary and pretty print options are\n" > + "mutually-exclusive. Choose one or the " > + "other.\n"); > + exit(EXIT_FAILURE); > + } > + > + if ((argc - optind) != 1 || error) > + display_help(EXIT_FAILURE); > + > + mtddev = argv[optind]; > +} > + > +#define PRETTY_ROW_SIZE 16 > +#define PRETTY_BUF_LEN 80 > + > +/** > + * pretty_dump_to_buffer - formats a blob of data to "hex ASCII" in memory > + * @buf: data blob to dump > + * @len: number of bytes in the @buf > + * @linebuf: where to put the converted data > + * @linebuflen: total size of @linebuf, including space for terminating NULL > + * @pagedump: true - dumping as page format; false - dumping as OOB format > + * @ascii: dump ascii formatted data next to hexdump > + * @prefix: address to print before line in a page dump, ignored if !pagedump > + * > + * pretty_dump_to_buffer() works on one "line" of output at a time, i.e., > + * PRETTY_ROW_SIZE bytes of input data converted to hex + ASCII output. > + * > + * Given a buffer of unsigned char data, pretty_dump_to_buffer() converts the > + * input data to a hex/ASCII dump at the supplied memory location. A prefix > + * is included based on whether we are dumping page or OOB data. The converted > + * output is always NULL-terminated. > + * > + * e.g. > + * pretty_dump_to_buffer(data, data_len, prettybuf, linelen, true, > + * false, 256); > + * produces: > + * 0x00000100: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f > + * NOTE: This function was adapted from linux kernel, "lib/hexdump.c" > + */ > +static void pretty_dump_to_buffer(const unsigned char *buf, size_t len, > + char *linebuf, size_t linebuflen, bool pagedump, bool ascii, > + unsigned long long prefix) > +{ > + static const char hex_asc[] = "0123456789abcdef"; > + unsigned char ch; > + unsigned int j, lx = 0, ascii_column; > + > + if (pagedump) > + lx += sprintf(linebuf, "0x%.8llx: ", prefix); > + else > + lx += sprintf(linebuf, " OOB Data: "); > + > + if (!len) > + goto nil; > + if (len > PRETTY_ROW_SIZE) /* limit to one line at a time */ > + len = PRETTY_ROW_SIZE; > + > + for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { > + ch = buf[j]; > + linebuf[lx++] = hex_asc[(ch & 0xf0) >> 4]; > + linebuf[lx++] = hex_asc[ch & 0x0f]; > + linebuf[lx++] = ' '; > + } > + if (j) > + lx--; > + > + ascii_column = 3 * PRETTY_ROW_SIZE + 14; > + > + if (!ascii) > + goto nil; > + > + /* Spacing between hex and ASCII - ensure at least one space */ > + lx += sprintf(linebuf + lx, "%*s", > + MAX((int)MIN(linebuflen, ascii_column) - 1 - lx, 1), > + " "); > + > + linebuf[lx++] = '|'; > + for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) > + linebuf[lx++] = (isascii(buf[j]) && isprint(buf[j])) ? buf[j] > + : '.'; > + linebuf[lx++] = '|'; > +nil: > + linebuf[lx++] = '\n'; > + linebuf[lx++] = '\0'; > +} > + > + > +/* > + * Main program > + */ > +int main(int argc, char * const argv[]) > +{ > + long long ofs, end_addr = 0; > + long long blockstart = 1; > + int i, fd, ofd = 0, bs, badblock = 0; > + struct mtd_dev_info mtd; > + char pretty_buf[PRETTY_BUF_LEN]; > + int firstblock = 1; > + struct mtd_ecc_stats stat1, stat2; > + bool eccstats = false; > + unsigned char *readbuf = NULL, *oobbuf = NULL; > + libmtd_t mtd_desc; > + > + process_options(argc, argv); > + > + /* Initialize libmtd */ > + mtd_desc = libmtd_open(); > + if (!mtd_desc) > + return errmsg("can't initialize libmtd"); > + > + /* Open MTD device */ > + if ((fd = open(mtddev, O_RDONLY)) == -1) { > + perror(mtddev); > + exit(EXIT_FAILURE); > + } > + > + /* Fill in MTD device capability structure */ > + if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) > + return errmsg("mtd_get_dev_info failed"); > + > + /* Allocate buffers */ > + oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); > + readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); > + > + if (noecc) { > + if (ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW) != 0) { > + perror("MTDFILEMODE"); > + goto closeall; > + } > + } else { > + /* check if we can read ecc stats */ > + if (!ioctl(fd, ECCGETSTATS, &stat1)) { > + eccstats = true; > + if (!quiet) { > + fprintf(stderr, "ECC failed: %d\n", stat1.failed); > + fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); > + fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); > + fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); > + } > + } else > + perror("No ECC status information available"); > + } > + > + /* 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(dumpfile); > + goto closeall; > + } > + > + if (!pretty_print && !forcebinary && isatty(ofd)) { > + fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" > + "or '--forcebinary' to override.\n"); > + goto closeall; > + } > + > + /* Initialize start/end addresses and block size */ > + if (start_addr & (mtd.min_io_size - 1)) { > + fprintf(stderr, "the start address (-s parameter) is not page-aligned!\n" > + "The pagesize of this NAND Flash is 0x%x.\n", > + mtd.min_io_size); > + goto closeall; > + } > + if (length) > + end_addr = start_addr + length; > + if (!length || end_addr > mtd.size) > + end_addr = mtd.size; > + > + bs = mtd.min_io_size; > + > + /* Print informative message */ > + if (!quiet) { > + fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", > + mtd.eb_size, mtd.min_io_size, mtd.oob_size); > + fprintf(stderr, > + "Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", > + start_addr, end_addr); > + } > + > + /* Dump the flash contents */ > + for (ofs = start_addr; ofs < end_addr; ofs += bs) { > + /* Check for bad block */ > + if (bb_method == dumpbad) { > + badblock = 0; > + } else if (blockstart != (ofs & (~mtd.eb_size + 1)) || > + firstblock) { > + blockstart = ofs & (~mtd.eb_size + 1); > + firstblock = 0; > + if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { > + errmsg("libmtd: mtd_is_bad"); > + goto closeall; > + } > + } > + > + if (badblock) { > + /* skip bad block, increase end_addr */ > + if (bb_method == skipbad) { > + end_addr += mtd.eb_size; > + ofs += mtd.eb_size - bs; > + if (end_addr > mtd.size) > + end_addr = mtd.size; > + continue; > + } > + memset(readbuf, 0xff, bs); > + } else { > + /* Read page data and exit on failure */ > + if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { > + errmsg("mtd_read"); > + goto closeall; > + } > + } > + > + /* ECC stats available ? */ > + if (eccstats) { > + if (ioctl(fd, ECCGETSTATS, &stat2)) { > + perror("ioctl(ECCGETSTATS)"); > + goto closeall; > + } > + if (stat1.failed != stat2.failed) > + fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" > + " at offset 0x%08llx\n", > + stat2.failed - stat1.failed, ofs); > + if (stat1.corrected != stat2.corrected) > + fprintf(stderr, "ECC: %d corrected bitflip(s) at" > + " offset 0x%08llx\n", > + stat2.corrected - stat1.corrected, ofs); > + stat1 = stat2; > + } > + > + /* Write out page data */ > + if (pretty_print) { > + for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { > + pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, > + pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); > + write(ofd, pretty_buf, strlen(pretty_buf)); > + } > + } else > + write(ofd, readbuf, bs); > + > + if (omitoob) > + continue; > + > + if (badblock) { > + memset(oobbuf, 0xff, mtd.oob_size); > + } else { > + /* Read OOB data and exit on failure */ > + if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { > + errmsg("libmtd: mtd_read_oob"); > + goto closeall; > + } > + } > + > + /* Write out OOB data */ > + if (pretty_print) { > + for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { > + pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, > + pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); > + write(ofd, pretty_buf, strlen(pretty_buf)); > + } > + } else > + write(ofd, oobbuf, mtd.oob_size); > + } > + > + /* Close the output file and MTD device, free memory */ > + close(fd); > + close(ofd); > + free(oobbuf); > + free(readbuf); > + > + /* Exit happy */ > + return EXIT_SUCCESS; > + > +closeall: > + close(fd); > + close(ofd); > + free(oobbuf); > + free(readbuf); > + exit(EXIT_FAILURE); > +} > diff --git a/nand-utils/nandtest.c b/nand-utils/nandtest.c > new file mode 100644 > index 0000000..0805387 > --- /dev/null > +++ b/nand-utils/nandtest.c > @@ -0,0 +1,313 @@ > +#define PROGRAM_NAME "nandtest" > + > +#include <ctype.h> > +#include <errno.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" > + > +void usage(int status) > +{ > + fprintf(status ? stderr : stdout, > + "usage: %s [OPTIONS] <device>\n\n" > + " -h, --help Display this help output\n" > + " -m, --markbad Mark blocks bad if they appear so\n" > + " -s, --seed Supply random seed\n" > + " -p, --passes Number of passes\n" > + " -r <n>, --reads=<n> Read & check <n> times per pass\n" > + " -o, --offset Start offset on flash\n" > + " -l, --length Length of flash to test\n" > + " -k, --keep Restore existing contents after test\n", > + PROGRAM_NAME); > + exit(status); > +} > + > +struct mtd_info_user meminfo; > +struct mtd_ecc_stats oldstats, newstats; > +int fd; > +int markbad=0; > +int seed; > + > +int read_and_compare(loff_t ofs, unsigned char *data, unsigned char *rbuf) > +{ > + ssize_t len; > + int i; > + > + len = pread(fd, rbuf, meminfo.erasesize, ofs); > + if (len < meminfo.erasesize) { > + printf("\n"); > + if (len) > + fprintf(stderr, "Short read (%zd bytes)\n", len); > + else > + perror("read"); > + exit(1); > + } > + > + if (ioctl(fd, ECCGETSTATS, &newstats)) { > + printf("\n"); > + perror("ECCGETSTATS"); > + close(fd); > + exit(1); > + } > + > + if (newstats.corrected > oldstats.corrected) { > + printf("\n %d bit(s) ECC corrected at %08x\n", > + newstats.corrected - oldstats.corrected, > + (unsigned) ofs); > + oldstats.corrected = newstats.corrected; > + } > + if (newstats.failed > oldstats.failed) { > + printf("\nECC failed at %08x\n", (unsigned) ofs); > + oldstats.failed = newstats.failed; > + } > + > + printf("\r%08x: checking...", (unsigned)ofs); > + fflush(stdout); > + > + if (memcmp(data, rbuf, meminfo.erasesize)) { > + printf("\n"); > + fprintf(stderr, "compare failed. seed %d\n", seed); > + for (i=0; i<meminfo.erasesize; i++) { > + if (data[i] != rbuf[i]) > + printf("Byte 0x%x is %02x should be %02x\n", > + i, rbuf[i], data[i]); > + } > + return 1; > + } > + return 0; > +} > + > +int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, int nr_reads) > +{ > + struct erase_info_user er; > + ssize_t len; > + int i, read_errs = 0; > + > + printf("\r%08x: erasing... ", (unsigned)ofs); > + fflush(stdout); > + > + er.start = ofs; > + er.length = meminfo.erasesize; > + > + if (ioctl(fd, MEMERASE, &er)) { > + perror("MEMERASE"); > + if (markbad) { > + printf("Mark block bad at %08lx\n", (long)ofs); > + ioctl(fd, MEMSETBADBLOCK, &ofs); > + } > + return 1; > + } > + > + printf("\r%08x: writing...", (unsigned)ofs); > + fflush(stdout); > + > + len = pwrite(fd, data, meminfo.erasesize, ofs); > + if (len < 0) { > + printf("\n"); > + perror("write"); > + if (markbad) { > + printf("Mark block bad at %08lx\n", (long)ofs); > + ioctl(fd, MEMSETBADBLOCK, &ofs); > + } > + return 1; > + } > + if (len < meminfo.erasesize) { > + printf("\n"); > + fprintf(stderr, "Short write (%zd bytes)\n", len); > + exit(1); > + } > + > + for (i=1; i<=nr_reads; i++) { > + printf("\r%08x: reading (%d of %d)...", (unsigned)ofs, i, nr_reads); > + fflush(stdout); > + if (read_and_compare(ofs, data, rbuf)) > + read_errs++; > + } > + if (read_errs) { > + fprintf(stderr, "read/check %d of %d failed. seed %d\n", read_errs, nr_reads, seed); > + return 1; > + } > + return 0; > +} > + > + > +/* > + * Main program > + */ > +int main(int argc, char **argv) > +{ > + int i; > + unsigned char *wbuf, *rbuf, *kbuf; > + int pass; > + int nr_passes = 1; > + int nr_reads = 4; > + int keep_contents = 0; > + uint32_t offset = 0; > + uint32_t length = -1; > + > + seed = time(NULL); > + > + for (;;) { > + static const char short_options[] = "hkl:mo:p:r:s:"; > + static const struct option long_options[] = { > + { "help", no_argument, 0, 'h' }, > + { "markbad", no_argument, 0, 'm' }, > + { "seed", required_argument, 0, 's' }, > + { "passes", required_argument, 0, 'p' }, > + { "offset", required_argument, 0, 'o' }, > + { "length", required_argument, 0, 'l' }, > + { "reads", required_argument, 0, 'r' }, > + { "keep", no_argument, 0, 'k' }, > + {0, 0, 0, 0}, > + }; > + int option_index = 0; > + int c = getopt_long(argc, argv, short_options, long_options, &option_index); > + if (c == EOF) > + break; > + > + switch (c) { > + case 'h': > + usage(0); > + break; > + > + case '?': > + usage(1); > + break; > + > + case 'm': > + markbad = 1; > + break; > + > + case 'k': > + keep_contents = 1; > + break; > + > + case 's': > + seed = atol(optarg); > + break; > + > + case 'p': > + nr_passes = atol(optarg); > + break; > + > + case 'r': > + nr_reads = atol(optarg); > + break; > + > + case 'o': > + offset = atol(optarg); > + break; > + > + case 'l': > + length = strtol(optarg, NULL, 0); > + break; > + > + } > + } > + if (argc - optind != 1) > + usage(1); > + > + fd = open(argv[optind], O_RDWR); > + if (fd < 0) { > + perror("open"); > + exit(1); > + } > + > + if (ioctl(fd, MEMGETINFO, &meminfo)) { > + perror("MEMGETINFO"); > + close(fd); > + exit(1); > + } > + > + if (length == -1) > + length = meminfo.size; > + > + if (offset % meminfo.erasesize) { > + fprintf(stderr, "Offset %x not multiple of erase size %x\n", > + offset, meminfo.erasesize); > + exit(1); > + } > + if (length % meminfo.erasesize) { > + fprintf(stderr, "Length %x not multiple of erase size %x\n", > + length, meminfo.erasesize); > + exit(1); > + } > + if (length + offset > meminfo.size) { > + fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", > + length, offset, meminfo.size); > + exit(1); > + } > + > + wbuf = malloc(meminfo.erasesize * 3); > + if (!wbuf) { > + fprintf(stderr, "Could not allocate %d bytes for buffer\n", > + meminfo.erasesize * 2); > + exit(1); > + } > + rbuf = wbuf + meminfo.erasesize; > + kbuf = rbuf + meminfo.erasesize; > + > + if (ioctl(fd, ECCGETSTATS, &oldstats)) { > + perror("ECCGETSTATS"); > + close(fd); > + exit(1); > + } > + > + printf("ECC corrections: %d\n", oldstats.corrected); > + printf("ECC failures : %d\n", oldstats.failed); > + printf("Bad blocks : %d\n", oldstats.badblocks); > + printf("BBT blocks : %d\n", oldstats.bbtblocks); > + > + srand(seed); > + > + for (pass = 0; pass < nr_passes; pass++) { > + loff_t test_ofs; > + > + for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { > + ssize_t len; > + > + seed = rand(); > + srand(seed); > + > + if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { > + printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); > + continue; > + } > + > + for (i=0; i<meminfo.erasesize; i++) > + wbuf[i] = rand(); > + > + if (keep_contents) { > + printf("\r%08x: reading... ", (unsigned)test_ofs); > + fflush(stdout); > + > + len = pread(fd, kbuf, meminfo.erasesize, test_ofs); > + if (len < meminfo.erasesize) { > + printf("\n"); > + if (len) > + fprintf(stderr, "Short read (%zd bytes)\n", len); > + else > + perror("read"); > + exit(1); > + } > + } > + if (erase_and_write(test_ofs, wbuf, rbuf, nr_reads)) > + continue; > + if (keep_contents) > + erase_and_write(test_ofs, kbuf, rbuf, 1); > + } > + printf("\nFinished pass %d successfully\n", pass+1); > + } > + /* Return happy */ > + return 0; > +} > diff --git a/nand-utils/nandwrite.c b/nand-utils/nandwrite.c > new file mode 100644 > index 0000000..9c3fe8f > --- /dev/null > +++ b/nand-utils/nandwrite.c > @@ -0,0 +1,578 @@ > +/* > + * nandwrite.c > + * > + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) > + * 2003 Thomas Gleixner (tglx@linutronix.de) > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Overview: > + * This utility 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 PROGRAM_NAME "nandwrite" > + > +#include <ctype.h> > +#include <errno.h> > +#include <fcntl.h> > +#include <stdbool.h> > +#include <stddef.h> > +#include <stdint.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" > +#include "common.h" > +#include <libmtd.h> > + > +static void display_help(int status) > +{ > + fprintf(status == EXIT_SUCCESS ? stdout : stderr, > +"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" > +"Writes to the specified MTD device.\n" > +"\n" > +" -a, --autoplace Use auto OOB layout\n" > +" -m, --markbad Mark blocks bad if write fails\n" > +" -n, --noecc Write without ecc\n" > +" -N, --noskipbad Write without bad block skipping\n" > +" -o, --oob Input contains oob data\n" > +" -O, --onlyoob Input contains oob data and only write the oob part\n" > +" -s addr, --start=addr Set output start address (default is 0)\n" > +" -p, --pad Pad writes to page size\n" > +" -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n" > +" --input-skip=length Skip |length| bytes of the input file\n" > +" --input-size=length Only read |length| bytes of the input file\n" > +" -q, --quiet Don't display progress messages\n" > +" -h, --help Display this help and exit\n" > +" --version Output version information and exit\n" > + ); > + exit(status); > +} > + > +static void display_version(void) > +{ > + printf("%1$s " VERSION "\n" > + "\n" > + "Copyright (C) 2003 Thomas Gleixner \n" > + "\n" > + "%1$s comes with NO WARRANTY\n" > + "to the extent permitted by law.\n" > + "\n" > + "You may redistribute copies of %1$s\n" > + "under the terms of the GNU General Public Licence.\n" > + "See the file `COPYING' for more information.\n", > + PROGRAM_NAME); > + exit(EXIT_SUCCESS); > +} > + > +static const char *standard_input = "-"; > +static const char *mtd_device, *img; > +static long long mtdoffset = 0; > +static long long inputskip = 0; > +static long long inputsize = 0; > +static bool quiet = false; > +static bool writeoob = false; > +static bool onlyoob = false; > +static bool markbad = false; > +static bool noecc = false; > +static bool autoplace = false; > +static bool noskipbad = false; > +static bool pad = false; > +static int blockalign = 1; /* default to using actual block size */ > + > +static void process_options(int argc, char * const argv[]) > +{ > + int error = 0; > + > + for (;;) { > + int option_index = 0; > + static const char short_options[] = "hb:mnNoOpqs:a"; > + static const struct option long_options[] = { > + /* Order of these args with val==0 matters; see option_index. */ > + {"version", no_argument, 0, 0}, > + {"input-skip", required_argument, 0, 0}, > + {"input-size", required_argument, 0, 0}, > + {"help", no_argument, 0, 'h'}, > + {"blockalign", required_argument, 0, 'b'}, > + {"markbad", no_argument, 0, 'm'}, > + {"noecc", no_argument, 0, 'n'}, > + {"noskipbad", no_argument, 0, 'N'}, > + {"oob", no_argument, 0, 'o'}, > + {"onlyoob", no_argument, 0, 'O'}, > + {"pad", no_argument, 0, 'p'}, > + {"quiet", no_argument, 0, 'q'}, > + {"start", required_argument, 0, 's'}, > + {"autoplace", no_argument, 0, 'a'}, > + {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: /* --version */ > + display_version(); > + break; > + case 1: /* --input-skip */ > + inputskip = simple_strtoll(optarg, &error); > + break; > + case 2: /* --input-size */ > + inputsize = simple_strtoll(optarg, &error); > + break; > + } > + break; > + case 'q': > + quiet = true; > + break; > + case 'n': > + noecc = true; > + break; > + case 'N': > + noskipbad = true; > + break; > + case 'm': > + markbad = true; > + break; > + case 'o': > + writeoob = true; > + break; > + case 'O': > + writeoob = true; > + onlyoob = true; > + break; > + case 'p': > + pad = true; > + break; > + case 's': > + mtdoffset = simple_strtoll(optarg, &error); > + break; > + case 'b': > + blockalign = atoi(optarg); > + break; > + case 'a': > + autoplace = true; > + break; > + case 'h': > + display_help(EXIT_SUCCESS); > + break; > + case '?': > + error++; > + break; > + } > + } > + > + if (mtdoffset < 0) > + errmsg_die("Can't specify negative device offset with option" > + " -s: %lld", mtdoffset); > + > + if (blockalign < 0) > + errmsg_die("Can't specify negative blockalign with option -b:" > + " %d", blockalign); > + > + if (autoplace && noecc) > + errmsg_die("Autoplacement and no-ECC are mutually exclusive"); > + > + if (!onlyoob && (pad && writeoob)) > + errmsg_die("Can't pad when oob data is present"); > + > + argc -= optind; > + argv += optind; > + > + /* > + * There must be at least the MTD device node positional > + * argument remaining and, optionally, the input file. > + */ > + > + if (argc < 1 || argc > 2 || error) > + display_help(EXIT_FAILURE); > + > + mtd_device = argv[0]; > + > + /* > + * Standard input may be specified either explictly as "-" or > + * implicity by simply omitting the second of the two > + * positional arguments. > + */ > + > + img = ((argc == 2) ? argv[1] : standard_input); > +} > + > +static void erase_buffer(void *buffer, size_t size) > +{ > + const uint8_t kEraseByte = 0xff; > + > + if (buffer != NULL && size > 0) > + memset(buffer, kEraseByte, size); > +} > + > +/* > + * Main program > + */ > +int main(int argc, char * const argv[]) > +{ > + int fd = -1; > + int ifd = -1; > + int pagelen; > + long long imglen = 0; > + bool baderaseblock = false; > + long long blockstart = -1; > + struct mtd_dev_info mtd; > + long long offs; > + int ret; > + bool failed = true; > + /* contains all the data read from the file so far for the current eraseblock */ > + unsigned char *filebuf = NULL; > + size_t filebuf_max = 0; > + size_t filebuf_len = 0; > + /* points to the current page inside filebuf */ > + unsigned char *writebuf = NULL; > + /* points to the OOB for the current page in filebuf */ > + unsigned char *oobbuf = NULL; > + libmtd_t mtd_desc; > + int ebsize_aligned; > + uint8_t write_mode; > + > + process_options(argc, argv); > + > + /* Open the device */ > + if ((fd = open(mtd_device, O_RDWR)) == -1) > + sys_errmsg_die("%s", mtd_device); > + > + mtd_desc = libmtd_open(); > + if (!mtd_desc) > + errmsg_die("can't initialize libmtd"); > + > + /* Fill in MTD device capability structure */ > + if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) > + errmsg_die("mtd_get_dev_info failed"); > + > + /* > + * Pretend erasesize is specified number of blocks - to match jffs2 > + * (virtual) block size > + * Use this value throughout unless otherwise necessary > + */ > + ebsize_aligned = mtd.eb_size * blockalign; > + > + if (mtdoffset & (mtd.min_io_size - 1)) > + errmsg_die("The start address is not page-aligned !\n" > + "The pagesize of this NAND Flash is 0x%x.\n", > + mtd.min_io_size); > + > + /* Select OOB write mode */ > + if (noecc) > + write_mode = MTD_OPS_RAW; > + else if (autoplace) > + write_mode = MTD_OPS_AUTO_OOB; > + else > + write_mode = MTD_OPS_PLACE_OOB; > + > + if (noecc) { > + ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); > + if (ret) { > + switch (errno) { > + case ENOTTY: > + errmsg_die("ioctl MTDFILEMODE is missing"); > + default: > + sys_errmsg_die("MTDFILEMODE"); > + } > + } > + } > + > + /* Determine if we are reading from standard input or from a file. */ > + if (strcmp(img, standard_input) == 0) > + ifd = STDIN_FILENO; > + else > + ifd = open(img, O_RDONLY); > + > + if (ifd == -1) { > + perror(img); > + goto closeall; > + } > + > + pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); > + > + if (ifd == STDIN_FILENO) { > + imglen = inputsize ? : pagelen; > + if (inputskip) { > + errmsg("seeking stdin not supported"); > + goto closeall; > + } > + } else { > + if (!inputsize) { > + struct stat st; > + if (fstat(ifd, &st)) { > + sys_errmsg("unable to stat input image"); > + goto closeall; > + } > + imglen = st.st_size - inputskip; > + } else > + imglen = inputsize; > + > + if (inputskip && lseek(ifd, inputskip, SEEK_CUR) == -1) { > + sys_errmsg("lseek input by %lld failed", inputskip); > + goto closeall; > + } > + } > + > + /* Check, if file is page-aligned */ > + if (!pad && (imglen % pagelen) != 0) { > + fprintf(stderr, "Input file is not page-aligned. Use the padding " > + "option.\n"); > + goto closeall; > + } > + > + /* Check, if length fits into device */ > + if ((imglen / pagelen) * mtd.min_io_size > mtd.size - mtdoffset) { > + fprintf(stderr, "Image %lld bytes, NAND page %d bytes, OOB area %d" > + " bytes, device size %lld bytes\n", > + imglen, pagelen, mtd.oob_size, mtd.size); > + sys_errmsg("Input file does not fit into device"); > + goto closeall; > + } > + > + /* > + * Allocate a buffer big enough to contain all the data (OOB included) > + * for one eraseblock. The order of operations here matters; if ebsize > + * and pagelen are large enough, then "ebsize_aligned * pagelen" could > + * overflow a 32-bit data type. > + */ > + filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; > + filebuf = xmalloc(filebuf_max); > + erase_buffer(filebuf, filebuf_max); > + > + /* > + * Get data from input and write to the device while there is > + * still input to read and we are still within the device > + * bounds. Note that in the case of standard input, the input > + * length is simply a quasi-boolean flag whose values are page > + * length or zero. > + */ > + while ((imglen > 0 || writebuf < filebuf + filebuf_len) > + && mtdoffset < mtd.size) { > + /* > + * New eraseblock, check for bad block(s) > + * Stay in the loop to be sure that, if mtdoffset changes because > + * of a bad block, the next block that will be written to > + * is also checked. Thus, we avoid errors if the block(s) after the > + * skipped block(s) is also bad (number of blocks depending on > + * the blockalign). > + */ > + while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { > + blockstart = mtdoffset & (~ebsize_aligned + 1); > + offs = blockstart; > + > + /* > + * if writebuf == filebuf, we are rewinding so we must > + * not reset the buffer but just replay it > + */ > + if (writebuf != filebuf) { > + erase_buffer(filebuf, filebuf_len); > + filebuf_len = 0; > + writebuf = filebuf; > + } > + > + baderaseblock = false; > + if (!quiet) > + fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", > + blockstart / ebsize_aligned, blockstart); > + > + /* Check all the blocks in an erase block for bad blocks */ > + if (noskipbad) > + continue; > + > + do { > + ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned); > + if (ret < 0) { > + sys_errmsg("%s: MTD get bad block failed", mtd_device); > + goto closeall; > + } else if (ret == 1) { > + baderaseblock = true; > + if (!quiet) > + fprintf(stderr, "Bad block at %llx, %u block(s) " > + "from %llx will be skipped\n", > + offs, blockalign, blockstart); > + } > + > + if (baderaseblock) { > + mtdoffset = blockstart + ebsize_aligned; > + > + if (mtdoffset > mtd.size) { > + errmsg("too many bad blocks, cannot complete request"); > + goto closeall; > + } > + } > + > + offs += ebsize_aligned / blockalign; > + } while (offs < blockstart + ebsize_aligned); > + > + } > + > + /* Read more data from the input if there isn't enough in the buffer */ > + if (writebuf + mtd.min_io_size > filebuf + filebuf_len) { > + size_t readlen = mtd.min_io_size; > + size_t alreadyread = (filebuf + filebuf_len) - writebuf; > + size_t tinycnt = alreadyread; > + ssize_t cnt = 0; > + > + while (tinycnt < readlen) { > + cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); > + if (cnt == 0) { /* EOF */ > + break; > + } else if (cnt < 0) { > + perror("File I/O error on input"); > + goto closeall; > + } > + tinycnt += cnt; > + } > + > + /* No padding needed - we are done */ > + if (tinycnt == 0) { > + /* > + * For standard input, set imglen to 0 to signal > + * the end of the "file". For nonstandard input, > + * leave it as-is to detect an early EOF. > + */ > + if (ifd == STDIN_FILENO) > + imglen = 0; > + > + break; > + } > + > + /* Padding */ > + if (tinycnt < readlen) { > + if (!pad) { > + fprintf(stderr, "Unexpected EOF. Expecting at least " > + "%zu more bytes. Use the padding option.\n", > + readlen - tinycnt); > + goto closeall; > + } > + erase_buffer(writebuf + tinycnt, readlen - tinycnt); > + } > + > + filebuf_len += readlen - alreadyread; > + if (ifd != STDIN_FILENO) { > + imglen -= tinycnt - alreadyread; > + } else if (cnt == 0) { > + /* No more bytes - we are done after writing the remaining bytes */ > + imglen = 0; > + } > + } > + > + if (writeoob) { > + oobbuf = writebuf + mtd.min_io_size; > + > + /* Read more data for the OOB from the input if there isn't enough in the buffer */ > + if (oobbuf + mtd.oob_size > filebuf + filebuf_len) { > + size_t readlen = mtd.oob_size; > + size_t alreadyread = (filebuf + filebuf_len) - oobbuf; > + size_t tinycnt = alreadyread; > + ssize_t cnt; > + > + while (tinycnt < readlen) { > + cnt = read(ifd, oobbuf + tinycnt, readlen - tinycnt); > + if (cnt == 0) { /* EOF */ > + break; > + } else if (cnt < 0) { > + perror("File I/O error on input"); > + goto closeall; > + } > + tinycnt += cnt; > + } > + > + if (tinycnt < readlen) { > + fprintf(stderr, "Unexpected EOF. Expecting at least " > + "%zu more bytes for OOB\n", readlen - tinycnt); > + goto closeall; > + } > + > + filebuf_len += readlen - alreadyread; > + if (ifd != STDIN_FILENO) { > + imglen -= tinycnt - alreadyread; > + } else if (cnt == 0) { > + /* No more bytes - we are done after writing the remaining bytes */ > + imglen = 0; > + } > + } > + } > + > + /* Write out data */ > + ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, > + mtdoffset % mtd.eb_size, > + onlyoob ? NULL : writebuf, > + onlyoob ? 0 : mtd.min_io_size, > + writeoob ? oobbuf : NULL, > + writeoob ? mtd.oob_size : 0, > + write_mode); > + if (ret) { > + long long i; > + if (errno != EIO) { > + sys_errmsg("%s: MTD write failure", mtd_device); > + goto closeall; > + } > + > + /* Must rewind to blockstart if we can */ > + writebuf = filebuf; > + > + fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", > + blockstart, blockstart + ebsize_aligned - 1); > + for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { > + if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { > + int errno_tmp = errno; > + sys_errmsg("%s: MTD Erase failure", mtd_device); > + if (errno_tmp != EIO) > + goto closeall; > + } > + } > + > + if (markbad) { > + fprintf(stderr, "Marking block at %08llx bad\n", > + mtdoffset & (~mtd.eb_size + 1)); > + if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { > + sys_errmsg("%s: MTD Mark bad block failure", mtd_device); > + goto closeall; > + } > + } > + mtdoffset = blockstart + ebsize_aligned; > + > + continue; > + } > + mtdoffset += mtd.min_io_size; > + writebuf += pagelen; > + } > + > + failed = false; > + > +closeall: > + close(ifd); > + libmtd_close(mtd_desc); > + free(filebuf); > + close(fd); > + > + if (failed || (ifd != STDIN_FILENO && imglen > 0) > + || (writebuf < filebuf + filebuf_len)) > + sys_errmsg_die("Data was only partially written due to error"); > + > + /* Return happy */ > + return EXIT_SUCCESS; > +} > diff --git a/nand-utils/nftl_format.c b/nand-utils/nftl_format.c > new file mode 100644 > index 0000000..1fc3b36 > --- /dev/null > +++ b/nand-utils/nftl_format.c > @@ -0,0 +1,422 @@ > +/* > + * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device > + * > + * 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 PROGRAM_NAME "nftl_format" > + > +#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> > +#include <mtd_swab.h> > + > +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("%s: failed to read BBT, errno=%d\n", PROGRAM_NAME, 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: %s [-ib] <mtddevice> [<start offset> [<size>]]\n", PROGRAM_NAME); > + 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; > + const char *nftl; > + int c, do_inftl = 0, do_bbt = 0; > + > + > + printf("version 1.24 2005/11/07 11:15:13 gleixner\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/nand-utils/nftldump.c b/nand-utils/nftldump.c > new file mode 100644 > index 0000000..32f4f2f > --- /dev/null > +++ b/nand-utils/nftldump.c > @@ -0,0 +1,278 @@ > +/* > + * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" > + * > + * 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 PROGRAM_NAME "nftldump" > + > +#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> > +#include <mtd_swab.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]; > + > +#define SWAP16(x) do { x = le16_to_cpu(x); } while(0) > +#define SWAP32(x) do { x = le32_to_cpu(x); } while(0) > + > +/* 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", PROGRAM_NAME); > + 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/nanddump.c b/nanddump.c > deleted file mode 100644 > index 4ee7ed4..0000000 > --- a/nanddump.c > +++ /dev/null > @@ -1,490 +0,0 @@ > -/* > - * nanddump.c > - * > - * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) > - * Steven J. Hill (sjhill@realitydiluted.com) > - * > - * 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 PROGRAM_NAME "nanddump" > - > -#include <ctype.h> > -#include <errno.h> > -#include <fcntl.h> > -#include <stdbool.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> > -#include "common.h" > -#include <libmtd.h> > - > -static void display_help(int status) > -{ > - fprintf(status == EXIT_SUCCESS ? stdout : stderr, > -"Usage: %s [OPTIONS] MTD-device\n" > -"Dumps the contents of a nand mtd partition.\n" > -"\n" > -"-h --help Display this help and exit\n" > -" --version Output version information and exit\n" > -" --bb=METHOD Choose bad block handling method (see below).\n" > -"-a --forcebinary Force printing of binary data to tty\n" > -"-c --canonicalprint Print canonical Hex+ASCII dump\n" > -"-f file --file=file Dump to file\n" > -"-l length --length=length Length\n" > -"-n --noecc Read without error correction\n" > -" --omitoob Omit OOB data (default)\n" > -"-o --oob Dump OOB data\n" > -"-p --prettyprint Print nice (hexdump)\n" > -"-q --quiet Don't display progress and status messages\n" > -"-s addr --startaddress=addr Start address\n" > -"\n" > -"--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':\n" > -" padbad: dump flash data, substituting 0xFF for any bad blocks\n" > -" dumpbad: dump flash data, including any bad blocks\n" > -" skipbad: dump good data, completely skipping any bad blocks (default)\n", > - PROGRAM_NAME); > - exit(status); > -} > - > -static void display_version(void) > -{ > - printf("%1$s " VERSION "\n" > - "\n" > - "%1$s comes with NO WARRANTY\n" > - "to the extent permitted by law.\n" > - "\n" > - "You may redistribute copies of %1$s\n" > - "under the terms of the GNU General Public Licence.\n" > - "See the file `COPYING' for more information.\n", > - PROGRAM_NAME); > - exit(EXIT_SUCCESS); > -} > - > -// Option variables > - > -static bool pretty_print = false; // print nice > -static bool noecc = false; // don't error correct > -static bool omitoob = true; // omit oob data > -static long long start_addr; // start address > -static long long length; // dump length > -static const char *mtddev; // mtd device name > -static const char *dumpfile; // dump file name > -static bool quiet = false; // suppress diagnostic output > -static bool canonical = false; // print nice + ascii > -static bool forcebinary = false; // force printing binary to tty > - > -static enum { > - padbad, // dump flash data, substituting 0xFF for any bad blocks > - dumpbad, // dump flash data, including any bad blocks > - skipbad, // dump good data, completely skipping any bad blocks > -} bb_method = skipbad; > - > -static void process_options(int argc, char * const argv[]) > -{ > - int error = 0; > - bool oob_default = true; > - > - for (;;) { > - int option_index = 0; > - static const char short_options[] = "hs:f:l:opqnca"; > - static const struct option long_options[] = { > - {"version", no_argument, 0, 0}, > - {"bb", required_argument, 0, 0}, > - {"omitoob", no_argument, 0, 0}, > - {"help", no_argument, 0, 'h'}, > - {"forcebinary", no_argument, 0, 'a'}, > - {"canonicalprint", no_argument, 0, 'c'}, > - {"file", required_argument, 0, 'f'}, > - {"oob", no_argument, 0, 'o'}, > - {"prettyprint", no_argument, 0, 'p'}, > - {"startaddress", required_argument, 0, 's'}, > - {"length", required_argument, 0, 'l'}, > - {"noecc", no_argument, 0, 'n'}, > - {"quiet", 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_version(); > - break; > - case 1: > - /* Handle --bb=METHOD */ > - if (!strcmp(optarg, "padbad")) > - bb_method = padbad; > - else if (!strcmp(optarg, "dumpbad")) > - bb_method = dumpbad; > - else if (!strcmp(optarg, "skipbad")) > - bb_method = skipbad; > - else > - error++; > - break; > - case 2: /* --omitoob */ > - if (oob_default) { > - oob_default = false; > - omitoob = true; > - } else { > - errmsg_die("--oob and --oomitoob are mutually exclusive"); > - } > - break; > - } > - break; > - case 's': > - start_addr = simple_strtoll(optarg, &error); > - break; > - case 'f': > - dumpfile = xstrdup(optarg); > - break; > - case 'l': > - length = simple_strtoll(optarg, &error); > - break; > - case 'o': > - if (oob_default) { > - oob_default = false; > - omitoob = false; > - } else { > - errmsg_die("--oob and --oomitoob are mutually exclusive"); > - } > - break; > - case 'a': > - forcebinary = true; > - break; > - case 'c': > - canonical = true; > - case 'p': > - pretty_print = true; > - break; > - case 'q': > - quiet = true; > - break; > - case 'n': > - noecc = true; > - break; > - case 'h': > - display_help(EXIT_SUCCESS); > - break; > - case '?': > - error++; > - break; > - } > - } > - > - if (start_addr < 0) > - errmsg_die("Can't specify negative offset with option -s: %lld", > - start_addr); > - > - if (length < 0) > - errmsg_die("Can't specify negative length with option -l: %lld", length); > - > - if (quiet && pretty_print) { > - fprintf(stderr, "The quiet and pretty print options are mutually-\n" > - "exclusive. Choose one or the other.\n"); > - exit(EXIT_FAILURE); > - } > - > - if (forcebinary && pretty_print) { > - fprintf(stderr, "The forcebinary and pretty print options are\n" > - "mutually-exclusive. Choose one or the " > - "other.\n"); > - exit(EXIT_FAILURE); > - } > - > - if ((argc - optind) != 1 || error) > - display_help(EXIT_FAILURE); > - > - mtddev = argv[optind]; > -} > - > -#define PRETTY_ROW_SIZE 16 > -#define PRETTY_BUF_LEN 80 > - > -/** > - * pretty_dump_to_buffer - formats a blob of data to "hex ASCII" in memory > - * @buf: data blob to dump > - * @len: number of bytes in the @buf > - * @linebuf: where to put the converted data > - * @linebuflen: total size of @linebuf, including space for terminating NULL > - * @pagedump: true - dumping as page format; false - dumping as OOB format > - * @ascii: dump ascii formatted data next to hexdump > - * @prefix: address to print before line in a page dump, ignored if !pagedump > - * > - * pretty_dump_to_buffer() works on one "line" of output at a time, i.e., > - * PRETTY_ROW_SIZE bytes of input data converted to hex + ASCII output. > - * > - * Given a buffer of unsigned char data, pretty_dump_to_buffer() converts the > - * input data to a hex/ASCII dump at the supplied memory location. A prefix > - * is included based on whether we are dumping page or OOB data. The converted > - * output is always NULL-terminated. > - * > - * e.g. > - * pretty_dump_to_buffer(data, data_len, prettybuf, linelen, true, > - * false, 256); > - * produces: > - * 0x00000100: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f > - * NOTE: This function was adapted from linux kernel, "lib/hexdump.c" > - */ > -static void pretty_dump_to_buffer(const unsigned char *buf, size_t len, > - char *linebuf, size_t linebuflen, bool pagedump, bool ascii, > - unsigned long long prefix) > -{ > - static const char hex_asc[] = "0123456789abcdef"; > - unsigned char ch; > - unsigned int j, lx = 0, ascii_column; > - > - if (pagedump) > - lx += sprintf(linebuf, "0x%.8llx: ", prefix); > - else > - lx += sprintf(linebuf, " OOB Data: "); > - > - if (!len) > - goto nil; > - if (len > PRETTY_ROW_SIZE) /* limit to one line at a time */ > - len = PRETTY_ROW_SIZE; > - > - for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { > - ch = buf[j]; > - linebuf[lx++] = hex_asc[(ch & 0xf0) >> 4]; > - linebuf[lx++] = hex_asc[ch & 0x0f]; > - linebuf[lx++] = ' '; > - } > - if (j) > - lx--; > - > - ascii_column = 3 * PRETTY_ROW_SIZE + 14; > - > - if (!ascii) > - goto nil; > - > - /* Spacing between hex and ASCII - ensure at least one space */ > - lx += sprintf(linebuf + lx, "%*s", > - MAX((int)MIN(linebuflen, ascii_column) - 1 - lx, 1), > - " "); > - > - linebuf[lx++] = '|'; > - for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) > - linebuf[lx++] = (isascii(buf[j]) && isprint(buf[j])) ? buf[j] > - : '.'; > - linebuf[lx++] = '|'; > -nil: > - linebuf[lx++] = '\n'; > - linebuf[lx++] = '\0'; > -} > - > - > -/* > - * Main program > - */ > -int main(int argc, char * const argv[]) > -{ > - long long ofs, end_addr = 0; > - long long blockstart = 1; > - int i, fd, ofd = 0, bs, badblock = 0; > - struct mtd_dev_info mtd; > - char pretty_buf[PRETTY_BUF_LEN]; > - int firstblock = 1; > - struct mtd_ecc_stats stat1, stat2; > - bool eccstats = false; > - unsigned char *readbuf = NULL, *oobbuf = NULL; > - libmtd_t mtd_desc; > - > - process_options(argc, argv); > - > - /* Initialize libmtd */ > - mtd_desc = libmtd_open(); > - if (!mtd_desc) > - return errmsg("can't initialize libmtd"); > - > - /* Open MTD device */ > - if ((fd = open(mtddev, O_RDONLY)) == -1) { > - perror(mtddev); > - exit(EXIT_FAILURE); > - } > - > - /* Fill in MTD device capability structure */ > - if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) > - return errmsg("mtd_get_dev_info failed"); > - > - /* Allocate buffers */ > - oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); > - readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); > - > - if (noecc) { > - if (ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW) != 0) { > - perror("MTDFILEMODE"); > - goto closeall; > - } > - } else { > - /* check if we can read ecc stats */ > - if (!ioctl(fd, ECCGETSTATS, &stat1)) { > - eccstats = true; > - if (!quiet) { > - fprintf(stderr, "ECC failed: %d\n", stat1.failed); > - fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); > - fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); > - fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); > - } > - } else > - perror("No ECC status information available"); > - } > - > - /* 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(dumpfile); > - goto closeall; > - } > - > - if (!pretty_print && !forcebinary && isatty(ofd)) { > - fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" > - "or '--forcebinary' to override.\n"); > - goto closeall; > - } > - > - /* Initialize start/end addresses and block size */ > - if (start_addr & (mtd.min_io_size - 1)) { > - fprintf(stderr, "the start address (-s parameter) is not page-aligned!\n" > - "The pagesize of this NAND Flash is 0x%x.\n", > - mtd.min_io_size); > - goto closeall; > - } > - if (length) > - end_addr = start_addr + length; > - if (!length || end_addr > mtd.size) > - end_addr = mtd.size; > - > - bs = mtd.min_io_size; > - > - /* Print informative message */ > - if (!quiet) { > - fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", > - mtd.eb_size, mtd.min_io_size, mtd.oob_size); > - fprintf(stderr, > - "Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", > - start_addr, end_addr); > - } > - > - /* Dump the flash contents */ > - for (ofs = start_addr; ofs < end_addr; ofs += bs) { > - /* Check for bad block */ > - if (bb_method == dumpbad) { > - badblock = 0; > - } else if (blockstart != (ofs & (~mtd.eb_size + 1)) || > - firstblock) { > - blockstart = ofs & (~mtd.eb_size + 1); > - firstblock = 0; > - if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { > - errmsg("libmtd: mtd_is_bad"); > - goto closeall; > - } > - } > - > - if (badblock) { > - /* skip bad block, increase end_addr */ > - if (bb_method == skipbad) { > - end_addr += mtd.eb_size; > - ofs += mtd.eb_size - bs; > - if (end_addr > mtd.size) > - end_addr = mtd.size; > - continue; > - } > - memset(readbuf, 0xff, bs); > - } else { > - /* Read page data and exit on failure */ > - if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { > - errmsg("mtd_read"); > - goto closeall; > - } > - } > - > - /* ECC stats available ? */ > - if (eccstats) { > - if (ioctl(fd, ECCGETSTATS, &stat2)) { > - perror("ioctl(ECCGETSTATS)"); > - goto closeall; > - } > - if (stat1.failed != stat2.failed) > - fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" > - " at offset 0x%08llx\n", > - stat2.failed - stat1.failed, ofs); > - if (stat1.corrected != stat2.corrected) > - fprintf(stderr, "ECC: %d corrected bitflip(s) at" > - " offset 0x%08llx\n", > - stat2.corrected - stat1.corrected, ofs); > - stat1 = stat2; > - } > - > - /* Write out page data */ > - if (pretty_print) { > - for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { > - pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, > - pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); > - write(ofd, pretty_buf, strlen(pretty_buf)); > - } > - } else > - write(ofd, readbuf, bs); > - > - if (omitoob) > - continue; > - > - if (badblock) { > - memset(oobbuf, 0xff, mtd.oob_size); > - } else { > - /* Read OOB data and exit on failure */ > - if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { > - errmsg("libmtd: mtd_read_oob"); > - goto closeall; > - } > - } > - > - /* Write out OOB data */ > - if (pretty_print) { > - for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { > - pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, > - pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); > - write(ofd, pretty_buf, strlen(pretty_buf)); > - } > - } else > - write(ofd, oobbuf, mtd.oob_size); > - } > - > - /* Close the output file and MTD device, free memory */ > - close(fd); > - close(ofd); > - free(oobbuf); > - free(readbuf); > - > - /* Exit happy */ > - return EXIT_SUCCESS; > - > -closeall: > - close(fd); > - close(ofd); > - free(oobbuf); > - free(readbuf); > - exit(EXIT_FAILURE); > -} > diff --git a/nandtest.c b/nandtest.c > deleted file mode 100644 > index 0805387..0000000 > --- a/nandtest.c > +++ /dev/null > @@ -1,313 +0,0 @@ > -#define PROGRAM_NAME "nandtest" > - > -#include <ctype.h> > -#include <errno.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" > - > -void usage(int status) > -{ > - fprintf(status ? stderr : stdout, > - "usage: %s [OPTIONS] <device>\n\n" > - " -h, --help Display this help output\n" > - " -m, --markbad Mark blocks bad if they appear so\n" > - " -s, --seed Supply random seed\n" > - " -p, --passes Number of passes\n" > - " -r <n>, --reads=<n> Read & check <n> times per pass\n" > - " -o, --offset Start offset on flash\n" > - " -l, --length Length of flash to test\n" > - " -k, --keep Restore existing contents after test\n", > - PROGRAM_NAME); > - exit(status); > -} > - > -struct mtd_info_user meminfo; > -struct mtd_ecc_stats oldstats, newstats; > -int fd; > -int markbad=0; > -int seed; > - > -int read_and_compare(loff_t ofs, unsigned char *data, unsigned char *rbuf) > -{ > - ssize_t len; > - int i; > - > - len = pread(fd, rbuf, meminfo.erasesize, ofs); > - if (len < meminfo.erasesize) { > - printf("\n"); > - if (len) > - fprintf(stderr, "Short read (%zd bytes)\n", len); > - else > - perror("read"); > - exit(1); > - } > - > - if (ioctl(fd, ECCGETSTATS, &newstats)) { > - printf("\n"); > - perror("ECCGETSTATS"); > - close(fd); > - exit(1); > - } > - > - if (newstats.corrected > oldstats.corrected) { > - printf("\n %d bit(s) ECC corrected at %08x\n", > - newstats.corrected - oldstats.corrected, > - (unsigned) ofs); > - oldstats.corrected = newstats.corrected; > - } > - if (newstats.failed > oldstats.failed) { > - printf("\nECC failed at %08x\n", (unsigned) ofs); > - oldstats.failed = newstats.failed; > - } > - > - printf("\r%08x: checking...", (unsigned)ofs); > - fflush(stdout); > - > - if (memcmp(data, rbuf, meminfo.erasesize)) { > - printf("\n"); > - fprintf(stderr, "compare failed. seed %d\n", seed); > - for (i=0; i<meminfo.erasesize; i++) { > - if (data[i] != rbuf[i]) > - printf("Byte 0x%x is %02x should be %02x\n", > - i, rbuf[i], data[i]); > - } > - return 1; > - } > - return 0; > -} > - > -int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, int nr_reads) > -{ > - struct erase_info_user er; > - ssize_t len; > - int i, read_errs = 0; > - > - printf("\r%08x: erasing... ", (unsigned)ofs); > - fflush(stdout); > - > - er.start = ofs; > - er.length = meminfo.erasesize; > - > - if (ioctl(fd, MEMERASE, &er)) { > - perror("MEMERASE"); > - if (markbad) { > - printf("Mark block bad at %08lx\n", (long)ofs); > - ioctl(fd, MEMSETBADBLOCK, &ofs); > - } > - return 1; > - } > - > - printf("\r%08x: writing...", (unsigned)ofs); > - fflush(stdout); > - > - len = pwrite(fd, data, meminfo.erasesize, ofs); > - if (len < 0) { > - printf("\n"); > - perror("write"); > - if (markbad) { > - printf("Mark block bad at %08lx\n", (long)ofs); > - ioctl(fd, MEMSETBADBLOCK, &ofs); > - } > - return 1; > - } > - if (len < meminfo.erasesize) { > - printf("\n"); > - fprintf(stderr, "Short write (%zd bytes)\n", len); > - exit(1); > - } > - > - for (i=1; i<=nr_reads; i++) { > - printf("\r%08x: reading (%d of %d)...", (unsigned)ofs, i, nr_reads); > - fflush(stdout); > - if (read_and_compare(ofs, data, rbuf)) > - read_errs++; > - } > - if (read_errs) { > - fprintf(stderr, "read/check %d of %d failed. seed %d\n", read_errs, nr_reads, seed); > - return 1; > - } > - return 0; > -} > - > - > -/* > - * Main program > - */ > -int main(int argc, char **argv) > -{ > - int i; > - unsigned char *wbuf, *rbuf, *kbuf; > - int pass; > - int nr_passes = 1; > - int nr_reads = 4; > - int keep_contents = 0; > - uint32_t offset = 0; > - uint32_t length = -1; > - > - seed = time(NULL); > - > - for (;;) { > - static const char short_options[] = "hkl:mo:p:r:s:"; > - static const struct option long_options[] = { > - { "help", no_argument, 0, 'h' }, > - { "markbad", no_argument, 0, 'm' }, > - { "seed", required_argument, 0, 's' }, > - { "passes", required_argument, 0, 'p' }, > - { "offset", required_argument, 0, 'o' }, > - { "length", required_argument, 0, 'l' }, > - { "reads", required_argument, 0, 'r' }, > - { "keep", no_argument, 0, 'k' }, > - {0, 0, 0, 0}, > - }; > - int option_index = 0; > - int c = getopt_long(argc, argv, short_options, long_options, &option_index); > - if (c == EOF) > - break; > - > - switch (c) { > - case 'h': > - usage(0); > - break; > - > - case '?': > - usage(1); > - break; > - > - case 'm': > - markbad = 1; > - break; > - > - case 'k': > - keep_contents = 1; > - break; > - > - case 's': > - seed = atol(optarg); > - break; > - > - case 'p': > - nr_passes = atol(optarg); > - break; > - > - case 'r': > - nr_reads = atol(optarg); > - break; > - > - case 'o': > - offset = atol(optarg); > - break; > - > - case 'l': > - length = strtol(optarg, NULL, 0); > - break; > - > - } > - } > - if (argc - optind != 1) > - usage(1); > - > - fd = open(argv[optind], O_RDWR); > - if (fd < 0) { > - perror("open"); > - exit(1); > - } > - > - if (ioctl(fd, MEMGETINFO, &meminfo)) { > - perror("MEMGETINFO"); > - close(fd); > - exit(1); > - } > - > - if (length == -1) > - length = meminfo.size; > - > - if (offset % meminfo.erasesize) { > - fprintf(stderr, "Offset %x not multiple of erase size %x\n", > - offset, meminfo.erasesize); > - exit(1); > - } > - if (length % meminfo.erasesize) { > - fprintf(stderr, "Length %x not multiple of erase size %x\n", > - length, meminfo.erasesize); > - exit(1); > - } > - if (length + offset > meminfo.size) { > - fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", > - length, offset, meminfo.size); > - exit(1); > - } > - > - wbuf = malloc(meminfo.erasesize * 3); > - if (!wbuf) { > - fprintf(stderr, "Could not allocate %d bytes for buffer\n", > - meminfo.erasesize * 2); > - exit(1); > - } > - rbuf = wbuf + meminfo.erasesize; > - kbuf = rbuf + meminfo.erasesize; > - > - if (ioctl(fd, ECCGETSTATS, &oldstats)) { > - perror("ECCGETSTATS"); > - close(fd); > - exit(1); > - } > - > - printf("ECC corrections: %d\n", oldstats.corrected); > - printf("ECC failures : %d\n", oldstats.failed); > - printf("Bad blocks : %d\n", oldstats.badblocks); > - printf("BBT blocks : %d\n", oldstats.bbtblocks); > - > - srand(seed); > - > - for (pass = 0; pass < nr_passes; pass++) { > - loff_t test_ofs; > - > - for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { > - ssize_t len; > - > - seed = rand(); > - srand(seed); > - > - if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { > - printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); > - continue; > - } > - > - for (i=0; i<meminfo.erasesize; i++) > - wbuf[i] = rand(); > - > - if (keep_contents) { > - printf("\r%08x: reading... ", (unsigned)test_ofs); > - fflush(stdout); > - > - len = pread(fd, kbuf, meminfo.erasesize, test_ofs); > - if (len < meminfo.erasesize) { > - printf("\n"); > - if (len) > - fprintf(stderr, "Short read (%zd bytes)\n", len); > - else > - perror("read"); > - exit(1); > - } > - } > - if (erase_and_write(test_ofs, wbuf, rbuf, nr_reads)) > - continue; > - if (keep_contents) > - erase_and_write(test_ofs, kbuf, rbuf, 1); > - } > - printf("\nFinished pass %d successfully\n", pass+1); > - } > - /* Return happy */ > - return 0; > -} > diff --git a/nandwrite.c b/nandwrite.c > deleted file mode 100644 > index 9c3fe8f..0000000 > --- a/nandwrite.c > +++ /dev/null > @@ -1,578 +0,0 @@ > -/* > - * nandwrite.c > - * > - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) > - * 2003 Thomas Gleixner (tglx@linutronix.de) > - * > - * This program is free software; you can redistribute it and/or modify > - * it under the terms of the GNU General Public License version 2 as > - * published by the Free Software Foundation. > - * > - * Overview: > - * This utility 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 PROGRAM_NAME "nandwrite" > - > -#include <ctype.h> > -#include <errno.h> > -#include <fcntl.h> > -#include <stdbool.h> > -#include <stddef.h> > -#include <stdint.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" > -#include "common.h" > -#include <libmtd.h> > - > -static void display_help(int status) > -{ > - fprintf(status == EXIT_SUCCESS ? stdout : stderr, > -"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" > -"Writes to the specified MTD device.\n" > -"\n" > -" -a, --autoplace Use auto OOB layout\n" > -" -m, --markbad Mark blocks bad if write fails\n" > -" -n, --noecc Write without ecc\n" > -" -N, --noskipbad Write without bad block skipping\n" > -" -o, --oob Input contains oob data\n" > -" -O, --onlyoob Input contains oob data and only write the oob part\n" > -" -s addr, --start=addr Set output start address (default is 0)\n" > -" -p, --pad Pad writes to page size\n" > -" -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n" > -" --input-skip=length Skip |length| bytes of the input file\n" > -" --input-size=length Only read |length| bytes of the input file\n" > -" -q, --quiet Don't display progress messages\n" > -" -h, --help Display this help and exit\n" > -" --version Output version information and exit\n" > - ); > - exit(status); > -} > - > -static void display_version(void) > -{ > - printf("%1$s " VERSION "\n" > - "\n" > - "Copyright (C) 2003 Thomas Gleixner \n" > - "\n" > - "%1$s comes with NO WARRANTY\n" > - "to the extent permitted by law.\n" > - "\n" > - "You may redistribute copies of %1$s\n" > - "under the terms of the GNU General Public Licence.\n" > - "See the file `COPYING' for more information.\n", > - PROGRAM_NAME); > - exit(EXIT_SUCCESS); > -} > - > -static const char *standard_input = "-"; > -static const char *mtd_device, *img; > -static long long mtdoffset = 0; > -static long long inputskip = 0; > -static long long inputsize = 0; > -static bool quiet = false; > -static bool writeoob = false; > -static bool onlyoob = false; > -static bool markbad = false; > -static bool noecc = false; > -static bool autoplace = false; > -static bool noskipbad = false; > -static bool pad = false; > -static int blockalign = 1; /* default to using actual block size */ > - > -static void process_options(int argc, char * const argv[]) > -{ > - int error = 0; > - > - for (;;) { > - int option_index = 0; > - static const char short_options[] = "hb:mnNoOpqs:a"; > - static const struct option long_options[] = { > - /* Order of these args with val==0 matters; see option_index. */ > - {"version", no_argument, 0, 0}, > - {"input-skip", required_argument, 0, 0}, > - {"input-size", required_argument, 0, 0}, > - {"help", no_argument, 0, 'h'}, > - {"blockalign", required_argument, 0, 'b'}, > - {"markbad", no_argument, 0, 'm'}, > - {"noecc", no_argument, 0, 'n'}, > - {"noskipbad", no_argument, 0, 'N'}, > - {"oob", no_argument, 0, 'o'}, > - {"onlyoob", no_argument, 0, 'O'}, > - {"pad", no_argument, 0, 'p'}, > - {"quiet", no_argument, 0, 'q'}, > - {"start", required_argument, 0, 's'}, > - {"autoplace", no_argument, 0, 'a'}, > - {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: /* --version */ > - display_version(); > - break; > - case 1: /* --input-skip */ > - inputskip = simple_strtoll(optarg, &error); > - break; > - case 2: /* --input-size */ > - inputsize = simple_strtoll(optarg, &error); > - break; > - } > - break; > - case 'q': > - quiet = true; > - break; > - case 'n': > - noecc = true; > - break; > - case 'N': > - noskipbad = true; > - break; > - case 'm': > - markbad = true; > - break; > - case 'o': > - writeoob = true; > - break; > - case 'O': > - writeoob = true; > - onlyoob = true; > - break; > - case 'p': > - pad = true; > - break; > - case 's': > - mtdoffset = simple_strtoll(optarg, &error); > - break; > - case 'b': > - blockalign = atoi(optarg); > - break; > - case 'a': > - autoplace = true; > - break; > - case 'h': > - display_help(EXIT_SUCCESS); > - break; > - case '?': > - error++; > - break; > - } > - } > - > - if (mtdoffset < 0) > - errmsg_die("Can't specify negative device offset with option" > - " -s: %lld", mtdoffset); > - > - if (blockalign < 0) > - errmsg_die("Can't specify negative blockalign with option -b:" > - " %d", blockalign); > - > - if (autoplace && noecc) > - errmsg_die("Autoplacement and no-ECC are mutually exclusive"); > - > - if (!onlyoob && (pad && writeoob)) > - errmsg_die("Can't pad when oob data is present"); > - > - argc -= optind; > - argv += optind; > - > - /* > - * There must be at least the MTD device node positional > - * argument remaining and, optionally, the input file. > - */ > - > - if (argc < 1 || argc > 2 || error) > - display_help(EXIT_FAILURE); > - > - mtd_device = argv[0]; > - > - /* > - * Standard input may be specified either explictly as "-" or > - * implicity by simply omitting the second of the two > - * positional arguments. > - */ > - > - img = ((argc == 2) ? argv[1] : standard_input); > -} > - > -static void erase_buffer(void *buffer, size_t size) > -{ > - const uint8_t kEraseByte = 0xff; > - > - if (buffer != NULL && size > 0) > - memset(buffer, kEraseByte, size); > -} > - > -/* > - * Main program > - */ > -int main(int argc, char * const argv[]) > -{ > - int fd = -1; > - int ifd = -1; > - int pagelen; > - long long imglen = 0; > - bool baderaseblock = false; > - long long blockstart = -1; > - struct mtd_dev_info mtd; > - long long offs; > - int ret; > - bool failed = true; > - /* contains all the data read from the file so far for the current eraseblock */ > - unsigned char *filebuf = NULL; > - size_t filebuf_max = 0; > - size_t filebuf_len = 0; > - /* points to the current page inside filebuf */ > - unsigned char *writebuf = NULL; > - /* points to the OOB for the current page in filebuf */ > - unsigned char *oobbuf = NULL; > - libmtd_t mtd_desc; > - int ebsize_aligned; > - uint8_t write_mode; > - > - process_options(argc, argv); > - > - /* Open the device */ > - if ((fd = open(mtd_device, O_RDWR)) == -1) > - sys_errmsg_die("%s", mtd_device); > - > - mtd_desc = libmtd_open(); > - if (!mtd_desc) > - errmsg_die("can't initialize libmtd"); > - > - /* Fill in MTD device capability structure */ > - if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) > - errmsg_die("mtd_get_dev_info failed"); > - > - /* > - * Pretend erasesize is specified number of blocks - to match jffs2 > - * (virtual) block size > - * Use this value throughout unless otherwise necessary > - */ > - ebsize_aligned = mtd.eb_size * blockalign; > - > - if (mtdoffset & (mtd.min_io_size - 1)) > - errmsg_die("The start address is not page-aligned !\n" > - "The pagesize of this NAND Flash is 0x%x.\n", > - mtd.min_io_size); > - > - /* Select OOB write mode */ > - if (noecc) > - write_mode = MTD_OPS_RAW; > - else if (autoplace) > - write_mode = MTD_OPS_AUTO_OOB; > - else > - write_mode = MTD_OPS_PLACE_OOB; > - > - if (noecc) { > - ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); > - if (ret) { > - switch (errno) { > - case ENOTTY: > - errmsg_die("ioctl MTDFILEMODE is missing"); > - default: > - sys_errmsg_die("MTDFILEMODE"); > - } > - } > - } > - > - /* Determine if we are reading from standard input or from a file. */ > - if (strcmp(img, standard_input) == 0) > - ifd = STDIN_FILENO; > - else > - ifd = open(img, O_RDONLY); > - > - if (ifd == -1) { > - perror(img); > - goto closeall; > - } > - > - pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); > - > - if (ifd == STDIN_FILENO) { > - imglen = inputsize ? : pagelen; > - if (inputskip) { > - errmsg("seeking stdin not supported"); > - goto closeall; > - } > - } else { > - if (!inputsize) { > - struct stat st; > - if (fstat(ifd, &st)) { > - sys_errmsg("unable to stat input image"); > - goto closeall; > - } > - imglen = st.st_size - inputskip; > - } else > - imglen = inputsize; > - > - if (inputskip && lseek(ifd, inputskip, SEEK_CUR) == -1) { > - sys_errmsg("lseek input by %lld failed", inputskip); > - goto closeall; > - } > - } > - > - /* Check, if file is page-aligned */ > - if (!pad && (imglen % pagelen) != 0) { > - fprintf(stderr, "Input file is not page-aligned. Use the padding " > - "option.\n"); > - goto closeall; > - } > - > - /* Check, if length fits into device */ > - if ((imglen / pagelen) * mtd.min_io_size > mtd.size - mtdoffset) { > - fprintf(stderr, "Image %lld bytes, NAND page %d bytes, OOB area %d" > - " bytes, device size %lld bytes\n", > - imglen, pagelen, mtd.oob_size, mtd.size); > - sys_errmsg("Input file does not fit into device"); > - goto closeall; > - } > - > - /* > - * Allocate a buffer big enough to contain all the data (OOB included) > - * for one eraseblock. The order of operations here matters; if ebsize > - * and pagelen are large enough, then "ebsize_aligned * pagelen" could > - * overflow a 32-bit data type. > - */ > - filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; > - filebuf = xmalloc(filebuf_max); > - erase_buffer(filebuf, filebuf_max); > - > - /* > - * Get data from input and write to the device while there is > - * still input to read and we are still within the device > - * bounds. Note that in the case of standard input, the input > - * length is simply a quasi-boolean flag whose values are page > - * length or zero. > - */ > - while ((imglen > 0 || writebuf < filebuf + filebuf_len) > - && mtdoffset < mtd.size) { > - /* > - * New eraseblock, check for bad block(s) > - * Stay in the loop to be sure that, if mtdoffset changes because > - * of a bad block, the next block that will be written to > - * is also checked. Thus, we avoid errors if the block(s) after the > - * skipped block(s) is also bad (number of blocks depending on > - * the blockalign). > - */ > - while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { > - blockstart = mtdoffset & (~ebsize_aligned + 1); > - offs = blockstart; > - > - /* > - * if writebuf == filebuf, we are rewinding so we must > - * not reset the buffer but just replay it > - */ > - if (writebuf != filebuf) { > - erase_buffer(filebuf, filebuf_len); > - filebuf_len = 0; > - writebuf = filebuf; > - } > - > - baderaseblock = false; > - if (!quiet) > - fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", > - blockstart / ebsize_aligned, blockstart); > - > - /* Check all the blocks in an erase block for bad blocks */ > - if (noskipbad) > - continue; > - > - do { > - ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned); > - if (ret < 0) { > - sys_errmsg("%s: MTD get bad block failed", mtd_device); > - goto closeall; > - } else if (ret == 1) { > - baderaseblock = true; > - if (!quiet) > - fprintf(stderr, "Bad block at %llx, %u block(s) " > - "from %llx will be skipped\n", > - offs, blockalign, blockstart); > - } > - > - if (baderaseblock) { > - mtdoffset = blockstart + ebsize_aligned; > - > - if (mtdoffset > mtd.size) { > - errmsg("too many bad blocks, cannot complete request"); > - goto closeall; > - } > - } > - > - offs += ebsize_aligned / blockalign; > - } while (offs < blockstart + ebsize_aligned); > - > - } > - > - /* Read more data from the input if there isn't enough in the buffer */ > - if (writebuf + mtd.min_io_size > filebuf + filebuf_len) { > - size_t readlen = mtd.min_io_size; > - size_t alreadyread = (filebuf + filebuf_len) - writebuf; > - size_t tinycnt = alreadyread; > - ssize_t cnt = 0; > - > - while (tinycnt < readlen) { > - cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); > - if (cnt == 0) { /* EOF */ > - break; > - } else if (cnt < 0) { > - perror("File I/O error on input"); > - goto closeall; > - } > - tinycnt += cnt; > - } > - > - /* No padding needed - we are done */ > - if (tinycnt == 0) { > - /* > - * For standard input, set imglen to 0 to signal > - * the end of the "file". For nonstandard input, > - * leave it as-is to detect an early EOF. > - */ > - if (ifd == STDIN_FILENO) > - imglen = 0; > - > - break; > - } > - > - /* Padding */ > - if (tinycnt < readlen) { > - if (!pad) { > - fprintf(stderr, "Unexpected EOF. Expecting at least " > - "%zu more bytes. Use the padding option.\n", > - readlen - tinycnt); > - goto closeall; > - } > - erase_buffer(writebuf + tinycnt, readlen - tinycnt); > - } > - > - filebuf_len += readlen - alreadyread; > - if (ifd != STDIN_FILENO) { > - imglen -= tinycnt - alreadyread; > - } else if (cnt == 0) { > - /* No more bytes - we are done after writing the remaining bytes */ > - imglen = 0; > - } > - } > - > - if (writeoob) { > - oobbuf = writebuf + mtd.min_io_size; > - > - /* Read more data for the OOB from the input if there isn't enough in the buffer */ > - if (oobbuf + mtd.oob_size > filebuf + filebuf_len) { > - size_t readlen = mtd.oob_size; > - size_t alreadyread = (filebuf + filebuf_len) - oobbuf; > - size_t tinycnt = alreadyread; > - ssize_t cnt; > - > - while (tinycnt < readlen) { > - cnt = read(ifd, oobbuf + tinycnt, readlen - tinycnt); > - if (cnt == 0) { /* EOF */ > - break; > - } else if (cnt < 0) { > - perror("File I/O error on input"); > - goto closeall; > - } > - tinycnt += cnt; > - } > - > - if (tinycnt < readlen) { > - fprintf(stderr, "Unexpected EOF. Expecting at least " > - "%zu more bytes for OOB\n", readlen - tinycnt); > - goto closeall; > - } > - > - filebuf_len += readlen - alreadyread; > - if (ifd != STDIN_FILENO) { > - imglen -= tinycnt - alreadyread; > - } else if (cnt == 0) { > - /* No more bytes - we are done after writing the remaining bytes */ > - imglen = 0; > - } > - } > - } > - > - /* Write out data */ > - ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, > - mtdoffset % mtd.eb_size, > - onlyoob ? NULL : writebuf, > - onlyoob ? 0 : mtd.min_io_size, > - writeoob ? oobbuf : NULL, > - writeoob ? mtd.oob_size : 0, > - write_mode); > - if (ret) { > - long long i; > - if (errno != EIO) { > - sys_errmsg("%s: MTD write failure", mtd_device); > - goto closeall; > - } > - > - /* Must rewind to blockstart if we can */ > - writebuf = filebuf; > - > - fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", > - blockstart, blockstart + ebsize_aligned - 1); > - for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { > - if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { > - int errno_tmp = errno; > - sys_errmsg("%s: MTD Erase failure", mtd_device); > - if (errno_tmp != EIO) > - goto closeall; > - } > - } > - > - if (markbad) { > - fprintf(stderr, "Marking block at %08llx bad\n", > - mtdoffset & (~mtd.eb_size + 1)); > - if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { > - sys_errmsg("%s: MTD Mark bad block failure", mtd_device); > - goto closeall; > - } > - } > - mtdoffset = blockstart + ebsize_aligned; > - > - continue; > - } > - mtdoffset += mtd.min_io_size; > - writebuf += pagelen; > - } > - > - failed = false; > - > -closeall: > - close(ifd); > - libmtd_close(mtd_desc); > - free(filebuf); > - close(fd); > - > - if (failed || (ifd != STDIN_FILENO && imglen > 0) > - || (writebuf < filebuf + filebuf_len)) > - sys_errmsg_die("Data was only partially written due to error"); > - > - /* Return happy */ > - return EXIT_SUCCESS; > -} > diff --git a/nftl_format.c b/nftl_format.c > deleted file mode 100644 > index 1fc3b36..0000000 > --- a/nftl_format.c > +++ /dev/null > @@ -1,422 +0,0 @@ > -/* > - * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device > - * > - * 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 PROGRAM_NAME "nftl_format" > - > -#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> > -#include <mtd_swab.h> > - > -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("%s: failed to read BBT, errno=%d\n", PROGRAM_NAME, 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: %s [-ib] <mtddevice> [<start offset> [<size>]]\n", PROGRAM_NAME); > - 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; > - const char *nftl; > - int c, do_inftl = 0, do_bbt = 0; > - > - > - printf("version 1.24 2005/11/07 11:15:13 gleixner\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 > deleted file mode 100644 > index 32f4f2f..0000000 > --- a/nftldump.c > +++ /dev/null > @@ -1,278 +0,0 @@ > -/* > - * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" > - * > - * 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 PROGRAM_NAME "nftldump" > - > -#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> > -#include <mtd_swab.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]; > - > -#define SWAP16(x) do { x = le16_to_cpu(x); } while(0) > -#define SWAP32(x) do { x = le32_to_cpu(x); } while(0) > - > -/* 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", PROGRAM_NAME); > - 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/nor-utils/rfddump.c b/nor-utils/rfddump.c > new file mode 100644 > index 0000000..0375bac > --- /dev/null > +++ b/nor-utils/rfddump.c > @@ -0,0 +1,337 @@ > +/* > + * 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. > + */ > + > +#define PROGRAM_NAME "rfddump" > +#define VERSION "$Revision 1.0 $" > + > +#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 <mtd_swab.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; > + uint16_t *header; > + int sector_count; > + int *sector_map; > + const char *mtd_filename; > + const char *out_filename; > + int verbose; > +}; > + > +void display_help(void) > +{ > + printf("Usage: %s [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", > + PROGRAM_NAME); > + exit(0); > +} > + > +void display_version(void) > +{ > + printf("%s " 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", > + PROGRAM_NAME); > + > + 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++) { > + uint16_t 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; > + uint8_t 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(uint16_t) + 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(uint16_t); > + > + rfd.header = malloc(rfd.header_size); > + if (!rfd.header) { > + perror(PROGRAM_NAME); > + close(fd); > + return 2; > + } > + rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); > + if (!rfd.sector_map) { > + perror(PROGRAM_NAME); > + 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/nor-utils/rfdformat.c b/nor-utils/rfdformat.c > new file mode 100644 > index 0000000..17d9d2d > --- /dev/null > +++ b/nor-utils/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. > + * > + * This is very easy: just erase all the blocks and put the magic at > + * the beginning of each block. > + */ > + > +#define PROGRAM_NAME "rfdformat" > +#define VERSION "$Revision 1.0 $" > + > +#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> > + > +void display_help(void) > +{ > + printf("Usage: %s [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", > + PROGRAM_NAME); > + exit(0); > +} > + > +void display_version(void) > +{ > + printf("%s " 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", > + PROGRAM_NAME); > + > + 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 uint8_t 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/rbtree.c b/rbtree.c > deleted file mode 100644 > index 329e098..0000000 > --- a/rbtree.c > +++ /dev/null > @@ -1,390 +0,0 @@ > -/* > - Red Black Trees > - (C) 1999 Andrea Arcangeli <andrea@suse.de> > - (C) 2002 David Woodhouse <dwmw2@infradead.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. > - > - 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 > - > - linux/lib/rbtree.c > -*/ > - > -#include <stdlib.h> > -#include "rbtree.h" > - > -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) > -{ > - struct rb_node *right = node->rb_right; > - struct rb_node *parent = rb_parent(node); > - > - if ((node->rb_right = right->rb_left)) > - rb_set_parent(right->rb_left, node); > - right->rb_left = node; > - > - rb_set_parent(right, parent); > - > - if (parent) > - { > - if (node == parent->rb_left) > - parent->rb_left = right; > - else > - parent->rb_right = right; > - } > - else > - root->rb_node = right; > - rb_set_parent(node, right); > -} > - > -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) > -{ > - struct rb_node *left = node->rb_left; > - struct rb_node *parent = rb_parent(node); > - > - if ((node->rb_left = left->rb_right)) > - rb_set_parent(left->rb_right, node); > - left->rb_right = node; > - > - rb_set_parent(left, parent); > - > - if (parent) > - { > - if (node == parent->rb_right) > - parent->rb_right = left; > - else > - parent->rb_left = left; > - } > - else > - root->rb_node = left; > - rb_set_parent(node, left); > -} > - > -void rb_insert_color(struct rb_node *node, struct rb_root *root) > -{ > - struct rb_node *parent, *gparent; > - > - while ((parent = rb_parent(node)) && rb_is_red(parent)) > - { > - gparent = rb_parent(parent); > - > - if (parent == gparent->rb_left) > - { > - { > - register struct rb_node *uncle = gparent->rb_right; > - if (uncle && rb_is_red(uncle)) > - { > - rb_set_black(uncle); > - rb_set_black(parent); > - rb_set_red(gparent); > - node = gparent; > - continue; > - } > - } > - > - if (parent->rb_right == node) > - { > - register struct rb_node *tmp; > - __rb_rotate_left(parent, root); > - tmp = parent; > - parent = node; > - node = tmp; > - } > - > - rb_set_black(parent); > - rb_set_red(gparent); > - __rb_rotate_right(gparent, root); > - } else { > - { > - register struct rb_node *uncle = gparent->rb_left; > - if (uncle && rb_is_red(uncle)) > - { > - rb_set_black(uncle); > - rb_set_black(parent); > - rb_set_red(gparent); > - node = gparent; > - continue; > - } > - } > - > - if (parent->rb_left == node) > - { > - register struct rb_node *tmp; > - __rb_rotate_right(parent, root); > - tmp = parent; > - parent = node; > - node = tmp; > - } > - > - rb_set_black(parent); > - rb_set_red(gparent); > - __rb_rotate_left(gparent, root); > - } > - } > - > - rb_set_black(root->rb_node); > -} > - > -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, > - struct rb_root *root) > -{ > - struct rb_node *other; > - > - while ((!node || rb_is_black(node)) && node != root->rb_node) > - { > - if (parent->rb_left == node) > - { > - other = parent->rb_right; > - if (rb_is_red(other)) > - { > - rb_set_black(other); > - rb_set_red(parent); > - __rb_rotate_left(parent, root); > - other = parent->rb_right; > - } > - if ((!other->rb_left || rb_is_black(other->rb_left)) && > - (!other->rb_right || rb_is_black(other->rb_right))) > - { > - rb_set_red(other); > - node = parent; > - parent = rb_parent(node); > - } > - else > - { > - if (!other->rb_right || rb_is_black(other->rb_right)) > - { > - struct rb_node *o_left; > - if ((o_left = other->rb_left)) > - rb_set_black(o_left); > - rb_set_red(other); > - __rb_rotate_right(other, root); > - other = parent->rb_right; > - } > - rb_set_color(other, rb_color(parent)); > - rb_set_black(parent); > - if (other->rb_right) > - rb_set_black(other->rb_right); > - __rb_rotate_left(parent, root); > - node = root->rb_node; > - break; > - } > - } > - else > - { > - other = parent->rb_left; > - if (rb_is_red(other)) > - { > - rb_set_black(other); > - rb_set_red(parent); > - __rb_rotate_right(parent, root); > - other = parent->rb_left; > - } > - if ((!other->rb_left || rb_is_black(other->rb_left)) && > - (!other->rb_right || rb_is_black(other->rb_right))) > - { > - rb_set_red(other); > - node = parent; > - parent = rb_parent(node); > - } > - else > - { > - if (!other->rb_left || rb_is_black(other->rb_left)) > - { > - register struct rb_node *o_right; > - if ((o_right = other->rb_right)) > - rb_set_black(o_right); > - rb_set_red(other); > - __rb_rotate_left(other, root); > - other = parent->rb_left; > - } > - rb_set_color(other, rb_color(parent)); > - rb_set_black(parent); > - if (other->rb_left) > - rb_set_black(other->rb_left); > - __rb_rotate_right(parent, root); > - node = root->rb_node; > - break; > - } > - } > - } > - if (node) > - rb_set_black(node); > -} > - > -void rb_erase(struct rb_node *node, struct rb_root *root) > -{ > - struct rb_node *child, *parent; > - int color; > - > - if (!node->rb_left) > - child = node->rb_right; > - else if (!node->rb_right) > - child = node->rb_left; > - else > - { > - struct rb_node *old = node, *left; > - > - node = node->rb_right; > - while ((left = node->rb_left) != NULL) > - node = left; > - child = node->rb_right; > - parent = rb_parent(node); > - color = rb_color(node); > - > - if (child) > - rb_set_parent(child, parent); > - if (parent == old) { > - parent->rb_right = child; > - parent = node; > - } else > - parent->rb_left = child; > - > - node->rb_parent_color = old->rb_parent_color; > - node->rb_right = old->rb_right; > - node->rb_left = old->rb_left; > - > - if (rb_parent(old)) > - { > - if (rb_parent(old)->rb_left == old) > - rb_parent(old)->rb_left = node; > - else > - rb_parent(old)->rb_right = node; > - } else > - root->rb_node = node; > - > - rb_set_parent(old->rb_left, node); > - if (old->rb_right) > - rb_set_parent(old->rb_right, node); > - goto color; > - } > - > - parent = rb_parent(node); > - color = rb_color(node); > - > - if (child) > - rb_set_parent(child, parent); > - if (parent) > - { > - if (parent->rb_left == node) > - parent->rb_left = child; > - else > - parent->rb_right = child; > - } > - else > - root->rb_node = child; > - > - color: > - if (color == RB_BLACK) > - __rb_erase_color(child, parent, root); > -} > - > -/* > - * This function returns the first node (in sort order) of the tree. > - */ > -struct rb_node *rb_first(struct rb_root *root) > -{ > - struct rb_node *n; > - > - n = root->rb_node; > - if (!n) > - return NULL; > - while (n->rb_left) > - n = n->rb_left; > - return n; > -} > - > -struct rb_node *rb_last(struct rb_root *root) > -{ > - struct rb_node *n; > - > - n = root->rb_node; > - if (!n) > - return NULL; > - while (n->rb_right) > - n = n->rb_right; > - return n; > -} > - > -struct rb_node *rb_next(struct rb_node *node) > -{ > - struct rb_node *parent; > - > - if (rb_parent(node) == node) > - return NULL; > - > - /* If we have a right-hand child, go down and then left as far > - as we can. */ > - if (node->rb_right) { > - node = node->rb_right; > - while (node->rb_left) > - node=node->rb_left; > - return node; > - } > - > - /* No right-hand children. Everything down and left is > - smaller than us, so any 'next' node must be in the general > - direction of our parent. Go up the tree; any time the > - ancestor is a right-hand child of its parent, keep going > - up. First time it's a left-hand child of its parent, said > - parent is our 'next' node. */ > - while ((parent = rb_parent(node)) && node == parent->rb_right) > - node = parent; > - > - return parent; > -} > - > -struct rb_node *rb_prev(struct rb_node *node) > -{ > - struct rb_node *parent; > - > - if (rb_parent(node) == node) > - return NULL; > - > - /* If we have a left-hand child, go down and then right as far > - as we can. */ > - if (node->rb_left) { > - node = node->rb_left; > - while (node->rb_right) > - node=node->rb_right; > - return node; > - } > - > - /* No left-hand children. Go up till we find an ancestor which > - is a right-hand child of its parent */ > - while ((parent = rb_parent(node)) && node == parent->rb_left) > - node = parent; > - > - return parent; > -} > - > -void rb_replace_node(struct rb_node *victim, struct rb_node *new, > - struct rb_root *root) > -{ > - struct rb_node *parent = rb_parent(victim); > - > - /* Set the surrounding nodes to point to the replacement */ > - if (parent) { > - if (victim == parent->rb_left) > - parent->rb_left = new; > - else > - parent->rb_right = new; > - } else { > - root->rb_node = new; > - } > - if (victim->rb_left) > - rb_set_parent(victim->rb_left, new); > - if (victim->rb_right) > - rb_set_parent(victim->rb_right, new); > - > - /* Copy the pointers/colour from the victim to the replacement */ > - *new = *victim; > -} > diff --git a/rbtree.h b/rbtree.h > deleted file mode 100644 > index 0d77b65..0000000 > --- a/rbtree.h > +++ /dev/null > @@ -1,171 +0,0 @@ > -/* > - Red Black Trees > - (C) 1999 Andrea Arcangeli <andrea@suse.de> > - > - 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 > - > - linux/include/linux/rbtree.h > - > - To use rbtrees you'll have to implement your own insert and search cores. > - This will avoid us to use callbacks and to drop drammatically performances. > - I know it's not the cleaner way, but in C (not in C++) to get > - performances and genericity... > - > - Some example of insert and search follows here. The search is a plain > - normal search over an ordered tree. The insert instead must be implemented > - int two steps: as first thing the code must insert the element in > - order as a red leaf in the tree, then the support library function > - rb_insert_color() must be called. Such function will do the > - not trivial work to rebalance the rbtree if necessary. > - > ------------------------------------------------------------------------ > -static inline struct page * rb_search_page_cache(struct inode * inode, > - unsigned long offset) > -{ > - struct rb_node * n = inode->i_rb_page_cache.rb_node; > - struct page * page; > - > - while (n) > - { > - page = rb_entry(n, struct page, rb_page_cache); > - > - if (offset < page->offset) > - n = n->rb_left; > - else if (offset > page->offset) > - n = n->rb_right; > - else > - return page; > - } > - return NULL; > -} > - > -static inline struct page * __rb_insert_page_cache(struct inode * inode, > - unsigned long offset, > - struct rb_node * node) > -{ > - struct rb_node ** p = &inode->i_rb_page_cache.rb_node; > - struct rb_node * parent = NULL; > - struct page * page; > - > - while (*p) > - { > - parent = *p; > - page = rb_entry(parent, struct page, rb_page_cache); > - > - if (offset < page->offset) > - p = &(*p)->rb_left; > - else if (offset > page->offset) > - p = &(*p)->rb_right; > - else > - return page; > - } > - > - rb_link_node(node, parent, p); > - > - return NULL; > -} > - > -static inline struct page * rb_insert_page_cache(struct inode * inode, > - unsigned long offset, > - struct rb_node * node) > -{ > - struct page * ret; > - if ((ret = __rb_insert_page_cache(inode, offset, node))) > - goto out; > - rb_insert_color(node, &inode->i_rb_page_cache); > - out: > - return ret; > -} > ------------------------------------------------------------------------ > -*/ > - > -#ifndef _LINUX_RBTREE_H > -#define _LINUX_RBTREE_H > - > -#include <linux/kernel.h> > -#include <linux/stddef.h> > - > -struct rb_node > -{ > - unsigned long rb_parent_color; > -#define RB_RED 0 > -#define RB_BLACK 1 > - struct rb_node *rb_right; > - struct rb_node *rb_left; > -} __attribute__((aligned(sizeof(long)))); > - /* The alignment might seem pointless, but allegedly CRIS needs it */ > - > -struct rb_root > -{ > - struct rb_node *rb_node; > -}; > - > - > -#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) > -#define rb_color(r) ((r)->rb_parent_color & 1) > -#define rb_is_red(r) (!rb_color(r)) > -#define rb_is_black(r) rb_color(r) > -#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) > -#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) > - > -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) > -{ > - rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; > -} > -static inline void rb_set_color(struct rb_node *rb, int color) > -{ > - rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; > -} > - > -#define RB_ROOT (struct rb_root) { NULL, } > - > -/* Newer gcc versions take care of exporting this */ > -#ifndef offsetof > -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) > -#endif > - > -#define container_of(ptr, type, member) ({ \ > - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ > - (type *)( (char *)__mptr - offsetof(type,member) );}) > - > -#define rb_entry(ptr, type, member) container_of(ptr, type, member) > - > -#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) > -#define RB_EMPTY_NODE(node) (rb_parent(node) == node) > -#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) > - > -extern void rb_insert_color(struct rb_node *, struct rb_root *); > -extern void rb_erase(struct rb_node *, struct rb_root *); > - > -/* Find logical next and previous nodes in a tree */ > -extern struct rb_node *rb_next(struct rb_node *); > -extern struct rb_node *rb_prev(struct rb_node *); > -extern struct rb_node *rb_first(struct rb_root *); > -extern struct rb_node *rb_last(struct rb_root *); > - > -/* Fast replacement of a single node without remove/rebalance/add/rebalance */ > -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, > - struct rb_root *root); > - > -static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, > - struct rb_node ** rb_link) > -{ > - node->rb_parent_color = (unsigned long )parent; > - node->rb_left = node->rb_right = NULL; > - > - *rb_link = node; > -} > - > -#endif /* _LINUX_RBTREE_H */ > diff --git a/recv_image.c b/recv_image.c > deleted file mode 100644 > index 0093831..0000000 > --- a/recv_image.c > +++ /dev/null > @@ -1,484 +0,0 @@ > - > -#define PROGRAM_NAME "recv_image" > -#define _XOPEN_SOURCE 500 > -#define _BSD_SOURCE /* struct ip_mreq */ > - > -#include <errno.h> > -#include <stdio.h> > -#include <netdb.h> > -#include <stdlib.h> > -#include <string.h> > -#include <unistd.h> > -#include <fcntl.h> > -#include <sys/types.h> > -#include <sys/stat.h> > -#include <sys/socket.h> > -#include <netinet/in.h> > -#include <sys/ioctl.h> > -#include <sys/time.h> > -#include <crc32.h> > -#include "mtd/mtd-user.h" > -#include "mcast_image.h" > - > -#include "common.h" > - > -#define WBUF_SIZE 4096 > -struct eraseblock { > - uint32_t flash_offset; > - unsigned char wbuf[WBUF_SIZE]; > - int wbuf_ofs; > - int nr_pkts; > - int *pkt_indices; > - uint32_t crc; > -}; > - > -int main(int argc, char **argv) > -{ > - struct addrinfo *ai; > - struct addrinfo hints; > - struct addrinfo *runp; > - int ret; > - int sock; > - ssize_t len; > - int flfd; > - struct mtd_info_user meminfo; > - unsigned char *eb_buf, *decode_buf, **src_pkts; > - int nr_blocks = 0; > - int pkts_per_block; > - int block_nr = -1; > - uint32_t image_crc = 0; > - int total_pkts = 0; > - int ignored_pkts = 0; > - loff_t mtdoffset = 0; > - int badcrcs = 0; > - int duplicates = 0; > - int file_mode = 0; > - struct fec_parms *fec = NULL; > - int i; > - struct eraseblock *eraseblocks = NULL; > - uint32_t start_seq = 0; > - struct timeval start, now; > - unsigned long fec_time = 0, flash_time = 0, crc_time = 0, > - rflash_time = 0, erase_time = 0, net_time = 0; > - > - if (argc != 4) { > - fprintf(stderr, "usage: %s <host> <port> <mtddev>\n", > - PROGRAM_NAME); > - exit(1); > - } > - /* Open the device */ > - flfd = open(argv[3], O_RDWR); > - > - if (flfd >= 0) { > - /* Fill in MTD device capability structure */ > - if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { > - perror("MEMGETINFO"); > - close(flfd); > - flfd = -1; > - } else { > - printf("Receive to MTD device %s with erasesize %d\n", > - argv[3], meminfo.erasesize); > - } > - } > - if (flfd == -1) { > - /* Try again, as if it's a file */ > - flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); > - if (flfd < 0) { > - perror("open"); > - exit(1); > - } > - meminfo.erasesize = 131072; > - file_mode = 1; > - printf("Receive to file %s with (assumed) erasesize %d\n", > - argv[3], meminfo.erasesize); > - } > - > - pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; > - > - eb_buf = malloc(pkts_per_block * PKT_SIZE); > - decode_buf = malloc(pkts_per_block * PKT_SIZE); > - if (!eb_buf && !decode_buf) { > - fprintf(stderr, "No memory for eraseblock buffer\n"); > - exit(1); > - } > - src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); > - if (!src_pkts) { > - fprintf(stderr, "No memory for decode packet pointers\n"); > - exit(1); > - } > - > - memset(&hints, 0, sizeof(hints)); > - hints.ai_flags = AI_ADDRCONFIG; > - hints.ai_socktype = SOCK_DGRAM; > - > - ret = getaddrinfo(argv[1], argv[2], &hints, &ai); > - if (ret) { > - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); > - exit(1); > - } > - runp = ai; > - for (runp = ai; runp; runp = runp->ai_next) { > - sock = socket(runp->ai_family, runp->ai_socktype, > - runp->ai_protocol); > - if (sock == -1) { > - perror("socket"); > - continue; > - } > - if (runp->ai_family == AF_INET && > - IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { > - struct ip_mreq rq; > - rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; > - rq.imr_interface.s_addr = INADDR_ANY; > - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { > - perror("IP_ADD_MEMBERSHIP"); > - close(sock); > - continue; > - } > - > - } else if (runp->ai_family == AF_INET6 && > - ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { > - struct ipv6_mreq rq; > - rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; > - rq.ipv6mr_interface = 0; > - if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { > - perror("IPV6_ADD_MEMBERSHIP"); > - close(sock); > - continue; > - } > - } > - if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { > - perror("bind"); > - close(sock); > - continue; > - } > - break; > - } > - if (!runp) > - exit(1); > - > - while (1) { > - struct image_pkt thispkt; > - > - len = read(sock, &thispkt, sizeof(thispkt)); > - > - if (len < 0) { > - perror("read socket"); > - break; > - } > - if (len < sizeof(thispkt)) { > - fprintf(stderr, "Wrong length %zd bytes (expected %zu)\n", > - len, sizeof(thispkt)); > - continue; > - } > - if (!eraseblocks) { > - image_crc = thispkt.hdr.totcrc; > - start_seq = ntohl(thispkt.hdr.pkt_sequence); > - > - if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { > - fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", > - ntohl(thispkt.hdr.blocksize), meminfo.erasesize); > - exit(1); > - } > - nr_blocks = ntohl(thispkt.hdr.nr_blocks); > - > - fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); > - > - eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); > - if (!eraseblocks) { > - fprintf(stderr, "No memory for block map\n"); > - exit(1); > - } > - for (i = 0; i < nr_blocks; i++) { > - eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); > - if (!eraseblocks[i].pkt_indices) { > - fprintf(stderr, "Failed to allocate packet indices\n"); > - exit(1); > - } > - eraseblocks[i].nr_pkts = 0; > - if (!file_mode) { > - if (mtdoffset >= meminfo.size) { > - fprintf(stderr, "Run out of space on flash\n"); > - exit(1); > - } > -#if 1 /* Deliberately use bad blocks... test write failures */ > - while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { > - printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); > - mtdoffset += meminfo.erasesize; > - } > -#endif > - } > - eraseblocks[i].flash_offset = mtdoffset; > - mtdoffset += meminfo.erasesize; > - eraseblocks[i].wbuf_ofs = 0; > - } > - gettimeofday(&start, NULL); > - } > - if (image_crc != thispkt.hdr.totcrc) { > - fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", > - ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); > - exit(1); > - } > - > - block_nr = ntohl(thispkt.hdr.block_nr); > - if (block_nr >= nr_blocks) { > - fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", > - block_nr, nr_blocks); > - exit(1); > - } > - for (i=0; i<eraseblocks[block_nr].nr_pkts; i++) { > - if (eraseblocks[block_nr].pkt_indices[i] == ntohs(thispkt.hdr.pkt_nr)) { > -// printf("Discarding duplicate packet at %08x pkt %d\n", > -// block_nr * meminfo.erasesize, eraseblocks[block_nr].pkt_indices[i]); > - duplicates++; > - break; > - } > - } > - if (i < eraseblocks[block_nr].nr_pkts) { > - continue; > - } > - > - if (eraseblocks[block_nr].nr_pkts >= pkts_per_block) { > - /* We have a block which we didn't really need */ > - eraseblocks[block_nr].nr_pkts++; > - ignored_pkts++; > - continue; > - } > - > - if (mtd_crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { > - printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", > - block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), > - mtd_crc32(-1, thispkt.data, PKT_SIZE), > - ntohl(thispkt.hdr.thiscrc)); > - badcrcs++; > - continue; > - } > - pkt_again: > - eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = > - ntohs(thispkt.hdr.pkt_nr); > - total_pkts++; > - if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { > - uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; > - long time_msec; > - gettimeofday(&now, NULL); > - > - time_msec = ((now.tv_usec - start.tv_usec) / 1000) + > - (now.tv_sec - start.tv_sec) * 1000; > - > - printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", > - total_pkts, nr_blocks * pkts_per_block, > - total_pkts * 100 / nr_blocks / pkts_per_block, > - time_msec / 1000, > - total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, > - pkts_sent - total_pkts - duplicates - ignored_pkts, > - (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, > - duplicates + ignored_pkts); > - fflush(stdout); > - } > - > - if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { > - /* New packet doesn't full the wbuf */ > - memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, > - thispkt.data, PKT_SIZE); > - eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; > - } else { > - int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; > - ssize_t wrotelen; > - static int faked = 1; > - > - memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, > - thispkt.data, fits); > - wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, > - eraseblocks[block_nr].flash_offset); > - > - if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { > - faked = 1; > - if (wrotelen < 0) > - perror("\npacket write"); > - else > - fprintf(stderr, "\nshort write of packet wbuf\n"); > - > - if (!file_mode) { > - struct erase_info_user erase; > - /* FIXME: Perhaps we should store pkt crcs and try > - to recover data from the offending eraseblock */ > - > - /* We have increased nr_pkts but not yet flash_offset */ > - erase.start = eraseblocks[block_nr].flash_offset & > - ~(meminfo.erasesize - 1); > - erase.length = meminfo.erasesize; > - > - printf("Will erase at %08x len %08x (bad write was at %08x)\n", > - erase.start, erase.length, eraseblocks[block_nr].flash_offset); > - if (ioctl(flfd, MEMERASE, &erase)) { > - perror("MEMERASE"); > - exit(1); > - } > - if (mtdoffset >= meminfo.size) { > - fprintf(stderr, "Run out of space on flash\n"); > - exit(1); > - } > - while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { > - printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); > - mtdoffset += meminfo.erasesize; > - if (mtdoffset >= meminfo.size) { > - fprintf(stderr, "Run out of space on flash\n"); > - exit(1); > - } > - } > - eraseblocks[block_nr].flash_offset = mtdoffset; > - printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); > - total_pkts -= eraseblocks[block_nr].nr_pkts; > - eraseblocks[block_nr].nr_pkts = 0; > - eraseblocks[block_nr].wbuf_ofs = 0; > - mtdoffset += meminfo.erasesize; > - goto pkt_again; > - } > - else /* Usually nothing we can do in file mode */ > - exit(1); > - } > - eraseblocks[block_nr].flash_offset += WBUF_SIZE; > - /* Copy the remainder into the wbuf */ > - memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); > - eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; > - } > - > - if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { > - eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); > - > - if (total_pkts == nr_blocks * pkts_per_block) > - break; > - } > - } > - printf("\n"); > - gettimeofday(&now, NULL); > - net_time = (now.tv_usec - start.tv_usec) / 1000; > - net_time += (now.tv_sec - start.tv_sec) * 1000; > - close(sock); > - for (block_nr = 0; block_nr < nr_blocks; block_nr++) { > - ssize_t rwlen; > - gettimeofday(&start, NULL); > - eraseblocks[block_nr].flash_offset -= meminfo.erasesize; > - rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); > - > - gettimeofday(&now, NULL); > - rflash_time += (now.tv_usec - start.tv_usec) / 1000; > - rflash_time += (now.tv_sec - start.tv_sec) * 1000; > - if (rwlen < 0) { > - perror("read"); > - /* Argh. Perhaps we could go back and try again, but if the flash is > - going to fail to read back what we write to it, and the whole point > - in this program is to write to it, what's the point? */ > - fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); > - exit(1); > - } > - > - memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, > - eraseblocks[block_nr].wbuf_ofs); > - > - for (i=0; i < pkts_per_block; i++) > - src_pkts[i] = &eb_buf[i * PKT_SIZE]; > - > - gettimeofday(&start, NULL); > - if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { > - /* Eep. This cannot happen */ > - printf("The world is broken. fec_decode() returned error\n"); > - exit(1); > - } > - gettimeofday(&now, NULL); > - fec_time += (now.tv_usec - start.tv_usec) / 1000; > - fec_time += (now.tv_sec - start.tv_sec) * 1000; > - > - for (i=0; i < pkts_per_block; i++) > - memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); > - > - /* Paranoia */ > - gettimeofday(&start, NULL); > - if (mtd_crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { > - printf("\nCRC mismatch for block #%d: want %08x got %08x\n", > - block_nr, eraseblocks[block_nr].crc, > - mtd_crc32(-1, decode_buf, meminfo.erasesize)); > - exit(1); > - } > - gettimeofday(&now, NULL); > - crc_time += (now.tv_usec - start.tv_usec) / 1000; > - crc_time += (now.tv_sec - start.tv_sec) * 1000; > - start = now; > - > - if (!file_mode) { > - struct erase_info_user erase; > - > - erase.start = eraseblocks[block_nr].flash_offset; > - erase.length = meminfo.erasesize; > - > - printf("\rErasing block at %08x...", erase.start); > - > - if (ioctl(flfd, MEMERASE, &erase)) { > - perror("MEMERASE"); > - /* This block has dirty data on it. If the erase failed, we're screwed */ > - fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); > - exit(1); > - } > - gettimeofday(&now, NULL); > - erase_time += (now.tv_usec - start.tv_usec) / 1000; > - erase_time += (now.tv_sec - start.tv_sec) * 1000; > - start = now; > - } > - else printf("\r"); > - write_again: > - rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); > - if (rwlen < meminfo.erasesize) { > - if (rwlen < 0) { > - perror("\ndecoded data write"); > - } else > - fprintf(stderr, "\nshort write of decoded data\n"); > - > - if (!file_mode) { > - struct erase_info_user erase; > - erase.start = eraseblocks[block_nr].flash_offset; > - erase.length = meminfo.erasesize; > - > - printf("Erasing failed block at %08x\n", > - eraseblocks[block_nr].flash_offset); > - > - if (ioctl(flfd, MEMERASE, &erase)) { > - perror("MEMERASE"); > - exit(1); > - } > - if (mtdoffset >= meminfo.size) { > - fprintf(stderr, "Run out of space on flash\n"); > - exit(1); > - } > - while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { > - printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); > - mtdoffset += meminfo.erasesize; > - if (mtdoffset >= meminfo.size) { > - fprintf(stderr, "Run out of space on flash\n"); > - exit(1); > - } > - } > - printf("Will try again at %08lx...", (long)mtdoffset); > - eraseblocks[block_nr].flash_offset = mtdoffset; > - > - goto write_again; > - } > - else /* Usually nothing we can do in file mode */ > - exit(1); > - } > - gettimeofday(&now, NULL); > - flash_time += (now.tv_usec - start.tv_usec) / 1000; > - flash_time += (now.tv_sec - start.tv_sec) * 1000; > - > - printf("wrote image block %08x (%d pkts) ", > - block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); > - fflush(stdout); > - } > - close(flfd); > - printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); > - printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); > - printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); > - printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); > - printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); > - printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); > - > - return 0; > -} > diff --git a/rfddump.c b/rfddump.c > deleted file mode 100644 > index 0375bac..0000000 > --- a/rfddump.c > +++ /dev/null > @@ -1,337 +0,0 @@ > -/* > - * 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. > - */ > - > -#define PROGRAM_NAME "rfddump" > -#define VERSION "$Revision 1.0 $" > - > -#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 <mtd_swab.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; > - uint16_t *header; > - int sector_count; > - int *sector_map; > - const char *mtd_filename; > - const char *out_filename; > - int verbose; > -}; > - > -void display_help(void) > -{ > - printf("Usage: %s [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", > - PROGRAM_NAME); > - exit(0); > -} > - > -void display_version(void) > -{ > - printf("%s " 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", > - PROGRAM_NAME); > - > - 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++) { > - uint16_t 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; > - uint8_t 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(uint16_t) + 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(uint16_t); > - > - rfd.header = malloc(rfd.header_size); > - if (!rfd.header) { > - perror(PROGRAM_NAME); > - close(fd); > - return 2; > - } > - rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); > - if (!rfd.sector_map) { > - perror(PROGRAM_NAME); > - 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 > deleted file mode 100644 > index 17d9d2d..0000000 > --- a/rfdformat.c > +++ /dev/null > @@ -1,160 +0,0 @@ > -/* > - * 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. > - * > - * This is very easy: just erase all the blocks and put the magic at > - * the beginning of each block. > - */ > - > -#define PROGRAM_NAME "rfdformat" > -#define VERSION "$Revision 1.0 $" > - > -#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> > - > -void display_help(void) > -{ > - printf("Usage: %s [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", > - PROGRAM_NAME); > - exit(0); > -} > - > -void display_version(void) > -{ > - printf("%s " 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", > - PROGRAM_NAME); > - > - 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 uint8_t 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/serve_image.c b/serve_image.c > deleted file mode 100644 > index d3794ec..0000000 > --- a/serve_image.c > +++ /dev/null > @@ -1,300 +0,0 @@ > -#define PROGRAM_NAME "serve_image" > -#define _POSIX_C_SOURCE 199309 > - > -#include <time.h> > -#include <errno.h> > -#include <netdb.h> > -#include <stdio.h> > -#include <stdlib.h> > -#include <string.h> > -#include <unistd.h> > -#include <fcntl.h> > -#include <sys/types.h> > -#include <sys/stat.h> > -#include <sys/socket.h> > -#include <sys/mman.h> > -#include <netinet/in.h> > -#include <sys/time.h> > -#include <crc32.h> > -#include <inttypes.h> > - > -#include "mcast_image.h" > - > -int tx_rate = 80000; > -int pkt_delay; > - > -#undef RANDOMDROP > - > -int main(int argc, char **argv) > -{ > - struct addrinfo *ai; > - struct addrinfo hints; > - struct addrinfo *runp; > - int ret; > - int sock; > - struct image_pkt pktbuf; > - int rfd; > - struct stat st; > - int writeerrors = 0; > - uint32_t erasesize; > - unsigned char *image, *blockptr = NULL; > - uint32_t block_nr, pkt_nr; > - int nr_blocks; > - struct timeval then, now, nextpkt; > - long time_msecs; > - int pkts_per_block; > - int total_pkts_per_block; > - struct fec_parms *fec; > - unsigned char *last_block; > - uint32_t *block_crcs; > - long tosleep; > - uint32_t sequence = 0; > - > - if (argc == 6) { > - tx_rate = atol(argv[5]) * 1024; > - if (tx_rate < PKT_SIZE || tx_rate > 20000000) { > - fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); > - exit(1); > - } > - argc = 5; > - } > - if (argc != 5) { > - fprintf(stderr, "usage: %s <host> <port> <image> <erasesize> [<tx_rate>]\n", > - PROGRAM_NAME); > - exit(1); > - } > - pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; > - printf("Inter-packet delay (avg): %dµs\n", pkt_delay); > - printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); > - > - erasesize = atol(argv[4]); > - if (!erasesize) { > - fprintf(stderr, "erasesize cannot be zero\n"); > - exit(1); > - } > - > - pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; > - total_pkts_per_block = pkts_per_block * 3 / 2; > - > - /* We have to pad it with zeroes, so can't use it in-place */ > - last_block = malloc(pkts_per_block * PKT_SIZE); > - if (!last_block) { > - fprintf(stderr, "Failed to allocate last-block buffer\n"); > - exit(1); > - } > - > - fec = fec_new(pkts_per_block, total_pkts_per_block); > - if (!fec) { > - fprintf(stderr, "Error initialising FEC\n"); > - exit(1); > - } > - > - memset(&hints, 0, sizeof(hints)); > - hints.ai_flags = AI_ADDRCONFIG; > - hints.ai_socktype = SOCK_DGRAM; > - > - ret = getaddrinfo(argv[1], argv[2], &hints, &ai); > - if (ret) { > - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); > - exit(1); > - } > - runp = ai; > - for (runp = ai; runp; runp = runp->ai_next) { > - sock = socket(runp->ai_family, runp->ai_socktype, > - runp->ai_protocol); > - if (sock == -1) { > - perror("socket"); > - continue; > - } > - if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) > - break; > - perror("connect"); > - close(sock); > - } > - if (!runp) > - exit(1); > - > - rfd = open(argv[3], O_RDONLY); > - if (rfd < 0) { > - perror("open"); > - exit(1); > - } > - > - if (fstat(rfd, &st)) { > - perror("fstat"); > - exit(1); > - } > - > - if (st.st_size % erasesize) { > - fprintf(stderr, "Image size %" PRIu64 " bytes is not a multiple of erasesize %d bytes\n", > - st.st_size, erasesize); > - exit(1); > - } > - image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); > - if (image == MAP_FAILED) { > - perror("mmap"); > - exit(1); > - } > - > - nr_blocks = st.st_size / erasesize; > - > - block_crcs = malloc(nr_blocks * sizeof(uint32_t)); > - if (!block_crcs) { > - fprintf(stderr, "Failed to allocate memory for CRCs\n"); > - exit(1); > - } > - > - memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); > - memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); > - > - printf("Checking CRC...."); > - fflush(stdout); > - > - pktbuf.hdr.resend = 0; > - pktbuf.hdr.totcrc = htonl(mtd_crc32(-1, image, st.st_size)); > - pktbuf.hdr.nr_blocks = htonl(nr_blocks); > - pktbuf.hdr.blocksize = htonl(erasesize); > - pktbuf.hdr.thislen = htonl(PKT_SIZE); > - pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); > - > - printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); > - printf("Checking block CRCs...."); > - fflush(stdout); > - for (block_nr=0; block_nr < nr_blocks; block_nr++) { > - printf("\rChecking block CRCS.... %d/%d", > - block_nr + 1, nr_blocks); > - fflush(stdout); > - block_crcs[block_nr] = mtd_crc32(-1, image + (block_nr * erasesize), erasesize); > - } > - > - printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" > - "Estimated transmit time per cycle: %ds\n", > - (long)st.st_size / 1024, (long) st.st_size, > - nr_blocks, pkts_per_block, > - nr_blocks * pkts_per_block * pkt_delay / 1000000); > - gettimeofday(&then, NULL); > - nextpkt = then; > - > -#ifdef RANDOMDROP > - srand((unsigned)then.tv_usec); > - printf("Random seed %u\n", (unsigned)then.tv_usec); > -#endif > - while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { > - > - if (blockptr && pkt_nr == 0) { > - unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); > - gettimeofday(&now, NULL); > - > - time_msecs = (now.tv_sec - then.tv_sec) * 1000; > - time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; > - printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", > - amt_sent / 1024, time_msecs, > - amt_sent / 1024 * 1000 / time_msecs); > - then = now; > - } > - > - for (block_nr = 0; block_nr < nr_blocks; block_nr++) { > - > - int actualpkt; > - > - /* Calculating the redundant FEC blocks is expensive; > - the first $pkts_per_block are cheap enough though > - because they're just copies. So alternate between > - simple and complex stuff, so that we don't start > - to choke and fail to keep up with the expected > - bitrate in the second half of the sequence */ > - if (block_nr & 1) > - actualpkt = pkt_nr; > - else > - actualpkt = total_pkts_per_block - 1 - pkt_nr; > - > - blockptr = image + (erasesize * block_nr); > - if (block_nr == nr_blocks - 1) > - blockptr = last_block; > - > - fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); > - > - pktbuf.hdr.thiscrc = htonl(mtd_crc32(-1, pktbuf.data, PKT_SIZE)); > - pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); > - pktbuf.hdr.block_nr = htonl(block_nr); > - pktbuf.hdr.pkt_nr = htons(actualpkt); > - pktbuf.hdr.pkt_sequence = htonl(sequence++); > - > - printf("\rSending data block %08x packet %3d/%d", > - block_nr * erasesize, > - pkt_nr, total_pkts_per_block); > - > - if (pkt_nr && !block_nr) { > - unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); > - > - gettimeofday(&now, NULL); > - > - time_msecs = (now.tv_sec - then.tv_sec) * 1000; > - time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; > - printf(" (%ld KiB/s) ", > - amt_sent / 1024 * 1000 / time_msecs); > - } > - > - fflush(stdout); > - > -#ifdef RANDOMDROP > - if ((rand() % 1000) < 20) { > - printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); > - continue; > - } > -#endif > - gettimeofday(&now, NULL); > -#if 1 > - tosleep = nextpkt.tv_usec - now.tv_usec + > - (1000000 * (nextpkt.tv_sec - now.tv_sec)); > - > - /* We need hrtimers for this to actually work */ > - if (tosleep > 0) { > - struct timespec req; > - > - req.tv_nsec = (tosleep % 1000000) * 1000; > - req.tv_sec = tosleep / 1000000; > - > - nanosleep(&req, NULL); > - } > -#else > - while (now.tv_sec < nextpkt.tv_sec || > - (now.tv_sec == nextpkt.tv_sec && > - now.tv_usec < nextpkt.tv_usec)) { > - gettimeofday(&now, NULL); > - } > -#endif > - nextpkt.tv_usec += pkt_delay; > - if (nextpkt.tv_usec >= 1000000) { > - nextpkt.tv_sec += nextpkt.tv_usec / 1000000; > - nextpkt.tv_usec %= 1000000; > - } > - > - /* If the time for the next packet has already > - passed (by some margin), then we've lost time > - Adjust our expected timings accordingly. If > - we're only a little way behind, don't slip yet */ > - if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + > - 1000000 * (nextpkt.tv_sec - now.tv_sec))) { > - nextpkt = now; > - } > - > - if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { > - perror("write"); > - writeerrors++; > - if (writeerrors > 10) { > - fprintf(stderr, "Too many consecutive write errors\n"); > - exit(1); > - } > - } else > - writeerrors = 0; > - > - > - > - } > - } > - munmap(image, st.st_size); > - close(rfd); > - close(sock); > - return 0; > -} > diff --git a/summary.h b/summary.h > deleted file mode 100644 > index e9d95a5..0000000 > --- a/summary.h > +++ /dev/null > @@ -1,177 +0,0 @@ > -/* > - * 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. > - */ > - > -#ifndef JFFS2_SUMMARY_H > -#define JFFS2_SUMMARY_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)) > -#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) > -#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) > - > -/* 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)); > - > -struct jffs2_sum_xattr_flash > -{ > - jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ > - jint32_t xid; /* xattr identifier */ > - jint32_t version; /* version number */ > - jint32_t offset; /* offset on jeb */ > - jint32_t totlen; /* node length */ > -} __attribute__((packed)); > - > -struct jffs2_sum_xref_flash > -{ > - jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ > - jint32_t offset; /* offset on jeb */ > -} __attribute__((packed)); > - > -union jffs2_sum_flash > -{ > - struct jffs2_sum_unknown_flash u; > - struct jffs2_sum_inode_flash i; > - struct jffs2_sum_dirent_flash d; > - struct jffs2_sum_xattr_flash x; > - struct jffs2_sum_xref_flash r; > -}; > - > -/* 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)); > - > -struct jffs2_sum_xattr_mem > -{ > - union jffs2_sum_mem *next; > - jint16_t nodetype; > - jint32_t xid; > - jint32_t version; > - jint32_t offset; > - jint32_t totlen; > -} __attribute__((packed)); > - > -struct jffs2_sum_xref_mem > -{ > - union jffs2_sum_mem *next; > - jint16_t nodetype; > - jint32_t offset; > -} __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_sum_xattr_mem x; > - struct jffs2_sum_xref_mem r; > -}; > - > -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 > deleted file mode 100644 > index 886b545..0000000 > --- a/sumtool.c > +++ /dev/null > @@ -1,872 +0,0 @@ > -/* > - * sumtool.c > - * > - * Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>, > - * Ferenc Havasi <havasi@inf.u-szeged.hu> > - * University of Szeged, Hungary > - * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> > - * > - * 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 > - * > - */ > - > -#define PROGRAM_NAME "sumtool" > - > -#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" > -#include "common.h" > - > -#define PAD(x) (((x)+3)&~3) > - > -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 const 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 const 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 full_write(void *target_buff, const void *buf, int len); > - > -void setup_cleanmarker(void) > -{ > - 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(mtd_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) > - errmsg_die("output filename specified more than once"); > - out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); > - if (out_fd == -1) > - sys_errmsg_die("open output file"); > - break; > - > - case 'i': > - if (in_fd != -1) > - errmsg_die("input filename specified more than once"); > - in_fd = open(optarg, O_RDONLY); > - if (in_fd == -1) > - sys_errmsg_die("open input file"); > - break; > - case 'b': > - target_endian = __BIG_ENDIAN; > - break; > - case 'l': > - target_endian = __LITTLE_ENDIAN; > - break; > - case 'h': > - case '?': > - errmsg_die("%s", helptext); > - case 'v': > - verbose = 1; > - break; > - > - case 'V': > - errmsg_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) > - errmsg_die("Unrecognisable erase size\n"); > - > - if (*next) { > - if (!strcmp(next, "KiB")) { > - units = 1024; > - } else if (!strcmp(next, "MiB")) { > - units = 1024 * 1024; > - } else { > - errmsg_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) { > - warnmsg("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)) { > - errmsg_die("cleanmarker size must be >= 12"); > - } > - if (cleanmarker_size >= erase_block_size) { > - errmsg_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(void) > -{ > - data_buffer = xmalloc(erase_block_size); > - file_buffer = xmalloc(erase_block_size); > -} > - > -void init_sumlist(void) > -{ > - sum_collected = xzalloc(sizeof(*sum_collected)); > -} > - > -void clean_buffers(void) > -{ > - free(data_buffer); > - free(file_buffer); > -} > - > -void clean_sumlist(void) > -{ > - 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) > - warnmsg("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); > - > - free(sum_collected); > - } > -} > - > -int load_next_block(void) > -{ > - int ret; > - ret = read(in_fd, file_buffer, erase_block_size); > - file_ofs = 0; > - > - bareverbose(verbose, "Load next block : %d bytes read\n", ret); > - > - return ret; > -} > - > -void write_buff_to_file(void) > -{ > - 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) > - sys_errmsg_die("write"); > - > - if (ret == 0) > - sys_errmsg_die("write returned zero"); > - > - len -= ret; > - buf += ret; > - } > - > - data_ofs = 0; > -} > - > -void dump_sum_records(void) > -{ > - > - 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 = xmalloc(datasize); > - > - 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(mtd_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; > - } > - > - case JFFS2_NODETYPE_XATTR: { > - struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; > - > - sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; > - sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; > - sxattr_ptr->version = sum_collected->sum_list_head->x.version; > - sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; > - sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; > - > - wpage += JFFS2_SUMMARY_XATTR_SIZE; > - break; > - } > - > - case JFFS2_NODETYPE_XREF: { > - struct jffs2_sum_xref_flash *sxref_ptr = wpage; > - > - sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; > - sxref_ptr->offset = sum_collected->sum_list_head->r.offset; > - > - wpage += JFFS2_SUMMARY_XREF_SIZE; > - break; > - } > - > - default : { > - warnmsg("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(mtd_crc32(0, tpage, datasize)); > - isum.node_crc = cpu_to_je32(mtd_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(void) > -{ > - 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(void) > -{ > - > - 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; > - > - case JFFS2_NODETYPE_XATTR: > - sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; > - sum_collected->sum_num++; > - break; > - > - case JFFS2_NODETYPE_XREF: > - sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; > - sum_collected->sum_num++; > - break; > - > - default: > - errmsg_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 = xmalloc(sizeof(*temp)); > - > - 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 = xmalloc(sizeof(*temp) + node->d.nsize); > - > - 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 add_sum_xattr_mem(union jffs2_node_union *node) > -{ > - struct jffs2_sum_xattr_mem *temp = xmalloc(sizeof(*temp)); > - > - temp->nodetype = node->x.nodetype; > - temp->xid = node->x.xid; > - temp->version = node->x.version; > - temp->offset = cpu_to_je32(data_ofs); > - temp->totlen = node->x.totlen; > - temp->next = NULL; > - > - add_sum_mem((union jffs2_sum_mem *) temp); > -} > - > -void add_sum_xref_mem(union jffs2_node_union *node) > -{ > - struct jffs2_sum_xref_mem *temp = xmalloc(sizeof(*temp)); > - > - temp->nodetype = node->r.nodetype; > - temp->offset = cpu_to_je32(data_ofs); > - temp->next = NULL; > - > - 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 write_xattr_to_buff(union jffs2_node_union *node) > -{ > - pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); > - add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ > - full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); > - padword(); > -} > - > -void write_xref_to_buff(union jffs2_node_union *node) > -{ > - pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); > - add_sum_xref_mem(node); /* Add xref summary mem to summary list */ > - full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); > - padword(); > -} > - > -void create_summed_image(int inp_size) > -{ > - uint8_t *p = file_buffer; > - union jffs2_node_union *node; > - uint32_t crc, length; > - 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++) > - warnmsg("Wrong bitmask at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); > - if (crc != je32_to_cpu (node->u.hdr_crc)) { > - warnmsg("Wrong hdr_crc at 0x%08zx, 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: > - bareverbose(verbose, > - "%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", > - obsolete ? "Obsolete" : "", > - p - 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); > - if (crc != je32_to_cpu (node->i.node_crc)) { > - warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); > - if (crc != je32_to_cpu(node->i.data_crc)) { > - warnmsg("Wrong data_crc at 0x%08zx, 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; > - > - bareverbose(verbose, > - "%8s Dirent node at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); > - if (crc != je32_to_cpu (node->d.node_crc)) { > - warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); > - if (crc != je32_to_cpu(node->d.name_crc)) { > - warnmsg("Wrong name_crc at 0x%08zx, 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_XATTR: > - if (je32_to_cpu(node->x.node_crc) == 0xffffffff) > - obsolete = 1; > - bareverbose(verbose, > - "%8s Xdatum node at 0x%08zx, totlen 0x%08x, #xid %5u, version %5u\n", > - obsolete ? "Obsolete" : "", > - p - file_buffer, je32_to_cpu (node->x.totlen), > - je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); > - crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); > - if (crc != je32_to_cpu(node->x.node_crc)) { > - warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", > - p - file_buffer, je32_to_cpu(node->x.node_crc), crc); > - p += PAD(je32_to_cpu (node->x.totlen)); > - continue; > - } > - length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); > - crc = mtd_crc32(0, node->x.data, length); > - if (crc != je32_to_cpu(node->x.data_crc)) { > - warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", > - p - file_buffer, je32_to_cpu(node->x.data_crc), crc); > - p += PAD(je32_to_cpu (node->x.totlen)); > - continue; > - } > - > - write_xattr_to_buff(node); > - p += PAD(je32_to_cpu (node->x.totlen)); > - break; > - > - case JFFS2_NODETYPE_XREF: > - if (je32_to_cpu(node->r.node_crc) == 0xffffffff) > - obsolete = 1; > - bareverbose(verbose, > - "%8s Xref node at 0x%08zx, totlen 0x%08x, #ino %5u, xid %5u\n", > - obsolete ? "Obsolete" : "", > - p - file_buffer, je32_to_cpu(node->r.totlen), > - je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); > - crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); > - if (crc != je32_to_cpu(node->r.node_crc)) { > - warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", > - p - file_buffer, je32_to_cpu(node->r.node_crc), crc); > - p += PAD(je32_to_cpu (node->r.totlen)); > - continue; > - } > - > - write_xref_to_buff(node); > - p += PAD(je32_to_cpu (node->r.totlen)); > - break; > - > - case JFFS2_NODETYPE_CLEANMARKER: > - bareverbose(verbose, > - "%8s Cleanmarker at 0x%08zx, 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: > - bareverbose(verbose, > - "%8s Padding node at 0x%08zx, 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: > - bareverbose(verbose, > - "%8s Unknown node at 0x%08zx, 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, "%s", helptext); > - errmsg_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; > -} > diff --git a/ubifs-utils/mkfs.ubifs/.gitignore b/ubifs-utils/mkfs.ubifs/.gitignore > new file mode 100644 > index 0000000..6b0e85c > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/.gitignore > @@ -0,0 +1 @@ > +/mkfs.ubifs > diff --git a/ubifs-utils/mkfs.ubifs/COPYING b/ubifs-utils/mkfs.ubifs/COPYING > new file mode 100644 > index 0000000..60549be > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/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/ubifs-utils/mkfs.ubifs/README b/ubifs-utils/mkfs.ubifs/README > new file mode 100644 > index 0000000..7e19939 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/README > @@ -0,0 +1,9 @@ > +UBIFS File System - Make File System program > + > +* crc16.h and crc16.c were copied from the linux kernel. > +* crc32.h and crc32.c were copied from mtd-utils and amended. > +* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. > +* key.h is copied from fs/ubifs/key.h from the linux kernel. > +* defs.h is a bunch of definitions to smooth things over. > +* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. > +* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ > diff --git a/ubifs-utils/mkfs.ubifs/compr.c b/ubifs-utils/mkfs.ubifs/compr.c > new file mode 100644 > index 0000000..34b2f60 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/compr.c > @@ -0,0 +1,219 @@ > +/* > + * Copyright (C) 2008 Nokia Corporation. > + * Copyright (C) 2008 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 version 2 as published by > + * the Free Software Foundation. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Artem Bityutskiy > + * Adrian Hunter > + * Zoltan Sogor > + */ > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <stdint.h> > +#include <string.h> > +#include <lzo/lzo1x.h> > +#include <linux/types.h> > + > +#define crc32 __zlib_crc32 > +#include <zlib.h> > +#undef crc32 > + > +#include "compr.h" > +#include "mkfs.ubifs.h" > + > +static void *lzo_mem; > +static unsigned long long errcnt = 0; > +static struct ubifs_info *c = &info_; > + > +#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION > +#define DEFLATE_DEF_WINBITS 11 > +#define DEFLATE_DEF_MEMLEVEL 8 > + > +static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, > + size_t *out_len) > +{ > + z_stream strm; > + > + strm.zalloc = NULL; > + strm.zfree = NULL; > + > + /* > + * Match exactly the zlib parameters used by the Linux kernel crypto > + * API. > + */ > + if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, > + -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, > + Z_DEFAULT_STRATEGY)) { > + errcnt += 1; > + return -1; > + } > + > + strm.next_in = in_buf; > + strm.avail_in = in_len; > + strm.total_in = 0; > + > + strm.next_out = out_buf; > + strm.avail_out = *out_len; > + strm.total_out = 0; > + > + if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { > + deflateEnd(&strm); > + errcnt += 1; > + return -1; > + } > + > + if (deflateEnd(&strm) != Z_OK) { > + errcnt += 1; > + return -1; > + } > + > + *out_len = strm.total_out; > + > + return 0; > +} > + > +static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, > + size_t *out_len) > +{ > + lzo_uint len; > + int ret; > + > + len = *out_len; > + ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); > + *out_len = len; > + > + if (ret != LZO_E_OK) { > + errcnt += 1; > + return -1; > + } > + > + return 0; > +} > + > +static int no_compress(void *in_buf, size_t in_len, void *out_buf, > + size_t *out_len) > +{ > + memcpy(out_buf, in_buf, in_len); > + *out_len = in_len; > + return 0; > +} > + > +static char *zlib_buf; > + > +static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, > + size_t *out_len, int *type) > +{ > + int lzo_ret, zlib_ret; > + size_t lzo_len, zlib_len; > + > + lzo_len = zlib_len = *out_len; > + lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); > + zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); > + > + if (lzo_ret && zlib_ret) > + /* Both compressors failed */ > + return -1; > + > + if (!lzo_ret && !zlib_ret) { > + double percent; > + > + /* Both compressors succeeded */ > + if (lzo_len <= zlib_len ) > + goto select_lzo; > + > + percent = (double)zlib_len / (double)lzo_len; > + percent *= 100; > + if (percent > 100 - c->favor_percent) > + goto select_lzo; > + goto select_zlib; > + } > + > + if (lzo_ret) > + /* Only zlib compressor succeeded */ > + goto select_zlib; > + > + /* Only LZO compressor succeeded */ > + > +select_lzo: > + *out_len = lzo_len; > + *type = MKFS_UBIFS_COMPR_LZO; > + return 0; > + > +select_zlib: > + *out_len = zlib_len; > + *type = MKFS_UBIFS_COMPR_ZLIB; > + memcpy(out_buf, zlib_buf, zlib_len); > + return 0; > +} > + > +int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, > + int type) > +{ > + int ret; > + > + if (in_len < UBIFS_MIN_COMPR_LEN) { > + no_compress(in_buf, in_len, out_buf, out_len); > + return MKFS_UBIFS_COMPR_NONE; > + } > + > + if (c->favor_lzo) > + ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); > + else { > + switch (type) { > + case MKFS_UBIFS_COMPR_LZO: > + ret = lzo_compress(in_buf, in_len, out_buf, out_len); > + break; > + case MKFS_UBIFS_COMPR_ZLIB: > + ret = zlib_deflate(in_buf, in_len, out_buf, out_len); > + break; > + case MKFS_UBIFS_COMPR_NONE: > + ret = 1; > + break; > + default: > + errcnt += 1; > + ret = 1; > + break; > + } > + } > + if (ret || *out_len >= in_len) { > + no_compress(in_buf, in_len, out_buf, out_len); > + return MKFS_UBIFS_COMPR_NONE; > + } > + return type; > +} > + > +int init_compression(void) > +{ > + lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); > + if (!lzo_mem) > + return -1; > + > + zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); > + if (!zlib_buf) { > + free(lzo_mem); > + return -1; > + } > + > + return 0; > +} > + > +void destroy_compression(void) > +{ > + free(zlib_buf); > + free(lzo_mem); > + if (errcnt) > + fprintf(stderr, "%llu compression errors occurred\n", errcnt); > +} > diff --git a/ubifs-utils/mkfs.ubifs/compr.h b/ubifs-utils/mkfs.ubifs/compr.h > new file mode 100644 > index 0000000..e3dd95c > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/compr.h > @@ -0,0 +1,46 @@ > +/* > + * Copyright (C) 2008 Nokia Corporation. > + * Copyright (C) 2008 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 version 2 as published by > + * the Free Software Foundation. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Artem Bityutskiy > + * Adrian Hunter > + * Zoltan Sogor > + */ > + > +#ifndef __UBIFS_COMPRESS_H__ > +#define __UBIFS_COMPRESS_H__ > + > +/* > + * Compressors may end-up with more data in the output buffer than in the input > + * buffer. This constant defined the worst case factor, i.e. we assume that the > + * output buffer may be at max. WORST_COMPR_FACTOR times larger than input > + * buffer. > + */ > +#define WORST_COMPR_FACTOR 4 > + > +enum compression_type > +{ > + MKFS_UBIFS_COMPR_NONE, > + MKFS_UBIFS_COMPR_LZO, > + MKFS_UBIFS_COMPR_ZLIB, > +}; > + > +int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, > + int type); > +int init_compression(void); > +void destroy_compression(void); > + > +#endif > diff --git a/ubifs-utils/mkfs.ubifs/crc16.c b/ubifs-utils/mkfs.ubifs/crc16.c > new file mode 100644 > index 0000000..a19512e > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/crc16.c > @@ -0,0 +1,56 @@ > +/* > + * This code was taken from the linux kernel. The license is GPL Version 2. > + */ > + > +#include "crc16.h" > + > +/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ > +uint16_t const crc16_table[256] = { > + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, > + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, > + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, > + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, > + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, > + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, > + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, > + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, > + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, > + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, > + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, > + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, > + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, > + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, > + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, > + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, > + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, > + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, > + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, > + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, > + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, > + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, > + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, > + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, > + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, > + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, > + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, > + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, > + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, > + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, > + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, > + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 > +}; > + > +/** > + * crc16 - compute the CRC-16 for the data buffer > + * @crc: previous CRC value > + * @buffer: data pointer > + * @len: number of bytes in the buffer > + * > + * Returns the updated CRC value. > + */ > +uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) > +{ > + while (len--) > + crc = crc16_byte(crc, *buffer++); > + return crc; > +} > diff --git a/ubifs-utils/mkfs.ubifs/crc16.h b/ubifs-utils/mkfs.ubifs/crc16.h > new file mode 100644 > index 0000000..539d21a > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/crc16.h > @@ -0,0 +1,27 @@ > +/* > + * Implements the standard CRC-16: > + * Width 16 > + * Poly 0x8005 (x^16 + x^15 + x^2 + 1) > + * Init 0 > + * > + * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> > + * > + * This code was taken from the linux kernel. The license is GPL Version 2. > + */ > + > +#ifndef __CRC16_H__ > +#define __CRC16_H__ > + > +#include <stdlib.h> > +#include <stdint.h> > + > +extern uint16_t const crc16_table[256]; > + > +extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len); > + > +static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data) > +{ > + return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; > +} > + > +#endif /* __CRC16_H__ */ > diff --git a/ubifs-utils/mkfs.ubifs/defs.h b/ubifs-utils/mkfs.ubifs/defs.h > new file mode 100644 > index 0000000..1fa3316 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/defs.h > @@ -0,0 +1,92 @@ > +/* > + * Greate deal of the code was taken from the kernel UBIFS implementation, and > + * this file contains some "glue" definitions. > + */ > + > +#ifndef __UBIFS_DEFS_H__ > +#define __UBIFS_DEFS_H__ > + > +#define t16(x) ({ \ > + uint16_t __b = (x); \ > + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ > +}) > + > +#define t32(x) ({ \ > + uint32_t __b = (x); \ > + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ > +}) > + > +#define t64(x) ({ \ > + uint64_t __b = (x); \ > + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ > +}) > + > +#define cpu_to_le16(x) ((__le16){t16(x)}) > +#define cpu_to_le32(x) ((__le32){t32(x)}) > +#define cpu_to_le64(x) ((__le64){t64(x)}) > + > +#define le16_to_cpu(x) (t16((x))) > +#define le32_to_cpu(x) (t32((x))) > +#define le64_to_cpu(x) (t64((x))) > + > +#define unlikely(x) (x) > + > +#define ubifs_assert(x) ({}) > + > +struct qstr > +{ > + char *name; > + size_t len; > +}; > + > +/** > + * fls - find last (most-significant) bit set > + * @x: the word to search > + * > + * This is defined the same way as ffs. > + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. > + */ > +static inline int fls(int x) > +{ > + int r = 32; > + > + if (!x) > + return 0; > + if (!(x & 0xffff0000u)) { > + x <<= 16; > + r -= 16; > + } > + if (!(x & 0xff000000u)) { > + x <<= 8; > + r -= 8; > + } > + if (!(x & 0xf0000000u)) { > + x <<= 4; > + r -= 4; > + } > + if (!(x & 0xc0000000u)) { > + x <<= 2; > + r -= 2; > + } > + if (!(x & 0x80000000u)) { > + x <<= 1; > + r -= 1; > + } > + return r; > +} > + > +#define do_div(n,base) ({ \ > +int __res; \ > +__res = ((unsigned long) n) % (unsigned) base; \ > +n = ((unsigned long) n) / (unsigned) base; \ > +__res; }) > + > +#if INT_MAX != 0x7fffffff > +#error : sizeof(int) must be 4 for this program > +#endif > + > +#if (~0ULL) != 0xffffffffffffffffULL > +#error : sizeof(long long) must be 8 for this program > +#endif > + > +#endif > diff --git a/ubifs-utils/mkfs.ubifs/devtable.c b/ubifs-utils/mkfs.ubifs/devtable.c > new file mode 100644 > index 0000000..dee035d > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/devtable.c > @@ -0,0 +1,524 @@ > +/* > + * Copyright (C) 2008 Nokia Corporation. > + * > + * 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. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Author: Artem Bityutskiy > + * > + * Part of the device table parsing code was taken from the mkfs.jffs2 utility. > + * The original author of that code is Erik Andersen, hence: > + * Copyright (C) 2001, 2002 Erik Andersen <andersen@codepoet.org> > + */ > + > +/* > + * This file implemented device table support. 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) > + * > + * 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. > + * > + * Please, refer the device_table.txt file which can be found at MTD utilities > + * for more information about what the device table is. > + */ > + > +#include "mkfs.ubifs.h" > +#include "hashtable/hashtable.h" > +#include "hashtable/hashtable_itr.h" > + > +/* > + * The hash table which contains paths to files/directories/device nodes > + * referred to in the device table. For example, if the device table refers > + * "/dev/loop0", the @path_htbl will contain "/dev" element. > + */ > +static struct hashtable *path_htbl; > + > +/* Hash function used for hash tables */ > +static unsigned int r5_hash(void *s) > +{ > + unsigned int a = 0; > + const signed char *str = s; > + > + while (*str) { > + a += *str << 4; > + a += *str >> 4; > + a *= 11; > + str++; > + } > + > + return a; > +} > + > +/* > + * Check whether 2 keys of a hash table are equivalent. The keys are path/file > + * names, so we simply use 'strcmp()'. > + */ > +static int is_equivalent(void *k1, void *k2) > +{ > + return !strcmp(k1, k2); > +} > + > +/** > + * separate_last - separate out the last path component > + * @buf: the path to split > + * @len: length of the @buf string > + * @path: the beginning of path is returned here > + * @name: the last path component is returned here > + * > + * This helper function separates out the the last component of the full path > + * string. For example, "/dev/loop" would be split on "/dev" and "loop". This > + * function allocates memory for @path and @name and return the result there. > + * Returns zero in case of success and a negative error code in case of > + * failure. > + */ > +static int separate_last(const char *buf, int len, char **path, char **name) > +{ > + int path_len = len, name_len; > + const char *p = buf + len, *n; > + > + while (*--p != '/') > + path_len -= 1; > + > + /* Drop the final '/' unless this is the root directory */ > + name_len = len - path_len; > + n = buf + path_len; > + if (path_len > 1) > + path_len -= 1; > + > + *path = malloc(path_len + 1); > + if (!*path) > + return err_msg("cannot allocate %d bytes of memory", > + path_len + 1); > + memcpy(*path, buf, path_len); > + (*path)[path_len] = '\0'; > + > + *name = malloc(name_len + 1); > + if (!*name) { > + free(*path); > + return err_msg("cannot allocate %d bytes of memory", > + name_len + 1); > + } > + memcpy(*name, n, name_len + 1); > + > + return 0; > +} > + > +static int interpret_table_entry(const char *line) > +{ > + char buf[1024], type, *path = NULL, *name = NULL; > + int len; > + struct path_htbl_element *ph_elt = NULL; > + struct name_htbl_element *nh_elt = NULL; > + unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; > + unsigned int start = 0, increment = 0, count = 0; > + > + if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u", > + buf, &type, &mode, &uid, &gid, &major, &minor, > + &start, &increment, &count) < 0) > + return sys_err_msg("sscanf failed"); > + > + dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, " > + "minor %u, start %u, inc %u, cnt %u", > + buf, type, mode, uid, gid, major, minor, start, > + increment, count); > + > + len = strnlen(buf, 1024); > + if (len == 1024) > + return err_msg("too long path"); > + > + if (!strcmp(buf, "/")) > + return err_msg("device table entries require absolute paths"); > + if (buf[1] == '\0') > + return err_msg("root directory cannot be created"); > + if (strstr(buf, "//")) > + return err_msg("'//' cannot be used in the path"); > + if (buf[len - 1] == '/') > + return err_msg("do not put '/' at the end"); > + > + if (strstr(buf, "/./") || strstr(buf, "/../") || > + !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/..")) > + return err_msg("'.' and '..' cannot be used in the path"); > + > + 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: > + return err_msg("unsupported file type '%c'", type); > + } > + > + if (separate_last(buf, len, &path, &name)) > + return -1; > + > + /* > + * Check if this path already exist in the path hash table and add it > + * if it is not. > + */ > + ph_elt = hashtable_search(path_htbl, path); > + if (!ph_elt) { > + dbg_msg(3, "inserting '%s' into path hash table", path); > + ph_elt = malloc(sizeof(struct path_htbl_element)); > + if (!ph_elt) { > + err_msg("cannot allocate %zd bytes of memory", > + sizeof(struct path_htbl_element)); > + goto out_free; > + } > + > + if (!hashtable_insert(path_htbl, path, ph_elt)) { > + err_msg("cannot insert into path hash table"); > + goto out_free; > + } > + > + ph_elt->path = path; > + path = NULL; > + ph_elt->name_htbl = create_hashtable(128, &r5_hash, > + &is_equivalent); > + if (!ph_elt->name_htbl) { > + err_msg("cannot create name hash table"); > + goto out_free; > + } > + } > + > + if (increment != 0 && count == 0) > + return err_msg("count cannot be zero if increment is non-zero"); > + > + /* > + * Add the file/directory/device node (last component of the path) to > + * the name hashtable. The name hashtable resides in the corresponding > + * path hashtable element. > + */ > + > + if (count == 0) { > + /* This entry does not require any iterating */ > + nh_elt = malloc(sizeof(struct name_htbl_element)); > + if (!nh_elt) { > + err_msg("cannot allocate %zd bytes of memory", > + sizeof(struct name_htbl_element)); > + goto out_free; > + } > + > + nh_elt->mode = mode; > + nh_elt->uid = uid; > + nh_elt->gid = gid; > + nh_elt->dev = makedev(major, minor); > + > + dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", > + name, major(nh_elt->dev), minor(nh_elt->dev)); > + > + if (hashtable_search(ph_elt->name_htbl, name)) > + return err_msg("'%s' is referred twice", buf); > + > + nh_elt->name = name; > + if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) { > + err_msg("cannot insert into name hash table"); > + goto out_free; > + } > + } else { > + int i, num = start + count, len = strlen(name) + 20; > + char *nm; > + > + for (i = start; i < num; i++) { > + nh_elt = malloc(sizeof(struct name_htbl_element)); > + if (!nh_elt) { > + err_msg("cannot allocate %zd bytes of memory", > + sizeof(struct name_htbl_element)); > + goto out_free; > + } > + > + nh_elt->mode = mode; > + nh_elt->uid = uid; > + nh_elt->gid = gid; > + nh_elt->dev = makedev(major, minor + (i - start) * increment); > + > + nm = malloc(len); > + if (!nm) { > + err_msg("cannot allocate %d bytes of memory", len); > + goto out_free; > + } > + > + sprintf(nm, "%s%d", name, i); > + nh_elt->name = nm; > + > + dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", > + nm, major(nh_elt->dev), minor(nh_elt->dev)); > + > + if (hashtable_search(ph_elt->name_htbl, nm)) { > + err_msg("'%s' is referred twice", buf); > + free (nm); > + goto out_free; > + } > + > + if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { > + err_msg("cannot insert into name hash table"); > + free (nm); > + goto out_free; > + } > + } > + free(name); > + name = NULL; > + } > + > + return 0; > + > +out_free: > + free(ph_elt); > + free(nh_elt); > + free(path); > + free(name); > + return -1; > +} > + > +/** > + * parse_devtable - parse the device table. > + * @tbl_file: device table file name > + * > + * This function parses the device table and prepare the hash table which will > + * later be used by mkfs.ubifs to create the specified files/device nodes. > + * Returns zero in case of success and a negative error code in case of > + * failure. > + */ > +int parse_devtable(const char *tbl_file) > +{ > + FILE *f; > + char *line = NULL; > + struct stat st; > + size_t len; > + > + dbg_msg(1, "parsing device table file '%s'", tbl_file); > + > + path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); > + if (!path_htbl) > + return err_msg("cannot create path hash table"); > + > + f = fopen(tbl_file, "r"); > + if (!f) > + return sys_err_msg("cannot open '%s'", tbl_file); > + > + if (fstat(fileno(f), &st) < 0) { > + sys_err_msg("cannot stat '%s'", tbl_file); > + goto out_close; > + } > + > + if (st.st_size < 10) { > + sys_err_msg("'%s' is too short", tbl_file); > + goto out_close; > + } > + > + /* > + * 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 > + */ > + while (getline(&line, &len, f) != -1) { > + /* First trim off any white-space */ > + len = strlen(line); > + > + /* Trim trailing white-space */ > + while (len > 0 && isspace(line[len - 1])) > + line[--len] = '\0'; > + /* Trim leading white-space */ > + 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(line)) { > + err_msg("cannot parse '%s'", line); > + goto out_close; > + } > + } > + > + free(line); > + line = NULL; > + } > + > + dbg_msg(1, "finished parsing"); > + fclose(f); > + return 0; > + > +out_close: > + fclose(f); > + free_devtable_info(); > + return -1; > +} > + > +/** > + * devtbl_find_path - find a path in the path hash table. > + * @path: UBIFS path to find. > + * > + * This looks up the path hash table. Returns the path hash table element > + * reference if @path was found and %NULL if not. > + */ > +struct path_htbl_element *devtbl_find_path(const char *path) > +{ > + if (!path_htbl) > + return NULL; > + > + return hashtable_search(path_htbl, (void *)path); > +} > + > +/** > + * devtbl_find_name - find a name in the name hash table. > + * @ph_etl: path hash table element to find at > + * @name: name to find > + * > + * This looks up the name hash table. Returns the name hash table element > + * reference if @name found and %NULL if not. > + */ > +struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, > + const char *name) > +{ > + if (!path_htbl) > + return NULL; > + > + return hashtable_search(ph_elt->name_htbl, (void *)name); > +} > + > +/** > + * override_attributes - override inode attributes. > + * @st: struct stat object to containing the attributes to override > + * @ph_elt: path hash table element object > + * @nh_elt: name hash table element object containing the new values > + * > + * The device table file may override attributes like UID of files. For > + * example, the device table may contain a "/dev" entry, and the UBIFS FS on > + * the host may contain "/dev" directory. In this case the attributes of the > + * "/dev" directory inode has to be as the device table specifies. > + * > + * Note, the hash element is removed by this function as well. > + */ > +int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, > + struct name_htbl_element *nh_elt) > +{ > + if (!path_htbl) > + return 0; > + > + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || > + S_ISFIFO(st->st_mode)) > + return err_msg("%s/%s both exists at UBIFS root at host, " > + "and is referred from the device table", > + strcmp(ph_elt->path, "/") ? ph_elt->path : "", > + nh_elt->name); > + > + if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) > + return err_msg("%s/%s is referred from the device table also exists in " > + "the UBIFS root directory at host, but the file type is " > + "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", > + nh_elt->name); > + > + dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", > + nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); > + > + st->st_uid = nh_elt->uid; > + st->st_gid = nh_elt->gid; > + st->st_mode = nh_elt->mode; > + > + hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); > + return 0; > +} > + > +/** > + * first_name_htbl_element - return first element of the name hash table. > + * @ph_elt: the path hash table the name hash table belongs to > + * @itr: double pointer to a 'struct hashtable_itr' object where the > + * information about further iterations is stored > + * > + * This function implements name hash table iteration together with > + * 'next_name_htbl_element()'. Returns the first name hash table element or > + * %NULL if the hash table is empty. > + */ > +struct name_htbl_element * > +first_name_htbl_element(struct path_htbl_element *ph_elt, > + struct hashtable_itr **itr) > +{ > + if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) > + return NULL; > + > + *itr = hashtable_iterator(ph_elt->name_htbl); > + return hashtable_iterator_value(*itr); > +} > + > +/** > + * first_name_htbl_element - return next element of the name hash table. > + * @ph_elt: the path hash table the name hash table belongs to > + * @itr: double pointer to a 'struct hashtable_itr' object where the > + * information about further iterations is stored > + * > + * This function implements name hash table iteration together with > + * 'first_name_htbl_element()'. Returns the next name hash table element or > + * %NULL if there are no more elements. > + */ > +struct name_htbl_element * > +next_name_htbl_element(struct path_htbl_element *ph_elt, > + struct hashtable_itr **itr) > +{ > + if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) > + return NULL; > + > + return hashtable_iterator_value(*itr); > +} > + > +/** > + * free_devtable_info - free device table information. > + * > + * This function frees the path hash table and the name hash tables. > + */ > +void free_devtable_info(void) > +{ > + struct hashtable_itr *ph_itr; > + struct path_htbl_element *ph_elt; > + > + if (!path_htbl) > + return; > + > + if (hashtable_count(path_htbl) > 0) { > + ph_itr = hashtable_iterator(path_htbl); > + do { > + ph_elt = hashtable_iterator_value(ph_itr); > + /* > + * Note, since we use the same string for the key and > + * @name in the name hash table elements, we do not > + * have to iterate name hash table because @name memory > + * will be freed when freeing the key. > + */ > + hashtable_destroy(ph_elt->name_htbl, 1); > + } while (hashtable_iterator_advance(ph_itr)); > + } > + hashtable_destroy(path_htbl, 1); > +} > diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c > new file mode 100644 > index 0000000..c1f99ed > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c > @@ -0,0 +1,277 @@ > +/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > + > +#define PROGRAM_NAME "hashtable" > + > +#include "common.h" > +#include "hashtable.h" > +#include "hashtable_private.h" > +#include <stdlib.h> > +#include <stdio.h> > +#include <string.h> > +#include <math.h> > + > +/* > +Credit for primes table: Aaron Krowne > + http://br.endernet.org/~akrowne/ > + http://planetmath.org/encyclopedia/GoodHashTablePrimes.html > +*/ > +static const unsigned int primes[] = { > +53, 97, 193, 389, > +769, 1543, 3079, 6151, > +12289, 24593, 49157, 98317, > +196613, 393241, 786433, 1572869, > +3145739, 6291469, 12582917, 25165843, > +50331653, 100663319, 201326611, 402653189, > +805306457, 1610612741 > +}; > +const unsigned int prime_table_length = ARRAY_SIZE(primes); > +const float max_load_factor = 0.65; > + > +/*****************************************************************************/ > +struct hashtable * > +create_hashtable(unsigned int minsize, > + unsigned int (*hashf) (void*), > + int (*eqf) (void*,void*)) > +{ > + struct hashtable *h; > + unsigned int pindex, size = primes[0]; > + /* Check requested hashtable isn't too large */ > + if (minsize > (1u << 30)) return NULL; > + /* Enforce size as prime */ > + for (pindex=0; pindex < prime_table_length; pindex++) { > + if (primes[pindex] > minsize) { size = primes[pindex]; break; } > + } > + h = (struct hashtable *)malloc(sizeof(struct hashtable)); > + if (NULL == h) return NULL; /*oom*/ > + h->table = (struct entry **)malloc(sizeof(struct entry*) * size); > + if (NULL == h->table) { free(h); return NULL; } /*oom*/ > + memset(h->table, 0, size * sizeof(struct entry *)); > + h->tablelength = size; > + h->primeindex = pindex; > + h->entrycount = 0; > + h->hashfn = hashf; > + h->eqfn = eqf; > + h->loadlimit = (unsigned int) ceil(size * max_load_factor); > + return h; > +} > + > +/*****************************************************************************/ > +unsigned int > +hash(struct hashtable *h, void *k) > +{ > + /* Aim to protect against poor hash functions by adding logic here > + * - logic taken from java 1.4 hashtable source */ > + unsigned int i = h->hashfn(k); > + i += ~(i << 9); > + i ^= ((i >> 14) | (i << 18)); /* >>> */ > + i += (i << 4); > + i ^= ((i >> 10) | (i << 22)); /* >>> */ > + return i; > +} > + > +/*****************************************************************************/ > +static int > +hashtable_expand(struct hashtable *h) > +{ > + /* Double the size of the table to accomodate more entries */ > + struct entry **newtable; > + struct entry *e; > + struct entry **pE; > + unsigned int newsize, i, index; > + /* Check we're not hitting max capacity */ > + if (h->primeindex == (prime_table_length - 1)) return 0; > + newsize = primes[++(h->primeindex)]; > + > + newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); > + if (NULL != newtable) > + { > + memset(newtable, 0, newsize * sizeof(struct entry *)); > + /* This algorithm is not 'stable'. ie. it reverses the list > + * when it transfers entries between the tables */ > + for (i = 0; i < h->tablelength; i++) { > + while (NULL != (e = h->table[i])) { > + h->table[i] = e->next; > + index = indexFor(newsize,e->h); > + e->next = newtable[index]; > + newtable[index] = e; > + } > + } > + free(h->table); > + h->table = newtable; > + } > + /* Plan B: realloc instead */ > + else > + { > + newtable = (struct entry **) > + realloc(h->table, newsize * sizeof(struct entry *)); > + if (NULL == newtable) { (h->primeindex)--; return 0; } > + h->table = newtable; > + memset(newtable[h->tablelength], 0, newsize - h->tablelength); > + for (i = 0; i < h->tablelength; i++) { > + for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { > + index = indexFor(newsize,e->h); > + if (index == i) > + { > + pE = &(e->next); > + } > + else > + { > + *pE = e->next; > + e->next = newtable[index]; > + newtable[index] = e; > + } > + } > + } > + } > + h->tablelength = newsize; > + h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); > + return -1; > +} > + > +/*****************************************************************************/ > +unsigned int > +hashtable_count(struct hashtable *h) > +{ > + return h->entrycount; > +} > + > +/*****************************************************************************/ > +int > +hashtable_insert(struct hashtable *h, void *k, void *v) > +{ > + /* This method allows duplicate keys - but they shouldn't be used */ > + unsigned int index; > + struct entry *e; > + if (++(h->entrycount) > h->loadlimit) > + { > + /* Ignore the return value. If expand fails, we should > + * still try cramming just this value into the existing table > + * -- we may not have memory for a larger table, but one more > + * element may be ok. Next time we insert, we'll try expanding again.*/ > + hashtable_expand(h); > + } > + e = (struct entry *)malloc(sizeof(struct entry)); > + if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ > + e->h = hash(h,k); > + index = indexFor(h->tablelength,e->h); > + e->k = k; > + e->v = v; > + e->next = h->table[index]; > + h->table[index] = e; > + return -1; > +} > + > +/*****************************************************************************/ > +void * /* returns value associated with key */ > +hashtable_search(struct hashtable *h, void *k) > +{ > + struct entry *e; > + unsigned int hashvalue, index; > + hashvalue = hash(h,k); > + index = indexFor(h->tablelength,hashvalue); > + e = h->table[index]; > + while (NULL != e) > + { > + /* Check hash value to short circuit heavier comparison */ > + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; > + e = e->next; > + } > + return NULL; > +} > + > +/*****************************************************************************/ > +void * /* returns value associated with key */ > +hashtable_remove(struct hashtable *h, void *k) > +{ > + /* TODO: consider compacting the table when the load factor drops enough, > + * or provide a 'compact' method. */ > + > + struct entry *e; > + struct entry **pE; > + void *v; > + unsigned int hashvalue, index; > + > + hashvalue = hash(h,k); > + index = indexFor(h->tablelength,hash(h,k)); > + pE = &(h->table[index]); > + e = *pE; > + while (NULL != e) > + { > + /* Check hash value to short circuit heavier comparison */ > + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) > + { > + *pE = e->next; > + h->entrycount--; > + v = e->v; > + freekey(e->k); > + free(e); > + return v; > + } > + pE = &(e->next); > + e = e->next; > + } > + return NULL; > +} > + > +/*****************************************************************************/ > +/* destroy */ > +void > +hashtable_destroy(struct hashtable *h, int free_values) > +{ > + unsigned int i; > + struct entry *e, *f; > + struct entry **table = h->table; > + if (free_values) > + { > + for (i = 0; i < h->tablelength; i++) > + { > + e = table[i]; > + while (NULL != e) > + { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } > + } > + } > + else > + { > + for (i = 0; i < h->tablelength; i++) > + { > + e = table[i]; > + while (NULL != e) > + { f = e; e = e->next; freekey(f->k); free(f); } > + } > + } > + free(h->table); > + free(h); > +} > + > +/* > + * Copyright (c) 2002, Christopher Clark > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * > + * * 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. > + * > + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > + * 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. > +*/ > diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h > new file mode 100644 > index 0000000..c0b0acd > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h > @@ -0,0 +1,199 @@ > +/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > + > +#ifndef __HASHTABLE_CWC22_H__ > +#define __HASHTABLE_CWC22_H__ > + > +struct hashtable; > + > +/* Example of use: > + * > + * struct hashtable *h; > + * struct some_key *k; > + * struct some_value *v; > + * > + * static unsigned int hash_from_key_fn( void *k ); > + * static int keys_equal_fn ( void *key1, void *key2 ); > + * > + * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); > + * k = (struct some_key *) malloc(sizeof(struct some_key)); > + * v = (struct some_value *) malloc(sizeof(struct some_value)); > + * > + * (initialise k and v to suitable values) > + * > + * if (! hashtable_insert(h,k,v) ) > + * { exit(-1); } > + * > + * if (NULL == (found = hashtable_search(h,k) )) > + * { printf("not found!"); } > + * > + * if (NULL == (found = hashtable_remove(h,k) )) > + * { printf("Not found\n"); } > + * > + */ > + > +/* Macros may be used to define type-safe(r) hashtable access functions, with > + * methods specialized to take known key and value types as parameters. > + * > + * Example: > + * > + * Insert this at the start of your file: > + * > + * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); > + * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); > + * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); > + * > + * This defines the functions 'insert_some', 'search_some' and 'remove_some'. > + * These operate just like hashtable_insert etc., with the same parameters, > + * but their function signatures have 'struct some_key *' rather than > + * 'void *', and hence can generate compile time errors if your program is > + * supplying incorrect data as a key (and similarly for value). > + * > + * Note that the hash and key equality functions passed to create_hashtable > + * still take 'void *' parameters instead of 'some key *'. This shouldn't be > + * a difficult issue as they're only defined and passed once, and the other > + * functions will ensure that only valid keys are supplied to them. > + * > + * The cost for this checking is increased code size and runtime overhead > + * - if performance is important, it may be worth switching back to the > + * unsafe methods once your program has been debugged with the safe methods. > + * This just requires switching to some simple alternative defines - eg: > + * #define insert_some hashtable_insert > + * > + */ > + > +/***************************************************************************** > + * create_hashtable > + > + * @name create_hashtable > + * @param minsize minimum initial size of hashtable > + * @param hashfunction function for hashing keys > + * @param key_eq_fn function for determining key equality > + * @return newly created hashtable or NULL on failure > + */ > + > +struct hashtable * > +create_hashtable(unsigned int minsize, > + unsigned int (*hashfunction) (void*), > + int (*key_eq_fn) (void*,void*)); > + > +/***************************************************************************** > + * hashtable_insert > + > + * @name hashtable_insert > + * @param h the hashtable to insert into > + * @param k the key - hashtable claims ownership and will free on removal > + * @param v the value - does not claim ownership > + * @return non-zero for successful insertion > + * > + * This function will cause the table to expand if the insertion would take > + * the ratio of entries to table size over the maximum load factor. > + * > + * This function does not check for repeated insertions with a duplicate key. > + * The value returned when using a duplicate key is undefined -- when > + * the hashtable changes size, the order of retrieval of duplicate key > + * entries is reversed. > + * If in doubt, remove before insert. > + */ > + > +int > +hashtable_insert(struct hashtable *h, void *k, void *v); > + > +#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ > +int fnname (struct hashtable *h, keytype *k, valuetype *v) \ > +{ \ > + return hashtable_insert(h,k,v); \ > +} > + > +/***************************************************************************** > + * hashtable_search > + > + * @name hashtable_search > + * @param h the hashtable to search > + * @param k the key to search for - does not claim ownership > + * @return the value associated with the key, or NULL if none found > + */ > + > +void * > +hashtable_search(struct hashtable *h, void *k); > + > +#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ > +valuetype * fnname (struct hashtable *h, keytype *k) \ > +{ \ > + return (valuetype *) (hashtable_search(h,k)); \ > +} > + > +/***************************************************************************** > + * hashtable_remove > + > + * @name hashtable_remove > + * @param h the hashtable to remove the item from > + * @param k the key to search for - does not claim ownership > + * @return the value associated with the key, or NULL if none found > + */ > + > +void * /* returns value */ > +hashtable_remove(struct hashtable *h, void *k); > + > +#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ > +valuetype * fnname (struct hashtable *h, keytype *k) \ > +{ \ > + return (valuetype *) (hashtable_remove(h,k)); \ > +} > + > + > +/***************************************************************************** > + * hashtable_count > + > + * @name hashtable_count > + * @param h the hashtable > + * @return the number of items stored in the hashtable > + */ > +unsigned int > +hashtable_count(struct hashtable *h); > + > + > +/***************************************************************************** > + * hashtable_destroy > + > + * @name hashtable_destroy > + * @param h the hashtable > + * @param free_values whether to call 'free' on the remaining values > + */ > + > +void > +hashtable_destroy(struct hashtable *h, int free_values); > + > +#endif /* __HASHTABLE_CWC22_H__ */ > + > +/* > + * Copyright (c) 2002, Christopher Clark > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * > + * * 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. > + * > + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > + * 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. > +*/ > diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c > new file mode 100644 > index 0000000..d102453 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c > @@ -0,0 +1,176 @@ > +/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > + > +#include "hashtable.h" > +#include "hashtable_private.h" > +#include "hashtable_itr.h" > +#include <stdlib.h> /* defines NULL */ > + > +/*****************************************************************************/ > +/* hashtable_iterator - iterator constructor */ > + > +struct hashtable_itr * > +hashtable_iterator(struct hashtable *h) > +{ > + unsigned int i, tablelength; > + struct hashtable_itr *itr = (struct hashtable_itr *) > + malloc(sizeof(struct hashtable_itr)); > + if (NULL == itr) return NULL; > + itr->h = h; > + itr->e = NULL; > + itr->parent = NULL; > + tablelength = h->tablelength; > + itr->index = tablelength; > + if (0 == h->entrycount) return itr; > + > + for (i = 0; i < tablelength; i++) > + { > + if (NULL != h->table[i]) > + { > + itr->e = h->table[i]; > + itr->index = i; > + break; > + } > + } > + return itr; > +} > + > +/*****************************************************************************/ > +/* advance - advance the iterator to the next element > + * returns zero if advanced to end of table */ > + > +int > +hashtable_iterator_advance(struct hashtable_itr *itr) > +{ > + unsigned int j,tablelength; > + struct entry **table; > + struct entry *next; > + if (NULL == itr->e) return 0; /* stupidity check */ > + > + next = itr->e->next; > + if (NULL != next) > + { > + itr->parent = itr->e; > + itr->e = next; > + return -1; > + } > + tablelength = itr->h->tablelength; > + itr->parent = NULL; > + if (tablelength <= (j = ++(itr->index))) > + { > + itr->e = NULL; > + return 0; > + } > + table = itr->h->table; > + while (NULL == (next = table[j])) > + { > + if (++j >= tablelength) > + { > + itr->index = tablelength; > + itr->e = NULL; > + return 0; > + } > + } > + itr->index = j; > + itr->e = next; > + return -1; > +} > + > +/*****************************************************************************/ > +/* remove - remove the entry at the current iterator position > + * and advance the iterator, if there is a successive > + * element. > + * If you want the value, read it before you remove: > + * beware memory leaks if you don't. > + * Returns zero if end of iteration. */ > + > +int > +hashtable_iterator_remove(struct hashtable_itr *itr) > +{ > + struct entry *remember_e, *remember_parent; > + int ret; > + > + /* Do the removal */ > + if (NULL == (itr->parent)) > + { > + /* element is head of a chain */ > + itr->h->table[itr->index] = itr->e->next; > + } else { > + /* element is mid-chain */ > + itr->parent->next = itr->e->next; > + } > + /* itr->e is now outside the hashtable */ > + remember_e = itr->e; > + itr->h->entrycount--; > + freekey(remember_e->k); > + > + /* Advance the iterator, correcting the parent */ > + remember_parent = itr->parent; > + ret = hashtable_iterator_advance(itr); > + if (itr->parent == remember_e) { itr->parent = remember_parent; } > + free(remember_e); > + return ret; > +} > + > +/*****************************************************************************/ > +int /* returns zero if not found */ > +hashtable_iterator_search(struct hashtable_itr *itr, > + struct hashtable *h, void *k) > +{ > + struct entry *e, *parent; > + unsigned int hashvalue, index; > + > + hashvalue = hash(h,k); > + index = indexFor(h->tablelength,hashvalue); > + > + e = h->table[index]; > + parent = NULL; > + while (NULL != e) > + { > + /* Check hash value to short circuit heavier comparison */ > + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) > + { > + itr->index = index; > + itr->e = e; > + itr->parent = parent; > + itr->h = h; > + return -1; > + } > + parent = e; > + e = e->next; > + } > + return 0; > +} > + > + > +/* > + * Copyright (c) 2002, 2004, Christopher Clark > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * > + * * 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. > + * > + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > + * 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. > +*/ > diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h > new file mode 100644 > index 0000000..5c94a04 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h > @@ -0,0 +1,112 @@ > +/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > + > +#ifndef __HASHTABLE_ITR_CWC22__ > +#define __HASHTABLE_ITR_CWC22__ > +#include "hashtable.h" > +#include "hashtable_private.h" /* needed to enable inlining */ > + > +/*****************************************************************************/ > +/* This struct is only concrete here to allow the inlining of two of the > + * accessor functions. */ > +struct hashtable_itr > +{ > + struct hashtable *h; > + struct entry *e; > + struct entry *parent; > + unsigned int index; > +}; > + > + > +/*****************************************************************************/ > +/* hashtable_iterator > + */ > + > +struct hashtable_itr * > +hashtable_iterator(struct hashtable *h); > + > +/*****************************************************************************/ > +/* hashtable_iterator_key > + * - return the value of the (key,value) pair at the current position */ > + > +static inline void * > +hashtable_iterator_key(struct hashtable_itr *i) > +{ > + return i->e->k; > +} > + > +/*****************************************************************************/ > +/* value - return the value of the (key,value) pair at the current position */ > + > +static inline void * > +hashtable_iterator_value(struct hashtable_itr *i) > +{ > + return i->e->v; > +} > + > +/*****************************************************************************/ > +/* advance - advance the iterator to the next element > + * returns zero if advanced to end of table */ > + > +int > +hashtable_iterator_advance(struct hashtable_itr *itr); > + > +/*****************************************************************************/ > +/* remove - remove current element and advance the iterator to the next element > + * NB: if you need the value to free it, read it before > + * removing. ie: beware memory leaks! > + * returns zero if advanced to end of table */ > + > +int > +hashtable_iterator_remove(struct hashtable_itr *itr); > + > +/*****************************************************************************/ > +/* search - overwrite the supplied iterator, to point to the entry > + * matching the supplied key. > + h points to the hashtable to be searched. > + * returns zero if not found. */ > +int > +hashtable_iterator_search(struct hashtable_itr *itr, > + struct hashtable *h, void *k); > + > +#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ > +int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ > +{ \ > + return (hashtable_iterator_search(i,h,k)); \ > +} > + > + > + > +#endif /* __HASHTABLE_ITR_CWC22__*/ > + > +/* > + * Copyright (c) 2002, 2004, Christopher Clark > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * > + * * 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. > + * > + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > + * 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. > +*/ > diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h > new file mode 100644 > index 0000000..3a558e6 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h > @@ -0,0 +1,85 @@ > +/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ > + > +#ifndef __HASHTABLE_PRIVATE_CWC22_H__ > +#define __HASHTABLE_PRIVATE_CWC22_H__ > + > +#include "hashtable.h" > + > +/*****************************************************************************/ > +struct entry > +{ > + void *k, *v; > + unsigned int h; > + struct entry *next; > +}; > + > +struct hashtable { > + unsigned int tablelength; > + struct entry **table; > + unsigned int entrycount; > + unsigned int loadlimit; > + unsigned int primeindex; > + unsigned int (*hashfn) (void *k); > + int (*eqfn) (void *k1, void *k2); > +}; > + > +/*****************************************************************************/ > +unsigned int > +hash(struct hashtable *h, void *k); > + > +/*****************************************************************************/ > +/* indexFor */ > +static inline unsigned int > +indexFor(unsigned int tablelength, unsigned int hashvalue) { > + return (hashvalue % tablelength); > +}; > + > +/* Only works if tablelength == 2^N */ > +/*static inline unsigned int > +indexFor(unsigned int tablelength, unsigned int hashvalue) > +{ > + return (hashvalue & (tablelength - 1u)); > +} > +*/ > + > +/*****************************************************************************/ > +#define freekey(X) free(X) > +/*define freekey(X) ; */ > + > + > +/*****************************************************************************/ > + > +#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ > + > +/* > + * Copyright (c) 2002, Christopher Clark > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * > + * * 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. > + * > + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER > + * 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. > +*/ > diff --git a/ubifs-utils/mkfs.ubifs/key.h b/ubifs-utils/mkfs.ubifs/key.h > new file mode 100644 > index 0000000..d3a02d4 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/key.h > @@ -0,0 +1,189 @@ > +/* > + * This file is part of UBIFS. > + * > + * Copyright (C) 2006-2008 Nokia Corporation. > + * > + * 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. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Artem Bityutskiy (Битюцкий Артём) > + * Adrian Hunter > + */ > + > +/* > + * This header contains various key-related definitions and helper function. > + * UBIFS allows several key schemes, so we access key fields only via these > + * helpers. At the moment only one key scheme is supported. > + * > + * Simple key scheme > + * ~~~~~~~~~~~~~~~~~ > + * > + * Keys are 64-bits long. First 32-bits are inode number (parent inode number > + * in case of direntry key). Next 3 bits are node type. The last 29 bits are > + * 4KiB offset in case of inode node, and direntry hash in case of a direntry > + * node. We use "r5" hash borrowed from reiserfs. > + */ > + > +#ifndef __UBIFS_KEY_H__ > +#define __UBIFS_KEY_H__ > + > +/** > + * key_mask_hash - mask a valid hash value. > + * @val: value to be masked > + * > + * We use hash values as offset in directories, so values %0 and %1 are > + * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This > + * function makes sure the reserved values are not used. > + */ > +static inline uint32_t key_mask_hash(uint32_t hash) > +{ > + hash &= UBIFS_S_KEY_HASH_MASK; > + if (unlikely(hash <= 2)) > + hash += 3; > + return hash; > +} > + > +/** > + * key_r5_hash - R5 hash function (borrowed from reiserfs). > + * @s: direntry name > + * @len: name length > + */ > +static inline uint32_t key_r5_hash(const char *s, int len) > +{ > + uint32_t a = 0; > + const signed char *str = (const signed char *)s; > + > + len = len; > + while (*str) { > + a += *str << 4; > + a += *str >> 4; > + a *= 11; > + str++; > + } > + > + return key_mask_hash(a); > +} > + > +/** > + * key_test_hash - testing hash function. > + * @str: direntry name > + * @len: name length > + */ > +static inline uint32_t key_test_hash(const char *str, int len) > +{ > + uint32_t a = 0; > + > + len = min_t(uint32_t, len, 4); > + memcpy(&a, str, len); > + return key_mask_hash(a); > +} > + > +/** > + * ino_key_init - initialize inode key. > + * @c: UBIFS file-system description object > + * @key: key to initialize > + * @inum: inode number > + */ > +static inline void ino_key_init(union ubifs_key *key, ino_t inum) > +{ > + key->u32[0] = inum; > + key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; > +} > + > +/** > + * dent_key_init - initialize directory entry key. > + * @c: UBIFS file-system description object > + * @key: key to initialize > + * @inum: parent inode number > + * @nm: direntry name and length > + */ > +static inline void dent_key_init(const struct ubifs_info *c, > + union ubifs_key *key, ino_t inum, > + const struct qstr *nm) > +{ > + uint32_t hash = c->key_hash(nm->name, nm->len); > + > + ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); > + key->u32[0] = inum; > + key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); > +} > + > +/** > + * data_key_init - initialize data key. > + * @c: UBIFS file-system description object > + * @key: key to initialize > + * @inum: inode number > + * @block: block number > + */ > +static inline void data_key_init(union ubifs_key *key, ino_t inum, > + unsigned int block) > +{ > + ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); > + key->u32[0] = inum; > + key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); > +} > + > +/** > + * key_write - transform a key from in-memory format. > + * @c: UBIFS file-system description object > + * @from: the key to transform > + * @to: the key to store the result > + */ > +static inline void key_write(const union ubifs_key *from, void *to) > +{ > + union ubifs_key *t = to; > + > + t->j32[0] = cpu_to_le32(from->u32[0]); > + t->j32[1] = cpu_to_le32(from->u32[1]); > + memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); > +} > + > +/** > + * key_write_idx - transform a key from in-memory format for the index. > + * @c: UBIFS file-system description object > + * @from: the key to transform > + * @to: the key to store the result > + */ > +static inline void key_write_idx(const union ubifs_key *from, void *to) > +{ > + union ubifs_key *t = to; > + > + t->j32[0] = cpu_to_le32(from->u32[0]); > + t->j32[1] = cpu_to_le32(from->u32[1]); > +} > + > +/** > + * keys_cmp - compare keys. > + * @c: UBIFS file-system description object > + * @key1: the first key to compare > + * @key2: the second key to compare > + * > + * This function compares 2 keys and returns %-1 if @key1 is less than > + * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. > + */ > +static inline int keys_cmp(const union ubifs_key *key1, > + const union ubifs_key *key2) > +{ > + if (key1->u32[0] < key2->u32[0]) > + return -1; > + if (key1->u32[0] > key2->u32[0]) > + return 1; > + if (key1->u32[1] < key2->u32[1]) > + return -1; > + if (key1->u32[1] > key2->u32[1]) > + return 1; > + > + return 0; > +} > + > +#endif /* !__UBIFS_KEY_H__ */ > diff --git a/ubifs-utils/mkfs.ubifs/lpt.c b/ubifs-utils/mkfs.ubifs/lpt.c > new file mode 100644 > index 0000000..6aa0b88 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/lpt.c > @@ -0,0 +1,578 @@ > +/* > + * This file is part of UBIFS. > + * > + * Copyright (C) 2006, 2007 Nokia Corporation. > + * > + * 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. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Adrian Hunter > + * Artem Bityutskiy > + */ > + > +#include "mkfs.ubifs.h" > + > +/** > + * do_calc_lpt_geom - calculate sizes for the LPT area. > + * @c: the UBIFS file-system description object > + * > + * Calculate the sizes of LPT bit fields, nodes, and tree, based on the > + * properties of the flash and whether LPT is "big" (c->big_lpt). > + */ > +static void do_calc_lpt_geom(struct ubifs_info *c) > +{ > + int n, bits, per_leb_wastage; > + long long sz, tot_wastage; > + > + c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > + > + n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > + c->nnode_cnt = n; > + while (n > 1) { > + n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > + c->nnode_cnt += n; > + } > + > + c->lpt_hght = 1; > + n = UBIFS_LPT_FANOUT; > + while (n < c->pnode_cnt) { > + c->lpt_hght += 1; > + n <<= UBIFS_LPT_FANOUT_SHIFT; > + } > + > + c->space_bits = fls(c->leb_size) - 3; > + c->lpt_lnum_bits = fls(c->lpt_lebs); > + c->lpt_offs_bits = fls(c->leb_size - 1); > + c->lpt_spc_bits = fls(c->leb_size); > + > + n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > + c->pcnt_bits = fls(n - 1); > + > + c->lnum_bits = fls(c->max_leb_cnt - 1); > + > + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > + (c->big_lpt ? c->pcnt_bits : 0) + > + (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; > + c->pnode_sz = (bits + 7) / 8; > + > + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > + (c->big_lpt ? c->pcnt_bits : 0) + > + (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; > + c->nnode_sz = (bits + 7) / 8; > + > + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > + c->lpt_lebs * c->lpt_spc_bits * 2; > + c->ltab_sz = (bits + 7) / 8; > + > + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + > + c->lnum_bits * c->lsave_cnt; > + c->lsave_sz = (bits + 7) / 8; > + > + /* Calculate the minimum LPT size */ > + c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; > + c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; > + c->lpt_sz += c->ltab_sz; > + c->lpt_sz += c->lsave_sz; > + > + /* Add wastage */ > + sz = c->lpt_sz; > + per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); > + sz += per_leb_wastage; > + tot_wastage = per_leb_wastage; > + while (sz > c->leb_size) { > + sz += per_leb_wastage; > + sz -= c->leb_size; > + tot_wastage += per_leb_wastage; > + } > + tot_wastage += ALIGN(sz, c->min_io_size) - sz; > + c->lpt_sz += tot_wastage; > +} > + > +/** > + * calc_dflt_lpt_geom - calculate default LPT geometry. > + * @c: the UBIFS file-system description object > + * @main_lebs: number of main area LEBs is passed and returned here > + * @big_lpt: whether the LPT area is "big" is returned here > + * > + * The size of the LPT area depends on parameters that themselves are dependent > + * on the size of the LPT area. This function, successively recalculates the LPT > + * area geometry until the parameters and resultant geometry are consistent. > + * > + * This function returns %0 on success and a negative error code on failure. > + */ > +int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) > +{ > + int i, lebs_needed; > + long long sz; > + > + /* Start by assuming the minimum number of LPT LEBs */ > + c->lpt_lebs = UBIFS_MIN_LPT_LEBS; > + c->main_lebs = *main_lebs - c->lpt_lebs; > + if (c->main_lebs <= 0) > + return -EINVAL; > + > + /* And assume we will use the small LPT model */ > + c->big_lpt = 0; > + > + /* > + * Calculate the geometry based on assumptions above and then see if it > + * makes sense > + */ > + do_calc_lpt_geom(c); > + > + /* Small LPT model must have lpt_sz < leb_size */ > + if (c->lpt_sz > c->leb_size) { > + /* Nope, so try again using big LPT model */ > + c->big_lpt = 1; > + do_calc_lpt_geom(c); > + } > + > + /* Now check there are enough LPT LEBs */ > + for (i = 0; i < 64 ; i++) { > + sz = c->lpt_sz * 4; /* Allow 4 times the size */ > + sz += c->leb_size - 1; > + do_div(sz, c->leb_size); > + lebs_needed = sz; > + if (lebs_needed > c->lpt_lebs) { > + /* Not enough LPT LEBs so try again with more */ > + c->lpt_lebs = lebs_needed; > + c->main_lebs = *main_lebs - c->lpt_lebs; > + if (c->main_lebs <= 0) > + return -EINVAL; > + do_calc_lpt_geom(c); > + continue; > + } > + if (c->ltab_sz > c->leb_size) { > + err_msg("LPT ltab too big"); > + return -EINVAL; > + } > + *main_lebs = c->main_lebs; > + *big_lpt = c->big_lpt; > + return 0; > + } > + return -EINVAL; > +} > + > +/** > + * pack_bits - pack bit fields end-to-end. > + * @addr: address at which to pack (passed and next address returned) > + * @pos: bit position at which to pack (passed and next position returned) > + * @val: value to pack > + * @nrbits: number of bits of value to pack (1-32) > + */ > +static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) > +{ > + uint8_t *p = *addr; > + int b = *pos; > + > + if (b) { > + *p |= ((uint8_t)val) << b; > + nrbits += b; > + if (nrbits > 8) { > + *++p = (uint8_t)(val >>= (8 - b)); > + if (nrbits > 16) { > + *++p = (uint8_t)(val >>= 8); > + if (nrbits > 24) { > + *++p = (uint8_t)(val >>= 8); > + if (nrbits > 32) > + *++p = (uint8_t)(val >>= 8); > + } > + } > + } > + } else { > + *p = (uint8_t)val; > + if (nrbits > 8) { > + *++p = (uint8_t)(val >>= 8); > + if (nrbits > 16) { > + *++p = (uint8_t)(val >>= 8); > + if (nrbits > 24) > + *++p = (uint8_t)(val >>= 8); > + } > + } > + } > + b = nrbits & 7; > + if (b == 0) > + p++; > + *addr = p; > + *pos = b; > +} > + > +/** > + * pack_pnode - pack all the bit fields of a pnode. > + * @c: UBIFS file-system description object > + * @buf: buffer into which to pack > + * @pnode: pnode to pack > + */ > +static void pack_pnode(struct ubifs_info *c, void *buf, > + struct ubifs_pnode *pnode) > +{ > + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > + int i, pos = 0; > + uint16_t crc; > + > + pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); > + if (c->big_lpt) > + pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); > + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { > + pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, > + c->space_bits); > + pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, > + c->space_bits); > + if (pnode->lprops[i].flags & LPROPS_INDEX) > + pack_bits(&addr, &pos, 1, 1); > + else > + pack_bits(&addr, &pos, 0, 1); > + } > + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > + c->pnode_sz - UBIFS_LPT_CRC_BYTES); > + addr = buf; > + pos = 0; > + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > +} > + > +/** > + * pack_nnode - pack all the bit fields of a nnode. > + * @c: UBIFS file-system description object > + * @buf: buffer into which to pack > + * @nnode: nnode to pack > + */ > +static void pack_nnode(struct ubifs_info *c, void *buf, > + struct ubifs_nnode *nnode) > +{ > + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > + int i, pos = 0; > + uint16_t crc; > + > + pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); > + if (c->big_lpt) > + pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); > + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { > + int lnum = nnode->nbranch[i].lnum; > + > + if (lnum == 0) > + lnum = c->lpt_last + 1; > + pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); > + pack_bits(&addr, &pos, nnode->nbranch[i].offs, > + c->lpt_offs_bits); > + } > + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > + c->nnode_sz - UBIFS_LPT_CRC_BYTES); > + addr = buf; > + pos = 0; > + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > +} > + > +/** > + * pack_ltab - pack the LPT's own lprops table. > + * @c: UBIFS file-system description object > + * @buf: buffer into which to pack > + * @ltab: LPT's own lprops table to pack > + */ > +static void pack_ltab(struct ubifs_info *c, void *buf, > + struct ubifs_lpt_lprops *ltab) > +{ > + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > + int i, pos = 0; > + uint16_t crc; > + > + pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); > + for (i = 0; i < c->lpt_lebs; i++) { > + pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); > + pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); > + } > + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > + c->ltab_sz - UBIFS_LPT_CRC_BYTES); > + addr = buf; > + pos = 0; > + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > +} > + > +/** > + * pack_lsave - pack the LPT's save table. > + * @c: UBIFS file-system description object > + * @buf: buffer into which to pack > + * @lsave: LPT's save table to pack > + */ > +static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) > +{ > + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; > + int i, pos = 0; > + uint16_t crc; > + > + pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); > + for (i = 0; i < c->lsave_cnt; i++) > + pack_bits(&addr, &pos, lsave[i], c->lnum_bits); > + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, > + c->lsave_sz - UBIFS_LPT_CRC_BYTES); > + addr = buf; > + pos = 0; > + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); > +} > + > +/** > + * set_ltab - set LPT LEB properties. > + * @c: UBIFS file-system description object > + * @lnum: LEB number > + * @free: amount of free space > + * @dirty: amount of dirty space > + */ > +static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) > +{ > + dbg_msg(3, "LEB %d free %d dirty %d to %d %d", > + lnum, c->ltab[lnum - c->lpt_first].free, > + c->ltab[lnum - c->lpt_first].dirty, free, dirty); > + c->ltab[lnum - c->lpt_first].free = free; > + c->ltab[lnum - c->lpt_first].dirty = dirty; > +} > + > +/** > + * calc_nnode_num - calculate nnode number. > + * @row: the row in the tree (root is zero) > + * @col: the column in the row (leftmost is zero) > + * > + * The nnode number is a number that uniquely identifies a nnode and can be used > + * easily to traverse the tree from the root to that nnode. > + * > + * This function calculates and returns the nnode number for the nnode at @row > + * and @col. > + */ > +static int calc_nnode_num(int row, int col) > +{ > + int num, bits; > + > + num = 1; > + while (row--) { > + bits = (col & (UBIFS_LPT_FANOUT - 1)); > + col >>= UBIFS_LPT_FANOUT_SHIFT; > + num <<= UBIFS_LPT_FANOUT_SHIFT; > + num |= bits; > + } > + return num; > +} > + > +/** > + * create_lpt - create LPT. > + * @c: UBIFS file-system description object > + * > + * This function returns %0 on success and a negative error code on failure. > + */ > +int create_lpt(struct ubifs_info *c) > +{ > + int lnum, err = 0, i, j, cnt, len, alen, row; > + int blnum, boffs, bsz, bcnt; > + struct ubifs_pnode *pnode = NULL; > + struct ubifs_nnode *nnode = NULL; > + void *buf = NULL, *p; > + int *lsave = NULL; > + > + pnode = malloc(sizeof(struct ubifs_pnode)); > + nnode = malloc(sizeof(struct ubifs_nnode)); > + buf = malloc(c->leb_size); > + lsave = malloc(sizeof(int) * c->lsave_cnt); > + if (!pnode || !nnode || !buf || !lsave) { > + err = -ENOMEM; > + goto out; > + } > + memset(pnode, 0 , sizeof(struct ubifs_pnode)); > + memset(nnode, 0 , sizeof(struct ubifs_nnode)); > + > + c->lscan_lnum = c->main_first; > + > + lnum = c->lpt_first; > + p = buf; > + len = 0; > + /* Number of leaf nodes (pnodes) */ > + cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; > + //printf("pnode_cnt=%d\n",cnt); > + > + /* > + * To calculate the internal node branches, we keep information about > + * the level below. > + */ > + blnum = lnum; /* LEB number of level below */ > + boffs = 0; /* Offset of level below */ > + bcnt = cnt; /* Number of nodes in level below */ > + bsz = c->pnode_sz; /* Size of nodes in level below */ > + > + /* Add pnodes */ > + for (i = 0; i < cnt; i++) { > + if (len + c->pnode_sz > c->leb_size) { > + alen = ALIGN(len, c->min_io_size); > + set_ltab(c, lnum, c->leb_size - alen, alen - len); > + memset(p, 0xff, alen - len); > + err = write_leb(lnum++, alen, buf); > + if (err) > + goto out; > + p = buf; > + len = 0; > + } > + /* Fill in the pnode */ > + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { > + int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; > + > + if (k < c->main_lebs) > + pnode->lprops[j] = c->lpt[k]; > + else { > + pnode->lprops[j].free = c->leb_size; > + pnode->lprops[j].dirty = 0; > + pnode->lprops[j].flags = 0; > + } > + } > + pack_pnode(c, p, pnode); > + p += c->pnode_sz; > + len += c->pnode_sz; > + /* > + * pnodes are simply numbered left to right starting at zero, > + * which means the pnode number can be used easily to traverse > + * down the tree to the corresponding pnode. > + */ > + pnode->num += 1; > + } > + > + row = c->lpt_hght - 1; > + /* Add all nnodes, one level at a time */ > + while (1) { > + /* Number of internal nodes (nnodes) at next level */ > + cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; > + if (cnt == 0) > + cnt = 1; > + for (i = 0; i < cnt; i++) { > + if (len + c->nnode_sz > c->leb_size) { > + alen = ALIGN(len, c->min_io_size); > + set_ltab(c, lnum, c->leb_size - alen, > + alen - len); > + memset(p, 0xff, alen - len); > + err = write_leb(lnum++, alen, buf); > + if (err) > + goto out; > + p = buf; > + len = 0; > + } > + /* The root is on row zero */ > + if (row == 0) { > + c->lpt_lnum = lnum; > + c->lpt_offs = len; > + } > + /* Set branches to the level below */ > + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { > + if (bcnt) { > + if (boffs + bsz > c->leb_size) { > + blnum += 1; > + boffs = 0; > + } > + nnode->nbranch[j].lnum = blnum; > + nnode->nbranch[j].offs = boffs; > + boffs += bsz; > + bcnt--; > + } else { > + nnode->nbranch[j].lnum = 0; > + nnode->nbranch[j].offs = 0; > + } > + } > + nnode->num = calc_nnode_num(row, i); > + pack_nnode(c, p, nnode); > + p += c->nnode_sz; > + len += c->nnode_sz; > + } > + /* Row zero is the top row */ > + if (row == 0) > + break; > + /* Update the information about the level below */ > + bcnt = cnt; > + bsz = c->nnode_sz; > + row -= 1; > + } > + > + if (c->big_lpt) { > + /* Need to add LPT's save table */ > + if (len + c->lsave_sz > c->leb_size) { > + alen = ALIGN(len, c->min_io_size); > + set_ltab(c, lnum, c->leb_size - alen, alen - len); > + memset(p, 0xff, alen - len); > + err = write_leb(lnum++, alen, buf); > + if (err) > + goto out; > + p = buf; > + len = 0; > + } > + > + c->lsave_lnum = lnum; > + c->lsave_offs = len; > + > + for (i = 0; i < c->lsave_cnt; i++) > + lsave[i] = c->main_first + i; > + > + pack_lsave(c, p, lsave); > + p += c->lsave_sz; > + len += c->lsave_sz; > + } > + > + /* Need to add LPT's own LEB properties table */ > + if (len + c->ltab_sz > c->leb_size) { > + alen = ALIGN(len, c->min_io_size); > + set_ltab(c, lnum, c->leb_size - alen, alen - len); > + memset(p, 0xff, alen - len); > + err = write_leb(lnum++, alen, buf); > + if (err) > + goto out; > + p = buf; > + len = 0; > + } > + > + c->ltab_lnum = lnum; > + c->ltab_offs = len; > + > + /* Update ltab before packing it */ > + len += c->ltab_sz; > + alen = ALIGN(len, c->min_io_size); > + set_ltab(c, lnum, c->leb_size - alen, alen - len); > + > + pack_ltab(c, p, c->ltab); > + p += c->ltab_sz; > + > + /* Write remaining buffer */ > + memset(p, 0xff, alen - len); > + err = write_leb(lnum, alen, buf); > + if (err) > + goto out; > + > + c->nhead_lnum = lnum; > + c->nhead_offs = ALIGN(len, c->min_io_size); > + > + dbg_msg(1, "lpt_sz: %lld", c->lpt_sz); > + dbg_msg(1, "space_bits: %d", c->space_bits); > + dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); > + dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); > + dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); > + dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); > + dbg_msg(1, "lnum_bits: %d", c->lnum_bits); > + dbg_msg(1, "pnode_sz: %d", c->pnode_sz); > + dbg_msg(1, "nnode_sz: %d", c->nnode_sz); > + dbg_msg(1, "ltab_sz: %d", c->ltab_sz); > + dbg_msg(1, "lsave_sz: %d", c->lsave_sz); > + dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); > + dbg_msg(1, "lpt_hght: %d", c->lpt_hght); > + dbg_msg(1, "big_lpt: %d", c->big_lpt); > + dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); > + dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); > + dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); > + if (c->big_lpt) > + dbg_msg(1, "LPT lsave is at %d:%d", > + c->lsave_lnum, c->lsave_offs); > +out: > + free(lsave); > + free(buf); > + free(nnode); > + free(pnode); > + return err; > +} > diff --git a/ubifs-utils/mkfs.ubifs/lpt.h b/ubifs-utils/mkfs.ubifs/lpt.h > new file mode 100644 > index 0000000..4cde59d > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/lpt.h > @@ -0,0 +1,28 @@ > +/* > + * Copyright (C) 2008 Nokia Corporation. > + * Copyright (C) 2008 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 version 2 as published by > + * the Free Software Foundation. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Artem Bityutskiy > + * Adrian Hunter > + */ > + > +#ifndef __UBIFS_LPT_H__ > +#define __UBIFS_LPT_H__ > + > +int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); > +int create_lpt(struct ubifs_info *c); > + > +#endif > diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c > new file mode 100644 > index 0000000..ca17e2b > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c > @@ -0,0 +1,2324 @@ > +/* > + * Copyright (C) 2008 Nokia Corporation. > + * Copyright (C) 2008 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 version 2 as published by > + * the Free Software Foundation. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Adrian Hunter > + * Artem Bityutskiy > + * Zoltan Sogor > + */ > + > +#define _XOPEN_SOURCE 500 /* For realpath() */ > + > +#include "mkfs.ubifs.h" > +#include <crc32.h> > +#include "common.h" > + > +/* Size (prime number) of hash table for link counting */ > +#define HASH_TABLE_SIZE 10099 > + > +/* The node buffer must allow for worst case compression */ > +#define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + \ > + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) > + > +/* Default time granularity in nanoseconds */ > +#define DEFAULT_TIME_GRAN 1000000000 > + > +/** > + * struct idx_entry - index entry. > + * @next: next index entry (NULL at end of list) > + * @prev: previous index entry (NULL at beginning of list) > + * @key: key > + * @name: directory entry name used for sorting colliding keys by name > + * @lnum: LEB number > + * @offs: offset > + * @len: length > + * > + * The index is recorded as a linked list which is sorted and used to create > + * the bottom level of the on-flash index tree. The remaining levels of the > + * index tree are each built from the level below. > + */ > +struct idx_entry { > + struct idx_entry *next; > + struct idx_entry *prev; > + union ubifs_key key; > + char *name; > + int lnum; > + int offs; > + int len; > +}; > + > +/** > + * struct inum_mapping - inode number mapping for link counting. > + * @next: next inum_mapping (NULL at end of list) > + * @prev: previous inum_mapping (NULL at beginning of list) > + * @dev: source device on which the source inode number resides > + * @inum: source inode number of the file > + * @use_inum: target inode number of the file > + * @use_nlink: number of links > + * @path_name: a path name of the file > + * @st: struct stat object containing inode attributes which have to be used > + * when the inode is being created (actually only UID, GID, access > + * mode, major and minor device numbers) > + * > + * If a file has more than one hard link, then the number of hard links that > + * exist in the source directory hierarchy must be counted to exclude the > + * possibility that the file is linked from outside the source directory > + * hierarchy. > + * > + * The inum_mappings are stored in a hash_table of linked lists. > + */ > +struct inum_mapping { > + struct inum_mapping *next; > + struct inum_mapping *prev; > + dev_t dev; > + ino_t inum; > + ino_t use_inum; > + unsigned int use_nlink; > + char *path_name; > + struct stat st; > +}; > + > +/* > + * Because we copy functions from the kernel, we use a subset of the UBIFS > + * file-system description object struct ubifs_info. > + */ > +struct ubifs_info info_; > +static struct ubifs_info *c = &info_; > +static libubi_t ubi; > + > +/* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ > +int debug_level; > +int verbose; > +int yes; > + > +static char *root; > +static int root_len; > +static struct stat root_st; > +static char *output; > +static int out_fd; > +static int out_ubi; > +static int squash_owner; > + > +/* The 'head' (position) which nodes are written */ > +static int head_lnum; > +static int head_offs; > +static int head_flags; > + > +/* The index list */ > +static struct idx_entry *idx_list_first; > +static struct idx_entry *idx_list_last; > +static size_t idx_cnt; > + > +/* Global buffers */ > +static void *leb_buf; > +static void *node_buf; > +static void *block_buf; > + > +/* Hash table for inode link counting */ > +static struct inum_mapping **hash_table; > + > +/* Inode creation sequence number */ > +static unsigned long long creat_sqnum; > + > +static const char *optstring = "d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq"; > + > +static const struct option longopts[] = { > + {"root", 1, NULL, 'r'}, > + {"min-io-size", 1, NULL, 'm'}, > + {"leb-size", 1, NULL, 'e'}, > + {"max-leb-cnt", 1, NULL, 'c'}, > + {"output", 1, NULL, 'o'}, > + {"devtable", 1, NULL, 'D'}, > + {"yes", 0, NULL, 'y'}, > + {"help", 0, NULL, 'h'}, > + {"verbose", 0, NULL, 'v'}, > + {"version", 0, NULL, 'V'}, > + {"debug-level", 1, NULL, 'g'}, > + {"jrn-size", 1, NULL, 'j'}, > + {"reserved", 1, NULL, 'R'}, > + {"compr", 1, NULL, 'x'}, > + {"favor-percent", 1, NULL, 'X'}, > + {"fanout", 1, NULL, 'f'}, > + {"space-fixup", 0, NULL, 'F'}, > + {"keyhash", 1, NULL, 'k'}, > + {"log-lebs", 1, NULL, 'l'}, > + {"orph-lebs", 1, NULL, 'p'}, > + {"squash-uids" , 0, NULL, 'U'}, > + {NULL, 0, NULL, 0} > +}; > + > +static const char *helptext = > +"Usage: mkfs.ubifs [OPTIONS] target\n" > +"Make a UBIFS file system image from an existing directory tree\n\n" > +"Examples:\n" > +"Build file system from directory /opt/img, writting the result in the ubifs.img file\n" > +"\tmkfs.ubifs -m 512 -e 128KiB -c 100 -r /opt/img ubifs.img\n" > +"The same, but writting directly to an UBI volume\n" > +"\tmkfs.ubifs -r /opt/img /dev/ubi0_0\n" > +"Creating an empty UBIFS filesystem on an UBI volume\n" > +"\tmkfs.ubifs /dev/ubi0_0\n\n" > +"Options:\n" > +"-r, -d, --root=DIR build file system from directory DIR\n" > +"-m, --min-io-size=SIZE minimum I/O unit size\n" > +"-e, --leb-size=SIZE logical erase block size\n" > +"-c, --max-leb-cnt=COUNT maximum logical erase block count\n" > +"-o, --output=FILE output to FILE\n" > +"-j, --jrn-size=SIZE journal size\n" > +"-R, --reserved=SIZE how much space should be reserved for the super-user\n" > +"-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" > +" \"none\" (default: \"lzo\")\n" > +"-X, --favor-percent may only be used with favor LZO compression and defines\n" > +" how many percent better zlib should compress to make\n" > +" mkfs.ubifs use zlib instead of LZO (default 20%)\n" > +"-f, --fanout=NUM fanout NUM (default: 8)\n" > +"-F, --space-fixup file-system free space has to be fixed up on first mount\n" > +" (requires kernel version 3.0 or greater)\n" > +"-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" > +"-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" > +"-D, --devtable=FILE use device table FILE\n" > +"-U, --squash-uids squash owners making all files owned by root\n" > +"-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" > +" debugging)\n" > +"-y, --yes assume the answer is \"yes\" for all questions\n" > +"-v, --verbose verbose operation\n" > +"-V, --version display version information\n" > +"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" > +" 2 - files, 3 - more details)\n" > +"-h, --help display this help text\n\n" > +"Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" > +"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" > +"If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" > +"for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" > +"really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" > +"compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" > +"compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" > +"or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" > +"\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" > +"default 20%.\n\n" > +"The -F parameter is used to set the \"fix up free space\" flag in the superblock,\n" > +"which forces UBIFS to \"fixup\" all the free space which it is going to use. This\n" > +"option is useful to work-around the problem of double free space programming: if the\n" > +"flasher program which flashes the UBI image is unable to skip NAND pages containing\n" > +"only 0xFF bytes, the effect is that some NAND pages are written to twice - first time\n" > +"when flashing the image and the second time when UBIFS is mounted and writes useful\n" > +"data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n" > +"flag may make the first mount very slow, because the \"free space fixup\" procedure\n" > +"takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"; > + > +/** > + * make_path - make a path name from a directory and a name. > + * @dir: directory path name > + * @name: name > + */ > +static char *make_path(const char *dir, const char *name) > +{ > + char *s; > + > + s = malloc(strlen(dir) + strlen(name) + 2); > + if (!s) > + return NULL; > + strcpy(s, dir); > + if (dir[strlen(dir) - 1] != '/') > + strcat(s, "/"); > + strcat(s, name); > + return s; > +} > + > +/** > + * is_contained - determine if a file is beneath a directory. > + * @file: file path name > + * @dir: directory path name > + * > + * This function returns %1 if @file is accessible from the @dir directory and > + * %0 otherwise. In case of error, returns %-1. > + */ > +static int is_contained(const char *file, const char *dir) > +{ > + char *real_file = NULL; > + char *real_dir = NULL; > + char *file_base, *copy; > + int ret = -1; > + > + /* Make a copy of the file path because 'dirname()' can modify it */ > + copy = strdup(file); > + if (!copy) > + return -1; > + file_base = dirname(copy); > + > + /* Turn the paths into the canonical form */ > + real_file = malloc(PATH_MAX); > + if (!real_file) > + goto out_free; > + > + real_dir = malloc(PATH_MAX); > + if (!real_dir) > + goto out_free; > + > + if (!realpath(file_base, real_file)) { > + perror("Could not canonicalize file path"); > + goto out_free; > + } > + if (!realpath(dir, real_dir)) { > + perror("Could not canonicalize directory"); > + goto out_free; > + } > + > + ret = !!strstr(real_file, real_dir); > + > +out_free: > + free(copy); > + free(real_file); > + free(real_dir); > + return ret; > +} > + > +/** > + * calc_min_log_lebs - calculate the minimum number of log LEBs needed. > + * @max_bud_bytes: journal size (buds only) > + */ > +static int calc_min_log_lebs(unsigned long long max_bud_bytes) > +{ > + int buds, log_lebs; > + unsigned long long log_size; > + > + buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; > + log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); > + log_size *= buds; > + log_size += ALIGN(UBIFS_CS_NODE_SZ + > + UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), > + c->min_io_size); > + log_lebs = (log_size + c->leb_size - 1) / c->leb_size; > + log_lebs += 1; > + return log_lebs; > +} > + > +/** > + * add_space_overhead - add UBIFS overhead. > + * @size: flash space which should be visible to the user > + * > + * UBIFS has overhead, and if we need to reserve @size bytes for the user data, > + * we have to reserve more flash space, to compensate the overhead. This > + * function calculates and returns the amount of physical flash space which > + * should be reserved to provide @size bytes for the user. > + */ > +static long long add_space_overhead(long long size) > +{ > + int divisor, factor, f, max_idx_node_sz; > + > + /* > + * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS > + * function does. > + */ > + max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); > + f = c->fanout > 3 ? c->fanout >> 1 : 2; > + divisor = UBIFS_BLOCK_SIZE; > + factor = UBIFS_MAX_DATA_NODE_SZ; > + factor += (max_idx_node_sz * 3) / (f - 1); > + size *= factor; > + return size / divisor; > +} > + > +static int validate_options(void) > +{ > + int tmp; > + > + if (!output) > + return err_msg("no output file or UBI volume specified"); > + if (root) { > + tmp = is_contained(output, root); > + if (tmp < 0) > + return err_msg("failed to perform output file root check"); > + else if (tmp) > + return err_msg("output file cannot be in the UBIFS root " > + "directory"); > + } > + if (!is_power_of_2(c->min_io_size)) > + return err_msg("min. I/O unit size should be power of 2"); > + if (c->leb_size < c->min_io_size) > + return err_msg("min. I/O unit cannot be larger than LEB size"); > + if (c->leb_size < UBIFS_MIN_LEB_SZ) > + return err_msg("too small LEB size %d, minimum is %d", > + c->leb_size, UBIFS_MIN_LEB_SZ); > + if (c->leb_size % c->min_io_size) > + return err_msg("LEB should be multiple of min. I/O units"); > + if (c->leb_size % 8) > + return err_msg("LEB size has to be multiple of 8"); > + if (c->leb_size > UBIFS_MAX_LEB_SZ) > + return err_msg("too large LEB size %d, maximum is %d", > + c->leb_size, UBIFS_MAX_LEB_SZ); > + if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) > + return err_msg("too low max. count of LEBs, minimum is %d", > + UBIFS_MIN_LEB_CNT); > + if (c->fanout < UBIFS_MIN_FANOUT) > + return err_msg("too low fanout, minimum is %d", > + UBIFS_MIN_FANOUT); > + tmp = c->leb_size - UBIFS_IDX_NODE_SZ; > + tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; > + if (c->fanout > tmp) > + return err_msg("too high fanout, maximum is %d", tmp); > + if (c->log_lebs < UBIFS_MIN_LOG_LEBS) > + return err_msg("too few log LEBs, minimum is %d", > + UBIFS_MIN_LOG_LEBS); > + if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) > + return err_msg("too many log LEBs, maximum is %d", > + c->max_leb_cnt - UBIFS_MIN_LEB_CNT); > + if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) > + return err_msg("too few orphan LEBs, minimum is %d", > + UBIFS_MIN_ORPH_LEBS); > + if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) > + return err_msg("too many orphan LEBs, maximum is %d", > + c->max_leb_cnt - UBIFS_MIN_LEB_CNT); > + tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; > + tmp += c->orph_lebs + 4; > + if (tmp > c->max_leb_cnt) > + return err_msg("too low max. count of LEBs, expected at " > + "least %d", tmp); > + tmp = calc_min_log_lebs(c->max_bud_bytes); > + if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) > + return err_msg("too few log LEBs, expected at least %d", tmp); > + if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) > + return err_msg("too much reserved space %lld", c->rp_size); > + return 0; > +} > + > +/** > + * get_multiplier - convert size specifier to an integer multiplier. > + * @str: the size specifier string > + * > + * This function parses the @str size specifier, which may be one of > + * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive > + * size multiplier in case of success and %-1 in case of failure. > + */ > +static int get_multiplier(const char *str) > +{ > + if (!str) > + return 1; > + > + /* Remove spaces before the specifier */ > + while (*str == ' ' || *str == '\t') > + str += 1; > + > + if (!strcmp(str, "KiB")) > + return 1024; > + if (!strcmp(str, "MiB")) > + return 1024 * 1024; > + if (!strcmp(str, "GiB")) > + return 1024 * 1024 * 1024; > + > + return -1; > +} > + > +/** > + * get_bytes - convert a string containing amount of bytes into an > + * integer. > + * @str: string to convert > + * > + * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size > + * specifiers. Returns positive amount of bytes in case of success and %-1 in > + * case of failure. > + */ > +static long long get_bytes(const char *str) > +{ > + char *endp; > + long long bytes = strtoull(str, &endp, 0); > + > + if (endp == str || bytes < 0) > + return err_msg("incorrect amount of bytes: \"%s\"", str); > + > + if (*endp != '\0') { > + int mult = get_multiplier(endp); > + > + if (mult == -1) > + return err_msg("bad size specifier: \"%s\" - " > + "should be 'KiB', 'MiB' or 'GiB'", endp); > + bytes *= mult; > + } > + > + return bytes; > +} > +/** > + * open_ubi - open the UBI volume. > + * @node: name of the UBI volume character device to fetch information about > + * > + * Returns %0 in case of success and %-1 in case of failure > + */ > +static int open_ubi(const char *node) > +{ > + struct stat st; > + > + if (stat(node, &st) || !S_ISCHR(st.st_mode)) > + return -1; > + > + ubi = libubi_open(); > + if (!ubi) > + return -1; > + if (ubi_get_vol_info(ubi, node, &c->vi)) > + return -1; > + if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) > + return -1; > + return 0; > +} > + > +static int get_options(int argc, char**argv) > +{ > + int opt, i; > + const char *tbl_file = NULL; > + struct stat st; > + char *endp; > + > + c->fanout = 8; > + c->orph_lebs = 1; > + c->key_hash = key_r5_hash; > + c->key_len = UBIFS_SK_LEN; > + c->default_compr = UBIFS_COMPR_LZO; > + c->favor_percent = 20; > + c->lsave_cnt = 256; > + c->leb_size = -1; > + c->min_io_size = -1; > + c->max_leb_cnt = -1; > + c->max_bud_bytes = -1; > + c->log_lebs = -1; > + > + while (1) { > + opt = getopt_long(argc, argv, optstring, longopts, &i); > + if (opt == -1) > + break; > + switch (opt) { > + case 'r': > + case 'd': > + root_len = strlen(optarg); > + root = malloc(root_len + 2); > + if (!root) > + return err_msg("cannot allocate memory"); > + > + /* > + * The further code expects '/' at the end of the root > + * UBIFS directory on the host. > + */ > + memcpy(root, optarg, root_len); > + if (root[root_len - 1] != '/') > + root[root_len++] = '/'; > + root[root_len] = 0; > + > + /* Make sure the root directory exists */ > + if (stat(root, &st)) > + return sys_err_msg("bad root directory '%s'", > + root); > + break; > + case 'm': > + c->min_io_size = get_bytes(optarg); > + if (c->min_io_size <= 0) > + return err_msg("bad min. I/O size"); > + break; > + case 'e': > + c->leb_size = get_bytes(optarg); > + if (c->leb_size <= 0) > + return err_msg("bad LEB size"); > + break; > + case 'c': > + c->max_leb_cnt = get_bytes(optarg); > + if (c->max_leb_cnt <= 0) > + return err_msg("bad maximum LEB count"); > + break; > + case 'o': > + output = xstrdup(optarg); > + break; > + case 'D': > + tbl_file = optarg; > + if (stat(tbl_file, &st) < 0) > + return sys_err_msg("bad device table file '%s'", > + tbl_file); > + break; > + case 'y': > + yes = 1; > + break; > + case 'h': > + case '?': > + printf("%s", helptext); > + exit(0); > + case 'v': > + verbose = 1; > + break; > + case 'V': > + common_print_version(); > + exit(0); > + case 'g': > + debug_level = strtol(optarg, &endp, 0); > + if (*endp != '\0' || endp == optarg || > + debug_level < 0 || debug_level > 3) > + return err_msg("bad debugging level '%s'", > + optarg); > + break; > + case 'f': > + c->fanout = strtol(optarg, &endp, 0); > + if (*endp != '\0' || endp == optarg || c->fanout <= 0) > + return err_msg("bad fanout %s", optarg); > + break; > + case 'F': > + c->space_fixup = 1; > + break; > + case 'l': > + c->log_lebs = strtol(optarg, &endp, 0); > + if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) > + return err_msg("bad count of log LEBs '%s'", > + optarg); > + break; > + case 'p': > + c->orph_lebs = strtol(optarg, &endp, 0); > + if (*endp != '\0' || endp == optarg || > + c->orph_lebs <= 0) > + return err_msg("bad orphan LEB count '%s'", > + optarg); > + break; > + case 'k': > + if (strcmp(optarg, "r5") == 0) { > + c->key_hash = key_r5_hash; > + c->key_hash_type = UBIFS_KEY_HASH_R5; > + } else if (strcmp(optarg, "test") == 0) { > + c->key_hash = key_test_hash; > + c->key_hash_type = UBIFS_KEY_HASH_TEST; > + } else > + return err_msg("bad key hash"); > + break; > + case 'x': > + if (strcmp(optarg, "favor_lzo") == 0) > + c->favor_lzo = 1; > + else if (strcmp(optarg, "zlib") == 0) > + c->default_compr = UBIFS_COMPR_ZLIB; > + else if (strcmp(optarg, "none") == 0) > + c->default_compr = UBIFS_COMPR_NONE; > + else if (strcmp(optarg, "lzo") != 0) > + return err_msg("bad compressor name"); > + break; > + case 'X': > + c->favor_percent = strtol(optarg, &endp, 0); > + if (*endp != '\0' || endp == optarg || > + c->favor_percent <= 0 || c->favor_percent >= 100) > + return err_msg("bad favor LZO percent '%s'", > + optarg); > + break; > + case 'j': > + c->max_bud_bytes = get_bytes(optarg); > + if (c->max_bud_bytes <= 0) > + return err_msg("bad maximum amount of buds"); > + break; > + case 'R': > + c->rp_size = get_bytes(optarg); > + if (c->rp_size < 0) > + return err_msg("bad reserved bytes count"); > + break; > + case 'U': > + squash_owner = 1; > + break; > + } > + } > + > + if (optind != argc && !output) > + output = xstrdup(argv[optind]); > + > + if (!output) > + return err_msg("not output device or file specified"); > + > + out_ubi = !open_ubi(output); > + > + if (out_ubi) { > + c->min_io_size = c->di.min_io_size; > + c->leb_size = c->vi.leb_size; > + if (c->max_leb_cnt == -1) > + c->max_leb_cnt = c->vi.rsvd_lebs; > + } > + > + if (c->min_io_size == -1) > + return err_msg("min. I/O unit was not specified " > + "(use -h for help)"); > + > + if (c->leb_size == -1) > + return err_msg("LEB size was not specified (use -h for help)"); > + > + if (c->max_leb_cnt == -1) > + return err_msg("Maximum count of LEBs was not specified " > + "(use -h for help)"); > + > + if (c->max_bud_bytes == -1) { > + int lebs; > + > + lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; > + lebs -= c->orph_lebs; > + if (c->log_lebs != -1) > + lebs -= c->log_lebs; > + else > + lebs -= UBIFS_MIN_LOG_LEBS; > + /* > + * We do not know lprops geometry so far, so assume minimum > + * count of lprops LEBs. > + */ > + lebs -= UBIFS_MIN_LPT_LEBS; > + /* Make the journal about 12.5% of main area lebs */ > + c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; > + /* Make the max journal size 8MiB */ > + if (c->max_bud_bytes > 8 * 1024 * 1024) > + c->max_bud_bytes = 8 * 1024 * 1024; > + if (c->max_bud_bytes < 4 * c->leb_size) > + c->max_bud_bytes = 4 * c->leb_size; > + } > + > + if (c->log_lebs == -1) { > + c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); > + c->log_lebs += 2; > + } > + > + if (c->min_io_size < 8) > + c->min_io_size = 8; > + c->rp_size = add_space_overhead(c->rp_size); > + > + if (verbose) { > + printf("mkfs.ubifs\n"); > + printf("\troot: %s\n", root); > + printf("\tmin_io_size: %d\n", c->min_io_size); > + printf("\tleb_size: %d\n", c->leb_size); > + printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); > + printf("\toutput: %s\n", output); > + printf("\tjrn_size: %llu\n", c->max_bud_bytes); > + printf("\treserved: %llu\n", c->rp_size); > + switch (c->default_compr) { > + case UBIFS_COMPR_LZO: > + printf("\tcompr: lzo\n"); > + break; > + case UBIFS_COMPR_ZLIB: > + printf("\tcompr: zlib\n"); > + break; > + case UBIFS_COMPR_NONE: > + printf("\tcompr: none\n"); > + break; > + } > + printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? > + "r5" : "test"); > + printf("\tfanout: %d\n", c->fanout); > + printf("\torph_lebs: %d\n", c->orph_lebs); > + printf("\tspace_fixup: %d\n", c->space_fixup); > + } > + > + if (validate_options()) > + return -1; > + > + if (tbl_file && parse_devtable(tbl_file)) > + return err_msg("cannot parse device table file '%s'", tbl_file); > + > + return 0; > +} > + > +/** > + * prepare_node - fill in the common header. > + * @node: node > + * @len: node length > + */ > +static void prepare_node(void *node, int len) > +{ > + uint32_t crc; > + struct ubifs_ch *ch = node; > + > + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); > + ch->len = cpu_to_le32(len); > + ch->group_type = UBIFS_NO_NODE_GROUP; > + ch->sqnum = cpu_to_le64(++c->max_sqnum); > + ch->padding[0] = ch->padding[1] = 0; > + crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); > + ch->crc = cpu_to_le32(crc); > +} > + > +/** > + * write_leb - copy the image of a LEB to the output target. > + * @lnum: LEB number > + * @len: length of data in the buffer > + * @buf: buffer (must be at least c->leb_size bytes) > + */ > +int write_leb(int lnum, int len, void *buf) > +{ > + off_t pos = (off_t)lnum * c->leb_size; > + > + dbg_msg(3, "LEB %d len %d", lnum, len); > + memset(buf + len, 0xff, c->leb_size - len); > + if (out_ubi) > + if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size)) > + return sys_err_msg("ubi_leb_change_start failed"); > + > + if (lseek(out_fd, pos, SEEK_SET) != pos) > + return sys_err_msg("lseek failed seeking %"PRIdoff_t, pos); > + > + if (write(out_fd, buf, c->leb_size) != c->leb_size) > + return sys_err_msg("write failed writing %d bytes at pos %"PRIdoff_t, > + c->leb_size, pos); > + > + return 0; > +} > + > +/** > + * write_empty_leb - copy the image of an empty LEB to the output target. > + * @lnum: LEB number > + */ > +static int write_empty_leb(int lnum) > +{ > + return write_leb(lnum, 0, leb_buf); > +} > + > +/** > + * do_pad - pad a buffer to the minimum I/O size. > + * @buf: buffer > + * @len: buffer length > + */ > +static int do_pad(void *buf, int len) > +{ > + int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); > + uint32_t crc; > + > + memset(buf + len, 0xff, alen - len); > + pad_len = wlen - alen; > + dbg_msg(3, "len %d pad_len %d", len, pad_len); > + buf += alen; > + if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { > + struct ubifs_ch *ch = buf; > + struct ubifs_pad_node *pad_node = buf; > + > + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); > + ch->node_type = UBIFS_PAD_NODE; > + ch->group_type = UBIFS_NO_NODE_GROUP; > + ch->padding[0] = ch->padding[1] = 0; > + ch->sqnum = cpu_to_le64(0); > + ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); > + > + pad_len -= UBIFS_PAD_NODE_SZ; > + pad_node->pad_len = cpu_to_le32(pad_len); > + > + crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8, > + UBIFS_PAD_NODE_SZ - 8); > + ch->crc = cpu_to_le32(crc); > + > + memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); > + } else if (pad_len > 0) > + memset(buf, UBIFS_PADDING_BYTE, pad_len); > + > + return wlen; > +} > + > +/** > + * write_node - write a node to a LEB. > + * @node: node > + * @len: node length > + * @lnum: LEB number > + */ > +static int write_node(void *node, int len, int lnum) > +{ > + prepare_node(node, len); > + > + memcpy(leb_buf, node, len); > + > + len = do_pad(leb_buf, len); > + > + return write_leb(lnum, len, leb_buf); > +} > + > +/** > + * calc_dark - calculate LEB dark space size. > + * @c: the UBIFS file-system description object > + * @spc: amount of free and dirty space in the LEB > + * > + * This function calculates amount of dark space in an LEB which has @spc bytes > + * of free and dirty space. Returns the calculations result. > + * > + * Dark space is the space which is not always usable - it depends on which > + * nodes are written in which order. E.g., if an LEB has only 512 free bytes, > + * it is dark space, because it cannot fit a large data node. So UBIFS cannot > + * count on this LEB and treat these 512 bytes as usable because it is not true > + * if, for example, only big chunks of uncompressible data will be written to > + * the FS. > + */ > +static int calc_dark(struct ubifs_info *c, int spc) > +{ > + if (spc < c->dark_wm) > + return spc; > + > + /* > + * If we have slightly more space then the dark space watermark, we can > + * anyway safely assume it we'll be able to write a node of the > + * smallest size there. > + */ > + if (spc - c->dark_wm < (int)MIN_WRITE_SZ) > + return spc - MIN_WRITE_SZ; > + > + return c->dark_wm; > +} > + > +/** > + * set_lprops - set the LEB property values for a LEB. > + * @lnum: LEB number > + * @offs: end offset of data in the LEB > + * @flags: LEB property flags > + */ > +static void set_lprops(int lnum, int offs, int flags) > +{ > + int i = lnum - c->main_first, free, dirty; > + int a = max_t(int, c->min_io_size, 8); > + > + free = c->leb_size - ALIGN(offs, a); > + dirty = c->leb_size - free - ALIGN(offs, 8); > + dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, > + flags); > + if (i < c->main_lebs) { > + c->lpt[i].free = free; > + c->lpt[i].dirty = dirty; > + c->lpt[i].flags = flags; > + } > + c->lst.total_free += free; > + c->lst.total_dirty += dirty; > + if (flags & LPROPS_INDEX) > + c->lst.idx_lebs += 1; > + else { > + int spc; > + > + spc = free + dirty; > + if (spc < c->dead_wm) > + c->lst.total_dead += spc; > + else > + c->lst.total_dark += calc_dark(c, spc); > + c->lst.total_used += c->leb_size - spc; > + } > +} > + > +/** > + * add_to_index - add a node key and position to the index. > + * @key: node key > + * @lnum: node LEB number > + * @offs: node offset > + * @len: node length > + */ > +static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, > + int len) > +{ > + struct idx_entry *e; > + > + dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); > + e = malloc(sizeof(struct idx_entry)); > + if (!e) > + return err_msg("out of memory"); > + e->next = NULL; > + e->prev = idx_list_last; > + e->key = *key; > + e->name = name; > + e->lnum = lnum; > + e->offs = offs; > + e->len = len; > + if (!idx_list_first) > + idx_list_first = e; > + if (idx_list_last) > + idx_list_last->next = e; > + idx_list_last = e; > + idx_cnt += 1; > + return 0; > +} > + > +/** > + * flush_nodes - write the current head and move the head to the next LEB. > + */ > +static int flush_nodes(void) > +{ > + int len, err; > + > + if (!head_offs) > + return 0; > + len = do_pad(leb_buf, head_offs); > + err = write_leb(head_lnum, len, leb_buf); > + if (err) > + return err; > + set_lprops(head_lnum, head_offs, head_flags); > + head_lnum += 1; > + head_offs = 0; > + return 0; > +} > + > +/** > + * reserve_space - reserve space for a node on the head. > + * @len: node length > + * @lnum: LEB number is returned here > + * @offs: offset is returned here > + */ > +static int reserve_space(int len, int *lnum, int *offs) > +{ > + int err; > + > + if (len > c->leb_size - head_offs) { > + err = flush_nodes(); > + if (err) > + return err; > + } > + *lnum = head_lnum; > + *offs = head_offs; > + head_offs += ALIGN(len, 8); > + return 0; > +} > + > +/** > + * add_node - write a node to the head. > + * @key: node key > + * @node: node > + * @len: node length > + */ > +static int add_node(union ubifs_key *key, char *name, void *node, int len) > +{ > + int err, lnum, offs; > + > + prepare_node(node, len); > + > + err = reserve_space(len, &lnum, &offs); > + if (err) > + return err; > + > + memcpy(leb_buf + offs, node, len); > + memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); > + > + add_to_index(key, name, lnum, offs, len); > + > + return 0; > +} > + > +/** > + * add_inode_with_data - write an inode. > + * @st: stat information of source inode > + * @inum: target inode number > + * @data: inode data (for special inodes e.g. symlink path etc) > + * @data_len: inode data length > + * @flags: source inode flags > + */ > +static int add_inode_with_data(struct stat *st, ino_t inum, void *data, > + unsigned int data_len, int flags) > +{ > + struct ubifs_ino_node *ino = node_buf; > + union ubifs_key key; > + int len, use_flags = 0; > + > + if (c->default_compr != UBIFS_COMPR_NONE) > + use_flags |= UBIFS_COMPR_FL; > + if (flags & FS_COMPR_FL) > + use_flags |= UBIFS_COMPR_FL; > + if (flags & FS_SYNC_FL) > + use_flags |= UBIFS_SYNC_FL; > + if (flags & FS_IMMUTABLE_FL) > + use_flags |= UBIFS_IMMUTABLE_FL; > + if (flags & FS_APPEND_FL) > + use_flags |= UBIFS_APPEND_FL; > + if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) > + use_flags |= UBIFS_DIRSYNC_FL; > + > + memset(ino, 0, UBIFS_INO_NODE_SZ); > + > + ino_key_init(&key, inum); > + ino->ch.node_type = UBIFS_INO_NODE; > + key_write(&key, &ino->key); > + ino->creat_sqnum = cpu_to_le64(creat_sqnum); > + ino->size = cpu_to_le64(st->st_size); > + ino->nlink = cpu_to_le32(st->st_nlink); > + /* > + * The time fields are updated assuming the default time granularity > + * of 1 second. To support finer granularities, utime() would be needed. > + */ > + ino->atime_sec = cpu_to_le64(st->st_atime); > + ino->ctime_sec = cpu_to_le64(st->st_ctime); > + ino->mtime_sec = cpu_to_le64(st->st_mtime); > + ino->atime_nsec = 0; > + ino->ctime_nsec = 0; > + ino->mtime_nsec = 0; > + ino->uid = cpu_to_le32(st->st_uid); > + ino->gid = cpu_to_le32(st->st_gid); > + ino->mode = cpu_to_le32(st->st_mode); > + ino->flags = cpu_to_le32(use_flags); > + ino->data_len = cpu_to_le32(data_len); > + ino->compr_type = cpu_to_le16(c->default_compr); > + if (data_len) > + memcpy(&ino->data, data, data_len); > + > + len = UBIFS_INO_NODE_SZ + data_len; > + > + return add_node(&key, NULL, ino, len); > +} > + > +/** > + * add_inode - write an inode. > + * @st: stat information of source inode > + * @inum: target inode number > + * @flags: source inode flags > + */ > +static int add_inode(struct stat *st, ino_t inum, int flags) > +{ > + return add_inode_with_data(st, inum, NULL, 0, flags); > +} > + > +/** > + * add_dir_inode - write an inode for a directory. > + * @dir: source directory > + * @inum: target inode number > + * @size: target directory size > + * @nlink: target directory link count > + * @st: struct stat object describing attributes (except size and nlink) of the > + * target inode to create > + * > + * Note, this function may be called with %NULL @dir, when the directory which > + * is being created does not exist at the host file system, but is defined by > + * the device table. > + */ > +static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, > + struct stat *st) > +{ > + int fd, flags = 0; > + > + st->st_size = size; > + st->st_nlink = nlink; > + > + if (dir) { > + fd = dirfd(dir); > + if (fd == -1) > + return sys_err_msg("dirfd failed"); > + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) > + flags = 0; > + } > + > + return add_inode(st, inum, flags); > +} > + > +/** > + * add_dev_inode - write an inode for a character or block device. > + * @st: stat information of source inode > + * @inum: target inode number > + * @flags: source inode flags > + */ > +static int add_dev_inode(struct stat *st, ino_t inum, int flags) > +{ > + union ubifs_dev_desc dev; > + > + dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); > + return add_inode_with_data(st, inum, &dev, 8, flags); > +} > + > +/** > + * add_symlink_inode - write an inode for a symbolic link. > + * @path_name: path name of symbolic link inode itself (not the link target) > + * @st: stat information of source inode > + * @inum: target inode number > + * @flags: source inode flags > + */ > +static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, > + int flags) > +{ > + char buf[UBIFS_MAX_INO_DATA + 2]; > + ssize_t len; > + > + /* Take the symlink as is */ > + len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); > + if (len <= 0) > + return sys_err_msg("readlink failed for %s", path_name); > + if (len > UBIFS_MAX_INO_DATA) > + return err_msg("symlink too long for %s", path_name); > + > + return add_inode_with_data(st, inum, buf, len, flags); > +} > + > +/** > + * add_dent_node - write a directory entry node. > + * @dir_inum: target inode number of directory > + * @name: directory entry name > + * @inum: target inode number of the directory entry > + * @type: type of the target inode > + */ > +static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, > + unsigned char type) > +{ > + struct ubifs_dent_node *dent = node_buf; > + union ubifs_key key; > + struct qstr dname; > + char *kname; > + int len; > + > + dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum, > + (unsigned int)type, (unsigned long)dir_inum); > + memset(dent, 0, UBIFS_DENT_NODE_SZ); > + > + dname.name = (void *)name; > + dname.len = strlen(name); > + > + dent->ch.node_type = UBIFS_DENT_NODE; > + > + dent_key_init(c, &key, dir_inum, &dname); > + key_write(&key, dent->key); > + dent->inum = cpu_to_le64(inum); > + dent->padding1 = 0; > + dent->type = type; > + dent->nlen = cpu_to_le16(dname.len); > + memcpy(dent->name, dname.name, dname.len); > + dent->name[dname.len] = '\0'; > + > + len = UBIFS_DENT_NODE_SZ + dname.len + 1; > + > + kname = strdup(name); > + if (!kname) > + return err_msg("cannot allocate memory"); > + > + return add_node(&key, kname, dent, len); > +} > + > +/** > + * lookup_inum_mapping - add an inode mapping for link counting. > + * @dev: source device on which source inode number resides > + * @inum: source inode number > + */ > +static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) > +{ > + struct inum_mapping *im; > + unsigned int k; > + > + k = inum % HASH_TABLE_SIZE; > + im = hash_table[k]; > + while (im) { > + if (im->dev == dev && im->inum == inum) > + return im; > + im = im->next; > + } > + im = malloc(sizeof(struct inum_mapping)); > + if (!im) > + return NULL; > + im->next = hash_table[k]; > + im->prev = NULL; > + im->dev = dev; > + im->inum = inum; > + im->use_inum = 0; > + im->use_nlink = 0; > + if (hash_table[k]) > + hash_table[k]->prev = im; > + hash_table[k] = im; > + return im; > +} > + > +/** > + * all_zero - does a buffer contain only zero bytes. > + * @buf: buffer > + * @len: buffer length > + */ > +static int all_zero(void *buf, int len) > +{ > + unsigned char *p = buf; > + > + while (len--) > + if (*p++ != 0) > + return 0; > + return 1; > +} > + > +/** > + * add_file - write the data of a file and its inode to the output file. > + * @path_name: source path name > + * @st: source inode stat information > + * @inum: target inode number > + * @flags: source inode flags > + */ > +static int add_file(const char *path_name, struct stat *st, ino_t inum, > + int flags) > +{ > + struct ubifs_data_node *dn = node_buf; > + void *buf = block_buf; > + loff_t file_size = 0; > + ssize_t ret, bytes_read; > + union ubifs_key key; > + int fd, dn_len, err, compr_type, use_compr; > + unsigned int block_no = 0; > + size_t out_len; > + > + fd = open(path_name, O_RDONLY | O_LARGEFILE); > + if (fd == -1) > + return sys_err_msg("failed to open file '%s'", path_name); > + do { > + /* Read next block */ > + bytes_read = 0; > + do { > + ret = read(fd, buf + bytes_read, > + UBIFS_BLOCK_SIZE - bytes_read); > + if (ret == -1) { > + sys_err_msg("failed to read file '%s'", > + path_name); > + close(fd); > + return 1; > + } > + bytes_read += ret; > + } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); > + if (bytes_read == 0) > + break; > + file_size += bytes_read; > + /* Skip holes */ > + if (all_zero(buf, bytes_read)) { > + block_no += 1; > + continue; > + } > + /* Make data node */ > + memset(dn, 0, UBIFS_DATA_NODE_SZ); > + data_key_init(&key, inum, block_no++); > + dn->ch.node_type = UBIFS_DATA_NODE; > + key_write(&key, &dn->key); > + dn->size = cpu_to_le32(bytes_read); > + out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; > + if (c->default_compr == UBIFS_COMPR_NONE && > + (flags & FS_COMPR_FL)) > + use_compr = UBIFS_COMPR_LZO; > + else > + use_compr = c->default_compr; > + compr_type = compress_data(buf, bytes_read, &dn->data, > + &out_len, use_compr); > + dn->compr_type = cpu_to_le16(compr_type); > + dn_len = UBIFS_DATA_NODE_SZ + out_len; > + /* Add data node to file system */ > + err = add_node(&key, NULL, dn, dn_len); > + if (err) { > + close(fd); > + return err; > + } > + } while (ret != 0); > + if (close(fd) == -1) > + return sys_err_msg("failed to close file '%s'", path_name); > + if (file_size != st->st_size) > + return err_msg("file size changed during writing file '%s'", > + path_name); > + return add_inode(st, inum, flags); > +} > + > +/** > + * add_non_dir - write a non-directory to the output file. > + * @path_name: source path name > + * @inum: target inode number is passed and returned here (due to link counting) > + * @nlink: number of links if known otherwise zero > + * @type: UBIFS inode type is returned here > + * @st: struct stat object containing inode attributes which should be use when > + * creating the UBIFS inode > + */ > +static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, > + unsigned char *type, struct stat *st) > +{ > + int fd, flags = 0; > + > + dbg_msg(2, "%s", path_name); > + > + if (S_ISREG(st->st_mode)) { > + fd = open(path_name, O_RDONLY); > + if (fd == -1) > + return sys_err_msg("failed to open file '%s'", > + path_name); > + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) > + flags = 0; > + if (close(fd) == -1) > + return sys_err_msg("failed to close file '%s'", > + path_name); > + *type = UBIFS_ITYPE_REG; > + } else if (S_ISCHR(st->st_mode)) > + *type = UBIFS_ITYPE_CHR; > + else if (S_ISBLK(st->st_mode)) > + *type = UBIFS_ITYPE_BLK; > + else if (S_ISLNK(st->st_mode)) > + *type = UBIFS_ITYPE_LNK; > + else if (S_ISSOCK(st->st_mode)) > + *type = UBIFS_ITYPE_SOCK; > + else if (S_ISFIFO(st->st_mode)) > + *type = UBIFS_ITYPE_FIFO; > + else > + return err_msg("file '%s' has unknown inode type", path_name); > + > + if (nlink) > + st->st_nlink = nlink; > + else if (st->st_nlink > 1) { > + /* > + * If the number of links is greater than 1, then add this file > + * later when we know the number of links that we actually have. > + * For now, we just put the inode mapping in the hash table. > + */ > + struct inum_mapping *im; > + > + im = lookup_inum_mapping(st->st_dev, st->st_ino); > + if (!im) > + return err_msg("out of memory"); > + if (im->use_nlink == 0) { > + /* New entry */ > + im->use_inum = *inum; > + im->use_nlink = 1; > + im->path_name = malloc(strlen(path_name) + 1); > + if (!im->path_name) > + return err_msg("out of memory"); > + strcpy(im->path_name, path_name); > + } else { > + /* Existing entry */ > + *inum = im->use_inum; > + im->use_nlink += 1; > + /* Return unused inode number */ > + c->highest_inum -= 1; > + } > + > + memcpy(&im->st, st, sizeof(struct stat)); > + return 0; > + } else > + st->st_nlink = 1; > + > + creat_sqnum = ++c->max_sqnum; > + > + if (S_ISREG(st->st_mode)) > + return add_file(path_name, st, *inum, flags); > + if (S_ISCHR(st->st_mode)) > + return add_dev_inode(st, *inum, flags); > + if (S_ISBLK(st->st_mode)) > + return add_dev_inode(st, *inum, flags); > + if (S_ISLNK(st->st_mode)) > + return add_symlink_inode(path_name, st, *inum, flags); > + if (S_ISSOCK(st->st_mode)) > + return add_inode(st, *inum, flags); > + if (S_ISFIFO(st->st_mode)) > + return add_inode(st, *inum, flags); > + > + return err_msg("file '%s' has unknown inode type", path_name); > +} > + > +/** > + * add_directory - write a directory tree to the output file. > + * @dir_name: directory path name > + * @dir_inum: UBIFS inode number of directory > + * @st: directory inode statistics > + * @non_existing: non-zero if this function is called for a directory which > + * does not exist on the host file-system and it is being > + * created because it is defined in the device table file. > + */ > +static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, > + int non_existing) > +{ > + struct dirent *entry; > + DIR *dir = NULL; > + int err = 0; > + loff_t size = UBIFS_INO_NODE_SZ; > + char *name = NULL; > + unsigned int nlink = 2; > + struct path_htbl_element *ph_elt; > + struct name_htbl_element *nh_elt = NULL; > + struct hashtable_itr *itr; > + ino_t inum; > + unsigned char type; > + unsigned long long dir_creat_sqnum = ++c->max_sqnum; > + > + dbg_msg(2, "%s", dir_name); > + if (!non_existing) { > + dir = opendir(dir_name); > + if (dir == NULL) > + return sys_err_msg("cannot open directory '%s'", > + dir_name); > + } > + > + /* > + * Check whether this directory contains files which should be > + * added/changed because they were specified in the device table. > + * @ph_elt will be non-zero if yes. > + */ > + ph_elt = devtbl_find_path(dir_name + root_len - 1); > + > + /* > + * Before adding the directory itself, we have to iterate over all the > + * entries the device table adds to this directory and create them. > + */ > + for (; !non_existing;) { > + struct stat dent_st; > + > + errno = 0; > + entry = readdir(dir); > + if (!entry) { > + if (errno == 0) > + break; > + sys_err_msg("error reading directory '%s'", dir_name); > + err = -1; > + break; > + } > + > + if (strcmp(".", entry->d_name) == 0) > + continue; > + if (strcmp("..", entry->d_name) == 0) > + continue; > + > + if (ph_elt) > + /* > + * This directory was referred to at the device table > + * file. Check if this directory entry is referred at > + * too. > + */ > + nh_elt = devtbl_find_name(ph_elt, entry->d_name); > + > + /* > + * We are going to create the file corresponding to this > + * directory entry (@entry->d_name). We use 'struct stat' > + * object to pass information about file attributes (actually > + * only about UID, GID, mode, major, and minor). Get attributes > + * for this file from the UBIFS rootfs on the host. > + */ > + free(name); > + name = make_path(dir_name, entry->d_name); > + if (lstat(name, &dent_st) == -1) { > + sys_err_msg("lstat failed for file '%s'", name); > + goto out_free; > + } > + > + if (squash_owner) > + /* > + * Squash UID/GID. But the device table may override > + * this. > + */ > + dent_st.st_uid = dent_st.st_gid = 0; > + > + /* > + * And if the device table describes the same file, override > + * the attributes. However, this is not allowed for device node > + * files. > + */ > + if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) > + goto out_free; > + > + inum = ++c->highest_inum; > + > + if (S_ISDIR(dent_st.st_mode)) { > + err = add_directory(name, inum, &dent_st, 0); > + if (err) > + goto out_free; > + nlink += 1; > + type = UBIFS_ITYPE_DIR; > + } else { > + err = add_non_dir(name, &inum, 0, &type, &dent_st); > + if (err) > + goto out_free; > + } > + > + err = add_dent_node(dir_inum, entry->d_name, inum, type); > + if (err) > + goto out_free; > + size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, > + 8); > + } > + > + /* > + * OK, we have created all files in this directory (recursively), let's > + * also create all files described in the device table. All t > + */ > + nh_elt = first_name_htbl_element(ph_elt, &itr); > + while (nh_elt) { > + struct stat fake_st; > + > + /* > + * We prohibit creating regular files using the device table, > + * the device table may only re-define attributes of regular > + * files. > + */ > + if (S_ISREG(nh_elt->mode)) { > + err_msg("Bad device table entry %s/%s - it is " > + "prohibited to create regular files " > + "via device table", > + strcmp(ph_elt->path, "/") ? ph_elt->path : "", > + nh_elt->name); > + goto out_free; > + } > + > + memcpy(&fake_st, &root_st, sizeof(struct stat)); > + fake_st.st_uid = nh_elt->uid; > + fake_st.st_uid = nh_elt->uid; > + fake_st.st_mode = nh_elt->mode; > + fake_st.st_rdev = nh_elt->dev; > + fake_st.st_nlink = 1; > + > + free(name); > + name = make_path(dir_name, nh_elt->name); > + inum = ++c->highest_inum; > + > + if (S_ISDIR(nh_elt->mode)) { > + err = add_directory(name, inum, &fake_st, 1); > + if (err) > + goto out_free; > + nlink += 1; > + type = UBIFS_ITYPE_DIR; > + } else { > + err = add_non_dir(name, &inum, 0, &type, &fake_st); > + if (err) > + goto out_free; > + } > + > + err = add_dent_node(dir_inum, nh_elt->name, inum, type); > + if (err) > + goto out_free; > + size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); > + > + nh_elt = next_name_htbl_element(ph_elt, &itr); > + } > + > + creat_sqnum = dir_creat_sqnum; > + > + err = add_dir_inode(dir, dir_inum, size, nlink, st); > + if (err) > + goto out_free; > + > + free(name); > + if (!non_existing && closedir(dir) == -1) > + return sys_err_msg("error closing directory '%s'", dir_name); > + > + return 0; > + > +out_free: > + free(name); > + if (!non_existing) > + closedir(dir); > + return -1; > +} > + > +/** > + * add_multi_linked_files - write all the files for which we counted links. > + */ > +static int add_multi_linked_files(void) > +{ > + int i, err; > + > + for (i = 0; i < HASH_TABLE_SIZE; i++) { > + struct inum_mapping *im; > + unsigned char type = 0; > + > + for (im = hash_table[i]; im; im = im->next) { > + dbg_msg(2, "%s", im->path_name); > + err = add_non_dir(im->path_name, &im->use_inum, > + im->use_nlink, &type, &im->st); > + if (err) > + return err; > + } > + } > + return 0; > +} > + > +/** > + * write_data - write the files and directories. > + */ > +static int write_data(void) > +{ > + int err; > + mode_t mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; > + > + if (root) { > + err = stat(root, &root_st); > + if (err) > + return sys_err_msg("bad root file-system directory '%s'", > + root); > + } else { > + root_st.st_mtime = time(NULL); > + root_st.st_atime = root_st.st_ctime = root_st.st_mtime; > + root_st.st_mode = mode; > + } > + > + head_flags = 0; > + err = add_directory(root, UBIFS_ROOT_INO, &root_st, !root); > + if (err) > + return err; > + err = add_multi_linked_files(); > + if (err) > + return err; > + return flush_nodes(); > +} > + > +static int namecmp(const char *name1, const char *name2) > +{ > + size_t len1 = strlen(name1), len2 = strlen(name2); > + size_t clen = (len1 < len2) ? len1 : len2; > + int cmp; > + > + cmp = memcmp(name1, name2, clen); > + if (cmp) > + return cmp; > + return (len1 < len2) ? -1 : 1; > +} > + > +static int cmp_idx(const void *a, const void *b) > +{ > + const struct idx_entry *e1 = *(const struct idx_entry **)a; > + const struct idx_entry *e2 = *(const struct idx_entry **)b; > + int cmp; > + > + cmp = keys_cmp(&e1->key, &e2->key); > + if (cmp) > + return cmp; > + return namecmp(e1->name, e2->name); > +} > + > +/** > + * add_idx_node - write an index node to the head. > + * @node: index node > + * @child_cnt: number of children of this index node > + */ > +static int add_idx_node(void *node, int child_cnt) > +{ > + int err, lnum, offs, len; > + > + len = ubifs_idx_node_sz(c, child_cnt); > + > + prepare_node(node, len); > + > + err = reserve_space(len, &lnum, &offs); > + if (err) > + return err; > + > + memcpy(leb_buf + offs, node, len); > + memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); > + > + c->old_idx_sz += ALIGN(len, 8); > + > + dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, > + c->old_idx_sz); > + > + /* The last index node written will be the root */ > + c->zroot.lnum = lnum; > + c->zroot.offs = offs; > + c->zroot.len = len; > + > + return 0; > +} > + > +/** > + * write_index - write out the index. > + */ > +static int write_index(void) > +{ > + size_t sz, i, cnt, idx_sz, pstep, bcnt; > + struct idx_entry **idx_ptr, **p; > + struct ubifs_idx_node *idx; > + struct ubifs_branch *br; > + int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err; > + > + dbg_msg(1, "leaf node count: %zd", idx_cnt); > + > + /* Reset the head for the index */ > + head_flags = LPROPS_INDEX; > + /* Allocate index node */ > + idx_sz = ubifs_idx_node_sz(c, c->fanout); > + idx = malloc(idx_sz); > + if (!idx) > + return err_msg("out of memory"); > + /* Make an array of pointers to sort the index list */ > + sz = idx_cnt * sizeof(struct idx_entry *); > + if (sz / sizeof(struct idx_entry *) != idx_cnt) { > + free(idx); > + return err_msg("index is too big (%zu entries)", idx_cnt); > + } > + idx_ptr = malloc(sz); > + if (!idx_ptr) { > + free(idx); > + return err_msg("out of memory - needed %zu bytes for index", > + sz); > + } > + idx_ptr[0] = idx_list_first; > + for (i = 1; i < idx_cnt; i++) > + idx_ptr[i] = idx_ptr[i - 1]->next; > + qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); > + /* Write level 0 index nodes */ > + cnt = idx_cnt / c->fanout; > + if (idx_cnt % c->fanout) > + cnt += 1; > + p = idx_ptr; > + blnum = head_lnum; > + boffs = head_offs; > + for (i = 0; i < cnt; i++) { > + /* > + * Calculate the child count. All index nodes are created full > + * except for the last index node on each row. > + */ > + if (i == cnt - 1) { > + child_cnt = idx_cnt % c->fanout; > + if (child_cnt == 0) > + child_cnt = c->fanout; > + } else > + child_cnt = c->fanout; > + memset(idx, 0, idx_sz); > + idx->ch.node_type = UBIFS_IDX_NODE; > + idx->child_cnt = cpu_to_le16(child_cnt); > + idx->level = cpu_to_le16(0); > + for (j = 0; j < child_cnt; j++, p++) { > + br = ubifs_idx_branch(c, idx, j); > + key_write_idx(&(*p)->key, &br->key); > + br->lnum = cpu_to_le32((*p)->lnum); > + br->offs = cpu_to_le32((*p)->offs); > + br->len = cpu_to_le32((*p)->len); > + } > + add_idx_node(idx, child_cnt); > + } > + /* Write level 1 index nodes and above */ > + level = 0; > + pstep = 1; > + while (cnt > 1) { > + /* > + * 'blast_len' is the length of the last index node in the level > + * below. > + */ > + blast_len = ubifs_idx_node_sz(c, child_cnt); > + /* 'bcnt' is the number of index nodes in the level below */ > + bcnt = cnt; > + /* 'cnt' is the number of index nodes in this level */ > + cnt = (cnt + c->fanout - 1) / c->fanout; > + if (cnt == 0) > + cnt = 1; > + level += 1; > + /* > + * The key of an index node is the same as the key of its first > + * child. Thus we can get the key by stepping along the bottom > + * level 'p' with an increasing large step 'pstep'. > + */ > + p = idx_ptr; > + pstep *= c->fanout; > + for (i = 0; i < cnt; i++) { > + /* > + * Calculate the child count. All index nodes are > + * created full except for the last index node on each > + * row. > + */ > + if (i == cnt - 1) { > + child_cnt = bcnt % c->fanout; > + if (child_cnt == 0) > + child_cnt = c->fanout; > + } else > + child_cnt = c->fanout; > + memset(idx, 0, idx_sz); > + idx->ch.node_type = UBIFS_IDX_NODE; > + idx->child_cnt = cpu_to_le16(child_cnt); > + idx->level = cpu_to_le16(level); > + for (j = 0; j < child_cnt; j++) { > + size_t bn = i * c->fanout + j; > + > + /* > + * The length of the index node in the level > + * below is 'idx_sz' except when it is the last > + * node on the row. i.e. all the others on the > + * row are full. > + */ > + if (bn == bcnt - 1) > + blen = blast_len; > + else > + blen = idx_sz; > + /* > + * 'blnum' and 'boffs' hold the position of the > + * index node on the level below. > + */ > + if (boffs + blen > c->leb_size) { > + blnum += 1; > + boffs = 0; > + } > + /* > + * Fill in the branch with the key and position > + * of the index node from the level below. > + */ > + br = ubifs_idx_branch(c, idx, j); > + key_write_idx(&(*p)->key, &br->key); > + br->lnum = cpu_to_le32(blnum); > + br->offs = cpu_to_le32(boffs); > + br->len = cpu_to_le32(blen); > + /* > + * Step to the next index node on the level > + * below. > + */ > + boffs += ALIGN(blen, 8); > + p += pstep; > + } > + add_idx_node(idx, child_cnt); > + } > + } > + > + /* Free stuff */ > + for (i = 0; i < idx_cnt; i++) > + free(idx_ptr[i]); > + free(idx_ptr); > + free(idx); > + > + dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, > + c->zroot.len); > + > + /* Set the index head */ > + c->ihead_lnum = head_lnum; > + c->ihead_offs = ALIGN(head_offs, c->min_io_size); > + dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); > + > + /* Flush the last index LEB */ > + err = flush_nodes(); > + if (err) > + return err; > + > + return 0; > +} > + > +/** > + * set_gc_lnum - set the LEB number reserved for the garbage collector. > + */ > +static int set_gc_lnum(void) > +{ > + int err; > + > + c->gc_lnum = head_lnum++; > + err = write_empty_leb(c->gc_lnum); > + if (err) > + return err; > + set_lprops(c->gc_lnum, 0, 0); > + c->lst.empty_lebs += 1; > + return 0; > +} > + > +/** > + * finalize_leb_cnt - now that we know how many LEBs we used. > + */ > +static int finalize_leb_cnt(void) > +{ > + c->leb_cnt = head_lnum; > + if (c->leb_cnt > c->max_leb_cnt) > + return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); > + c->main_lebs = c->leb_cnt - c->main_first; > + if (verbose) { > + printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); > + printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); > + printf("\tlog_lebs: %d\n", c->log_lebs); > + printf("\tlpt_lebs: %d\n", c->lpt_lebs); > + printf("\torph_lebs: %d\n", c->orph_lebs); > + printf("\tmain_lebs: %d\n", c->main_lebs); > + printf("\tgc lebs: %d\n", 1); > + printf("\tindex lebs: %d\n", c->lst.idx_lebs); > + printf("\tleb_cnt: %d\n", c->leb_cnt); > + } > + dbg_msg(1, "total_free: %llu", c->lst.total_free); > + dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); > + dbg_msg(1, "total_used: %llu", c->lst.total_used); > + dbg_msg(1, "total_dead: %llu", c->lst.total_dead); > + dbg_msg(1, "total_dark: %llu", c->lst.total_dark); > + dbg_msg(1, "index size: %llu", c->old_idx_sz); > + dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); > + return 0; > +} > + > +/** > + * write_super - write the super block. > + */ > +static int write_super(void) > +{ > + struct ubifs_sb_node sup; > + > + memset(&sup, 0, UBIFS_SB_NODE_SZ); > + > + sup.ch.node_type = UBIFS_SB_NODE; > + sup.key_hash = c->key_hash_type; > + sup.min_io_size = cpu_to_le32(c->min_io_size); > + sup.leb_size = cpu_to_le32(c->leb_size); > + sup.leb_cnt = cpu_to_le32(c->leb_cnt); > + sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); > + sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); > + sup.log_lebs = cpu_to_le32(c->log_lebs); > + sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); > + sup.orph_lebs = cpu_to_le32(c->orph_lebs); > + sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); > + sup.fanout = cpu_to_le32(c->fanout); > + sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); > + sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); > + sup.default_compr = cpu_to_le16(c->default_compr); > + sup.rp_size = cpu_to_le64(c->rp_size); > + sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); > + uuid_generate_random(sup.uuid); > + if (verbose) { > + char s[40]; > + > + uuid_unparse_upper(sup.uuid, s); > + printf("\tUUID: %s\n", s); > + } > + if (c->big_lpt) > + sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); > + if (c->space_fixup) > + sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP); > + > + return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); > +} > + > +/** > + * write_master - write the master node. > + */ > +static int write_master(void) > +{ > + struct ubifs_mst_node mst; > + int err; > + > + memset(&mst, 0, UBIFS_MST_NODE_SZ); > + > + mst.ch.node_type = UBIFS_MST_NODE; > + mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); > + mst.highest_inum = cpu_to_le64(c->highest_inum); > + mst.cmt_no = cpu_to_le64(0); > + mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); > + mst.root_lnum = cpu_to_le32(c->zroot.lnum); > + mst.root_offs = cpu_to_le32(c->zroot.offs); > + mst.root_len = cpu_to_le32(c->zroot.len); > + mst.gc_lnum = cpu_to_le32(c->gc_lnum); > + mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); > + mst.ihead_offs = cpu_to_le32(c->ihead_offs); > + mst.index_size = cpu_to_le64(c->old_idx_sz); > + mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); > + mst.lpt_offs = cpu_to_le32(c->lpt_offs); > + mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); > + mst.nhead_offs = cpu_to_le32(c->nhead_offs); > + mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); > + mst.ltab_offs = cpu_to_le32(c->ltab_offs); > + mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); > + mst.lsave_offs = cpu_to_le32(c->lsave_offs); > + mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); > + mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); > + mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); > + mst.total_free = cpu_to_le64(c->lst.total_free); > + mst.total_dirty = cpu_to_le64(c->lst.total_dirty); > + mst.total_used = cpu_to_le64(c->lst.total_used); > + mst.total_dead = cpu_to_le64(c->lst.total_dead); > + mst.total_dark = cpu_to_le64(c->lst.total_dark); > + mst.leb_cnt = cpu_to_le32(c->leb_cnt); > + > + err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); > + if (err) > + return err; > + > + err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); > + if (err) > + return err; > + > + return 0; > +} > + > +/** > + * write_log - write an empty log. > + */ > +static int write_log(void) > +{ > + struct ubifs_cs_node cs; > + int err, i, lnum; > + > + lnum = UBIFS_LOG_LNUM; > + > + cs.ch.node_type = UBIFS_CS_NODE; > + cs.cmt_no = cpu_to_le64(0); > + > + err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); > + if (err) > + return err; > + > + lnum += 1; > + > + for (i = 1; i < c->log_lebs; i++, lnum++) { > + err = write_empty_leb(lnum); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +/** > + * write_lpt - write the LEB properties tree. > + */ > +static int write_lpt(void) > +{ > + int err, lnum; > + > + err = create_lpt(c); > + if (err) > + return err; > + > + lnum = c->nhead_lnum + 1; > + while (lnum <= c->lpt_last) { > + err = write_empty_leb(lnum++); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +/** > + * write_orphan_area - write an empty orphan area. > + */ > +static int write_orphan_area(void) > +{ > + int err, i, lnum; > + > + lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; > + for (i = 0; i < c->orph_lebs; i++, lnum++) { > + err = write_empty_leb(lnum); > + if (err) > + return err; > + } > + return 0; > +} > + > +/** > + * check_volume_empty - check if the UBI volume is empty. > + * > + * This function checks if the UBI volume is empty by looking if its LEBs are > + * mapped or not. > + * > + * Returns %0 in case of success, %1 is the volume is not empty, > + * and a negative error code in case of failure. > + */ > +static int check_volume_empty(void) > +{ > + int lnum, err; > + > + for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) { > + err = ubi_is_mapped(out_fd, lnum); > + if (err < 0) > + return err; > + if (err == 1) > + return 1; > + } > + return 0; > +} > + > +/** > + * open_target - open the output target. > + * > + * Open the output target. The target can be an UBI volume > + * or a file. > + * > + * Returns %0 in case of success and %-1 in case of failure. > + */ > +static int open_target(void) > +{ > + if (out_ubi) { > + out_fd = open(output, O_RDWR | O_EXCL); > + > + if (out_fd == -1) > + return sys_err_msg("cannot open the UBI volume '%s'", > + output); > + if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) > + return sys_err_msg("ubi_set_property failed"); > + > + if (!yes && check_volume_empty()) { > + if (!prompt("UBI volume is not empty. Format anyways?", false)) > + return err_msg("UBI volume is not empty"); > + } > + } else { > + out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, > + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); > + if (out_fd == -1) > + return sys_err_msg("cannot create output file '%s'", > + output); > + } > + return 0; > +} > + > + > +/** > + * close_target - close the output target. > + * > + * Close the output target. If the target was an UBI > + * volume, also close libubi. > + * > + * Returns %0 in case of success and %-1 in case of failure. > + */ > +static int close_target(void) > +{ > + if (ubi) > + libubi_close(ubi); > + if (out_fd >= 0 && close(out_fd) == -1) > + return sys_err_msg("cannot close the target '%s'", output); > + if (output) > + free(output); > + return 0; > +} > + > +/** > + * init - initialize things. > + */ > +static int init(void) > +{ > + int err, i, main_lebs, big_lpt = 0, sz; > + > + c->highest_inum = UBIFS_FIRST_INO; > + > + c->jhead_cnt = 1; > + > + main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; > + main_lebs -= c->log_lebs + c->orph_lebs; > + > + err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); > + if (err) > + return err; > + > + c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + > + c->orph_lebs; > + head_lnum = c->main_first; > + head_offs = 0; > + > + c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; > + c->lpt_last = c->lpt_first + c->lpt_lebs - 1; > + > + c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); > + if (!c->lpt) > + return err_msg("unable to allocate LPT"); > + > + c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); > + if (!c->ltab) > + return err_msg("unable to allocate LPT ltab"); > + > + /* Initialize LPT's own lprops */ > + for (i = 0; i < c->lpt_lebs; i++) { > + c->ltab[i].free = c->leb_size; > + c->ltab[i].dirty = 0; > + } > + > + c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); > + c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); > + dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); > + > + leb_buf = malloc(c->leb_size); > + if (!leb_buf) > + return err_msg("out of memory"); > + > + node_buf = malloc(NODE_BUFFER_SIZE); > + if (!node_buf) > + return err_msg("out of memory"); > + > + block_buf = malloc(UBIFS_BLOCK_SIZE); > + if (!block_buf) > + return err_msg("out of memory"); > + > + sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; > + hash_table = malloc(sz); > + if (!hash_table) > + return err_msg("out of memory"); > + memset(hash_table, 0, sz); > + > + err = init_compression(); > + if (err) > + return err; > + > + return 0; > +} > + > +static void destroy_hash_table(void) > +{ > + int i; > + > + for (i = 0; i < HASH_TABLE_SIZE; i++) { > + struct inum_mapping *im, *q; > + > + for (im = hash_table[i]; im; ) { > + q = im; > + im = im->next; > + free(q->path_name); > + free(q); > + } > + } > +} > + > +/** > + * deinit - deinitialize things. > + */ > +static void deinit(void) > +{ > + free(c->lpt); > + free(c->ltab); > + free(leb_buf); > + free(node_buf); > + free(block_buf); > + destroy_hash_table(); > + free(hash_table); > + destroy_compression(); > + free_devtable_info(); > +} > + > +/** > + * mkfs - make the file system. > + * > + * Each on-flash area has a corresponding function to create it. The order of > + * the functions reflects what information must be known to complete each stage. > + * As a consequence the output file is not written sequentially. No effort has > + * been made to make efficient use of memory or to allow for the possibility of > + * incremental updates to the output file. > + */ > +static int mkfs(void) > +{ > + int err = 0; > + > + err = init(); > + if (err) > + goto out; > + > + err = write_data(); > + if (err) > + goto out; > + > + err = set_gc_lnum(); > + if (err) > + goto out; > + > + err = write_index(); > + if (err) > + goto out; > + > + err = finalize_leb_cnt(); > + if (err) > + goto out; > + > + err = write_lpt(); > + if (err) > + goto out; > + > + err = write_super(); > + if (err) > + goto out; > + > + err = write_master(); > + if (err) > + goto out; > + > + err = write_log(); > + if (err) > + goto out; > + > + err = write_orphan_area(); > + > +out: > + deinit(); > + return err; > +} > + > +int main(int argc, char *argv[]) > +{ > + int err; > + > + err = get_options(argc, argv); > + if (err) > + return err; > + > + err = open_target(); > + if (err) > + return err; > + > + err = mkfs(); > + if (err) { > + close_target(); > + return err; > + } > + > + err = close_target(); > + if (err) > + return err; > + > + if (verbose) > + printf("Success!\n"); > + > + return 0; > +} > diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h > new file mode 100644 > index 0000000..944a159 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h > @@ -0,0 +1,150 @@ > +/* > + * Copyright (C) 2008 Nokia Corporation. > + * Copyright (C) 2008 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 version 2 as published by > + * the Free Software Foundation. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Artem Bityutskiy > + * Adrian Hunter > + * Zoltan Sogor > + */ > + > +#ifndef __MKFS_UBIFS_H__ > +#define __MKFS_UBIFS_H__ > + > +#include <unistd.h> > +#include <stdlib.h> > +#include <stdio.h> > +#include <limits.h> > +#include <string.h> > +#include <stdint.h> > +#include <endian.h> > +#include <byteswap.h> > +#include <linux/types.h> > +#include <linux/fs.h> > + > +#include <getopt.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/ioctl.h> > +#include <fcntl.h> > +#include <dirent.h> > +#include <errno.h> > +#include <libgen.h> > +#include <ctype.h> > +#include <uuid/uuid.h> > +#include <sys/file.h> > + > +#include <mtd/ubifs-media.h> > + > +/* common.h requires the PROGRAM_NAME macro */ > +#define PROGRAM_NAME "mkfs.ubifs" > +#include "common.h" > + > +#include "libubi.h" > +#include "defs.h" > +#include "crc16.h" > +#include "ubifs.h" > +#include "key.h" > +#include "lpt.h" > +#include "compr.h" > + > +/* > + * Compression flags are duplicated so that compr.c can compile without ubifs.h. > + * Here we make sure they are the same. > + */ > +#if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE > +#error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE > +#endif > +#if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO > +#error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO > +#endif > +#if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB > +#error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB > +#endif > + > +extern int verbose; > +extern int debug_level; > + > +#define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ > + printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ > +} while(0) > + > +#define err_msg(fmt, ...) ({ \ > + fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ > + -1; \ > +}) > + > +#define sys_err_msg(fmt, ...) ({ \ > + int err_ = errno; \ > + fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ > + fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ > + -1; \ > +}) > + > +/** > + * struct path_htbl_element - an element of the path hash table. > + * @path: the UBIFS path the element describes (the key of the element) > + * @name_htbl: one more (nested) hash table containing names of all > + * files/directories/device nodes which should be created at this > + * path > + * > + * See device table handling for more information. > + */ > +struct path_htbl_element { > + const char *path; > + struct hashtable *name_htbl; > +}; > + > +/** > + * struct name_htbl_element - an element in the name hash table > + * @name: name of the file/directory/device node (the key of the element) > + * @mode: accsess rights and file type > + * @uid: user ID > + * @gid: group ID > + * @major: device node major number > + * @minor: device node minor number > + * > + * This is an element of the name hash table. Name hash table sits in the path > + * hash table elements and describes file names which should be created/changed > + * at this path. > + */ > +struct name_htbl_element { > + const char *name; > + unsigned int mode; > + unsigned int uid; > + unsigned int gid; > + dev_t dev; > +}; > + > +extern struct ubifs_info info_; > + > +struct hashtable_itr; > + > +int write_leb(int lnum, int len, void *buf); > +int parse_devtable(const char *tbl_file); > +struct path_htbl_element *devtbl_find_path(const char *path); > +struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, > + const char *name); > +int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, > + struct name_htbl_element *nh_elt); > +struct name_htbl_element * > +first_name_htbl_element(struct path_htbl_element *ph_elt, > + struct hashtable_itr **itr); > +struct name_htbl_element * > +next_name_htbl_element(struct path_htbl_element *ph_elt, > + struct hashtable_itr **itr); > +void free_devtable_info(void); > + > +#endif > diff --git a/ubifs-utils/mkfs.ubifs/ubifs.h b/ubifs-utils/mkfs.ubifs/ubifs.h > new file mode 100644 > index 0000000..434b651 > --- /dev/null > +++ b/ubifs-utils/mkfs.ubifs/ubifs.h > @@ -0,0 +1,441 @@ > +/* > + * This file is part of UBIFS. > + * > + * Copyright (C) 2008 Nokia Corporation. > + * Copyright (C) 2008 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 version 2 as published by > + * the Free Software Foundation. > + * > + * 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., 51 > + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + * > + * Authors: Artem Bityutskiy > + * Adrian Hunter > + * Zoltan Sogor > + */ > + > +#ifndef __UBIFS_H__ > +#define __UBIFS_H__ > + > +/* Maximum logical eraseblock size in bytes */ > +#define UBIFS_MAX_LEB_SZ (2*1024*1024) > + > +/* Minimum amount of data UBIFS writes to the flash */ > +#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) > + > +/* Largest key size supported in this implementation */ > +#define CUR_MAX_KEY_LEN UBIFS_SK_LEN > + > +/* > + * There is no notion of truncation key because truncation nodes do not exist > + * in TNC. However, when replaying, it is handy to introduce fake "truncation" > + * keys for truncation nodes because the code becomes simpler. So we define > + * %UBIFS_TRUN_KEY type. > + */ > +#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT > + > +/* The below union makes it easier to deal with keys */ > +union ubifs_key > +{ > + uint8_t u8[CUR_MAX_KEY_LEN]; > + uint32_t u32[CUR_MAX_KEY_LEN/4]; > + uint64_t u64[CUR_MAX_KEY_LEN/8]; > + __le32 j32[CUR_MAX_KEY_LEN/4]; > +}; > + > +/* > + * LEB properties flags. > + * > + * LPROPS_UNCAT: not categorized > + * LPROPS_DIRTY: dirty > 0, not index > + * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index > + * LPROPS_FREE: free > 0, not empty, not index > + * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs > + * LPROPS_EMPTY: LEB is empty, not taken > + * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken > + * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken > + * LPROPS_CAT_MASK: mask for the LEB categories above > + * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) > + * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) > + */ > +enum { > + LPROPS_UNCAT = 0, > + LPROPS_DIRTY = 1, > + LPROPS_DIRTY_IDX = 2, > + LPROPS_FREE = 3, > + LPROPS_HEAP_CNT = 3, > + LPROPS_EMPTY = 4, > + LPROPS_FREEABLE = 5, > + LPROPS_FRDI_IDX = 6, > + LPROPS_CAT_MASK = 15, > + LPROPS_TAKEN = 16, > + LPROPS_INDEX = 32, > +}; > + > +/** > + * struct ubifs_lprops - logical eraseblock properties. > + * @free: amount of free space in bytes > + * @dirty: amount of dirty space in bytes > + * @flags: LEB properties flags (see above) > + */ > +struct ubifs_lprops > +{ > + int free; > + int dirty; > + int flags; > +}; > + > +/** > + * struct ubifs_lpt_lprops - LPT logical eraseblock properties. > + * @free: amount of free space in bytes > + * @dirty: amount of dirty space in bytes > + */ > +struct ubifs_lpt_lprops > +{ > + int free; > + int dirty; > +}; > + > +struct ubifs_nnode; > + > +/** > + * struct ubifs_cnode - LEB Properties Tree common node. > + * @parent: parent nnode > + * @cnext: next cnode to commit > + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) > + * @iip: index in parent > + * @level: level in the tree (zero for pnodes, greater than zero for nnodes) > + * @num: node number > + */ > +struct ubifs_cnode > +{ > + struct ubifs_nnode *parent; > + struct ubifs_cnode *cnext; > + unsigned long flags; > + int iip; > + int level; > + int num; > +}; > + > +/** > + * struct ubifs_pnode - LEB Properties Tree leaf node. > + * @parent: parent nnode > + * @cnext: next cnode to commit > + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) > + * @iip: index in parent > + * @level: level in the tree (always zero for pnodes) > + * @num: node number > + * @lprops: LEB properties array > + */ > +struct ubifs_pnode > +{ > + struct ubifs_nnode *parent; > + struct ubifs_cnode *cnext; > + unsigned long flags; > + int iip; > + int level; > + int num; > + struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; > +}; > + > +/** > + * struct ubifs_nbranch - LEB Properties Tree internal node branch. > + * @lnum: LEB number of child > + * @offs: offset of child > + * @nnode: nnode child > + * @pnode: pnode child > + * @cnode: cnode child > + */ > +struct ubifs_nbranch > +{ > + int lnum; > + int offs; > + union > + { > + struct ubifs_nnode *nnode; > + struct ubifs_pnode *pnode; > + struct ubifs_cnode *cnode; > + }; > +}; > + > +/** > + * struct ubifs_nnode - LEB Properties Tree internal node. > + * @parent: parent nnode > + * @cnext: next cnode to commit > + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) > + * @iip: index in parent > + * @level: level in the tree (always greater than zero for nnodes) > + * @num: node number > + * @nbranch: branches to child nodes > + */ > +struct ubifs_nnode > +{ > + struct ubifs_nnode *parent; > + struct ubifs_cnode *cnext; > + unsigned long flags; > + int iip; > + int level; > + int num; > + struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; > +}; > + > +/** > + * struct ubifs_lp_stats - statistics of eraseblocks in the main area. > + * @empty_lebs: number of empty LEBs > + * @taken_empty_lebs: number of taken LEBs > + * @idx_lebs: number of indexing LEBs > + * @total_free: total free space in bytes > + * @total_dirty: total dirty space in bytes > + * @total_used: total used space in bytes (includes only data LEBs) > + * @total_dead: total dead space in bytes (includes only data LEBs) > + * @total_dark: total dark space in bytes (includes only data LEBs) > + */ > +struct ubifs_lp_stats { > + int empty_lebs; > + int taken_empty_lebs; > + int idx_lebs; > + long long total_free; > + long long total_dirty; > + long long total_used; > + long long total_dead; > + long long total_dark; > +}; > + > +/** > + * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. > + * @key: key > + * @znode: znode address in memory > + * @lnum: LEB number of the indexing node > + * @offs: offset of the indexing node within @lnum > + * @len: target node length > + */ > +struct ubifs_zbranch > +{ > + union ubifs_key key; > + struct ubifs_znode *znode; > + int lnum; > + int offs; > + int len; > +}; > + > +/** > + * struct ubifs_znode - in-memory representation of an indexing node. > + * @parent: parent znode or NULL if it is the root > + * @cnext: next znode to commit > + * @flags: flags > + * @time: last access time (seconds) > + * @level: level of the entry in the TNC tree > + * @child_cnt: count of child znodes > + * @iip: index in parent's zbranch array > + * @alt: lower bound of key range has altered i.e. child inserted at slot 0 > + * @zbranch: array of znode branches (@c->fanout elements) > + */ > +struct ubifs_znode > +{ > + struct ubifs_znode *parent; > + struct ubifs_znode *cnext; > + unsigned long flags; > + unsigned long time; > + int level; > + int child_cnt; > + int iip; > + int alt; > +#ifdef CONFIG_UBIFS_FS_DEBUG > + int lnum, offs, len; > +#endif > + struct ubifs_zbranch zbranch[]; > +}; > + > +/** > + * struct ubifs_info - UBIFS file-system description data structure > + * (per-superblock). > + * > + * @highest_inum: highest used inode number > + * @max_sqnum: current global sequence number > + * > + * @jhead_cnt: count of journal heads > + * @max_bud_bytes: maximum number of bytes allowed in buds > + * > + * @zroot: zbranch which points to the root index node and znode > + * @ihead_lnum: LEB number of index head > + * @ihead_offs: offset of index head > + * > + * @log_lebs: number of logical eraseblocks in the log > + * @lpt_lebs: number of LEBs used for lprops table > + * @lpt_first: first LEB of the lprops table area > + * @lpt_last: last LEB of the lprops table area > + * @main_lebs: count of LEBs in the main area > + * @main_first: first LEB of the main area > + * @default_compr: default compression type > + * @favor_lzo: favor LZO compression method > + * @favor_percent: lzo vs. zlib threshold used in case favor LZO > + * > + * @key_hash_type: type of the key hash > + * @key_hash: direntry key hash function > + * @key_fmt: key format > + * @key_len: key length > + * @fanout: fanout of the index tree (number of links per indexing node) > + * > + * @min_io_size: minimal input/output unit size > + * @leb_size: logical eraseblock size in bytes > + * @leb_cnt: count of logical eraseblocks > + * @max_leb_cnt: maximum count of logical eraseblocks > + * > + * @old_idx_sz: size of index on flash > + * @lst: lprops statistics > + * > + * @dead_wm: LEB dead space watermark > + * @dark_wm: LEB dark space watermark > + * > + * @di: UBI device information > + * @vi: UBI volume information > + * > + * @gc_lnum: LEB number used for garbage collection > + * @rp_size: reserved pool size > + * > + * @space_bits: number of bits needed to record free or dirty space > + * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT > + * @lpt_offs_bits: number of bits needed to record an offset in the LPT > + * @lpt_spc_bits: number of bits needed to space in the LPT > + * @pcnt_bits: number of bits needed to record pnode or nnode number > + * @lnum_bits: number of bits needed to record LEB number > + * @nnode_sz: size of on-flash nnode > + * @pnode_sz: size of on-flash pnode > + * @ltab_sz: size of on-flash LPT lprops table > + * @lsave_sz: size of on-flash LPT save table > + * @pnode_cnt: number of pnodes > + * @nnode_cnt: number of nnodes > + * @lpt_hght: height of the LPT > + * > + * @lpt_lnum: LEB number of the root nnode of the LPT > + * @lpt_offs: offset of the root nnode of the LPT > + * @nhead_lnum: LEB number of LPT head > + * @nhead_offs: offset of LPT head > + * @big_lpt: flag that LPT is too big to write whole during commit > + * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up > + * @lpt_sz: LPT size > + * > + * @ltab_lnum: LEB number of LPT's own lprops table > + * @ltab_offs: offset of LPT's own lprops table > + * @lpt: lprops table > + * @ltab: LPT's own lprops table > + * @lsave_cnt: number of LEB numbers in LPT's save table > + * @lsave_lnum: LEB number of LPT's save table > + * @lsave_offs: offset of LPT's save table > + * @lsave: LPT's save table > + * @lscan_lnum: LEB number of last LPT scan > + */ > +struct ubifs_info > +{ > + ino_t highest_inum; > + unsigned long long max_sqnum; > + > + int jhead_cnt; > + long long max_bud_bytes; > + > + struct ubifs_zbranch zroot; > + int ihead_lnum; > + int ihead_offs; > + > + int log_lebs; > + int lpt_lebs; > + int lpt_first; > + int lpt_last; > + int orph_lebs; > + int main_lebs; > + int main_first; > + int default_compr; > + int favor_lzo; > + int favor_percent; > + > + uint8_t key_hash_type; > + uint32_t (*key_hash)(const char *str, int len); > + int key_fmt; > + int key_len; > + int fanout; > + > + int min_io_size; > + int leb_size; > + int leb_cnt; > + int max_leb_cnt; > + > + unsigned long long old_idx_sz; > + struct ubifs_lp_stats lst; > + > + int dead_wm; > + int dark_wm; > + > + struct ubi_dev_info di; > + struct ubi_vol_info vi; > + > + int gc_lnum; > + long long rp_size; > + > + int space_bits; > + int lpt_lnum_bits; > + int lpt_offs_bits; > + int lpt_spc_bits; > + int pcnt_bits; > + int lnum_bits; > + int nnode_sz; > + int pnode_sz; > + int ltab_sz; > + int lsave_sz; > + int pnode_cnt; > + int nnode_cnt; > + int lpt_hght; > + > + int lpt_lnum; > + int lpt_offs; > + int nhead_lnum; > + int nhead_offs; > + int big_lpt; > + int space_fixup; > + long long lpt_sz; > + > + int ltab_lnum; > + int ltab_offs; > + struct ubifs_lprops *lpt; > + struct ubifs_lpt_lprops *ltab; > + int lsave_cnt; > + int lsave_lnum; > + int lsave_offs; > + int *lsave; > + int lscan_lnum; > + > +}; > + > +/** > + * ubifs_idx_node_sz - return index node size. > + * @c: the UBIFS file-system description object > + * @child_cnt: number of children of this index node > + */ > +static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) > +{ > + return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; > +} > + > +/** > + * ubifs_idx_branch - return pointer to an index branch. > + * @c: the UBIFS file-system description object > + * @idx: index node > + * @bnum: branch number > + */ > +static inline > +struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, > + const struct ubifs_idx_node *idx, > + int bnum) > +{ > + return (struct ubifs_branch *)((void *)idx->branches + > + (UBIFS_BRANCH_SZ + c->key_len) * bnum); > +} > + > +#endif /* __UBIFS_H__ */ >
diff --git a/MAKEDEV b/MAKEDEV deleted file mode 100755 index b59e90e..0000000 --- a/MAKEDEV +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -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 index f4ce313..bb40929 100644 --- a/Makefile +++ b/Makefile @@ -16,24 +16,32 @@ endif TESTS = tests -MTD_BINS = \ - ftl_format flash_erase nanddump doc_loadbios \ - ftl_check mkfs.jffs2 flash_lock flash_unlock \ - flash_otp_info flash_otp_dump flash_otp_lock flash_otp_write \ - mtd_debug flashcp nandwrite nandtest mtdpart \ - jffs2dump \ - nftldump nftl_format docfdisk \ - rfddump rfdformat \ - serve_image recv_image \ - sumtool jffs2reader +MISC_BINS = \ + ftl_format doc_loadbios ftl_check mtd_debug docfdisk \ + serve_image recv_image mtdpart UBI_BINS = \ ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock - -BINS = $(MTD_BINS) -BINS += mkfs.ubifs/mkfs.ubifs +UBIFS_BINS = \ + mkfs.ubifs/mkfs.ubifs +JFFSX_BINS = \ + mkfs.jffs2 sumtool jffs2reader jffs2dump +FLASH_BINS = \ + flash_erase flash_lock flash_unlock flash_otp_info flash_otp_dump \ + flash_otp_lock flash_otp_write flashcp +NAND_BINS = \ + nanddump nandwrite nandtest nftldump nftl_format +NOR_BINS = \ + rfddump rfdformat + +BINS = $(addprefix misc-utils/,$(MISC_BINS)) BINS += $(addprefix ubi-utils/,$(UBI_BINS)) -SCRIPTS = flash_eraseall +BINS += $(addprefix ubifs-utils/,$(UBIFS_BINS)) +BINS += $(addprefix jffsX-utils/,$(JFFSX_BINS)) +BINS += $(addprefix flash-utils/,$(FLASH_BINS)) +BINS += $(addprefix nand-utils/,$(NAND_BINS)) +BINS += $(addprefix nor-utils/,$(NOR_BINS)) +SCRIPTS = $(addprefix flash-utils/,flash_eraseall) TARGETS = $(BINS) TARGETS += lib/libmtd.a @@ -61,11 +69,11 @@ endif rm -f $(BUILDDIR)/include/version.h $(MAKE) -C $(TESTS) clean -install:: $(addprefix $(BUILDDIR)/,${BINS}) ${SCRIPTS} +install:: $(addprefix $(BUILDDIR)/,${BINS} ${SCRIPTS}) mkdir -p ${DESTDIR}/${SBINDIR} install -m 0755 $^ ${DESTDIR}/${SBINDIR}/ mkdir -p ${DESTDIR}/${MANDIR}/man1 - install -m 0644 mkfs.jffs2.1 ${DESTDIR}/${MANDIR}/man1/ + install -m 0644 jffsX-utils/mkfs.jffs2.1 ${DESTDIR}/${MANDIR}/man1/ -gzip -9f ${DESTDIR}/${MANDIR}/man1/*.1 tests:: @@ -85,13 +93,17 @@ $(BUILDDIR)/include/version.h.tmp: # Utils in top level # obj-mkfs.jffs2 = compr_rtime.o compr_zlib.o compr_lzo.o compr.o rbtree.o -LDFLAGS_mkfs.jffs2 = $(ZLIBLDFLAGS) $(LZOLDFLAGS) +LDFLAGS_mkfs.jffs2 = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(CPPFLAGS) LDLIBS_mkfs.jffs2 = -lz $(LZOLDLIBS) LDFLAGS_jffs2reader = $(ZLIBLDFLAGS) $(LZOLDFLAGS) LDLIBS_jffs2reader = -lz $(LZOLDLIBS) -$(foreach v,$(MTD_BINS),$(eval $(call mkdep,,$(v)))) +$(foreach v,$(MISC_BINS),$(eval $(call mkdep,misc-utils/,$(v)))) +$(foreach v,$(JFFSX_BINS),$(eval $(call mkdep,jffsX-utils/,$(v)))) +$(foreach v,$(FLASH_BINS),$(eval $(call mkdep,flash-utils/,$(v)))) +$(foreach v,$(NAND_BINS),$(eval $(call mkdep,nand-utils/,$(v)))) +$(foreach v,$(NOR_BINS),$(eval $(call mkdep,nor-utils/,$(v)))) # # Common libmtd @@ -100,15 +112,6 @@ obj-libmtd.a = libmtd.o libmtd_legacy.o libcrc32.o libfec.o $(call _mkdep,lib/,libmtd.a) # -# Utils in mkfs.ubifs subdir -# -obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \ - hashtable/hashtable.o hashtable/hashtable_itr.o -LDFLAGS_mkfs.ubifs = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS) -LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid -$(call mkdep,mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a) - -# # Utils in ubi-utils/ subdir # obj-libiniparser.a = libiniparser.o dictionary.o @@ -122,3 +125,12 @@ obj-ubiformat = libubigen.a libscan.a $(foreach v,libubi.a libubigen.a libiniparser.a libscan.a,$(eval $(call _mkdep,ubi-utils/,$(v)))) $(foreach v,$(UBI_BINS),$(eval $(call mkdep,ubi-utils/,$(v),libubi.a ubiutils-common.o))) + +# +# Utils in ubifs-utils subdir +# +obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \ + hashtable/hashtable.o hashtable/hashtable_itr.o +LDFLAGS_mkfs.ubifs = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS) +LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid +$(call mkdep,ubifs-utils/mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a) diff --git a/compr.c b/compr.c deleted file mode 100644 index cb4432e..0000000 --- a/compr.c +++ /dev/null @@ -1,538 +0,0 @@ -/* - * 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. - */ - -#include "compr.h" -#include <string.h> -#include <stdlib.h> -#include <linux/jffs2.h> - -#define FAVOUR_LZO_PERCENT 80 - -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)) { - 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; - } - } - } -} - -/* - * Return 1 to use this compression - */ -static int jffs2_is_best_compression(struct jffs2_compressor *this, - struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) -{ - switch (jffs2_compression_mode) { - case JFFS2_COMPR_MODE_SIZE: - if (bestsize > size) - return 1; - return 0; - case JFFS2_COMPR_MODE_FAVOURLZO: - if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) - return 1; - if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) - return 1; - if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) - return 1; - if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) - return 1; - - return 0; - } - /* Shouldn't happen */ - return 0; -} - -/* 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); - 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_FAVOURLZO: - case JFFS2_COMPR_MODE_SIZE: - orig_slen = *datalen; - orig_dlen = *cdatalen; - list_for_each_entry(this, &jffs2_compressor_list, list) { - uint32_t needed_buf_size; - - if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) - needed_buf_size = orig_slen + jffs2_compression_check; - else - needed_buf_size = orig_dlen + jffs2_compression_check; - - /* 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 < needed_buf_size) && (this->compr_buf)) { - free(this->compr_buf); - this->compr_buf_size=0; - this->compr_buf=NULL; - } - if (!this->compr_buf) { - tmp_buf = malloc(needed_buf_size); - 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); - 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) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) - && (*cdatalen < *datalen)) { - 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: unknown 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; - case JFFS2_COMPR_MODE_FAVOURLZO: - act_buf += sprintf(act_buf, "favourlzo"); - break; - default: - act_buf += sprintf(act_buf, "unknown"); - 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; - } - if (!strcmp("favourlzo", name)) { - jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; - 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 -#ifdef CONFIG_JFFS2_LZO - jffs2_lzo_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 -#ifdef CONFIG_JFFS2_LZO - jffs2_lzo_exit(); -#endif - return 0; -} diff --git a/compr.h b/compr.h deleted file mode 100644 index a21e935..0000000 --- a/compr.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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. - */ - -#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 CONFIG_JFFS2_LZO - -#define JFFS2_RUBINMIPS_PRIORITY 10 -#define JFFS2_DYNRUBIN_PRIORITY 20 -#define JFFS2_RTIME_PRIORITY 50 -#define JFFS2_ZLIB_PRIORITY 60 -#define JFFS2_LZO_PRIORITY 80 - -#define JFFS2_COMPR_MODE_NONE 0 -#define JFFS2_COMPR_MODE_PRIORITY 1 -#define JFFS2_COMPR_MODE_SIZE 2 -#define JFFS2_COMPR_MODE_FAVOURLZO 3 - -#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 */ - const char *name; - char compr; /* JFFS2_COMPR_XXX */ - int (*compress)(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *srclen, uint32_t *destlen); - int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, - uint32_t cdatalen, uint32_t datalen); - 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 -#ifdef CONFIG_JFFS2_LZO -int jffs2_lzo_init(void); -void jffs2_lzo_exit(void); -#endif - -#endif /* __JFFS2_COMPR_H__ */ diff --git a/compr_lzo.c b/compr_lzo.c deleted file mode 100644 index d2e2afc..0000000 --- a/compr_lzo.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * JFFS2 LZO Compression Interface. - * - * Copyright (C) 2007 Nokia Corporation. All rights reserved. - * - * Author: Richard Purdie <rpurdie@openedhand.com> - * - * 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. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <stdint.h> -#include <stdio.h> -#include <string.h> - -#ifndef WITHOUT_LZO -#include <asm/types.h> -#include <linux/jffs2.h> -#include <lzo/lzo1x.h> -#include "compr.h" - -extern int page_size; - -static void *lzo_mem; -static void *lzo_compress_buf; - -/* - * Note about LZO compression. - * - * We want to use the _999_ compression routine which gives better compression - * rates at the expense of time. Decompression time is unaffected. We might as - * well use the standard lzo library routines for this but they will overflow - * the destination buffer since they don't check the destination size. - * - * We therefore compress to a temporary buffer and copy if it will fit. - * - */ -static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) -{ - lzo_uint compress_size; - int ret; - - ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); - - if (ret != LZO_E_OK) - return -1; - - if (compress_size > *dstlen) - return -1; - - memcpy(cpage_out, lzo_compress_buf, compress_size); - *dstlen = compress_size; - - return 0; -} - -static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen) -{ - int ret; - lzo_uint dl; - - ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); - - if (ret != LZO_E_OK || dl != destlen) - return -1; - - return 0; -} - -static struct jffs2_compressor jffs2_lzo_comp = { - .priority = JFFS2_LZO_PRIORITY, - .name = "lzo", - .compr = JFFS2_COMPR_LZO, - .compress = &jffs2_lzo_cmpr, - .decompress = &jffs2_lzo_decompress, - .disabled = 1, -}; - -int jffs2_lzo_init(void) -{ - int ret; - - lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); - if (!lzo_mem) - return -1; - - /* Worse case LZO compression size from their FAQ */ - lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); - if (!lzo_compress_buf) { - free(lzo_mem); - return -1; - } - - ret = jffs2_register_compressor(&jffs2_lzo_comp); - if (ret < 0) { - free(lzo_compress_buf); - free(lzo_mem); - } - - return ret; -} - -void jffs2_lzo_exit(void) -{ - jffs2_unregister_compressor(&jffs2_lzo_comp); - free(lzo_compress_buf); - free(lzo_mem); -} - -#else - -int jffs2_lzo_init(void) -{ - return 0; -} - -void jffs2_lzo_exit(void) -{ -} - -#endif diff --git a/compr_rtime.c b/compr_rtime.c deleted file mode 100644 index f24379d..0000000 --- a/compr_rtime.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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. - * - * 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) -{ - short positions[256]; - int outpos = 0; - int pos=0; - - memset(positions,0,sizeof(positions)); - - while (pos < (*sourcelen) && outpos+2 <= (*dstlen)) { - 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, - __attribute__((unused)) uint32_t srclen, uint32_t destlen) -{ - 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 deleted file mode 100644 index 1f94628..0000000 --- a/compr_zlib.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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. - */ - -#define PROGRAM_NAME "compr_zlib" - -#include <stdint.h> -#define crc32 __zlib_crc32 -#include <zlib.h> -#undef crc32 -#include <stdio.h> -#include <asm/types.h> -#include <linux/jffs2.h> -#include "common.h" -#include "compr.h" - -/* 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 - -static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) -{ - 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; -} - -static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen) -{ - z_stream strm; - int ret; - - strm.zalloc = (void *)0; - strm.zfree = (void *)0; - - if (Z_OK != inflateInit(&strm)) { - return 1; - } - strm.next_in = data_in; - strm.avail_in = srclen; - strm.total_in = 0; - - strm.next_out = cpage_out; - strm.avail_out = destlen; - strm.total_out = 0; - - while((ret = inflate(&strm, Z_FINISH)) == Z_OK) - ; - - inflateEnd(&strm); - return 0; -} - -static struct jffs2_compressor jffs2_zlib_comp = { - .priority = JFFS2_ZLIB_PRIORITY, - .name = "zlib", - .disabled = 0, - .compr = JFFS2_COMPR_ZLIB, - .compress = &jffs2_zlib_compress, - .decompress = &jffs2_zlib_decompress, -}; - -int jffs2_zlib_init(void) -{ - return jffs2_register_compressor(&jffs2_zlib_comp); -} - -void jffs2_zlib_exit(void) -{ - jffs2_unregister_compressor(&jffs2_zlib_comp); -} diff --git a/device_table.txt b/device_table.txt deleted file mode 100644 index 394a62b..0000000 --- a/device_table.txt +++ /dev/null @@ -1,128 +0,0 @@ -# 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 deleted file mode 100644 index b999c73..0000000 --- a/doc_loadbios.c +++ /dev/null @@ -1,150 +0,0 @@ -#define PROGRAM_NAME "doc_loadbios" - -#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> - -#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 deleted file mode 100644 index 9956de5..0000000 --- a/docfdisk.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * docfdisk.c: Modify INFTL partition tables - * - * 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 PROGRAM_NAME "docfdisk" - -#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> -#include <mtd_swab.h> - -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", - PROGRAM_NAME); - 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.writesize) * 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.writesize) { - if (ioctl(fd, MEMREADOOB, &oob)) { - perror("ioctl(MEMREADOOB)"); - return 1; - } - oob.start += meminfo.writesize; - 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.writesize) { - 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.writesize, oob.start)) < 0) { - perror("Write page"); - printf("Your MediaHeader may be hosed. UHOH!\n"); - return 1; - } - if (ret != meminfo.writesize) { - printf("Short write!\n"); - printf("Your MediaHeader may be hosed. UHOH!\n"); - return 1; - } - - oob.start += meminfo.writesize; - oob.ptr += meminfo.oobsize; - buf += meminfo.writesize; - } - - printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); - return 0; -} diff --git a/fectest.c b/fectest.c deleted file mode 100644 index fd577f3..0000000 --- a/fectest.c +++ /dev/null @@ -1,91 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <stdio.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include "mcast_image.h" -#include <crc32.h> - -#define ERASE_SIZE 131072 -#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE) -#define DROPS 8 - -int main(void) -{ - int i, j; - unsigned char buf[NR_PKTS * PKT_SIZE]; - unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE]; - struct fec_parms *fec; - unsigned char *srcs[NR_PKTS]; - unsigned char *pkt[NR_PKTS + DROPS]; - int pktnr[NR_PKTS + DROPS]; - struct timeval then, now; - - srand(3453); - for (i=0; i < sizeof(buf); i++) - if (i < ERASE_SIZE) - buf[i] = rand(); - else - buf[i] = 0; - - for (i=0; i < NR_PKTS + DROPS; i++) - srcs[i] = buf + (i * PKT_SIZE); - - for (i=0; i < NR_PKTS + DROPS; i++) { - pkt[i] = malloc(PKT_SIZE); - pktnr[i] = -1; - } - fec = fec_new(NR_PKTS, NR_PKTS + DROPS); - if (!fec) { - printf("fec_init() failed\n"); - exit(1); - } - j = 0; - for (i=0; i < NR_PKTS + DROPS; i++) { -#if 1 - if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 ) - continue; -#endif - if (i == 69 || i == 93 || i == 103) - continue; - fec_encode(fec, srcs, pkt[j], i, PKT_SIZE); - pktnr[j] = i; - j++; - } - gettimeofday(&then, NULL); - if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) { - printf("Decode failed\n"); - exit(1); - } - - for (i=0; i < NR_PKTS; i++) - memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE); - gettimeofday(&now, NULL); - now.tv_sec -= then.tv_sec; - now.tv_usec -= then.tv_usec; - if (now.tv_usec < 0) { - now.tv_usec += 1000000; - now.tv_sec--; - } - - if (memcmp(pktbuf, buf, ERASE_SIZE)) { - int fd; - printf("Compare failed\n"); - fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd >= 0) - write(fd, buf, ERASE_SIZE); - close(fd); - fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd >= 0) - write(fd, pktbuf, ERASE_SIZE); - - exit(1); - } - - printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec); - return 0; -} diff --git a/flash-utils/flash_erase.c b/flash-utils/flash_erase.c new file mode 100644 index 0000000..933373a --- /dev/null +++ b/flash-utils/flash_erase.c @@ -0,0 +1,295 @@ +/* flash_erase.c -- erase MTD devices + + Copyright (C) 2000 Arcom Control System Ltd + Copyright (C) 2010 Mike Frysinger <vapier@gentoo.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. + + 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 PROGRAM_NAME "flash_erase" + +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/types.h> + +#include <common.h> +#include <crc32.h> +#include <libmtd.h> + +#include <mtd/mtd-user.h> +#include <mtd/jffs2-user.h> + +static const char *mtd_device; + +static int quiet; /* true -- don't output progress */ +static int jffs2; /* format for jffs2 usage */ +static int noskipbad; /* do not skip bad blocks */ +static int unlock; /* unlock sectors before erasing */ + +static struct jffs2_unknown_node cleanmarker; +int target_endian = __BYTE_ORDER; + +static void show_progress(struct mtd_dev_info *mtd, off_t start, int eb, + int eb_start, int eb_cnt) +{ + bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIxoff_t" -- %2i %% complete ", + mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt); + fflush(stdout); +} + +static void display_help (void) +{ + printf("Usage: %s [options] MTD_DEVICE <start offset> <block count>\n" + "Erase blocks of the specified MTD device.\n" + "Specify a count of 0 to erase to end of device.\n" + "\n" + " -j, --jffs2 format the device for jffs2\n" + " -N, --noskipbad don't skip bad blocks\n" + " -u, --unlock unlock sectors before erasing\n" + " -q, --quiet do not display progress messages\n" + " --silent same as --quiet\n" + " --help display this help and exit\n" + " --version output version information and exit\n", + PROGRAM_NAME); +} + +static void display_version (void) +{ + printf("%1$s version " VERSION "\n" + "\n" + "Copyright (C) 2000 Arcom Control Systems Ltd\n" + "\n" + "%1$s comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of %1$s\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n", + PROGRAM_NAME); +} + +int main(int argc, char *argv[]) +{ + libmtd_t mtd_desc; + struct mtd_dev_info mtd; + int fd, clmpos = 0, clmlen = 8; + unsigned long long start; + unsigned int eb, eb_start, eb_cnt; + bool isNAND; + int error = 0; + off_t offset = 0; + + /* + * Process user arguments + */ + for (;;) { + int option_index = 0; + static const char *short_options = "jNqu"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"jffs2", no_argument, 0, 'j'}, + {"noskipbad", no_argument, 0, 'N'}, + {"quiet", no_argument, 0, 'q'}, + {"silent", no_argument, 0, 'q'}, + {"unlock", no_argument, 0, 'u'}, + + {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(); + return 0; + case 1: + display_version(); + return 0; + } + break; + case 'j': + jffs2 = 1; + break; + case 'N': + noskipbad = 1; + break; + case 'q': + quiet = 1; + break; + case 'u': + unlock = 1; + break; + case '?': + error = 1; + break; + } + } + switch (argc - optind) { + case 3: + mtd_device = argv[optind]; + start = simple_strtoull(argv[optind + 1], &error); + eb_cnt = simple_strtoul(argv[optind + 2], &error); + break; + default: + case 0: + errmsg("no MTD device specified"); + case 1: + errmsg("no start erase block specified"); + case 2: + errmsg("no erase block count specified"); + error = 1; + break; + } + if (error) + return errmsg("Try `--help' for more information"); + + /* + * Locate MTD and prepare for erasure + */ + mtd_desc = libmtd_open(); + if (mtd_desc == NULL) + return errmsg("can't initialize libmtd"); + + if ((fd = open(mtd_device, O_RDWR)) < 0) + return sys_errmsg("%s", mtd_device); + + if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) + return errmsg("mtd_get_dev_info failed"); + + if (jffs2 && mtd.type == MTD_MLCNANDFLASH) + return errmsg("JFFS2 cannot support MLC NAND."); + + eb_start = start / mtd.eb_size; + + isNAND = mtd.type == MTD_NANDFLASH || mtd.type == MTD_MLCNANDFLASH; + + if (jffs2) { + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + if (!isNAND) + cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker)); + else { + struct nand_oobinfo oobinfo; + + if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) + return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); + + /* Check for autoplacement */ + if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { + /* Get the position of the free bytes */ + if (!oobinfo.oobfree[0][1]) + return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); + clmpos = oobinfo.oobfree[0][0]; + clmlen = oobinfo.oobfree[0][1]; + if (clmlen > 8) + clmlen = 8; + } else { + /* Legacy mode */ + switch (mtd.oob_size) { + case 8: + clmpos = 6; + clmlen = 2; + break; + case 16: + clmpos = 8; + clmlen = 8; + break; + case 64: + clmpos = 16; + clmlen = 8; + break; + } + } + cleanmarker.totlen = cpu_to_je32(8); + } + cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); + } + + /* + * Now do the actual erasing of the MTD device + */ + if (eb_cnt == 0) + eb_cnt = (mtd.size / mtd.eb_size) - eb_start; + + for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { + offset = (off_t)eb * mtd.eb_size; + + if (!noskipbad) { + int ret = mtd_is_bad(&mtd, fd, eb); + if (ret > 0) { + verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, offset); + continue; + } else if (ret < 0) { + if (errno == EOPNOTSUPP) { + noskipbad = 1; + if (isNAND) + return errmsg("%s: Bad block check not available", mtd_device); + } else + return sys_errmsg("%s: MTD get bad block failed", mtd_device); + } + } + + show_progress(&mtd, offset, eb, eb_start, eb_cnt); + + if (unlock) { + if (mtd_unlock(&mtd, fd, eb) != 0) { + sys_errmsg("%s: MTD unlock failure", mtd_device); + continue; + } + } + + if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { + sys_errmsg("%s: MTD Erase failure", mtd_device); + continue; + } + + /* format for JFFS2 ? */ + if (!jffs2) + continue; + + /* write cleanmarker */ + if (isNAND) { + if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) { + sys_errmsg("%s: MTD writeoob failure", mtd_device); + continue; + } + } else { + if (pwrite(fd, &cleanmarker, sizeof(cleanmarker), (loff_t)offset) != sizeof(cleanmarker)) { + sys_errmsg("%s: MTD write failure", mtd_device); + continue; + } + } + verbose(!quiet, " Cleanmarker written at %"PRIxoff_t, offset); + } + show_progress(&mtd, offset, eb, eb_start, eb_cnt); + bareverbose(!quiet, "\n"); + + return 0; +} diff --git a/flash-utils/flash_eraseall b/flash-utils/flash_eraseall new file mode 100755 index 0000000..c5539b3 --- /dev/null +++ b/flash-utils/flash_eraseall @@ -0,0 +1,4 @@ +#!/bin/sh +echo "${0##*/} has been replaced by \`flash_erase <mtddev> 0 0\`; please use it" 1>&2 +[ $# -ne 0 ] && set -- "$@" 0 0 +exec flash_erase "$@" diff --git a/flash-utils/flash_lock.c b/flash-utils/flash_lock.c new file mode 100644 index 0000000..33f76c7 --- /dev/null +++ b/flash-utils/flash_lock.c @@ -0,0 +1,8 @@ +/* + * flash_{lock,unlock} + * + * utilities for locking/unlocking sectors of flash devices + */ + +#define PROGRAM_NAME "flash_lock" +#include "flash_unlock.c" diff --git a/flash-utils/flash_otp_dump.c b/flash-utils/flash_otp_dump.c new file mode 100644 index 0000000..f0c0fb9 --- /dev/null +++ b/flash-utils/flash_otp_dump.c @@ -0,0 +1,56 @@ +/* + * flash_otp_dump.c -- display One-Time-Programm data + */ + +#define PROGRAM_NAME "flash_otp_dump" + +#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", PROGRAM_NAME); + 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-utils/flash_otp_info.c b/flash-utils/flash_otp_info.c new file mode 100644 index 0000000..2061797 --- /dev/null +++ b/flash-utils/flash_otp_info.c @@ -0,0 +1,65 @@ +/* + * flash_otp_info.c -- print info about One-Time-Programm data + */ + +#define PROGRAM_NAME "flash_otp_info" + +#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", PROGRAM_NAME); + 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-utils/flash_otp_lock.c b/flash-utils/flash_otp_lock.c new file mode 100644 index 0000000..3c39a2d --- /dev/null +++ b/flash-utils/flash_otp_lock.c @@ -0,0 +1,72 @@ +/* + * flash_otp_lock.c -- lock area of One-Time-Program data + */ + +#define PROGRAM_NAME "flash_otp_lock" + +#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> +#include "common.h" + +int main(int argc,char *argv[]) +{ + int fd, val, ret, offset, size; + char *p; + + if (argc != 5 || strcmp(argv[1], "-u")) { + fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", PROGRAM_NAME); + 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", PROGRAM_NAME); + return ERANGE; + } + + size = strtoul(argv[4], &p, 0); + if (argv[4][0] == 0 || *p != 0) { + fprintf(stderr, "%s: bad size value\n", PROGRAM_NAME); + return ERANGE; + } + + printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", + argv[2], offset, offset + size); + if (prompt("Are you sure?", false)) { + 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-utils/flash_otp_write.c b/flash-utils/flash_otp_write.c new file mode 100644 index 0000000..111318d --- /dev/null +++ b/flash-utils/flash_otp_write.c @@ -0,0 +1,122 @@ +/* + * flash_otp_write.c -- write One-Time-Program data + */ + +#define PROGRAM_NAME "flash_otp_write" + +#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 <common.h> +#include <mtd/mtd-user.h> + +ssize_t xread(int fd, void *buf, size_t count) +{ + ssize_t ret, done = 0; + +retry: + ret = read(fd, buf + done, count - done); + if (ret < 0) + return ret; + + done += ret; + + if (ret == 0 /* EOF */ || done == count) + return done; + else + goto retry; +} + +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", PROGRAM_NAME); + 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 = (off_t)strtoull(argv[3], &p, 0); + if (argv[3][0] == 0 || *p != 0) { + fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME); + 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%"PRIxoff_t"\n", argv[2], offset); + + if (mtd_type_is_nand_user(&mtdInfo)) + len = mtdInfo.writesize; + else + len = 256; + + if (len > sizeof(buf)) { + printf("huh, writesize (%d) bigger than buffer (%zu)\n", + len, sizeof(buf)); + return ENOMEM; + } + + wrote = 0; + while ((size = xread(0, buf, len))) { + if (size < 0) { + perror("read()"); + return errno; + } + p = buf; + while (size > 0) { + if (mtd_type_is_nand_user(&mtdInfo)) { + /* Fill remain buffers with 0xff */ + memset(buf + size, 0xff, mtdInfo.writesize - size); + size = mtdInfo.writesize; + } + 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-utils/flash_unlock.c b/flash-utils/flash_unlock.c new file mode 100644 index 0000000..1cc8c2f --- /dev/null +++ b/flash-utils/flash_unlock.c @@ -0,0 +1,90 @@ +/* + * flash_{lock,unlock} + * + * utilities for locking/unlocking sectors of flash devices + */ + +#ifndef PROGRAM_NAME +#define PROGRAM_NAME "flash_unlock" +#define FLASH_MSG "unlock" +#define FLASH_UNLOCK 1 +#else +#define FLASH_MSG "lock" +#define FLASH_UNLOCK 0 +#endif + +#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 "common.h" +#include <mtd/mtd-user.h> + +static void usage(int status) +{ + fprintf(status ? stderr : stdout, + "Usage: %s <mtd device> [offset] [block count]\n\n" + "If offset is not specified, it defaults to 0.\n" + "If block count is not specified, it defaults to all blocks.\n", + PROGRAM_NAME); + exit(status); +} + +int main(int argc, char *argv[]) +{ + int fd, request; + struct mtd_info_user mtdInfo; + struct erase_info_user mtdLockInfo; + int count; + const char *dev; + + /* Parse command line options */ + if (argc < 2 || argc > 4) + usage(1); + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) + usage(0); + + dev = argv[1]; + + /* Get the device info to compare to command line sizes */ + fd = open(dev, O_RDWR); + if (fd < 0) + sys_errmsg_die("could not open: %s", dev); + + if (ioctl(fd, MEMGETINFO, &mtdInfo)) + sys_errmsg_die("could not get mtd info: %s", dev); + + /* Make sure user options are valid */ + if (argc > 2) + mtdLockInfo.start = strtol(argv[2], NULL, 0); + else + mtdLockInfo.start = 0; + if (mtdLockInfo.start > mtdInfo.size) + errmsg_die("%#x is beyond device size %#x", + mtdLockInfo.start, mtdInfo.size); + + if (argc > 3) { + count = strtol(argv[3], NULL, 0); + if (count == -1) + mtdLockInfo.length = mtdInfo.size; + else + mtdLockInfo.length = mtdInfo.erasesize * count; + } else + mtdLockInfo.length = mtdInfo.size; + if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size) + errmsg_die("range is more than device supports: %#x + %#x > %#x", + mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size); + + /* Finally do the operation */ + request = FLASH_UNLOCK ? MEMUNLOCK : MEMLOCK; + if (ioctl(fd, request, &mtdLockInfo)) + sys_errmsg_die("could not %s device: %s\n", + FLASH_MSG, dev); + + return 0; +} diff --git a/flash-utils/flashcp.c b/flash-utils/flashcp.c new file mode 100644 index 0000000..86334ac --- /dev/null +++ b/flash-utils/flashcp.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2d3D, Inc. + * Written by Abraham vd Merwe <abraham@2d3d.co.za> + * All rights reserved. + * + * 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. + */ + +#define PROGRAM_NAME "flashcp" + +#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(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: %1$s [ -v | --verbose ] <filename> <device>\n" + " %1$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", + PROGRAM_NAME); + + 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 *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]; + + /********************* + * 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(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 || device == NULL) + showusage(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) / 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/flash_erase.c b/flash_erase.c deleted file mode 100644 index 933373a..0000000 --- a/flash_erase.c +++ /dev/null @@ -1,295 +0,0 @@ -/* flash_erase.c -- erase MTD devices - - Copyright (C) 2000 Arcom Control System Ltd - Copyright (C) 2010 Mike Frysinger <vapier@gentoo.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. - - 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 PROGRAM_NAME "flash_erase" - -#include <inttypes.h> -#include <stdbool.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <stdint.h> -#include <getopt.h> -#include <sys/ioctl.h> -#include <sys/types.h> - -#include <common.h> -#include <crc32.h> -#include <libmtd.h> - -#include <mtd/mtd-user.h> -#include <mtd/jffs2-user.h> - -static const char *mtd_device; - -static int quiet; /* true -- don't output progress */ -static int jffs2; /* format for jffs2 usage */ -static int noskipbad; /* do not skip bad blocks */ -static int unlock; /* unlock sectors before erasing */ - -static struct jffs2_unknown_node cleanmarker; -int target_endian = __BYTE_ORDER; - -static void show_progress(struct mtd_dev_info *mtd, off_t start, int eb, - int eb_start, int eb_cnt) -{ - bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIxoff_t" -- %2i %% complete ", - mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt); - fflush(stdout); -} - -static void display_help (void) -{ - printf("Usage: %s [options] MTD_DEVICE <start offset> <block count>\n" - "Erase blocks of the specified MTD device.\n" - "Specify a count of 0 to erase to end of device.\n" - "\n" - " -j, --jffs2 format the device for jffs2\n" - " -N, --noskipbad don't skip bad blocks\n" - " -u, --unlock unlock sectors before erasing\n" - " -q, --quiet do not display progress messages\n" - " --silent same as --quiet\n" - " --help display this help and exit\n" - " --version output version information and exit\n", - PROGRAM_NAME); -} - -static void display_version (void) -{ - printf("%1$s version " VERSION "\n" - "\n" - "Copyright (C) 2000 Arcom Control Systems Ltd\n" - "\n" - "%1$s comes with NO WARRANTY\n" - "to the extent permitted by law.\n" - "\n" - "You may redistribute copies of %1$s\n" - "under the terms of the GNU General Public Licence.\n" - "See the file `COPYING' for more information.\n", - PROGRAM_NAME); -} - -int main(int argc, char *argv[]) -{ - libmtd_t mtd_desc; - struct mtd_dev_info mtd; - int fd, clmpos = 0, clmlen = 8; - unsigned long long start; - unsigned int eb, eb_start, eb_cnt; - bool isNAND; - int error = 0; - off_t offset = 0; - - /* - * Process user arguments - */ - for (;;) { - int option_index = 0; - static const char *short_options = "jNqu"; - static const struct option long_options[] = { - {"help", no_argument, 0, 0}, - {"version", no_argument, 0, 0}, - {"jffs2", no_argument, 0, 'j'}, - {"noskipbad", no_argument, 0, 'N'}, - {"quiet", no_argument, 0, 'q'}, - {"silent", no_argument, 0, 'q'}, - {"unlock", no_argument, 0, 'u'}, - - {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(); - return 0; - case 1: - display_version(); - return 0; - } - break; - case 'j': - jffs2 = 1; - break; - case 'N': - noskipbad = 1; - break; - case 'q': - quiet = 1; - break; - case 'u': - unlock = 1; - break; - case '?': - error = 1; - break; - } - } - switch (argc - optind) { - case 3: - mtd_device = argv[optind]; - start = simple_strtoull(argv[optind + 1], &error); - eb_cnt = simple_strtoul(argv[optind + 2], &error); - break; - default: - case 0: - errmsg("no MTD device specified"); - case 1: - errmsg("no start erase block specified"); - case 2: - errmsg("no erase block count specified"); - error = 1; - break; - } - if (error) - return errmsg("Try `--help' for more information"); - - /* - * Locate MTD and prepare for erasure - */ - mtd_desc = libmtd_open(); - if (mtd_desc == NULL) - return errmsg("can't initialize libmtd"); - - if ((fd = open(mtd_device, O_RDWR)) < 0) - return sys_errmsg("%s", mtd_device); - - if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) - return errmsg("mtd_get_dev_info failed"); - - if (jffs2 && mtd.type == MTD_MLCNANDFLASH) - return errmsg("JFFS2 cannot support MLC NAND."); - - eb_start = start / mtd.eb_size; - - isNAND = mtd.type == MTD_NANDFLASH || mtd.type == MTD_MLCNANDFLASH; - - if (jffs2) { - cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - if (!isNAND) - cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker)); - else { - struct nand_oobinfo oobinfo; - - if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) - return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); - - /* Check for autoplacement */ - if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { - /* Get the position of the free bytes */ - if (!oobinfo.oobfree[0][1]) - return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); - clmpos = oobinfo.oobfree[0][0]; - clmlen = oobinfo.oobfree[0][1]; - if (clmlen > 8) - clmlen = 8; - } else { - /* Legacy mode */ - switch (mtd.oob_size) { - case 8: - clmpos = 6; - clmlen = 2; - break; - case 16: - clmpos = 8; - clmlen = 8; - break; - case 64: - clmpos = 16; - clmlen = 8; - break; - } - } - cleanmarker.totlen = cpu_to_je32(8); - } - cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); - } - - /* - * Now do the actual erasing of the MTD device - */ - if (eb_cnt == 0) - eb_cnt = (mtd.size / mtd.eb_size) - eb_start; - - for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { - offset = (off_t)eb * mtd.eb_size; - - if (!noskipbad) { - int ret = mtd_is_bad(&mtd, fd, eb); - if (ret > 0) { - verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, offset); - continue; - } else if (ret < 0) { - if (errno == EOPNOTSUPP) { - noskipbad = 1; - if (isNAND) - return errmsg("%s: Bad block check not available", mtd_device); - } else - return sys_errmsg("%s: MTD get bad block failed", mtd_device); - } - } - - show_progress(&mtd, offset, eb, eb_start, eb_cnt); - - if (unlock) { - if (mtd_unlock(&mtd, fd, eb) != 0) { - sys_errmsg("%s: MTD unlock failure", mtd_device); - continue; - } - } - - if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { - sys_errmsg("%s: MTD Erase failure", mtd_device); - continue; - } - - /* format for JFFS2 ? */ - if (!jffs2) - continue; - - /* write cleanmarker */ - if (isNAND) { - if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) { - sys_errmsg("%s: MTD writeoob failure", mtd_device); - continue; - } - } else { - if (pwrite(fd, &cleanmarker, sizeof(cleanmarker), (loff_t)offset) != sizeof(cleanmarker)) { - sys_errmsg("%s: MTD write failure", mtd_device); - continue; - } - } - verbose(!quiet, " Cleanmarker written at %"PRIxoff_t, offset); - } - show_progress(&mtd, offset, eb, eb_start, eb_cnt); - bareverbose(!quiet, "\n"); - - return 0; -} diff --git a/flash_eraseall b/flash_eraseall deleted file mode 100755 index c5539b3..0000000 --- a/flash_eraseall +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -echo "${0##*/} has been replaced by \`flash_erase <mtddev> 0 0\`; please use it" 1>&2 -[ $# -ne 0 ] && set -- "$@" 0 0 -exec flash_erase "$@" diff --git a/flash_lock.c b/flash_lock.c deleted file mode 100644 index 33f76c7..0000000 --- a/flash_lock.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * flash_{lock,unlock} - * - * utilities for locking/unlocking sectors of flash devices - */ - -#define PROGRAM_NAME "flash_lock" -#include "flash_unlock.c" diff --git a/flash_otp_dump.c b/flash_otp_dump.c deleted file mode 100644 index f0c0fb9..0000000 --- a/flash_otp_dump.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * flash_otp_dump.c -- display One-Time-Programm data - */ - -#define PROGRAM_NAME "flash_otp_dump" - -#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", PROGRAM_NAME); - 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 deleted file mode 100644 index 2061797..0000000 --- a/flash_otp_info.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * flash_otp_info.c -- print info about One-Time-Programm data - */ - -#define PROGRAM_NAME "flash_otp_info" - -#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", PROGRAM_NAME); - 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 deleted file mode 100644 index 3c39a2d..0000000 --- a/flash_otp_lock.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * flash_otp_lock.c -- lock area of One-Time-Program data - */ - -#define PROGRAM_NAME "flash_otp_lock" - -#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> -#include "common.h" - -int main(int argc,char *argv[]) -{ - int fd, val, ret, offset, size; - char *p; - - if (argc != 5 || strcmp(argv[1], "-u")) { - fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", PROGRAM_NAME); - 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", PROGRAM_NAME); - return ERANGE; - } - - size = strtoul(argv[4], &p, 0); - if (argv[4][0] == 0 || *p != 0) { - fprintf(stderr, "%s: bad size value\n", PROGRAM_NAME); - return ERANGE; - } - - printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", - argv[2], offset, offset + size); - if (prompt("Are you sure?", false)) { - 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 deleted file mode 100644 index 111318d..0000000 --- a/flash_otp_write.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * flash_otp_write.c -- write One-Time-Program data - */ - -#define PROGRAM_NAME "flash_otp_write" - -#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 <common.h> -#include <mtd/mtd-user.h> - -ssize_t xread(int fd, void *buf, size_t count) -{ - ssize_t ret, done = 0; - -retry: - ret = read(fd, buf + done, count - done); - if (ret < 0) - return ret; - - done += ret; - - if (ret == 0 /* EOF */ || done == count) - return done; - else - goto retry; -} - -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", PROGRAM_NAME); - 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 = (off_t)strtoull(argv[3], &p, 0); - if (argv[3][0] == 0 || *p != 0) { - fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME); - 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%"PRIxoff_t"\n", argv[2], offset); - - if (mtd_type_is_nand_user(&mtdInfo)) - len = mtdInfo.writesize; - else - len = 256; - - if (len > sizeof(buf)) { - printf("huh, writesize (%d) bigger than buffer (%zu)\n", - len, sizeof(buf)); - return ENOMEM; - } - - wrote = 0; - while ((size = xread(0, buf, len))) { - if (size < 0) { - perror("read()"); - return errno; - } - p = buf; - while (size > 0) { - if (mtd_type_is_nand_user(&mtdInfo)) { - /* Fill remain buffers with 0xff */ - memset(buf + size, 0xff, mtdInfo.writesize - size); - size = mtdInfo.writesize; - } - 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 deleted file mode 100644 index 1cc8c2f..0000000 --- a/flash_unlock.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * flash_{lock,unlock} - * - * utilities for locking/unlocking sectors of flash devices - */ - -#ifndef PROGRAM_NAME -#define PROGRAM_NAME "flash_unlock" -#define FLASH_MSG "unlock" -#define FLASH_UNLOCK 1 -#else -#define FLASH_MSG "lock" -#define FLASH_UNLOCK 0 -#endif - -#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 "common.h" -#include <mtd/mtd-user.h> - -static void usage(int status) -{ - fprintf(status ? stderr : stdout, - "Usage: %s <mtd device> [offset] [block count]\n\n" - "If offset is not specified, it defaults to 0.\n" - "If block count is not specified, it defaults to all blocks.\n", - PROGRAM_NAME); - exit(status); -} - -int main(int argc, char *argv[]) -{ - int fd, request; - struct mtd_info_user mtdInfo; - struct erase_info_user mtdLockInfo; - int count; - const char *dev; - - /* Parse command line options */ - if (argc < 2 || argc > 4) - usage(1); - if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) - usage(0); - - dev = argv[1]; - - /* Get the device info to compare to command line sizes */ - fd = open(dev, O_RDWR); - if (fd < 0) - sys_errmsg_die("could not open: %s", dev); - - if (ioctl(fd, MEMGETINFO, &mtdInfo)) - sys_errmsg_die("could not get mtd info: %s", dev); - - /* Make sure user options are valid */ - if (argc > 2) - mtdLockInfo.start = strtol(argv[2], NULL, 0); - else - mtdLockInfo.start = 0; - if (mtdLockInfo.start > mtdInfo.size) - errmsg_die("%#x is beyond device size %#x", - mtdLockInfo.start, mtdInfo.size); - - if (argc > 3) { - count = strtol(argv[3], NULL, 0); - if (count == -1) - mtdLockInfo.length = mtdInfo.size; - else - mtdLockInfo.length = mtdInfo.erasesize * count; - } else - mtdLockInfo.length = mtdInfo.size; - if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size) - errmsg_die("range is more than device supports: %#x + %#x > %#x", - mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size); - - /* Finally do the operation */ - request = FLASH_UNLOCK ? MEMUNLOCK : MEMLOCK; - if (ioctl(fd, request, &mtdLockInfo)) - sys_errmsg_die("could not %s device: %s\n", - FLASH_MSG, dev); - - return 0; -} diff --git a/flashcp.c b/flashcp.c deleted file mode 100644 index 86334ac..0000000 --- a/flashcp.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2d3D, Inc. - * Written by Abraham vd Merwe <abraham@2d3d.co.za> - * All rights reserved. - * - * 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. - */ - -#define PROGRAM_NAME "flashcp" - -#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(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: %1$s [ -v | --verbose ] <filename> <device>\n" - " %1$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", - PROGRAM_NAME); - - 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 *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]; - - /********************* - * 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(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 || device == NULL) - showusage(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) / 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 deleted file mode 100644 index 0eada8f..0000000 --- a/ftl_check.c +++ /dev/null @@ -1,217 +0,0 @@ -/* Ported to MTD system. - * 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. - - ======================================================================*/ - -#define PROGRAM_NAME "ftl_check" - -#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 <mtd/ftl-user.h> -#include <mtd_swab.h> - -#include "common.h" - -/*====================================================================*/ - -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) -{ - mtd_info_t mtd; - erase_unit_header_t hdr, hdr2; - off_t i; - u_int 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 ((le32_to_cpu(hdr.FormattedSize) > 0) && - (le32_to_cpu(hdr.FormattedSize) <= mtd.size) && - (le16_to_cpu(hdr.NumEraseUnits) > 0) && - (le16_to_cpu(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(le32_to_cpu(hdr.FormattedSize)); - printf(", erase units = %d, transfer units = %d\n", - le16_to_cpu(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 < le16_to_cpu(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 %"PRIdoff_t":\n", i); - if ((hdr2.FormattedSize != hdr.FormattedSize) || - (hdr2.NumEraseUnits != hdr.NumEraseUnits) || - (hdr2.SerialNumber != hdr.SerialNumber)) - printf(" Erase unit header is corrupt.\n"); - else if (le16_to_cpu(hdr2.LogicalEUN) == 0xffff) - printf(" Transfer unit, erase count = %d\n", le32_to_cpu(hdr2.EraseCount)); - else { - printf(" Logical unit %d, erase count = %d\n", - le16_to_cpu(hdr2.LogicalEUN), le32_to_cpu(hdr2.EraseCount)); - if (lseek(fd, (i << hdr.EraseUnitSize)+le32_to_cpu(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(le32_to_cpu(bam[j]))) - free++; - else if (BLOCK_DELETED(le32_to_cpu(bam[j]))) - deleted++; - else switch (BLOCK_TYPE(le32_to_cpu(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(void) -{ - fprintf(stderr, "usage: %s device\n", PROGRAM_NAME); -} - -/*====================================================================*/ - -int main(int argc, char *argv[]) -{ - int optch, errflg, fd; - struct stat buf; - - errflg = 0; - while ((optch = getopt(argc, argv, "h")) != -1) { - switch (optch) { - case 'h': - errflg = 1; break; - default: - errflg = -1; break; - } - } - if (errflg || (optind != argc-1)) { - showusage(); - 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); - close(fd); - - exit(EXIT_SUCCESS); - return 0; -} diff --git a/ftl_format.c b/ftl_format.c deleted file mode 100644 index b58677f..0000000 --- a/ftl_format.c +++ /dev/null @@ -1,324 +0,0 @@ -/* Ported to MTD system. - * 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. - - ======================================================================*/ - -#define PROGRAM_NAME "ftl_format" - -#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 <mtd/ftl-user.h> -#include <mtd_swab.h> -#include "common.h" - -/*====================================================================*/ - -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 = cpu_to_le32(0); - hdr->FirstPhysicalEUN = cpu_to_le16(BootUnits); - hdr->NumEraseUnits = cpu_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 = cpu_to_le32(0x80); - /* Adjust size to account for BAM space */ - nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int) - + le32_to_cpu(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize; - - __FormattedSize -= - (le16_to_cpu(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize); - __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff); - - hdr->FormattedSize = cpu_to_le32(__FormattedSize); - - /* hdr->FirstVMAddress defaults to erased state */ - hdr->NumVMPages = cpu_to_le16(0); - hdr->Flags = 0; - /* hdr->Code defaults to erased state */ - hdr->SerialNumber = cpu_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(le16_to_cpu(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize); - printf(" allocated for boot image\n"); - } - printf("Reserved %d%%, formatted size = ", reserve); - print_size(le32_to_cpu(hdr.FormattedSize)); - printf("\n"); - fflush(stdout); - } - - if (interrogate) - if (!prompt("This will destroy all data on the target device. Confirm?", false)) - return -1; - - /* Create basic block allocation table for control blocks */ - nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int) - + le32_to_cpu(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize; - bam = malloc(nbam * sizeof(u_int)); - for (i = 0; i < nbam; i++) - bam[i] = cpu_to_le32(BLOCK_CONTROL); - - /* Erase partition */ - if (!quiet) { - printf("Erasing all blocks...\n"); - fflush(stdout); - } - erase.length = mtd.erasesize; - erase.start = mtd.erasesize * le16_to_cpu(hdr.FirstPhysicalEUN); - for (i = 0; i < le16_to_cpu(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 ? (le16_to_cpu(hdr.NumEraseUnits) / spare) : (le16_to_cpu(hdr.NumEraseUnits) + 1); - for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) { - off_t ofs = (off_t) (i + le16_to_cpu(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 = cpu_to_le16(0xffff); - else { - hdr.LogicalEUN = cpu_to_le16(lun); - lun++; - } - if (write(fd, &hdr, sizeof(hdr)) == -1) { - perror("write failed"); - break; - } - if (lseek(fd, ofs + le32_to_cpu(hdr.BAMOffset), SEEK_SET) == -1) { - perror("seek failed"); - break; - } - if (write(fd, bam, nbam * sizeof(u_int)) == -1) { - perror("write failed"); - break; - } - } - if (i < le16_to_cpu(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", PROGRAM_NAME); - 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 deleted file mode 100644 index 3176469..0000000 --- a/jffs-dump.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Dump JFFS filesystem. - * Useful when it buggers up. - */ - -#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> - -#include "common.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 \ - - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ - % JFFS_ALIGN_SIZE) - -#define JFFS_EMPTY_BITMASK 0xffffffff -#define JFFS_MAGIC_BITMASK 0x34383931 -#define JFFS_DIRTY_BITMASK 0x00000000 - -struct jffs_raw_inode -{ - uint32_t magic; /* A constant magic number. */ - uint32_t ino; /* Inode number. */ - uint32_t pino; /* Parent's inode number. */ - uint32_t version; /* Version number. */ - uint32_t mode; /* file_type, mode */ - uint16_t uid; - uint16_t gid; - uint32_t atime; - uint32_t mtime; - uint32_t ctime; - uint32_t offset; /* Where to begin to write. */ - uint32_t dsize; /* Size of the file data. */ - uint32_t rsize; /* How much are going to be replaced? */ - uint8_t nsize; /* Name length. */ - uint8_t nlink; /* Number of links. */ - uint8_t spare : 6; /* For future use. */ - uint8_t rename : 1; /* Is this a special rename? */ - uint8_t deleted : 1; /* Has this file been deleted? */ - uint8_t accurate; /* The inode is obsolete if accurate == 0. */ - uint32_t dchksum; /* Checksum for the data. */ - uint16_t nchksum; /* Checksum for the name. */ - uint16_t 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 uint32_t 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 uint32_t -jffs_checksum(void *data, int size) -{ - uint32_t sum = 0; - uint8_t *ptr = (uint8_t *)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(uint32_t *adr, uint32_t 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(uint16_t *adr, uint16_t 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 uint32_t read_val32(uint32_t *adr) -{ - uint32_t 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 uint16_t read_val16(uint16_t *adr) -{ - uint16_t 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; - uint32_t 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 deleted file mode 100644 index f8b8ac7..0000000 --- a/jffs2dump.c +++ /dev/null @@ -1,805 +0,0 @@ -/* - * dumpjffs2.c - * - * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This utility dumps the contents of a binary JFFS2 image - * - * - * Bug/ToDo: - */ - -#define PROGRAM_NAME "jffs2dump" - -#include <errno.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <time.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/param.h> -#include <asm/types.h> -#include <dirent.h> -#include <mtd/jffs2-user.h> -#include <endian.h> -#include <byteswap.h> -#include <getopt.h> -#include <crc32.h> -#include "summary.h" -#include "common.h" - -#define PAD(x) (((x)+3)&~3) - -/* For outputting a byte-swapped version of the input image. */ -#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) -#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) - -#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) -#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) - -// Global variables -long imglen; // length of image -char *data; // image data - -void display_help (void) -{ - printf("Usage: %s [OPTION]... INPUTFILE\n" - "Dump the contents of a binary JFFS2 image.\n\n" - " --help display this help and exit\n" - " --version display version information and exit\n" - " -b, --bigendian image is big endian\n" - " -l, --littleendian image is little endian\n" - " -c, --content dump image contents\n" - " -e, --endianconvert=FNAME convert image endianness, output to file fname\n" - " -r, --recalccrc recalc name and data crc on endian conversion\n" - " -d, --datsize=LEN size of data chunks, when oob data in binary image (NAND only)\n" - " -o, --oobsize=LEN size of oob data chunk in binary image (NAND only)\n" - " -v, --verbose verbose output\n", - PROGRAM_NAME); - exit(0); -} - -void display_version (void) -{ - printf("%1$s " VERSION "\n" - "\n" - "Copyright (C) 2003 Thomas Gleixner \n" - "\n" - "%1$s comes with NO WARRANTY\n" - "to the extent permitted by law.\n" - "\n" - "You may redistribute copies of %1$s\n" - "under the terms of the GNU General Public Licence.\n" - "See the file `COPYING' for more information.\n", - PROGRAM_NAME); - exit(0); -} - -// Option variables - -int verbose; // verbose output -char *img; // filename of image -int dumpcontent; // dump image content -int target_endian = __BYTE_ORDER; // image endianess -int convertendian; // convert endianness -int recalccrc; // recalc name and data crc's on endian conversion -char cnvfile[256]; // filename for conversion output -int datsize; // Size of data chunks, when oob data is inside the binary image -int oobsize; // Size of oob chunks, when oob data is inside the binary image - -void process_options (int argc, char *argv[]) -{ - int error = 0; - - for (;;) { - int option_index = 0; - static const char *short_options = "blce:rd:o:v"; - static const struct option long_options[] = { - {"help", no_argument, 0, 0}, - {"version", no_argument, 0, 0}, - {"bigendian", no_argument, 0, 'b'}, - {"littleendian", no_argument, 0, 'l'}, - {"content", no_argument, 0, 'c'}, - {"endianconvert", required_argument, 0, 'e'}, - {"datsize", required_argument, 0, 'd'}, - {"oobsize", required_argument, 0, 'o'}, - {"recalccrc", required_argument, 0, 'r'}, - {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0}, - }; - - int c = getopt_long(argc, argv, short_options, - long_options, &option_index); - if (c == EOF) { - break; - } - - switch (c) { - case 0: - switch (option_index) { - case 0: - display_help(); - break; - case 1: - display_version(); - break; - } - break; - case 'v': - verbose = 1; - break; - case 'b': - target_endian = __BIG_ENDIAN; - break; - case 'l': - target_endian = __LITTLE_ENDIAN; - break; - case 'c': - dumpcontent = 1; - break; - case 'd': - datsize = atoi(optarg); - break; - case 'o': - oobsize = atoi(optarg); - break; - case 'e': - convertendian = 1; - strcpy (cnvfile, optarg); - break; - case 'r': - recalccrc = 1; - break; - case '?': - error = 1; - break; - } - } - - if ((argc - optind) != 1 || error) - display_help (); - - img = argv[optind]; -} - - -/* - * Dump image contents - */ -void do_dumpcontent (void) -{ - char *p = data, *p_free_begin; - union jffs2_node_union *node; - int empty = 0, dirty = 0; - char name[256]; - uint32_t crc; - uint16_t type; - int bitchbitmask = 0; - int obsolete; - - p_free_begin = NULL; - while ( p < (data + imglen)) { - node = (union jffs2_node_union*) p; - - /* Skip empty space */ - if (!p_free_begin) - p_free_begin = p; - if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { - p += 4; - empty += 4; - continue; - } - - if (p != p_free_begin) - printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data); - p_free_begin = NULL; - - if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { - if (!bitchbitmask++) - printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); - p += 4; - dirty += 4; - continue; - } - bitchbitmask = 0; - - type = je16_to_cpu(node->u.nodetype); - if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { - obsolete = 1; - type |= JFFS2_NODE_ACCURATE; - } else - obsolete = 0; - /* Set accurate for CRC check */ - node->u.nodetype = cpu_to_je16(type); - - crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); - if (crc != je32_to_cpu (node->u.hdr_crc)) { - printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); - p += 4; - dirty += 4; - continue; - } - - switch(je16_to_cpu(node->u.nodetype)) { - - case JFFS2_NODETYPE_INODE: - printf ("%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", - obsolete ? "Obsolete" : "", - p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), - je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), - je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); - - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); - if (crc != je32_to_cpu (node->i.node_crc)) { - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); - p += PAD(je32_to_cpu (node->i.totlen)); - dirty += PAD(je32_to_cpu (node->i.totlen));; - continue; - } - - crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); - if (crc != je32_to_cpu(node->i.data_crc)) { - printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); - p += PAD(je32_to_cpu (node->i.totlen)); - dirty += PAD(je32_to_cpu (node->i.totlen));; - continue; - } - - p += PAD(je32_to_cpu (node->i.totlen)); - break; - - case JFFS2_NODETYPE_DIRENT: - memcpy (name, node->d.name, node->d.nsize); - name [node->d.nsize] = 0x0; - printf ("%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", - obsolete ? "Obsolete" : "", - p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), - je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), - node->d.nsize, name); - - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); - if (crc != je32_to_cpu (node->d.node_crc)) { - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); - p += PAD(je32_to_cpu (node->d.totlen)); - dirty += PAD(je32_to_cpu (node->d.totlen));; - continue; - } - - crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); - if (crc != je32_to_cpu(node->d.name_crc)) { - printf ("Wrong name_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); - p += PAD(je32_to_cpu (node->d.totlen)); - dirty += PAD(je32_to_cpu (node->d.totlen));; - continue; - } - - p += PAD(je32_to_cpu (node->d.totlen)); - break; - - case JFFS2_NODETYPE_XATTR: - memcpy(name, node->x.data, node->x.name_len); - name[node->x.name_len] = '\x00'; - printf ("%8s Xattr node at 0x%08zx, totlen 0x%08x, xid %5d, version %5d, name_len %3d, name %s\n", - obsolete ? "Obsolete" : "", - p - data, - je32_to_cpu (node->x.totlen), - je32_to_cpu (node->x.xid), - je32_to_cpu (node->x.version), - node->x.name_len, - name); - - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_xattr) - sizeof (node->x.node_crc)); - if (crc != je32_to_cpu (node->x.node_crc)) { - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.node_crc), crc); - p += PAD(je32_to_cpu (node->x.totlen)); - dirty += PAD(je32_to_cpu (node->x.totlen)); - continue; - } - - crc = mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1); - if (crc != je32_to_cpu (node->x.data_crc)) { - printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.data_crc), crc); - p += PAD(je32_to_cpu (node->x.totlen)); - dirty += PAD(je32_to_cpu (node->x.totlen)); - continue; - } - p += PAD(je32_to_cpu (node->x.totlen)); - break; - - case JFFS2_NODETYPE_XREF: - printf ("%8s Xref node at 0x%08zx, totlen 0x%08x, xid %5d, xseqno %5d, #ino %8d\n", - obsolete ? "Obsolete" : "", - p - data, - je32_to_cpu (node->r.totlen), - je32_to_cpu (node->r.xid), - je32_to_cpu (node->r.xseqno), - je32_to_cpu (node->r.ino)); - p += PAD(je32_to_cpu (node->r.totlen)); - break; - - case JFFS2_NODETYPE_SUMMARY: { - - int i; - struct jffs2_sum_marker * sm; - - printf("%8s Inode Sum node at 0x%08zx, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", - obsolete ? "Obsolete" : "", - p - data, - je32_to_cpu (node->s.totlen), - je32_to_cpu (node->s.sum_num), - je32_to_cpu (node->s.cln_mkr)); - - crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); - if (crc != je32_to_cpu (node->s.node_crc)) { - printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); - p += PAD(je32_to_cpu (node->s.totlen)); - dirty += PAD(je32_to_cpu (node->s.totlen));; - continue; - } - - crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); - if (crc != je32_to_cpu(node->s.sum_crc)) { - printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); - p += PAD(je32_to_cpu (node->s.totlen)); - dirty += PAD(je32_to_cpu (node->s.totlen));; - continue; - } - - if (verbose) { - void *sp; - sp = (p + sizeof(struct jffs2_raw_summary)); - - for(i=0; i<je32_to_cpu(node->s.sum_num); i++) { - - switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { - case JFFS2_NODETYPE_INODE : { - - struct jffs2_sum_inode_flash *spi; - spi = sp; - - printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", - "", - je32_to_cpu (spi->inode), - je32_to_cpu (spi->version), - je32_to_cpu (spi->offset), - je32_to_cpu (spi->totlen)); - - sp += JFFS2_SUMMARY_INODE_SIZE; - break; - } - - case JFFS2_NODETYPE_DIRENT : { - - char name[255]; - struct jffs2_sum_dirent_flash *spd; - spd = sp; - - memcpy(name,spd->name,spd->nsize); - name [spd->nsize] = 0x0; - - printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", - "", - je32_to_cpu (spd->offset), - je32_to_cpu (spd->totlen), - je32_to_cpu (spd->pino), - je32_to_cpu (spd->version), - je32_to_cpu (spd->ino), - spd->nsize, - name); - - sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); - break; - } - - case JFFS2_NODETYPE_XATTR : { - struct jffs2_sum_xattr_flash *spx; - spx = sp; - printf ("%14s Xattr offset 0x%08x, totlen 0x%08x, version %5d, #xid %8d\n", - "", - je32_to_cpu (spx->offset), - je32_to_cpu (spx->totlen), - je32_to_cpu (spx->version), - je32_to_cpu (spx->xid)); - sp += JFFS2_SUMMARY_XATTR_SIZE; - break; - } - - case JFFS2_NODETYPE_XREF : { - struct jffs2_sum_xref_flash *spr; - spr = sp; - printf ("%14s Xref offset 0x%08x\n", - "", - je32_to_cpu (spr->offset)); - sp += JFFS2_SUMMARY_XREF_SIZE; - break; - } - - default : - printf("Unknown summary node!\n"); - break; - } - } - - sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); - - printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", - "", - je32_to_cpu(sm->offset), - je32_to_cpu(sm->magic), - je32_to_cpu(node->s.padded)); - } - - p += PAD(je32_to_cpu (node->s.totlen)); - break; - } - - case JFFS2_NODETYPE_CLEANMARKER: - if (verbose) { - printf ("%8s Cleanmarker at 0x%08zx, totlen 0x%08x\n", - obsolete ? "Obsolete" : "", - p - data, je32_to_cpu (node->u.totlen)); - } - p += PAD(je32_to_cpu (node->u.totlen)); - break; - - case JFFS2_NODETYPE_PADDING: - if (verbose) { - printf ("%8s Padding node at 0x%08zx, totlen 0x%08x\n", - obsolete ? "Obsolete" : "", - p - data, je32_to_cpu (node->u.totlen)); - } - p += PAD(je32_to_cpu (node->u.totlen)); - break; - - case 0xffff: - p += 4; - empty += 4; - break; - - default: - if (verbose) { - printf ("%8s Unknown node at 0x%08zx, totlen 0x%08x\n", - obsolete ? "Obsolete" : "", - p - data, je32_to_cpu (node->u.totlen)); - } - p += PAD(je32_to_cpu (node->u.totlen)); - dirty += PAD(je32_to_cpu (node->u.totlen)); - - } - } - - if (verbose) - printf ("Empty space: %d, dirty space: %d\n", empty, dirty); -} - -/* - * Convert endianess - */ -void do_endianconvert (void) -{ - char *p = data; - union jffs2_node_union *node, newnode; - int fd, len; - jint32_t mode; - uint32_t crc; - - fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); - if (fd < 0) { - fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); - return; - } - - while ( p < (data + imglen)) { - node = (union jffs2_node_union*) p; - - /* Skip empty space */ - if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { - write (fd, p, 4); - p += 4; - continue; - } - - if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { - printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); - newnode.u.magic = cnv_e16 (node->u.magic); - newnode.u.nodetype = cnv_e16 (node->u.nodetype); - write (fd, &newnode, 4); - p += 4; - continue; - } - - crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); - if (crc != je32_to_cpu (node->u.hdr_crc)) { - printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); - } - - switch(je16_to_cpu(node->u.nodetype)) { - - case JFFS2_NODETYPE_INODE: - - newnode.i.magic = cnv_e16 (node->i.magic); - newnode.i.nodetype = cnv_e16 (node->i.nodetype); - newnode.i.totlen = cnv_e32 (node->i.totlen); - newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); - newnode.i.ino = cnv_e32 (node->i.ino); - newnode.i.version = cnv_e32 (node->i.version); - mode.v32 = node->i.mode.m; - mode = cnv_e32 (mode); - newnode.i.mode.m = mode.v32; - newnode.i.uid = cnv_e16 (node->i.uid); - newnode.i.gid = cnv_e16 (node->i.gid); - newnode.i.isize = cnv_e32 (node->i.isize); - newnode.i.atime = cnv_e32 (node->i.atime); - newnode.i.mtime = cnv_e32 (node->i.mtime); - newnode.i.ctime = cnv_e32 (node->i.ctime); - newnode.i.offset = cnv_e32 (node->i.offset); - newnode.i.csize = cnv_e32 (node->i.csize); - newnode.i.dsize = cnv_e32 (node->i.dsize); - newnode.i.compr = node->i.compr; - newnode.i.usercompr = node->i.usercompr; - newnode.i.flags = cnv_e16 (node->i.flags); - if (recalccrc) { - len = je32_to_cpu(node->i.csize); - newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len)); - } else - newnode.i.data_crc = cnv_e32 (node->i.data_crc); - - newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); - - write (fd, &newnode, sizeof (struct jffs2_raw_inode)); - write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); - - p += PAD(je32_to_cpu (node->i.totlen)); - break; - - case JFFS2_NODETYPE_DIRENT: - newnode.d.magic = cnv_e16 (node->d.magic); - newnode.d.nodetype = cnv_e16 (node->d.nodetype); - newnode.d.totlen = cnv_e32 (node->d.totlen); - newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); - newnode.d.pino = cnv_e32 (node->d.pino); - newnode.d.version = cnv_e32 (node->d.version); - newnode.d.ino = cnv_e32 (node->d.ino); - newnode.d.mctime = cnv_e32 (node->d.mctime); - newnode.d.nsize = node->d.nsize; - newnode.d.type = node->d.type; - newnode.d.unused[0] = node->d.unused[0]; - newnode.d.unused[1] = node->d.unused[1]; - newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); - if (recalccrc) - newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); - else - newnode.d.name_crc = cnv_e32 (node->d.name_crc); - - write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); - write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); - p += PAD(je32_to_cpu (node->d.totlen)); - break; - - case JFFS2_NODETYPE_XATTR: - newnode.x.magic = cnv_e16 (node->x.magic); - newnode.x.nodetype = cnv_e16 (node->x.nodetype); - newnode.x.totlen = cnv_e32 (node->x.totlen); - newnode.x.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); - newnode.x.xid = cnv_e32 (node->x.xid); - newnode.x.version = cnv_e32 (node->x.version); - newnode.x.xprefix = node->x.xprefix; - newnode.x.name_len = node->x.name_len; - newnode.x.value_len = cnv_e16 (node->x.value_len); - if (recalccrc) - newnode.x.data_crc = cpu_to_e32 (mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1)); - else - newnode.x.data_crc = cnv_e32 (node->x.data_crc); - newnode.x.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xattr) - sizeof (newnode.x.node_crc))); - - write (fd, &newnode, sizeof (struct jffs2_raw_xattr)); - write (fd, p + sizeof (struct jffs2_raw_xattr), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_xattr))); - p += PAD(je32_to_cpu (node->x.totlen)); - break; - - case JFFS2_NODETYPE_XREF: - newnode.r.magic = cnv_e16 (node->r.magic); - newnode.r.nodetype = cnv_e16 (node->r.nodetype); - newnode.r.totlen = cnv_e32 (node->r.totlen); - newnode.r.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - sizeof (newnode.r.hdr_crc))); - newnode.r.ino = cnv_e32 (node->r.ino); - newnode.r.xid = cnv_e32 (node->r.xid); - newnode.r.xseqno = cnv_e32 (node->r.xseqno); - newnode.r.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xref) - sizeof (newnode.r.node_crc))); - p += PAD(je32_to_cpu (node->x.totlen)); - break; - - case JFFS2_NODETYPE_CLEANMARKER: - case JFFS2_NODETYPE_PADDING: - newnode.u.magic = cnv_e16 (node->u.magic); - newnode.u.nodetype = cnv_e16 (node->u.nodetype); - newnode.u.totlen = cnv_e32 (node->u.totlen); - newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); - - write (fd, &newnode, sizeof (struct jffs2_unknown_node)); - len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); - if (len > 0) - write (fd, p + sizeof (struct jffs2_unknown_node), len); - - p += PAD(je32_to_cpu (node->u.totlen)); - break; - - case JFFS2_NODETYPE_SUMMARY : { - struct jffs2_sum_marker *sm_ptr; - int i,sum_len; - int counter = 0; - - newnode.s.magic = cnv_e16 (node->s.magic); - newnode.s.nodetype = cnv_e16 (node->s.nodetype); - newnode.s.totlen = cnv_e32 (node->s.totlen); - newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); - newnode.s.sum_num = cnv_e32 (node->s.sum_num); - newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); - newnode.s.padded = cnv_e32 (node->s.padded); - - newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); - - // summary header - p += sizeof (struct jffs2_raw_summary); - - // summary data - sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); - - for (i=0; i<je32_to_cpu (node->s.sum_num); i++) { - union jffs2_sum_flash *fl_ptr; - - fl_ptr = (union jffs2_sum_flash *) p; - - switch (je16_to_cpu (fl_ptr->u.nodetype)) { - case JFFS2_NODETYPE_INODE: - - fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); - fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); - fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); - fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); - fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); - p += sizeof (struct jffs2_sum_inode_flash); - counter += sizeof (struct jffs2_sum_inode_flash); - break; - - case JFFS2_NODETYPE_DIRENT: - fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); - fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); - fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); - fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); - fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); - fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); - p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; - counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; - break; - - case JFFS2_NODETYPE_XATTR: - fl_ptr->x.nodetype = cnv_e16 (fl_ptr->x.nodetype); - fl_ptr->x.xid = cnv_e32 (fl_ptr->x.xid); - fl_ptr->x.version = cnv_e32 (fl_ptr->x.version); - fl_ptr->x.offset = cnv_e32 (fl_ptr->x.offset); - fl_ptr->x.totlen = cnv_e32 (fl_ptr->x.totlen); - p += sizeof (struct jffs2_sum_xattr_flash); - counter += sizeof (struct jffs2_sum_xattr_flash); - break; - - case JFFS2_NODETYPE_XREF: - fl_ptr->r.nodetype = cnv_e16 (fl_ptr->r.nodetype); - fl_ptr->r.offset = cnv_e32 (fl_ptr->r.offset); - p += sizeof (struct jffs2_sum_xref_flash); - counter += sizeof (struct jffs2_sum_xref_flash); - break; - - default : - printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); - exit(EXIT_FAILURE); - break; - } - - } - - //pad - p += sum_len - counter; - - // summary marker - sm_ptr = (struct jffs2_sum_marker *) p; - sm_ptr->offset = cnv_e32 (sm_ptr->offset); - sm_ptr->magic = cnv_e32 (sm_ptr->magic); - p += sizeof (struct jffs2_sum_marker); - - // generate new crc on sum data - newnode.s.sum_crc = cpu_to_e32 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), - je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); - - // write out new node header - write(fd, &newnode, sizeof (struct jffs2_raw_summary)); - // write out new summary data - write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); - - break; - } - - case 0xffff: - write (fd, p, 4); - p += 4; - break; - - default: - printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); - p += PAD(je32_to_cpu (node->u.totlen)); - - } - } - - close (fd); - -} - -/* - * Main program - */ -int main(int argc, char **argv) -{ - int fd; - - process_options(argc, argv); - - /* Open the input file */ - if ((fd = open(img, O_RDONLY)) == -1) { - perror("open input file"); - exit(1); - } - - // get image length - imglen = lseek(fd, 0, SEEK_END); - lseek (fd, 0, SEEK_SET); - - data = malloc (imglen); - if (!data) { - perror("out of memory"); - close (fd); - exit(1); - } - - if (datsize && oobsize) { - int idx = 0; - long len = imglen; - uint8_t oob[oobsize]; - printf ("Peeling data out of combined data/oob image\n"); - while (len) { - // read image data - read (fd, &data[idx], datsize); - read (fd, oob, oobsize); - idx += datsize; - imglen -= oobsize; - len -= datsize + oobsize; - } - - } else { - // read image data - read (fd, data, imglen); - } - // Close the input file - close(fd); - - if (dumpcontent) - do_dumpcontent (); - - if (convertendian) - do_endianconvert (); - - // free memory - free (data); - - // Return happy - exit (0); -} diff --git a/jffs2reader.c b/jffs2reader.c deleted file mode 100644 index a62da9a..0000000 --- a/jffs2reader.c +++ /dev/null @@ -1,918 +0,0 @@ -/* 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 - */ - - -/* -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. - */ - -#define PROGRAM_NAME "jffs2reader" - -#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 <dirent.h> -#include <zlib.h> - -#include "mtd/jffs2-user.h" -#include "common.h" - -#define SCRATCH_SIZE (5*1024*1024) - -/* macro to avoid "lvalue required as left operand of assignment" error */ -#define ADD_BYTES(p, n) ((p) = (typeof(p))((char *)(p) + (n))) - -#define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0) -#define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0) - -struct dir { - struct dir *next; - uint8_t type; - uint8_t nsize; - uint32_t ino; - char name[256]; -}; - -int target_endian = __BYTE_ORDER; - -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, const char *path, - int recurse, int want_ctime); -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, const char *, - uint32_t *, int); -struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *, - uint32_t *); - -void lsdir(char *, size_t, const char *, int, 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 = je32_to_cpu(n->dsize); - - if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize) - errmsg_die("File does not fit into buffer!"); - - if (*rsize < je32_to_cpu(n->isize)) - bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize); - - switch (n->compr) { - case JFFS2_COMPR_ZLIB: - uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen, - (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), - (uLongf) je32_to_cpu(n->csize)); - break; - - case JFFS2_COMPR_NONE: - memcpy(b + je32_to_cpu(n->offset), - ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); - break; - - case JFFS2_COMPR_ZERO: - bzero(b + je32_to_cpu(n->offset), dlen); - break; - - /* [DYN]RUBIN support required! */ - - default: - errmsg_die("Unsupported compression method!"); - } - - *rsize = je32_to_cpu(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 (je32_to_cpu(n->ino)) { - if (dd == NULL) { - d = xmalloc(sizeof(struct dir)); - d->type = n->type; - memcpy(d->name, n->name, n->nsize); - d->nsize = n->nsize; - d->ino = je32_to_cpu(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 = je32_to_cpu(n->ino); - - return o; - } - - if (dd->next == NULL) { - dd->next = xmalloc(sizeof(struct dir)); - dd->next->type = n->type; - memcpy(dd->next->name, n->name, n->nsize); - dd->next->nsize = n->nsize; - dd->next->ino = je32_to_cpu(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, const char *path, int recurse, - int want_ctime) -{ - char m; - char *filetime; - time_t age; - struct jffs2_raw_inode *ri; - jint32_t mode; - - 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) { - warnmsg("bug: raw_inode missing!"); - d = d->next; - continue; - } - - filetime = ctime((const time_t *) &(ri->ctime)); - age = time(NULL) - je32_to_cpu(ri->ctime); - mode.v32 = ri->mode.m; - printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)), - 1, je16_to_cpu(ri->uid), je16_to_cpu(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 ", major(rdev), minor(rdev)); - } else { - printf("%9ld ", (long)je32_to_cpu(ri->dsize)); - } - d->name[d->nsize]='\0'; - if (want_ctime) { - if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) - /* hh:mm if less than 6 months old */ - printf("%6.6s %5.5s ", filetime + 4, filetime + 11); - else - printf("%6.6s %4.4s ", filetime + 4, filetime + 20); - } - printf("%s/%s%c", 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 = xmalloc(BUFSIZ); - sprintf(tmp, "%s/%s", path, d->name); - lsdir(o, size, tmp, recurse, want_ctime); /* 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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) - ADD_BYTES(n, 4); - - if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { - if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE && - je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(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)); - } - - ADD_BYTES(n, ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) - ADD_BYTES(n, 4); - - if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { - if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && - je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(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); - } - } - - ADD_BYTES(n, ((je32_to_cpu(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) + - ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) - ADD_BYTES(n, 4); - - if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { - if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && - (!ino || je32_to_cpu(n->d.ino) == ino) && - (v = je32_to_cpu(n->d.version)) > vmax && - (!pino || (je32_to_cpu(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); - } - } - - ADD_BYTES(n, ((je32_to_cpu(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, - const 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 = xstrdup(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, - const 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, const char *path, int recurse, int want_ctime) -{ - 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)) - errmsg_die("%s: No such file or directory", path); - - d = collectdir(o, size, ino, d); - printdir(o, size, d, path, recurse, want_ctime); - 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) - errmsg_die("%s: No such file or directory", path); - - if (dd == NULL || dd->type != DT_REG) - errmsg_die("%s: Not a regular file", path); - - 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, want_ctime = 0; - struct stat st; - - char *scratch, *dir = NULL, *file = NULL; - size_t ssize = 0; - - char *buf; - - while ((opt = getopt(argc, argv, "rd:f:t")) > 0) { - switch (opt) { - case 'd': - dir = optarg; - break; - case 'f': - file = optarg; - break; - case 'r': - recurse++; - break; - case 't': - want_ctime++; - break; - default: - fprintf(stderr, - "Usage: %s <image> [-d|-f] < path >\n", - PROGRAM_NAME); - exit(EXIT_FAILURE); - } - } - - fd = open(argv[optind], O_RDONLY); - if (fd == -1) - sys_errmsg_die("%s", argv[optind]); - - if (fstat(fd, &st)) - sys_errmsg_die("%s", argv[optind]); - - buf = xmalloc((size_t) st.st_size); - - if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) - sys_errmsg_die("%s", argv[optind]); - - if (dir) - lsdir(buf, st.st_size, dir, recurse, want_ctime); - - if (file) { - scratch = xmalloc(SCRATCH_SIZE); - - catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); - free(scratch); - } - - if (!dir && !file) - lsdir(buf, st.st_size, "/", 1, want_ctime); - - - free(buf); - exit(EXIT_SUCCESS); -} diff --git a/jffsX-utils/compr.c b/jffsX-utils/compr.c new file mode 100644 index 0000000..cb4432e --- /dev/null +++ b/jffsX-utils/compr.c @@ -0,0 +1,538 @@ +/* + * 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. + */ + +#include "compr.h" +#include <string.h> +#include <stdlib.h> +#include <linux/jffs2.h> + +#define FAVOUR_LZO_PERCENT 80 + +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)) { + 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; + } + } + } +} + +/* + * Return 1 to use this compression + */ +static int jffs2_is_best_compression(struct jffs2_compressor *this, + struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) +{ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_SIZE: + if (bestsize > size) + return 1; + return 0; + case JFFS2_COMPR_MODE_FAVOURLZO: + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) + return 1; + if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) + return 1; + if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) + return 1; + if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) + return 1; + + return 0; + } + /* Shouldn't happen */ + return 0; +} + +/* 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); + 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_FAVOURLZO: + case JFFS2_COMPR_MODE_SIZE: + orig_slen = *datalen; + orig_dlen = *cdatalen; + list_for_each_entry(this, &jffs2_compressor_list, list) { + uint32_t needed_buf_size; + + if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) + needed_buf_size = orig_slen + jffs2_compression_check; + else + needed_buf_size = orig_dlen + jffs2_compression_check; + + /* 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 < needed_buf_size) && (this->compr_buf)) { + free(this->compr_buf); + this->compr_buf_size=0; + this->compr_buf=NULL; + } + if (!this->compr_buf) { + tmp_buf = malloc(needed_buf_size); + 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); + 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) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) + && (*cdatalen < *datalen)) { + 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: unknown 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; + case JFFS2_COMPR_MODE_FAVOURLZO: + act_buf += sprintf(act_buf, "favourlzo"); + break; + default: + act_buf += sprintf(act_buf, "unknown"); + 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; + } + if (!strcmp("favourlzo", name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; + 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 +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_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 +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); +#endif + return 0; +} diff --git a/jffsX-utils/compr.h b/jffsX-utils/compr.h new file mode 100644 index 0000000..a21e935 --- /dev/null +++ b/jffsX-utils/compr.h @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#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 CONFIG_JFFS2_LZO + +#define JFFS2_RUBINMIPS_PRIORITY 10 +#define JFFS2_DYNRUBIN_PRIORITY 20 +#define JFFS2_RTIME_PRIORITY 50 +#define JFFS2_ZLIB_PRIORITY 60 +#define JFFS2_LZO_PRIORITY 80 + +#define JFFS2_COMPR_MODE_NONE 0 +#define JFFS2_COMPR_MODE_PRIORITY 1 +#define JFFS2_COMPR_MODE_SIZE 2 +#define JFFS2_COMPR_MODE_FAVOURLZO 3 + +#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 */ + const char *name; + char compr; /* JFFS2_COMPR_XXX */ + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *srclen, uint32_t *destlen); + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, + uint32_t cdatalen, uint32_t datalen); + 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 +#ifdef CONFIG_JFFS2_LZO +int jffs2_lzo_init(void); +void jffs2_lzo_exit(void); +#endif + +#endif /* __JFFS2_COMPR_H__ */ diff --git a/jffsX-utils/compr_lzo.c b/jffsX-utils/compr_lzo.c new file mode 100644 index 0000000..d2e2afc --- /dev/null +++ b/jffsX-utils/compr_lzo.c @@ -0,0 +1,135 @@ +/* + * JFFS2 LZO Compression Interface. + * + * Copyright (C) 2007 Nokia Corporation. All rights reserved. + * + * Author: Richard Purdie <rpurdie@openedhand.com> + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#ifndef WITHOUT_LZO +#include <asm/types.h> +#include <linux/jffs2.h> +#include <lzo/lzo1x.h> +#include "compr.h" + +extern int page_size; + +static void *lzo_mem; +static void *lzo_compress_buf; + +/* + * Note about LZO compression. + * + * We want to use the _999_ compression routine which gives better compression + * rates at the expense of time. Decompression time is unaffected. We might as + * well use the standard lzo library routines for this but they will overflow + * the destination buffer since they don't check the destination size. + * + * We therefore compress to a temporary buffer and copy if it will fit. + * + */ +static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen) +{ + lzo_uint compress_size; + int ret; + + ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); + + if (ret != LZO_E_OK) + return -1; + + if (compress_size > *dstlen) + return -1; + + memcpy(cpage_out, lzo_compress_buf, compress_size); + *dstlen = compress_size; + + return 0; +} + +static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen) +{ + int ret; + lzo_uint dl; + + ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); + + if (ret != LZO_E_OK || dl != destlen) + return -1; + + return 0; +} + +static struct jffs2_compressor jffs2_lzo_comp = { + .priority = JFFS2_LZO_PRIORITY, + .name = "lzo", + .compr = JFFS2_COMPR_LZO, + .compress = &jffs2_lzo_cmpr, + .decompress = &jffs2_lzo_decompress, + .disabled = 1, +}; + +int jffs2_lzo_init(void) +{ + int ret; + + lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); + if (!lzo_mem) + return -1; + + /* Worse case LZO compression size from their FAQ */ + lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); + if (!lzo_compress_buf) { + free(lzo_mem); + return -1; + } + + ret = jffs2_register_compressor(&jffs2_lzo_comp); + if (ret < 0) { + free(lzo_compress_buf); + free(lzo_mem); + } + + return ret; +} + +void jffs2_lzo_exit(void) +{ + jffs2_unregister_compressor(&jffs2_lzo_comp); + free(lzo_compress_buf); + free(lzo_mem); +} + +#else + +int jffs2_lzo_init(void) +{ + return 0; +} + +void jffs2_lzo_exit(void) +{ +} + +#endif diff --git a/jffsX-utils/compr_rtime.c b/jffsX-utils/compr_rtime.c new file mode 100644 index 0000000..f24379d --- /dev/null +++ b/jffsX-utils/compr_rtime.c @@ -0,0 +1,119 @@ +/* + * 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. + * + * 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) +{ + short positions[256]; + int outpos = 0; + int pos=0; + + memset(positions,0,sizeof(positions)); + + while (pos < (*sourcelen) && outpos+2 <= (*dstlen)) { + 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, + __attribute__((unused)) uint32_t srclen, uint32_t destlen) +{ + 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/jffsX-utils/compr_zlib.c b/jffsX-utils/compr_zlib.c new file mode 100644 index 0000000..1f94628 --- /dev/null +++ b/jffsX-utils/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. + */ + +#define PROGRAM_NAME "compr_zlib" + +#include <stdint.h> +#define crc32 __zlib_crc32 +#include <zlib.h> +#undef crc32 +#include <stdio.h> +#include <asm/types.h> +#include <linux/jffs2.h> +#include "common.h" +#include "compr.h" + +/* 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 + +static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen) +{ + 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; +} + +static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen) +{ + z_stream strm; + int ret; + + strm.zalloc = (void *)0; + strm.zfree = (void *)0; + + if (Z_OK != inflateInit(&strm)) { + return 1; + } + strm.next_in = data_in; + strm.avail_in = srclen; + strm.total_in = 0; + + strm.next_out = cpage_out; + strm.avail_out = destlen; + strm.total_out = 0; + + while((ret = inflate(&strm, Z_FINISH)) == Z_OK) + ; + + inflateEnd(&strm); + return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { + .priority = JFFS2_ZLIB_PRIORITY, + .name = "zlib", + .disabled = 0, + .compr = JFFS2_COMPR_ZLIB, + .compress = &jffs2_zlib_compress, + .decompress = &jffs2_zlib_decompress, +}; + +int jffs2_zlib_init(void) +{ + return jffs2_register_compressor(&jffs2_zlib_comp); +} + +void jffs2_zlib_exit(void) +{ + jffs2_unregister_compressor(&jffs2_zlib_comp); +} diff --git a/jffsX-utils/device_table.txt b/jffsX-utils/device_table.txt new file mode 100644 index 0000000..394a62b --- /dev/null +++ b/jffsX-utils/device_table.txt @@ -0,0 +1,128 @@ +# 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/jffsX-utils/jffs-dump.c b/jffsX-utils/jffs-dump.c new file mode 100644 index 0000000..3176469 --- /dev/null +++ b/jffsX-utils/jffs-dump.c @@ -0,0 +1,359 @@ +/* + * Dump JFFS filesystem. + * Useful when it buggers up. + */ + +#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> + +#include "common.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 \ + - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ + % JFFS_ALIGN_SIZE) + +#define JFFS_EMPTY_BITMASK 0xffffffff +#define JFFS_MAGIC_BITMASK 0x34383931 +#define JFFS_DIRTY_BITMASK 0x00000000 + +struct jffs_raw_inode +{ + uint32_t magic; /* A constant magic number. */ + uint32_t ino; /* Inode number. */ + uint32_t pino; /* Parent's inode number. */ + uint32_t version; /* Version number. */ + uint32_t mode; /* file_type, mode */ + uint16_t uid; + uint16_t gid; + uint32_t atime; + uint32_t mtime; + uint32_t ctime; + uint32_t offset; /* Where to begin to write. */ + uint32_t dsize; /* Size of the file data. */ + uint32_t rsize; /* How much are going to be replaced? */ + uint8_t nsize; /* Name length. */ + uint8_t nlink; /* Number of links. */ + uint8_t spare : 6; /* For future use. */ + uint8_t rename : 1; /* Is this a special rename? */ + uint8_t deleted : 1; /* Has this file been deleted? */ + uint8_t accurate; /* The inode is obsolete if accurate == 0. */ + uint32_t dchksum; /* Checksum for the data. */ + uint16_t nchksum; /* Checksum for the name. */ + uint16_t 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 uint32_t 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 uint32_t +jffs_checksum(void *data, int size) +{ + uint32_t sum = 0; + uint8_t *ptr = (uint8_t *)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(uint32_t *adr, uint32_t 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(uint16_t *adr, uint16_t 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 uint32_t read_val32(uint32_t *adr) +{ + uint32_t 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 uint16_t read_val16(uint16_t *adr) +{ + uint16_t 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; + uint32_t 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/jffsX-utils/jffs2dump.c b/jffsX-utils/jffs2dump.c new file mode 100644 index 0000000..f8b8ac7 --- /dev/null +++ b/jffsX-utils/jffs2dump.c @@ -0,0 +1,805 @@ +/* + * dumpjffs2.c + * + * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility dumps the contents of a binary JFFS2 image + * + * + * Bug/ToDo: + */ + +#define PROGRAM_NAME "jffs2dump" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <asm/types.h> +#include <dirent.h> +#include <mtd/jffs2-user.h> +#include <endian.h> +#include <byteswap.h> +#include <getopt.h> +#include <crc32.h> +#include "summary.h" +#include "common.h" + +#define PAD(x) (((x)+3)&~3) + +/* For outputting a byte-swapped version of the input image. */ +#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) +#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) + +#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) +#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) + +// Global variables +long imglen; // length of image +char *data; // image data + +void display_help (void) +{ + printf("Usage: %s [OPTION]... INPUTFILE\n" + "Dump the contents of a binary JFFS2 image.\n\n" + " --help display this help and exit\n" + " --version display version information and exit\n" + " -b, --bigendian image is big endian\n" + " -l, --littleendian image is little endian\n" + " -c, --content dump image contents\n" + " -e, --endianconvert=FNAME convert image endianness, output to file fname\n" + " -r, --recalccrc recalc name and data crc on endian conversion\n" + " -d, --datsize=LEN size of data chunks, when oob data in binary image (NAND only)\n" + " -o, --oobsize=LEN size of oob data chunk in binary image (NAND only)\n" + " -v, --verbose verbose output\n", + PROGRAM_NAME); + exit(0); +} + +void display_version (void) +{ + printf("%1$s " VERSION "\n" + "\n" + "Copyright (C) 2003 Thomas Gleixner \n" + "\n" + "%1$s comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of %1$s\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n", + PROGRAM_NAME); + exit(0); +} + +// Option variables + +int verbose; // verbose output +char *img; // filename of image +int dumpcontent; // dump image content +int target_endian = __BYTE_ORDER; // image endianess +int convertendian; // convert endianness +int recalccrc; // recalc name and data crc's on endian conversion +char cnvfile[256]; // filename for conversion output +int datsize; // Size of data chunks, when oob data is inside the binary image +int oobsize; // Size of oob chunks, when oob data is inside the binary image + +void process_options (int argc, char *argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "blce:rd:o:v"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"bigendian", no_argument, 0, 'b'}, + {"littleendian", no_argument, 0, 'l'}, + {"content", no_argument, 0, 'c'}, + {"endianconvert", required_argument, 0, 'e'}, + {"datsize", required_argument, 0, 'd'}, + {"oobsize", required_argument, 0, 'o'}, + {"recalccrc", required_argument, 0, 'r'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'v': + verbose = 1; + break; + case 'b': + target_endian = __BIG_ENDIAN; + break; + case 'l': + target_endian = __LITTLE_ENDIAN; + break; + case 'c': + dumpcontent = 1; + break; + case 'd': + datsize = atoi(optarg); + break; + case 'o': + oobsize = atoi(optarg); + break; + case 'e': + convertendian = 1; + strcpy (cnvfile, optarg); + break; + case 'r': + recalccrc = 1; + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 1 || error) + display_help (); + + img = argv[optind]; +} + + +/* + * Dump image contents + */ +void do_dumpcontent (void) +{ + char *p = data, *p_free_begin; + union jffs2_node_union *node; + int empty = 0, dirty = 0; + char name[256]; + uint32_t crc; + uint16_t type; + int bitchbitmask = 0; + int obsolete; + + p_free_begin = NULL; + while ( p < (data + imglen)) { + node = (union jffs2_node_union*) p; + + /* Skip empty space */ + if (!p_free_begin) + p_free_begin = p; + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { + p += 4; + empty += 4; + continue; + } + + if (p != p_free_begin) + printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data); + p_free_begin = NULL; + + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { + if (!bitchbitmask++) + printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); + p += 4; + dirty += 4; + continue; + } + bitchbitmask = 0; + + type = je16_to_cpu(node->u.nodetype); + if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { + obsolete = 1; + type |= JFFS2_NODE_ACCURATE; + } else + obsolete = 0; + /* Set accurate for CRC check */ + node->u.nodetype = cpu_to_je16(type); + + crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu (node->u.hdr_crc)) { + printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); + p += 4; + dirty += 4; + continue; + } + + switch(je16_to_cpu(node->u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + printf ("%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), + je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), + je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); + + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); + if (crc != je32_to_cpu (node->i.node_crc)) { + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); + p += PAD(je32_to_cpu (node->i.totlen)); + dirty += PAD(je32_to_cpu (node->i.totlen));; + continue; + } + + crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); + if (crc != je32_to_cpu(node->i.data_crc)) { + printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); + p += PAD(je32_to_cpu (node->i.totlen)); + dirty += PAD(je32_to_cpu (node->i.totlen));; + continue; + } + + p += PAD(je32_to_cpu (node->i.totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: + memcpy (name, node->d.name, node->d.nsize); + name [node->d.nsize] = 0x0; + printf ("%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), + je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), + node->d.nsize, name); + + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); + if (crc != je32_to_cpu (node->d.node_crc)) { + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); + p += PAD(je32_to_cpu (node->d.totlen)); + dirty += PAD(je32_to_cpu (node->d.totlen));; + continue; + } + + crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); + if (crc != je32_to_cpu(node->d.name_crc)) { + printf ("Wrong name_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); + p += PAD(je32_to_cpu (node->d.totlen)); + dirty += PAD(je32_to_cpu (node->d.totlen));; + continue; + } + + p += PAD(je32_to_cpu (node->d.totlen)); + break; + + case JFFS2_NODETYPE_XATTR: + memcpy(name, node->x.data, node->x.name_len); + name[node->x.name_len] = '\x00'; + printf ("%8s Xattr node at 0x%08zx, totlen 0x%08x, xid %5d, version %5d, name_len %3d, name %s\n", + obsolete ? "Obsolete" : "", + p - data, + je32_to_cpu (node->x.totlen), + je32_to_cpu (node->x.xid), + je32_to_cpu (node->x.version), + node->x.name_len, + name); + + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_xattr) - sizeof (node->x.node_crc)); + if (crc != je32_to_cpu (node->x.node_crc)) { + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.node_crc), crc); + p += PAD(je32_to_cpu (node->x.totlen)); + dirty += PAD(je32_to_cpu (node->x.totlen)); + continue; + } + + crc = mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1); + if (crc != je32_to_cpu (node->x.data_crc)) { + printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.data_crc), crc); + p += PAD(je32_to_cpu (node->x.totlen)); + dirty += PAD(je32_to_cpu (node->x.totlen)); + continue; + } + p += PAD(je32_to_cpu (node->x.totlen)); + break; + + case JFFS2_NODETYPE_XREF: + printf ("%8s Xref node at 0x%08zx, totlen 0x%08x, xid %5d, xseqno %5d, #ino %8d\n", + obsolete ? "Obsolete" : "", + p - data, + je32_to_cpu (node->r.totlen), + je32_to_cpu (node->r.xid), + je32_to_cpu (node->r.xseqno), + je32_to_cpu (node->r.ino)); + p += PAD(je32_to_cpu (node->r.totlen)); + break; + + case JFFS2_NODETYPE_SUMMARY: { + + int i; + struct jffs2_sum_marker * sm; + + printf("%8s Inode Sum node at 0x%08zx, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", + obsolete ? "Obsolete" : "", + p - data, + je32_to_cpu (node->s.totlen), + je32_to_cpu (node->s.sum_num), + je32_to_cpu (node->s.cln_mkr)); + + crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); + if (crc != je32_to_cpu (node->s.node_crc)) { + printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); + p += PAD(je32_to_cpu (node->s.totlen)); + dirty += PAD(je32_to_cpu (node->s.totlen));; + continue; + } + + crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); + if (crc != je32_to_cpu(node->s.sum_crc)) { + printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); + p += PAD(je32_to_cpu (node->s.totlen)); + dirty += PAD(je32_to_cpu (node->s.totlen));; + continue; + } + + if (verbose) { + void *sp; + sp = (p + sizeof(struct jffs2_raw_summary)); + + for(i=0; i<je32_to_cpu(node->s.sum_num); i++) { + + switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { + case JFFS2_NODETYPE_INODE : { + + struct jffs2_sum_inode_flash *spi; + spi = sp; + + printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", + "", + je32_to_cpu (spi->inode), + je32_to_cpu (spi->version), + je32_to_cpu (spi->offset), + je32_to_cpu (spi->totlen)); + + sp += JFFS2_SUMMARY_INODE_SIZE; + break; + } + + case JFFS2_NODETYPE_DIRENT : { + + char name[255]; + struct jffs2_sum_dirent_flash *spd; + spd = sp; + + memcpy(name,spd->name,spd->nsize); + name [spd->nsize] = 0x0; + + printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", + "", + je32_to_cpu (spd->offset), + je32_to_cpu (spd->totlen), + je32_to_cpu (spd->pino), + je32_to_cpu (spd->version), + je32_to_cpu (spd->ino), + spd->nsize, + name); + + sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); + break; + } + + case JFFS2_NODETYPE_XATTR : { + struct jffs2_sum_xattr_flash *spx; + spx = sp; + printf ("%14s Xattr offset 0x%08x, totlen 0x%08x, version %5d, #xid %8d\n", + "", + je32_to_cpu (spx->offset), + je32_to_cpu (spx->totlen), + je32_to_cpu (spx->version), + je32_to_cpu (spx->xid)); + sp += JFFS2_SUMMARY_XATTR_SIZE; + break; + } + + case JFFS2_NODETYPE_XREF : { + struct jffs2_sum_xref_flash *spr; + spr = sp; + printf ("%14s Xref offset 0x%08x\n", + "", + je32_to_cpu (spr->offset)); + sp += JFFS2_SUMMARY_XREF_SIZE; + break; + } + + default : + printf("Unknown summary node!\n"); + break; + } + } + + sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); + + printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", + "", + je32_to_cpu(sm->offset), + je32_to_cpu(sm->magic), + je32_to_cpu(node->s.padded)); + } + + p += PAD(je32_to_cpu (node->s.totlen)); + break; + } + + case JFFS2_NODETYPE_CLEANMARKER: + if (verbose) { + printf ("%8s Cleanmarker at 0x%08zx, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_PADDING: + if (verbose) { + printf ("%8s Padding node at 0x%08zx, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case 0xffff: + p += 4; + empty += 4; + break; + + default: + if (verbose) { + printf ("%8s Unknown node at 0x%08zx, totlen 0x%08x\n", + obsolete ? "Obsolete" : "", + p - data, je32_to_cpu (node->u.totlen)); + } + p += PAD(je32_to_cpu (node->u.totlen)); + dirty += PAD(je32_to_cpu (node->u.totlen)); + + } + } + + if (verbose) + printf ("Empty space: %d, dirty space: %d\n", empty, dirty); +} + +/* + * Convert endianess + */ +void do_endianconvert (void) +{ + char *p = data; + union jffs2_node_union *node, newnode; + int fd, len; + jint32_t mode; + uint32_t crc; + + fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); + return; + } + + while ( p < (data + imglen)) { + node = (union jffs2_node_union*) p; + + /* Skip empty space */ + if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { + write (fd, p, 4); + p += 4; + continue; + } + + if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { + printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); + newnode.u.magic = cnv_e16 (node->u.magic); + newnode.u.nodetype = cnv_e16 (node->u.nodetype); + write (fd, &newnode, 4); + p += 4; + continue; + } + + crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu (node->u.hdr_crc)) { + printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); + } + + switch(je16_to_cpu(node->u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + + newnode.i.magic = cnv_e16 (node->i.magic); + newnode.i.nodetype = cnv_e16 (node->i.nodetype); + newnode.i.totlen = cnv_e32 (node->i.totlen); + newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.i.ino = cnv_e32 (node->i.ino); + newnode.i.version = cnv_e32 (node->i.version); + mode.v32 = node->i.mode.m; + mode = cnv_e32 (mode); + newnode.i.mode.m = mode.v32; + newnode.i.uid = cnv_e16 (node->i.uid); + newnode.i.gid = cnv_e16 (node->i.gid); + newnode.i.isize = cnv_e32 (node->i.isize); + newnode.i.atime = cnv_e32 (node->i.atime); + newnode.i.mtime = cnv_e32 (node->i.mtime); + newnode.i.ctime = cnv_e32 (node->i.ctime); + newnode.i.offset = cnv_e32 (node->i.offset); + newnode.i.csize = cnv_e32 (node->i.csize); + newnode.i.dsize = cnv_e32 (node->i.dsize); + newnode.i.compr = node->i.compr; + newnode.i.usercompr = node->i.usercompr; + newnode.i.flags = cnv_e16 (node->i.flags); + if (recalccrc) { + len = je32_to_cpu(node->i.csize); + newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len)); + } else + newnode.i.data_crc = cnv_e32 (node->i.data_crc); + + newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); + + write (fd, &newnode, sizeof (struct jffs2_raw_inode)); + write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); + + p += PAD(je32_to_cpu (node->i.totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: + newnode.d.magic = cnv_e16 (node->d.magic); + newnode.d.nodetype = cnv_e16 (node->d.nodetype); + newnode.d.totlen = cnv_e32 (node->d.totlen); + newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.d.pino = cnv_e32 (node->d.pino); + newnode.d.version = cnv_e32 (node->d.version); + newnode.d.ino = cnv_e32 (node->d.ino); + newnode.d.mctime = cnv_e32 (node->d.mctime); + newnode.d.nsize = node->d.nsize; + newnode.d.type = node->d.type; + newnode.d.unused[0] = node->d.unused[0]; + newnode.d.unused[1] = node->d.unused[1]; + newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); + if (recalccrc) + newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); + else + newnode.d.name_crc = cnv_e32 (node->d.name_crc); + + write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); + write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); + p += PAD(je32_to_cpu (node->d.totlen)); + break; + + case JFFS2_NODETYPE_XATTR: + newnode.x.magic = cnv_e16 (node->x.magic); + newnode.x.nodetype = cnv_e16 (node->x.nodetype); + newnode.x.totlen = cnv_e32 (node->x.totlen); + newnode.x.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.x.xid = cnv_e32 (node->x.xid); + newnode.x.version = cnv_e32 (node->x.version); + newnode.x.xprefix = node->x.xprefix; + newnode.x.name_len = node->x.name_len; + newnode.x.value_len = cnv_e16 (node->x.value_len); + if (recalccrc) + newnode.x.data_crc = cpu_to_e32 (mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1)); + else + newnode.x.data_crc = cnv_e32 (node->x.data_crc); + newnode.x.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xattr) - sizeof (newnode.x.node_crc))); + + write (fd, &newnode, sizeof (struct jffs2_raw_xattr)); + write (fd, p + sizeof (struct jffs2_raw_xattr), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_xattr))); + p += PAD(je32_to_cpu (node->x.totlen)); + break; + + case JFFS2_NODETYPE_XREF: + newnode.r.magic = cnv_e16 (node->r.magic); + newnode.r.nodetype = cnv_e16 (node->r.nodetype); + newnode.r.totlen = cnv_e32 (node->r.totlen); + newnode.r.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - sizeof (newnode.r.hdr_crc))); + newnode.r.ino = cnv_e32 (node->r.ino); + newnode.r.xid = cnv_e32 (node->r.xid); + newnode.r.xseqno = cnv_e32 (node->r.xseqno); + newnode.r.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xref) - sizeof (newnode.r.node_crc))); + p += PAD(je32_to_cpu (node->x.totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: + case JFFS2_NODETYPE_PADDING: + newnode.u.magic = cnv_e16 (node->u.magic); + newnode.u.nodetype = cnv_e16 (node->u.nodetype); + newnode.u.totlen = cnv_e32 (node->u.totlen); + newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + + write (fd, &newnode, sizeof (struct jffs2_unknown_node)); + len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); + if (len > 0) + write (fd, p + sizeof (struct jffs2_unknown_node), len); + + p += PAD(je32_to_cpu (node->u.totlen)); + break; + + case JFFS2_NODETYPE_SUMMARY : { + struct jffs2_sum_marker *sm_ptr; + int i,sum_len; + int counter = 0; + + newnode.s.magic = cnv_e16 (node->s.magic); + newnode.s.nodetype = cnv_e16 (node->s.nodetype); + newnode.s.totlen = cnv_e32 (node->s.totlen); + newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); + newnode.s.sum_num = cnv_e32 (node->s.sum_num); + newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); + newnode.s.padded = cnv_e32 (node->s.padded); + + newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); + + // summary header + p += sizeof (struct jffs2_raw_summary); + + // summary data + sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); + + for (i=0; i<je32_to_cpu (node->s.sum_num); i++) { + union jffs2_sum_flash *fl_ptr; + + fl_ptr = (union jffs2_sum_flash *) p; + + switch (je16_to_cpu (fl_ptr->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + + fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); + fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); + fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); + fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); + fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); + p += sizeof (struct jffs2_sum_inode_flash); + counter += sizeof (struct jffs2_sum_inode_flash); + break; + + case JFFS2_NODETYPE_DIRENT: + fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); + fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); + fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); + fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); + fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); + fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); + p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; + counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; + break; + + case JFFS2_NODETYPE_XATTR: + fl_ptr->x.nodetype = cnv_e16 (fl_ptr->x.nodetype); + fl_ptr->x.xid = cnv_e32 (fl_ptr->x.xid); + fl_ptr->x.version = cnv_e32 (fl_ptr->x.version); + fl_ptr->x.offset = cnv_e32 (fl_ptr->x.offset); + fl_ptr->x.totlen = cnv_e32 (fl_ptr->x.totlen); + p += sizeof (struct jffs2_sum_xattr_flash); + counter += sizeof (struct jffs2_sum_xattr_flash); + break; + + case JFFS2_NODETYPE_XREF: + fl_ptr->r.nodetype = cnv_e16 (fl_ptr->r.nodetype); + fl_ptr->r.offset = cnv_e32 (fl_ptr->r.offset); + p += sizeof (struct jffs2_sum_xref_flash); + counter += sizeof (struct jffs2_sum_xref_flash); + break; + + default : + printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); + exit(EXIT_FAILURE); + break; + } + + } + + //pad + p += sum_len - counter; + + // summary marker + sm_ptr = (struct jffs2_sum_marker *) p; + sm_ptr->offset = cnv_e32 (sm_ptr->offset); + sm_ptr->magic = cnv_e32 (sm_ptr->magic); + p += sizeof (struct jffs2_sum_marker); + + // generate new crc on sum data + newnode.s.sum_crc = cpu_to_e32 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), + je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); + + // write out new node header + write(fd, &newnode, sizeof (struct jffs2_raw_summary)); + // write out new summary data + write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); + + break; + } + + case 0xffff: + write (fd, p, 4); + p += 4; + break; + + default: + printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); + p += PAD(je32_to_cpu (node->u.totlen)); + + } + } + + close (fd); + +} + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int fd; + + process_options(argc, argv); + + /* Open the input file */ + if ((fd = open(img, O_RDONLY)) == -1) { + perror("open input file"); + exit(1); + } + + // get image length + imglen = lseek(fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + + data = malloc (imglen); + if (!data) { + perror("out of memory"); + close (fd); + exit(1); + } + + if (datsize && oobsize) { + int idx = 0; + long len = imglen; + uint8_t oob[oobsize]; + printf ("Peeling data out of combined data/oob image\n"); + while (len) { + // read image data + read (fd, &data[idx], datsize); + read (fd, oob, oobsize); + idx += datsize; + imglen -= oobsize; + len -= datsize + oobsize; + } + + } else { + // read image data + read (fd, data, imglen); + } + // Close the input file + close(fd); + + if (dumpcontent) + do_dumpcontent (); + + if (convertendian) + do_endianconvert (); + + // free memory + free (data); + + // Return happy + exit (0); +} diff --git a/jffsX-utils/jffs2reader.c b/jffsX-utils/jffs2reader.c new file mode 100644 index 0000000..a62da9a --- /dev/null +++ b/jffsX-utils/jffs2reader.c @@ -0,0 +1,918 @@ +/* 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 + */ + + +/* +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. + */ + +#define PROGRAM_NAME "jffs2reader" + +#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 <dirent.h> +#include <zlib.h> + +#include "mtd/jffs2-user.h" +#include "common.h" + +#define SCRATCH_SIZE (5*1024*1024) + +/* macro to avoid "lvalue required as left operand of assignment" error */ +#define ADD_BYTES(p, n) ((p) = (typeof(p))((char *)(p) + (n))) + +#define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0) +#define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0) + +struct dir { + struct dir *next; + uint8_t type; + uint8_t nsize; + uint32_t ino; + char name[256]; +}; + +int target_endian = __BYTE_ORDER; + +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, const char *path, + int recurse, int want_ctime); +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, const char *, + uint32_t *, int); +struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *, + uint32_t *); + +void lsdir(char *, size_t, const char *, int, 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 = je32_to_cpu(n->dsize); + + if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize) + errmsg_die("File does not fit into buffer!"); + + if (*rsize < je32_to_cpu(n->isize)) + bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize); + + switch (n->compr) { + case JFFS2_COMPR_ZLIB: + uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen, + (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), + (uLongf) je32_to_cpu(n->csize)); + break; + + case JFFS2_COMPR_NONE: + memcpy(b + je32_to_cpu(n->offset), + ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); + break; + + case JFFS2_COMPR_ZERO: + bzero(b + je32_to_cpu(n->offset), dlen); + break; + + /* [DYN]RUBIN support required! */ + + default: + errmsg_die("Unsupported compression method!"); + } + + *rsize = je32_to_cpu(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 (je32_to_cpu(n->ino)) { + if (dd == NULL) { + d = xmalloc(sizeof(struct dir)); + d->type = n->type; + memcpy(d->name, n->name, n->nsize); + d->nsize = n->nsize; + d->ino = je32_to_cpu(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 = je32_to_cpu(n->ino); + + return o; + } + + if (dd->next == NULL) { + dd->next = xmalloc(sizeof(struct dir)); + dd->next->type = n->type; + memcpy(dd->next->name, n->name, n->nsize); + dd->next->nsize = n->nsize; + dd->next->ino = je32_to_cpu(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, const char *path, int recurse, + int want_ctime) +{ + char m; + char *filetime; + time_t age; + struct jffs2_raw_inode *ri; + jint32_t mode; + + 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) { + warnmsg("bug: raw_inode missing!"); + d = d->next; + continue; + } + + filetime = ctime((const time_t *) &(ri->ctime)); + age = time(NULL) - je32_to_cpu(ri->ctime); + mode.v32 = ri->mode.m; + printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)), + 1, je16_to_cpu(ri->uid), je16_to_cpu(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 ", major(rdev), minor(rdev)); + } else { + printf("%9ld ", (long)je32_to_cpu(ri->dsize)); + } + d->name[d->nsize]='\0'; + if (want_ctime) { + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) + /* hh:mm if less than 6 months old */ + printf("%6.6s %5.5s ", filetime + 4, filetime + 11); + else + printf("%6.6s %4.4s ", filetime + 4, filetime + 20); + } + printf("%s/%s%c", 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 = xmalloc(BUFSIZ); + sprintf(tmp, "%s/%s", path, d->name); + lsdir(o, size, tmp, recurse, want_ctime); /* 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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) + ADD_BYTES(n, 4); + + if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { + if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE && + je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(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)); + } + + ADD_BYTES(n, ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) + ADD_BYTES(n, 4); + + if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { + if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && + je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(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); + } + } + + ADD_BYTES(n, ((je32_to_cpu(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) + + ((je32_to_cpu(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 && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) + ADD_BYTES(n, 4); + + if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { + if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && + (!ino || je32_to_cpu(n->d.ino) == ino) && + (v = je32_to_cpu(n->d.version)) > vmax && + (!pino || (je32_to_cpu(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); + } + } + + ADD_BYTES(n, ((je32_to_cpu(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, + const 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 = xstrdup(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, + const 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, const char *path, int recurse, int want_ctime) +{ + 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)) + errmsg_die("%s: No such file or directory", path); + + d = collectdir(o, size, ino, d); + printdir(o, size, d, path, recurse, want_ctime); + 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) + errmsg_die("%s: No such file or directory", path); + + if (dd == NULL || dd->type != DT_REG) + errmsg_die("%s: Not a regular file", path); + + 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, want_ctime = 0; + struct stat st; + + char *scratch, *dir = NULL, *file = NULL; + size_t ssize = 0; + + char *buf; + + while ((opt = getopt(argc, argv, "rd:f:t")) > 0) { + switch (opt) { + case 'd': + dir = optarg; + break; + case 'f': + file = optarg; + break; + case 'r': + recurse++; + break; + case 't': + want_ctime++; + break; + default: + fprintf(stderr, + "Usage: %s <image> [-d|-f] < path >\n", + PROGRAM_NAME); + exit(EXIT_FAILURE); + } + } + + fd = open(argv[optind], O_RDONLY); + if (fd == -1) + sys_errmsg_die("%s", argv[optind]); + + if (fstat(fd, &st)) + sys_errmsg_die("%s", argv[optind]); + + buf = xmalloc((size_t) st.st_size); + + if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) + sys_errmsg_die("%s", argv[optind]); + + if (dir) + lsdir(buf, st.st_size, dir, recurse, want_ctime); + + if (file) { + scratch = xmalloc(SCRATCH_SIZE); + + catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); + free(scratch); + } + + if (!dir && !file) + lsdir(buf, st.st_size, "/", 1, want_ctime); + + + free(buf); + exit(EXIT_SUCCESS); +} diff --git a/jffsX-utils/mkfs.jffs2.1 b/jffsX-utils/mkfs.jffs2.1 new file mode 100644 index 0000000..7c57ddc --- /dev/null +++ b/jffsX-utils/mkfs.jffs2.1 @@ -0,0 +1,268 @@ +.TH MKFS.JFFS2 1 +.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 --with-xattr +] +[ +.B --with-selinux +] +[ +.B --with-posix-acl +] +[ +.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 available 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. Set according to target system's memory +management page size (NOTE: this is NOT related to NAND page size). +.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 --with-xattr +Enables xattr, stuff all xattr entries into jffs2 image file. +.TP +.B --with-selinux +Enables xattr, stuff only SELinux Labels into jffs2 image file. +.TP +.B --with-posix-acl +Enable xattr, stuff only POSIX ACL entries into jffs2 image file. +.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 available compressors and their default states. +.TP +.B -X, --enable-compressor=NAME +Enable a compressor. Use +.B -L +to see the list of the available 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 available compressors and their default priority. +Priorities are used by priority compression mode. +.TP +.B -L, --list-compressors +Show the list of the available 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 LIMITATIONS +The format and grammar of the device table file does not allow it to +create symbolic links when the symbolic links are not already present +in the root working directory. + +However, symbolic links may be specified in the device table file +using the \fIl\fR type for the purposes of setting their permissions +and ownership. +.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/jffsX-utils/mkfs.jffs2.c b/jffsX-utils/mkfs.jffs2.c new file mode 100644 index 0000000..f09c0b2 --- /dev/null +++ b/jffsX-utils/mkfs.jffs2.c @@ -0,0 +1,1805 @@ +/* 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 + * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * 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 PROGRAM_NAME "mkfs.jffs2" + +#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> +#ifndef WITHOUT_XATTR +#include <sys/xattr.h> +#include <sys/acl.h> +#endif +#include <byteswap.h> +#include <crc32.h> +#include <inttypes.h> + +#include "rbtree.h" +#include "common.h" + +/* Do not use the weird XPG version of basename */ +#undef basename + +//#define DMALLOC +//#define mkfs_debug_msg errmsg +#define mkfs_debug_msg(a...) { } + +#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 */ + uint32_t ino; /* Inode number of this file in JFFS2 */ + 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 */ + struct rb_node hardlink_rb; +}; + +struct rb_root hardlinks; +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; + +uint32_t find_hardlink(struct filesystem_entry *e) +{ + struct filesystem_entry *f; + struct rb_node **n = &hardlinks.rb_node; + struct rb_node *parent = NULL; + + while (*n) { + parent = *n; + f = rb_entry(parent, struct filesystem_entry, hardlink_rb); + + if ((f->sb.st_dev < e->sb.st_dev) || + (f->sb.st_dev == e->sb.st_dev && + f->sb.st_ino < e->sb.st_ino)) + n = &parent->rb_left; + else if ((f->sb.st_dev > e->sb.st_dev) || + (f->sb.st_dev == e->sb.st_dev && + f->sb.st_ino > e->sb.st_ino)) { + n = &parent->rb_right; + } else + return f->ino; + } + + rb_link_node(&e->hardlink_rb, parent, n); + rb_insert_color(&e->hardlink_rb, &hardlinks); + return 0; +} + +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) { + sys_errmsg("%s:%s", PROGRAM_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) + sys_errmsg_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)) { + /* If this is the first call, and we actually want this + * directory, then return it now */ + if (strcmp(fullname, e->fullname) == 0) + return e; + + e = dir->files; + } + while (e) { + 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(const char *name, + const 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)) { + errmsg_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) { + errmsg_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_ino = sb.st_ino; + entry->sb.st_dev = sb.st_dev; + entry->sb.st_nlink = sb.st_nlink; + + 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, const char *targetpath, + const char *hostpath) +{ + int i, n; + struct stat sb; + char *hpath, *tpath; + struct dirent *dp, **namelist; + struct filesystem_entry *entry; + + + if (lstat(hostpath, &sb)) { + sys_errmsg_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) { + sys_errmsg_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; + } + + xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); + if (lstat(hpath, &sb)) { + sys_errmsg_die("%s", hpath); + } + if (strcmp(targetpath, "/") == 0) { + xasprintf(&tpath, "%s%s", targetpath, dp->d_name); + } else { + xasprintf(&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: + errmsg("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 = xmalloc(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, "/")) { + errmsg_die("Device table entries require absolute paths"); + } + + xasprintf(&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; + case 'l': + mode |= S_IFLNK; + break; + default: + errmsg_die("Unsupported file type '%c'", type); + } + entry = find_filesystem_entry(root, name, mode); + if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { + /* 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 = xstrdup(name); + dir = dirname(tmp); + parent = find_filesystem_entry(root, dir, S_IFDIR); + free(tmp); + if (parent == NULL) { + errmsg ("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 < (start + count); i++) { + xasprintf(&dname, "%s%lu", name, i); + xasprintf(&hpath, "%s/%s%lu", rootdir, name, i); + rdev = makedev(major, minor + (i - start) * increment); + 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: + errmsg_die("Unsupported file type '%c'", 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_cleanmarkers = 1; +static struct jffs2_unknown_node cleanmarker; +static int cleanmarker_size = sizeof(cleanmarker); +static unsigned char ffbuf[16] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* We set this at start of main() using sysconf(), -1 means we don't know */ +/* When building an fs for non-native systems, use --pagesize=SIZE option */ +int page_size = -1; + +#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) + sys_errmsg_die("write"); + + if (ret == 0) + sys_errmsg_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_cleanmarkers) { + if ((out_ofs % erase_block_size) == 0) { + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + padword(); + } + } + if ((out_ofs % erase_block_size) + req > erase_block_size) { + padblock(); + } + if (add_cleanmarkers) { + if ((out_ofs % erase_block_size) == 0) { + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + 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(mtd_crc32(0, &rd, + sizeof(struct jffs2_unknown_node) - 4)); + rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); + rd.version = cpu_to_je32(version++); + rd.ino = cpu_to_je32(e->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(mtd_crc32(0, &rd, sizeof(rd) - 8)); + rd.name_crc = cpu_to_je32(mtd_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 unsigned int 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; + unsigned int totcomp = 0; + + statbuf = &(e->sb); + if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { + errmsg("Skipping file \"%s\" too large.", e->path); + return -1; + } + fd = open(e->hostname, O_RDONLY); + if (fd == -1) { + sys_errmsg_die("%s: open file", e->hostname); + } + + e->ino = ++ino; + mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", + e->name, (unsigned long) e->ino, + (unsigned long) e->parent->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(e->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) { + sys_errmsg_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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); + + full_write(out_fd, &ri, sizeof(ri)); + totcomp += sizeof(ri); + full_write(out_fd, wbuf, space); + totcomp += 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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); + + full_write(out_fd, &ri, sizeof(ri)); + padword(); + } + free(buf); + close(fd); + return totcomp; +} + +static void write_symlink(struct filesystem_entry *e) +{ + int len; + struct stat *statbuf; + struct jffs2_raw_inode ri; + + statbuf = &(e->sb); + e->ino = ++ino; + mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", + e->name, (unsigned long) e->ino, + (unsigned long) e->parent->ino); + write_dirent(e); + + len = strlen(e->link); + if (len > JFFS2_MAX_SYMLINK_LEN) { + errmsg("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(mtd_crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(mtd_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); + e->ino = ++ino; + if (S_ISDIR(statbuf->st_mode)) { + mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", + e->name, (unsigned long) e->ino, + (unsigned long) (e->parent) ? e->parent->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(mtd_crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.ino = cpu_to_je32(e->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(mtd_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); + e->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(mtd_crc32(0, + &ri, sizeof(struct jffs2_unknown_node) - 4)); + + ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); + ri.data_crc = cpu_to_je32(mtd_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(); +} + +#ifndef WITHOUT_XATTR +typedef struct xattr_entry { + struct xattr_entry *next; + uint32_t xid; + int xprefix; + char *xname; + char *xvalue; + int name_len; + int value_len; +} xattr_entry_t; + +#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ +static uint32_t enable_xattr = 0; +static uint32_t highest_xid = 0; +static uint32_t highest_xseqno = 0; + +static struct { + int xprefix; + const char *string; + int length; +} xprefix_tbl[] = { + { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, + { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, + { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, + { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, + { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, + { 0, NULL, 0 } +}; + +static void formalize_posix_acl(void *xvalue, int *value_len) +{ + struct posix_acl_xattr_header *pacl_header; + struct posix_acl_xattr_entry *pent, *plim; + struct jffs2_acl_header *jacl_header; + struct jffs2_acl_entry *jent; + struct jffs2_acl_entry_short *jent_s; + char buffer[XATTR_BUFFER_SIZE]; + int offset = 0; + + pacl_header = xvalue;; + pent = pacl_header->a_entries; + plim = xvalue + *value_len; + + jacl_header = (struct jffs2_acl_header *)buffer; + offset += sizeof(struct jffs2_acl_header); + jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + + while (pent < plim) { + switch(le16_to_cpu(pent->e_tag)) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); + offset += sizeof(struct jffs2_acl_entry_short); + jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); + jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); + break; + case ACL_USER: + case ACL_GROUP: + jent = (struct jffs2_acl_entry *)(buffer + offset); + offset += sizeof(struct jffs2_acl_entry); + jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); + jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); + jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); + break; + default: + printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); + exit(1); + } + pent++; + } + if (offset > *value_len) { + printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", + offset, *value_len); + exit(1); + } + memcpy(xvalue, buffer, offset); + *value_len = offset; +} + +static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + xattr_entry_t *xe; + struct jffs2_raw_xattr rx; + int name_len; + + /* create xattr entry */ + name_len = strlen(xname); + xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); + xe->next = NULL; + xe->xid = ++highest_xid; + xe->xprefix = xprefix; + xe->xname = ((char *)xe) + sizeof(xattr_entry_t); + xe->xvalue = xe->xname + name_len + 1; + xe->name_len = name_len; + xe->value_len = value_len; + strcpy(xe->xname, xname); + memcpy(xe->xvalue, xvalue, value_len); + + /* write xattr node */ + memset(&rx, 0, sizeof(rx)); + rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); + rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); + rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xe->xid); + rx.version = cpu_to_je32(1); /* initial version */ + rx.xprefix = xprefix; + rx.name_len = xe->name_len; + rx.value_len = cpu_to_je16(xe->value_len); + rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); + rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4)); + + pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); + full_write(out_fd, &rx, sizeof(rx)); + full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); + padword(); + + return xe; +} + +#define XATTRENTRY_HASHSIZE 57 +static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + static xattr_entry_t **xentry_hash = NULL; + xattr_entry_t *xe; + int index, name_len; + + /* create hash table */ + if (!xentry_hash) + xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); + + if (xprefix == JFFS2_XPREFIX_ACL_ACCESS + || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) + formalize_posix_acl(xvalue, &value_len); + + name_len = strlen(xname); + index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; + for (xe = xentry_hash[index]; xe; xe = xe->next) { + if (xe->xprefix == xprefix + && xe->value_len == value_len + && !strcmp(xe->xname, xname) + && !memcmp(xe->xvalue, xvalue, value_len)) + break; + } + if (!xe) { + xe = create_xattr_entry(xprefix, xname, xvalue, value_len); + xe->next = xentry_hash[index]; + xentry_hash[index] = xe; + } + return xe; +} + +static void write_xattr_entry(struct filesystem_entry *e) +{ + struct jffs2_raw_xref ref; + struct xattr_entry *xe; + char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; + char *xname; + const char *prefix_str; + int i, xprefix, prefix_len; + int list_sz, offset, name_len, value_len; + + if (!enable_xattr) + return; + + list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); + if (list_sz < 0) { + if (verbose) + printf("llistxattr('%s') = %d : %s\n", + e->hostname, errno, strerror(errno)); + return; + } + + for (offset = 0; offset < list_sz; offset += name_len) { + xname = xlist + offset; + name_len = strlen(xname) + 1; + + for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { + prefix_str = xprefix_tbl[i].string; + prefix_len = xprefix_tbl[i].length; + if (prefix_str[prefix_len - 1] == '.') { + if (!strncmp(xname, prefix_str, prefix_len - 1)) + break; + } else { + if (!strcmp(xname, prefix_str)) + break; + } + } + if (!xprefix) { + if (verbose) + printf("%s: xattr '%s' is not supported.\n", + e->hostname, xname); + continue; + } + if ((enable_xattr & (1 << xprefix)) == 0) + continue; + + value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); + if (value_len < 0) { + if (verbose) + printf("lgetxattr('%s', '%s') = %d : %s\n", + e->hostname, xname, errno, strerror(errno)); + continue; + } + xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); + if (!xe) { + if (verbose) + printf("%s : xattr '%s' was ignored.\n", + e->hostname, xname); + continue; + } + + memset(&ref, 0, sizeof(ref)); + ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); + ref.totlen = cpu_to_je32(sizeof(ref)); + ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); + ref.ino = cpu_to_je32(e->ino); + ref.xid = cpu_to_je32(xe->xid); + ref.xseqno = cpu_to_je32(highest_xseqno += 2); + ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4)); + + pad_block_if_less_than(sizeof(ref)); + full_write(out_fd, &ref, sizeof(ref)); + padword(); + } +} + +#else /* WITHOUT_XATTR */ +#define write_xattr_entry(x) +#endif + +static void recursive_populate_directory(struct filesystem_entry *dir) +{ + struct filesystem_entry *e; + unsigned int wrote; + + if (verbose) { + printf("%s\n", dir->fullname); + } + write_xattr_entry(dir); /* for '/' */ + + e = dir->files; + while (e) { + if (e->sb.st_nlink >= 1 && + (e->ino = find_hardlink(e))) { + + write_dirent(e); + if (verbose) { + printf("\tL %04o %9lu %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, + (int) (e->sb.st_uid), (int) (e->sb.st_gid), + e->name); + } + } else switch (e->sb.st_mode & S_IFMT) { + case S_IFDIR: + if (verbose) { + printf("\td %04o %9" PRIdoff_t " %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); + write_xattr_entry(e); + break; + case S_IFSOCK: + if (verbose) { + printf("\ts %04o %9" PRIdoff_t " %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); + write_xattr_entry(e); + break; + case S_IFIFO: + if (verbose) { + printf("\tp %04o %9" PRIdoff_t " %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); + write_xattr_entry(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); + write_xattr_entry(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); + write_xattr_entry(e); + break; + case S_IFLNK: + if (verbose) { + printf("\tl %04o %9" PRIdoff_t " %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); + write_xattr_entry(e); + break; + case S_IFREG: + wrote = write_regular_file(e); + write_xattr_entry(e); + if (verbose) { + printf("\tf %04o %9" PRIdoff_t " (%9u) %5d:%-3d %s\n", + e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, + (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); + } + break; + default: + errmsg("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) +{ + 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(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); + + if (ino == 0) + ino = 1; + + root->ino = 1; + recursive_populate_directory(root); + + if (pad_fs_size == -1) { + padblock(); + } else { + if (pad_fs_size && add_cleanmarkers){ + padblock(); + while (out_ofs < pad_fs_size) { + full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); + pad(cleanmarker_size - sizeof(cleanmarker)); + 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-cleanmarkers", 0, NULL, 'n'}, + {"cleanmarker", 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'}, + {"enable-compressor", 1, NULL, 'X'}, + {"test-compression", 0, NULL, 't'}, + {"compressor-priority", 1, NULL, 'y'}, + {"incremental", 1, NULL, 'i'}, +#ifndef WITHOUT_XATTR + {"with-xattr", 0, NULL, 1000 }, + {"with-selinux", 0, NULL, 1001 }, + {"with-posix-acl", 0, NULL, 1002 }, +#endif + {NULL, 0, NULL, 0} +}; + +static const 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.\n" +" Set according to target system's memory management\n" +" page size (default: 4KiB)\n" +" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" +" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" +" -m, --compr-mode=MODE Select compression mode (default: priority)\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 available compressors\n" +" -t, --test-compression Call decompress and compare with the original (for test)\n" +" -n, --no-cleanmarkers Don't add a cleanmarker 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" +#ifndef WITHOUT_XATTR +" --with-xattr stuff all xattr entries into image\n" +" --with-selinux stuff only SELinux Labels into jffs2 image\n" +" --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" +#endif +" -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 const char revtext[] = "1.60"; + +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%08zx, 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%08zx, 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%08zx, 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%08zx, 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_PADDING: + if (verbose) { + printf ("%8s Padding node at 0x%08zx, 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%08zx, 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 = xmalloc(erase_block_size); + + 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; + int warn_page_size = 0; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) /* System doesn't know so ... */ + page_size = 4096; /* ... we make an educated guess */ + if (page_size != 4096) + warn_page_size = 1; /* warn user if page size not 4096 */ + + 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) + sys_errmsg_die("%s", optarg); + if (sb.st_size < 10) + errmsg_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) { + errmsg_die("root directory specified more than once"); + } + rootdir = xstrdup(optarg); + break; + + case 's': + page_size = strtol(optarg, NULL, 0); + warn_page_size = 0; /* set by user, so don't need to warn */ + break; + + case 'o': + if (out_fd != -1) { + errmsg_die("output filename specified more than once"); + } + out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (out_fd == -1) { + sys_errmsg_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 '?': + errmsg_die("%s", helptext); + + case 'v': + verbose = 1; + break; + + case 'V': + errmsg_die("revision %s\n", revtext); + + case 'e': { + char *next; + unsigned units = 0; + erase_block_size = strtol(optarg, &next, 0); + if (!erase_block_size) + errmsg_die("Unrecognisable erase size\n"); + + if (*next) { + if (!strcmp(next, "KiB")) { + units = 1024; + } else if (!strcmp(next, "MiB")) { + units = 1024 * 1024; + } else { + errmsg_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_cleanmarkers = 0; + break; + case 'c': + cleanmarker_size = strtol(optarg, NULL, 0); + if (cleanmarker_size < sizeof(cleanmarker)) { + errmsg_die("cleanmarker size must be >= 12"); + } + if (cleanmarker_size >= erase_block_size) { + errmsg_die("cleanmarker size must be < eraseblock size"); + } + break; + case 'm': + if (jffs2_set_compression_mode_name(optarg)) { + errmsg_die("Unknown compression mode %s", optarg); + } + break; + case 'x': + if (jffs2_disable_compressor_name(optarg)) { + errmsg_die("Unknown compressor name %s",optarg); + } + break; + case 'X': + if (jffs2_enable_compressor_name(optarg)) { + errmsg_die("Unknown compressor name %s",optarg); + } + break; + case 'L': + errmsg_die("\n%s",jffs2_list_compressors()); + break; + case 't': + jffs2_compression_check_set(1); + break; + case 'y': + compr_name = xmalloc(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 { + errmsg_die("Cannot parse %s",optarg); + } + free(compr_name); + break; + case 'i': + if (in_fd != -1) { + errmsg_die("(incremental) filename specified more than once"); + } + in_fd = open(optarg, O_RDONLY); + if (in_fd == -1) { + sys_errmsg_die("cannot open (incremental) file"); + } + break; +#ifndef WITHOUT_XATTR + case 1000: /* --with-xattr */ + enable_xattr |= (1 << JFFS2_XPREFIX_USER) + | (1 << JFFS2_XPREFIX_SECURITY) + | (1 << JFFS2_XPREFIX_ACL_ACCESS) + | (1 << JFFS2_XPREFIX_ACL_DEFAULT) + | (1 << JFFS2_XPREFIX_TRUSTED); + break; + case 1001: /* --with-selinux */ + enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); + break; + case 1002: /* --with-posix-acl */ + enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) + | (1 << JFFS2_XPREFIX_ACL_DEFAULT); + break; +#endif + } + } + if (warn_page_size) { + errmsg("Page size for this system is by default %d", page_size); + errmsg("Use the --pagesize=SIZE option if this is not what you want"); + } + if (out_fd == -1) { + if (isatty(1)) { + errmsg_die("%s", helptext); + } + out_fd = 1; + } + if (lstat(rootdir, &sb)) { + sys_errmsg_die("%s", rootdir); + } + if (chdir(rootdir)) + sys_errmsg_die("%s", rootdir); + + if (!(cwd = getcwd(0, GETCWD_SIZE))) + sys_errmsg_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/jffsX-utils/rbtree.c b/jffsX-utils/rbtree.c new file mode 100644 index 0000000..329e098 --- /dev/null +++ b/jffsX-utils/rbtree.c @@ -0,0 +1,390 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli <andrea@suse.de> + (C) 2002 David Woodhouse <dwmw2@infradead.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. + + 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 + + linux/lib/rbtree.c +*/ + +#include <stdlib.h> +#include "rbtree.h" + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *right = node->rb_right; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_right = right->rb_left)) + rb_set_parent(right->rb_left, node); + right->rb_left = node; + + rb_set_parent(right, parent); + + if (parent) + { + if (node == parent->rb_left) + parent->rb_left = right; + else + parent->rb_right = right; + } + else + root->rb_node = right; + rb_set_parent(node, right); +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *left = node->rb_left; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_left = left->rb_right)) + rb_set_parent(left->rb_right, node); + left->rb_right = node; + + rb_set_parent(left, parent); + + if (parent) + { + if (node == parent->rb_right) + parent->rb_right = left; + else + parent->rb_left = left; + } + else + root->rb_node = left; + rb_set_parent(node, left); +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *parent, *gparent; + + while ((parent = rb_parent(node)) && rb_is_red(parent)) + { + gparent = rb_parent(parent); + + if (parent == gparent->rb_left) + { + { + register struct rb_node *uncle = gparent->rb_right; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_right == node) + { + register struct rb_node *tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_right(gparent, root); + } else { + { + register struct rb_node *uncle = gparent->rb_left; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_left == node) + { + register struct rb_node *tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_left(gparent, root); + } + } + + rb_set_black(root->rb_node); +} + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, + struct rb_root *root) +{ + struct rb_node *other; + + while ((!node || rb_is_black(node)) && node != root->rb_node) + { + if (parent->rb_left == node) + { + other = parent->rb_right; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_left(parent, root); + other = parent->rb_right; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_right || rb_is_black(other->rb_right)) + { + struct rb_node *o_left; + if ((o_left = other->rb_left)) + rb_set_black(o_left); + rb_set_red(other); + __rb_rotate_right(other, root); + other = parent->rb_right; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + if (other->rb_right) + rb_set_black(other->rb_right); + __rb_rotate_left(parent, root); + node = root->rb_node; + break; + } + } + else + { + other = parent->rb_left; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_right(parent, root); + other = parent->rb_left; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_left || rb_is_black(other->rb_left)) + { + register struct rb_node *o_right; + if ((o_right = other->rb_right)) + rb_set_black(o_right); + rb_set_red(other); + __rb_rotate_left(other, root); + other = parent->rb_left; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + if (other->rb_left) + rb_set_black(other->rb_left); + __rb_rotate_right(parent, root); + node = root->rb_node; + break; + } + } + } + if (node) + rb_set_black(node); +} + +void rb_erase(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *child, *parent; + int color; + + if (!node->rb_left) + child = node->rb_right; + else if (!node->rb_right) + child = node->rb_left; + else + { + struct rb_node *old = node, *left; + + node = node->rb_right; + while ((left = node->rb_left) != NULL) + node = left; + child = node->rb_right; + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent == old) { + parent->rb_right = child; + parent = node; + } else + parent->rb_left = child; + + node->rb_parent_color = old->rb_parent_color; + node->rb_right = old->rb_right; + node->rb_left = old->rb_left; + + if (rb_parent(old)) + { + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; + else + rb_parent(old)->rb_right = node; + } else + root->rb_node = node; + + rb_set_parent(old->rb_left, node); + if (old->rb_right) + rb_set_parent(old->rb_right, node); + goto color; + } + + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_left) + n = n->rb_left; + return n; +} + +struct rb_node *rb_last(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_right) + n = n->rb_right; + return n; +} + +struct rb_node *rb_next(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) + node=node->rb_left; + return node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while ((parent = rb_parent(node)) && node == parent->rb_right) + node = parent; + + return parent; +} + +struct rb_node *rb_prev(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a left-hand child, go down and then right as far + as we can. */ + if (node->rb_left) { + node = node->rb_left; + while (node->rb_right) + node=node->rb_right; + return node; + } + + /* No left-hand children. Go up till we find an ancestor which + is a right-hand child of its parent */ + while ((parent = rb_parent(node)) && node == parent->rb_left) + node = parent; + + return parent; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root) +{ + struct rb_node *parent = rb_parent(victim); + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->rb_left) + parent->rb_left = new; + else + parent->rb_right = new; + } else { + root->rb_node = new; + } + if (victim->rb_left) + rb_set_parent(victim->rb_left, new); + if (victim->rb_right) + rb_set_parent(victim->rb_right, new); + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} diff --git a/jffsX-utils/rbtree.h b/jffsX-utils/rbtree.h new file mode 100644 index 0000000..0d77b65 --- /dev/null +++ b/jffsX-utils/rbtree.h @@ -0,0 +1,171 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli <andrea@suse.de> + + 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 + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + Some example of insert and search follows here. The search is a plain + normal search over an ordered tree. The insert instead must be implemented + int two steps: as first thing the code must insert the element in + order as a red leaf in the tree, then the support library function + rb_insert_color() must be called. Such function will do the + not trivial work to rebalance the rbtree if necessary. + +----------------------------------------------------------------------- +static inline struct page * rb_search_page_cache(struct inode * inode, + unsigned long offset) +{ + struct rb_node * n = inode->i_rb_page_cache.rb_node; + struct page * page; + + while (n) + { + page = rb_entry(n, struct page, rb_page_cache); + + if (offset < page->offset) + n = n->rb_left; + else if (offset > page->offset) + n = n->rb_right; + else + return page; + } + return NULL; +} + +static inline struct page * __rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct rb_node ** p = &inode->i_rb_page_cache.rb_node; + struct rb_node * parent = NULL; + struct page * page; + + while (*p) + { + parent = *p; + page = rb_entry(parent, struct page, rb_page_cache); + + if (offset < page->offset) + p = &(*p)->rb_left; + else if (offset > page->offset) + p = &(*p)->rb_right; + else + return page; + } + + rb_link_node(node, parent, p); + + return NULL; +} + +static inline struct page * rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct page * ret; + if ((ret = __rb_insert_page_cache(inode, offset, node))) + goto out; + rb_insert_color(node, &inode->i_rb_page_cache); + out: + return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef _LINUX_RBTREE_H +#define _LINUX_RBTREE_H + +#include <linux/kernel.h> +#include <linux/stddef.h> + +struct rb_node +{ + unsigned long rb_parent_color; +#define RB_RED 0 +#define RB_BLACK 1 + struct rb_node *rb_right; + struct rb_node *rb_left; +} __attribute__((aligned(sizeof(long)))); + /* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct rb_root +{ + struct rb_node *rb_node; +}; + + +#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) +#define rb_color(r) ((r)->rb_parent_color & 1) +#define rb_is_red(r) (!rb_color(r)) +#define rb_is_black(r) rb_color(r) +#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) +#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) + +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ + rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; +} +static inline void rb_set_color(struct rb_node *rb, int color) +{ + rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; +} + +#define RB_ROOT (struct rb_root) { NULL, } + +/* Newer gcc versions take care of exporting this */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) +#define RB_EMPTY_NODE(node) (rb_parent(node) == node) +#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(struct rb_node *); +extern struct rb_node *rb_prev(struct rb_node *); +extern struct rb_node *rb_first(struct rb_root *); +extern struct rb_node *rb_last(struct rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); + +static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, + struct rb_node ** rb_link) +{ + node->rb_parent_color = (unsigned long )parent; + node->rb_left = node->rb_right = NULL; + + *rb_link = node; +} + +#endif /* _LINUX_RBTREE_H */ diff --git a/jffsX-utils/summary.h b/jffsX-utils/summary.h new file mode 100644 index 0000000..e9d95a5 --- /dev/null +++ b/jffsX-utils/summary.h @@ -0,0 +1,177 @@ +/* + * 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. + */ + +#ifndef JFFS2_SUMMARY_H +#define JFFS2_SUMMARY_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)) +#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) +#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) + +/* 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)); + +struct jffs2_sum_xattr_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ + jint32_t xid; /* xattr identifier */ + jint32_t version; /* version number */ + jint32_t offset; /* offset on jeb */ + jint32_t totlen; /* node length */ +} __attribute__((packed)); + +struct jffs2_sum_xref_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ + jint32_t offset; /* offset on jeb */ +} __attribute__((packed)); + +union jffs2_sum_flash +{ + struct jffs2_sum_unknown_flash u; + struct jffs2_sum_inode_flash i; + struct jffs2_sum_dirent_flash d; + struct jffs2_sum_xattr_flash x; + struct jffs2_sum_xref_flash r; +}; + +/* 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)); + +struct jffs2_sum_xattr_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t xid; + jint32_t version; + jint32_t offset; + jint32_t totlen; +} __attribute__((packed)); + +struct jffs2_sum_xref_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t offset; +} __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_sum_xattr_mem x; + struct jffs2_sum_xref_mem r; +}; + +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/jffsX-utils/sumtool.c b/jffsX-utils/sumtool.c new file mode 100644 index 0000000..886b545 --- /dev/null +++ b/jffsX-utils/sumtool.c @@ -0,0 +1,872 @@ +/* + * sumtool.c + * + * Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>, + * Ferenc Havasi <havasi@inf.u-szeged.hu> + * University of Szeged, Hungary + * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> + * + * 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 + * + */ + +#define PROGRAM_NAME "sumtool" + +#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" +#include "common.h" + +#define PAD(x) (((x)+3)&~3) + +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 const 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 const 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 full_write(void *target_buff, const void *buf, int len); + +void setup_cleanmarker(void) +{ + 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(mtd_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) + errmsg_die("output filename specified more than once"); + out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (out_fd == -1) + sys_errmsg_die("open output file"); + break; + + case 'i': + if (in_fd != -1) + errmsg_die("input filename specified more than once"); + in_fd = open(optarg, O_RDONLY); + if (in_fd == -1) + sys_errmsg_die("open input file"); + break; + case 'b': + target_endian = __BIG_ENDIAN; + break; + case 'l': + target_endian = __LITTLE_ENDIAN; + break; + case 'h': + case '?': + errmsg_die("%s", helptext); + case 'v': + verbose = 1; + break; + + case 'V': + errmsg_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) + errmsg_die("Unrecognisable erase size\n"); + + if (*next) { + if (!strcmp(next, "KiB")) { + units = 1024; + } else if (!strcmp(next, "MiB")) { + units = 1024 * 1024; + } else { + errmsg_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) { + warnmsg("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)) { + errmsg_die("cleanmarker size must be >= 12"); + } + if (cleanmarker_size >= erase_block_size) { + errmsg_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(void) +{ + data_buffer = xmalloc(erase_block_size); + file_buffer = xmalloc(erase_block_size); +} + +void init_sumlist(void) +{ + sum_collected = xzalloc(sizeof(*sum_collected)); +} + +void clean_buffers(void) +{ + free(data_buffer); + free(file_buffer); +} + +void clean_sumlist(void) +{ + 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) + warnmsg("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); + + free(sum_collected); + } +} + +int load_next_block(void) +{ + int ret; + ret = read(in_fd, file_buffer, erase_block_size); + file_ofs = 0; + + bareverbose(verbose, "Load next block : %d bytes read\n", ret); + + return ret; +} + +void write_buff_to_file(void) +{ + 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) + sys_errmsg_die("write"); + + if (ret == 0) + sys_errmsg_die("write returned zero"); + + len -= ret; + buf += ret; + } + + data_ofs = 0; +} + +void dump_sum_records(void) +{ + + 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 = xmalloc(datasize); + + 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(mtd_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; + } + + case JFFS2_NODETYPE_XATTR: { + struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; + + sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; + sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; + sxattr_ptr->version = sum_collected->sum_list_head->x.version; + sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; + sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; + + wpage += JFFS2_SUMMARY_XATTR_SIZE; + break; + } + + case JFFS2_NODETYPE_XREF: { + struct jffs2_sum_xref_flash *sxref_ptr = wpage; + + sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; + sxref_ptr->offset = sum_collected->sum_list_head->r.offset; + + wpage += JFFS2_SUMMARY_XREF_SIZE; + break; + } + + default : { + warnmsg("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(mtd_crc32(0, tpage, datasize)); + isum.node_crc = cpu_to_je32(mtd_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(void) +{ + 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(void) +{ + + 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; + + case JFFS2_NODETYPE_XATTR: + sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; + sum_collected->sum_num++; + break; + + case JFFS2_NODETYPE_XREF: + sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; + sum_collected->sum_num++; + break; + + default: + errmsg_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 = xmalloc(sizeof(*temp)); + + 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 = xmalloc(sizeof(*temp) + node->d.nsize); + + 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 add_sum_xattr_mem(union jffs2_node_union *node) +{ + struct jffs2_sum_xattr_mem *temp = xmalloc(sizeof(*temp)); + + temp->nodetype = node->x.nodetype; + temp->xid = node->x.xid; + temp->version = node->x.version; + temp->offset = cpu_to_je32(data_ofs); + temp->totlen = node->x.totlen; + temp->next = NULL; + + add_sum_mem((union jffs2_sum_mem *) temp); +} + +void add_sum_xref_mem(union jffs2_node_union *node) +{ + struct jffs2_sum_xref_mem *temp = xmalloc(sizeof(*temp)); + + temp->nodetype = node->r.nodetype; + temp->offset = cpu_to_je32(data_ofs); + temp->next = NULL; + + 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 write_xattr_to_buff(union jffs2_node_union *node) +{ + pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); + add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ + full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); + padword(); +} + +void write_xref_to_buff(union jffs2_node_union *node) +{ + pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); + add_sum_xref_mem(node); /* Add xref summary mem to summary list */ + full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); + padword(); +} + +void create_summed_image(int inp_size) +{ + uint8_t *p = file_buffer; + union jffs2_node_union *node; + uint32_t crc, length; + 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++) + warnmsg("Wrong bitmask at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); + if (crc != je32_to_cpu (node->u.hdr_crc)) { + warnmsg("Wrong hdr_crc at 0x%08zx, 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: + bareverbose(verbose, + "%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", + obsolete ? "Obsolete" : "", + p - 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); + if (crc != je32_to_cpu (node->i.node_crc)) { + warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); + if (crc != je32_to_cpu(node->i.data_crc)) { + warnmsg("Wrong data_crc at 0x%08zx, 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; + + bareverbose(verbose, + "%8s Dirent node at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); + if (crc != je32_to_cpu (node->d.node_crc)) { + warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); + if (crc != je32_to_cpu(node->d.name_crc)) { + warnmsg("Wrong name_crc at 0x%08zx, 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_XATTR: + if (je32_to_cpu(node->x.node_crc) == 0xffffffff) + obsolete = 1; + bareverbose(verbose, + "%8s Xdatum node at 0x%08zx, totlen 0x%08x, #xid %5u, version %5u\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu (node->x.totlen), + je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); + crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); + if (crc != je32_to_cpu(node->x.node_crc)) { + warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", + p - file_buffer, je32_to_cpu(node->x.node_crc), crc); + p += PAD(je32_to_cpu (node->x.totlen)); + continue; + } + length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); + crc = mtd_crc32(0, node->x.data, length); + if (crc != je32_to_cpu(node->x.data_crc)) { + warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", + p - file_buffer, je32_to_cpu(node->x.data_crc), crc); + p += PAD(je32_to_cpu (node->x.totlen)); + continue; + } + + write_xattr_to_buff(node); + p += PAD(je32_to_cpu (node->x.totlen)); + break; + + case JFFS2_NODETYPE_XREF: + if (je32_to_cpu(node->r.node_crc) == 0xffffffff) + obsolete = 1; + bareverbose(verbose, + "%8s Xref node at 0x%08zx, totlen 0x%08x, #ino %5u, xid %5u\n", + obsolete ? "Obsolete" : "", + p - file_buffer, je32_to_cpu(node->r.totlen), + je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); + crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); + if (crc != je32_to_cpu(node->r.node_crc)) { + warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", + p - file_buffer, je32_to_cpu(node->r.node_crc), crc); + p += PAD(je32_to_cpu (node->r.totlen)); + continue; + } + + write_xref_to_buff(node); + p += PAD(je32_to_cpu (node->r.totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: + bareverbose(verbose, + "%8s Cleanmarker at 0x%08zx, 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: + bareverbose(verbose, + "%8s Padding node at 0x%08zx, 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: + bareverbose(verbose, + "%8s Unknown node at 0x%08zx, 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, "%s", helptext); + errmsg_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; +} diff --git a/load_nandsim.sh b/load_nandsim.sh deleted file mode 100755 index 4d9f0cb..0000000 --- a/load_nandsim.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/sh -euf - -# -# This script inserts NAND simulator module to emulate NAND flash of specified -# size. -# -# Author: Artem Bityutskiy -# - -fatal() -{ - echo "Error: $1" 1>&2 - exit 1 -} - -usage() -{ - cat 1>&2 <<EOF -Load NAND simulator to simulate flash of a specified size. - -Usage: ${0##*/} <size in MiB> <eraseblock size in KiB> \\ - <page size (512 or 2048)> - -Only the first parameter is mandatory. Default eraseblock size -is 16KiB, default NAND page size is 512 bytes. - -Only the following combinations are supported: --------------------------------------------------- -| size (MiB) | EB size (KiB) | Page size (bytes) | --------------------------------------------------- -| 16 | 16 | 512 | -| 32 | 16 | 512 | -| 64 | 16 | 512 | -| 128 | 16 | 512 | -| 256 | 16 | 512 | -| 64 | 64 | 2048 | -| 64 | 128 | 2048 | -| 64 | 256 | 2048 | -| 64 | 512 | 2048 | -| 128 | 64 | 2048 | -| 128 | 128 | 2048 | -| 128 | 256 | 2048 | -| 128 | 512 | 2048 | -| 256 | 64 | 2048 | -| 256 | 128 | 2048 | -| 256 | 256 | 2048 | -| 256 | 512 | 2048 | -| 512 | 64 | 2048 | -| 512 | 128 | 2048 | -| 512 | 256 | 2048 | -| 512 | 512 | 2048 | -| 1024 | 64 | 2048 | -| 1024 | 128 | 2048 | -| 1024 | 256 | 2048 | -| 1024 | 512 | 2048 | --------------------------------------------------- -EOF -} - -if grep -q "NAND simulator" /proc/mtd; then - fatal "nandsim is already loaded" -fi - -if [ "$#" -lt "1" ]; then - usage - exit 1 -fi - -size="$1" -eb_size="$2" -page_size="$3" -if [ "$#" = "1" ]; then - eb_size="16" - page_size="512" -elif [ "$#" = "2" ]; then - page_size="512" -fi - -if [ "$page_size" -eq 512 ] && [ "$eb_size" -ne "16" ]; then - fatal "only 16KiB eraseblocks are possible in case of 512 bytes page" -fi - -first= -second= -third= -fourth= - -if [ "$page_size" -eq "512" ]; then - first="0x20" - case "$size" in - 16) second=0x33 ;; - 32) second=0x35 ;; - 64) second=0x36 ;; - 128) second=0x78 ;; - 256) second=0x71 ;; - *) fatal "flash size ${size}MiB is not supported, try 16, 32, 64 or 256" - esac -elif [ "$page_size" -eq "2048" ]; then - case "$eb_size" in - 64) fourth="0x05" ;; - 128) fourth="0x15" ;; - 256) fourth="0x25" ;; - 512) fourth="0x35" ;; - *) fatal "eraseblock ${eb_size}KiB is not supported" - esac - - - case "$size" in - 64) first="0x20"; second="0xa2"; third="0x00 ";; - 128) first="0xec"; second="0xa1"; third="0x00 ";; - 256) first="0x20"; second="0xaa"; third="0x00 ";; - 512) first="0x20"; second="0xac"; third="0x00 ";; - 1024) first="0xec"; second="0xd3"; third="0x51 ";; - *) fatal "unable to emulate ${size}MiB flash with ${eb_size}KiB eraseblock" - esac -else - fatal "bad NAND page size ${page_size}KiB, it has to be either 512 or 2048" -fi - -first="first_id_byte=$first" -second="second_id_byte=$second" -[ -z "$third" ] || third="third_id_byte=$third" -[ -z "$fourth" ] || fourth="fourth_id_byte=$fourth" - -modprobe nandsim "$first" "$second" $third $fourth - -echo "Loaded NAND simulator (${size}MiB, ${eb_size}KiB eraseblock, $page_size bytes NAND page)" diff --git a/mcast_image.h b/mcast_image.h deleted file mode 100644 index 8e94ffa..0000000 --- a/mcast_image.h +++ /dev/null @@ -1,54 +0,0 @@ -#include <stdint.h> - -#define PKT_SIZE 2820 - -struct image_pkt_hdr { - uint32_t resend; - uint32_t totcrc; - uint32_t nr_blocks; - uint32_t blocksize; - uint32_t block_crc; - uint32_t block_nr; - uint32_t pkt_sequence; - uint16_t pkt_nr; - uint16_t nr_pkts; - uint32_t thislen; - uint32_t thiscrc; -}; - -struct image_pkt { - struct image_pkt_hdr hdr; - unsigned char data[PKT_SIZE]; -}; - -struct fec_parms; - -/* k - number of actual data packets - * n - total number of packets including data and redundant packets - * (actual packet size isn't relevant here) */ -struct fec_parms *fec_new(int k, int n); -void fec_free(struct fec_parms *p); - -/* src - array of (n) pointers to data packets - * fec - buffer for packet to be generated - * index - index of packet to be generated (0 <= index < n) - * sz - data packet size - * - * _linear version just takes a pointer to the raw data; no - * mucking about with packet pointers. - */ -void fec_encode(struct fec_parms *code, unsigned char *src[], - unsigned char *fec, int index, int sz); -void fec_encode_linear(struct fec_parms *code, unsigned char *src, - unsigned char *fec, int index, int sz); - -/* data - array of (k) pointers to data packets, in arbitrary order (see i) - * i - indices of (data) packets - * sz - data packet size - * - * Will never fail as long as you give it (k) individual data packets. - * Will re-order the (data) pointers but not the indices -- data packets - * are ordered on return. - */ -int fec_decode(struct fec_parms *code, unsigned char *data[], - int i[], int sz); diff --git a/misc-utils/MAKEDEV b/misc-utils/MAKEDEV new file mode 100755 index 0000000..b59e90e --- /dev/null +++ b/misc-utils/MAKEDEV @@ -0,0 +1,41 @@ +#!/bin/bash + +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/misc-utils/doc_loadbios.c b/misc-utils/doc_loadbios.c new file mode 100644 index 0000000..b999c73 --- /dev/null +++ b/misc-utils/doc_loadbios.c @@ -0,0 +1,150 @@ +#define PROGRAM_NAME "doc_loadbios" + +#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> + +#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/misc-utils/docfdisk.c b/misc-utils/docfdisk.c new file mode 100644 index 0000000..9956de5 --- /dev/null +++ b/misc-utils/docfdisk.c @@ -0,0 +1,318 @@ +/* + * docfdisk.c: Modify INFTL partition tables + * + * 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 PROGRAM_NAME "docfdisk" + +#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> +#include <mtd_swab.h> + +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", + PROGRAM_NAME); + 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.writesize) * 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.writesize) { + if (ioctl(fd, MEMREADOOB, &oob)) { + perror("ioctl(MEMREADOOB)"); + return 1; + } + oob.start += meminfo.writesize; + 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.writesize) { + 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.writesize, oob.start)) < 0) { + perror("Write page"); + printf("Your MediaHeader may be hosed. UHOH!\n"); + return 1; + } + if (ret != meminfo.writesize) { + printf("Short write!\n"); + printf("Your MediaHeader may be hosed. UHOH!\n"); + return 1; + } + + oob.start += meminfo.writesize; + oob.ptr += meminfo.oobsize; + buf += meminfo.writesize; + } + + printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); + return 0; +} diff --git a/misc-utils/fectest.c b/misc-utils/fectest.c new file mode 100644 index 0000000..fd577f3 --- /dev/null +++ b/misc-utils/fectest.c @@ -0,0 +1,91 @@ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "mcast_image.h" +#include <crc32.h> + +#define ERASE_SIZE 131072 +#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE) +#define DROPS 8 + +int main(void) +{ + int i, j; + unsigned char buf[NR_PKTS * PKT_SIZE]; + unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE]; + struct fec_parms *fec; + unsigned char *srcs[NR_PKTS]; + unsigned char *pkt[NR_PKTS + DROPS]; + int pktnr[NR_PKTS + DROPS]; + struct timeval then, now; + + srand(3453); + for (i=0; i < sizeof(buf); i++) + if (i < ERASE_SIZE) + buf[i] = rand(); + else + buf[i] = 0; + + for (i=0; i < NR_PKTS + DROPS; i++) + srcs[i] = buf + (i * PKT_SIZE); + + for (i=0; i < NR_PKTS + DROPS; i++) { + pkt[i] = malloc(PKT_SIZE); + pktnr[i] = -1; + } + fec = fec_new(NR_PKTS, NR_PKTS + DROPS); + if (!fec) { + printf("fec_init() failed\n"); + exit(1); + } + j = 0; + for (i=0; i < NR_PKTS + DROPS; i++) { +#if 1 + if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 ) + continue; +#endif + if (i == 69 || i == 93 || i == 103) + continue; + fec_encode(fec, srcs, pkt[j], i, PKT_SIZE); + pktnr[j] = i; + j++; + } + gettimeofday(&then, NULL); + if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) { + printf("Decode failed\n"); + exit(1); + } + + for (i=0; i < NR_PKTS; i++) + memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE); + gettimeofday(&now, NULL); + now.tv_sec -= then.tv_sec; + now.tv_usec -= then.tv_usec; + if (now.tv_usec < 0) { + now.tv_usec += 1000000; + now.tv_sec--; + } + + if (memcmp(pktbuf, buf, ERASE_SIZE)) { + int fd; + printf("Compare failed\n"); + fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd >= 0) + write(fd, buf, ERASE_SIZE); + close(fd); + fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd >= 0) + write(fd, pktbuf, ERASE_SIZE); + + exit(1); + } + + printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec); + return 0; +} diff --git a/misc-utils/ftl_check.c b/misc-utils/ftl_check.c new file mode 100644 index 0000000..0eada8f --- /dev/null +++ b/misc-utils/ftl_check.c @@ -0,0 +1,217 @@ +/* Ported to MTD system. + * 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. + + ======================================================================*/ + +#define PROGRAM_NAME "ftl_check" + +#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 <mtd/ftl-user.h> +#include <mtd_swab.h> + +#include "common.h" + +/*====================================================================*/ + +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) +{ + mtd_info_t mtd; + erase_unit_header_t hdr, hdr2; + off_t i; + u_int 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 ((le32_to_cpu(hdr.FormattedSize) > 0) && + (le32_to_cpu(hdr.FormattedSize) <= mtd.size) && + (le16_to_cpu(hdr.NumEraseUnits) > 0) && + (le16_to_cpu(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(le32_to_cpu(hdr.FormattedSize)); + printf(", erase units = %d, transfer units = %d\n", + le16_to_cpu(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 < le16_to_cpu(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 %"PRIdoff_t":\n", i); + if ((hdr2.FormattedSize != hdr.FormattedSize) || + (hdr2.NumEraseUnits != hdr.NumEraseUnits) || + (hdr2.SerialNumber != hdr.SerialNumber)) + printf(" Erase unit header is corrupt.\n"); + else if (le16_to_cpu(hdr2.LogicalEUN) == 0xffff) + printf(" Transfer unit, erase count = %d\n", le32_to_cpu(hdr2.EraseCount)); + else { + printf(" Logical unit %d, erase count = %d\n", + le16_to_cpu(hdr2.LogicalEUN), le32_to_cpu(hdr2.EraseCount)); + if (lseek(fd, (i << hdr.EraseUnitSize)+le32_to_cpu(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(le32_to_cpu(bam[j]))) + free++; + else if (BLOCK_DELETED(le32_to_cpu(bam[j]))) + deleted++; + else switch (BLOCK_TYPE(le32_to_cpu(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(void) +{ + fprintf(stderr, "usage: %s device\n", PROGRAM_NAME); +} + +/*====================================================================*/ + +int main(int argc, char *argv[]) +{ + int optch, errflg, fd; + struct stat buf; + + errflg = 0; + while ((optch = getopt(argc, argv, "h")) != -1) { + switch (optch) { + case 'h': + errflg = 1; break; + default: + errflg = -1; break; + } + } + if (errflg || (optind != argc-1)) { + showusage(); + 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); + close(fd); + + exit(EXIT_SUCCESS); + return 0; +} diff --git a/misc-utils/ftl_format.c b/misc-utils/ftl_format.c new file mode 100644 index 0000000..b58677f --- /dev/null +++ b/misc-utils/ftl_format.c @@ -0,0 +1,324 @@ +/* Ported to MTD system. + * 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. + + ======================================================================*/ + +#define PROGRAM_NAME "ftl_format" + +#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 <mtd/ftl-user.h> +#include <mtd_swab.h> +#include "common.h" + +/*====================================================================*/ + +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 = cpu_to_le32(0); + hdr->FirstPhysicalEUN = cpu_to_le16(BootUnits); + hdr->NumEraseUnits = cpu_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 = cpu_to_le32(0x80); + /* Adjust size to account for BAM space */ + nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int) + + le32_to_cpu(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize; + + __FormattedSize -= + (le16_to_cpu(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize); + __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff); + + hdr->FormattedSize = cpu_to_le32(__FormattedSize); + + /* hdr->FirstVMAddress defaults to erased state */ + hdr->NumVMPages = cpu_to_le16(0); + hdr->Flags = 0; + /* hdr->Code defaults to erased state */ + hdr->SerialNumber = cpu_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(le16_to_cpu(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize); + printf(" allocated for boot image\n"); + } + printf("Reserved %d%%, formatted size = ", reserve); + print_size(le32_to_cpu(hdr.FormattedSize)); + printf("\n"); + fflush(stdout); + } + + if (interrogate) + if (!prompt("This will destroy all data on the target device. Confirm?", false)) + return -1; + + /* Create basic block allocation table for control blocks */ + nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int) + + le32_to_cpu(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize; + bam = malloc(nbam * sizeof(u_int)); + for (i = 0; i < nbam; i++) + bam[i] = cpu_to_le32(BLOCK_CONTROL); + + /* Erase partition */ + if (!quiet) { + printf("Erasing all blocks...\n"); + fflush(stdout); + } + erase.length = mtd.erasesize; + erase.start = mtd.erasesize * le16_to_cpu(hdr.FirstPhysicalEUN); + for (i = 0; i < le16_to_cpu(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 ? (le16_to_cpu(hdr.NumEraseUnits) / spare) : (le16_to_cpu(hdr.NumEraseUnits) + 1); + for (i = 0; i < le16_to_cpu(hdr.NumEraseUnits); i++) { + off_t ofs = (off_t) (i + le16_to_cpu(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 = cpu_to_le16(0xffff); + else { + hdr.LogicalEUN = cpu_to_le16(lun); + lun++; + } + if (write(fd, &hdr, sizeof(hdr)) == -1) { + perror("write failed"); + break; + } + if (lseek(fd, ofs + le32_to_cpu(hdr.BAMOffset), SEEK_SET) == -1) { + perror("seek failed"); + break; + } + if (write(fd, bam, nbam * sizeof(u_int)) == -1) { + perror("write failed"); + break; + } + } + if (i < le16_to_cpu(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", PROGRAM_NAME); + 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/misc-utils/mcast_image.h b/misc-utils/mcast_image.h new file mode 100644 index 0000000..8e94ffa --- /dev/null +++ b/misc-utils/mcast_image.h @@ -0,0 +1,54 @@ +#include <stdint.h> + +#define PKT_SIZE 2820 + +struct image_pkt_hdr { + uint32_t resend; + uint32_t totcrc; + uint32_t nr_blocks; + uint32_t blocksize; + uint32_t block_crc; + uint32_t block_nr; + uint32_t pkt_sequence; + uint16_t pkt_nr; + uint16_t nr_pkts; + uint32_t thislen; + uint32_t thiscrc; +}; + +struct image_pkt { + struct image_pkt_hdr hdr; + unsigned char data[PKT_SIZE]; +}; + +struct fec_parms; + +/* k - number of actual data packets + * n - total number of packets including data and redundant packets + * (actual packet size isn't relevant here) */ +struct fec_parms *fec_new(int k, int n); +void fec_free(struct fec_parms *p); + +/* src - array of (n) pointers to data packets + * fec - buffer for packet to be generated + * index - index of packet to be generated (0 <= index < n) + * sz - data packet size + * + * _linear version just takes a pointer to the raw data; no + * mucking about with packet pointers. + */ +void fec_encode(struct fec_parms *code, unsigned char *src[], + unsigned char *fec, int index, int sz); +void fec_encode_linear(struct fec_parms *code, unsigned char *src, + unsigned char *fec, int index, int sz); + +/* data - array of (k) pointers to data packets, in arbitrary order (see i) + * i - indices of (data) packets + * sz - data packet size + * + * Will never fail as long as you give it (k) individual data packets. + * Will re-order the (data) pointers but not the indices -- data packets + * are ordered on return. + */ +int fec_decode(struct fec_parms *code, unsigned char *data[], + int i[], int sz); diff --git a/misc-utils/mtd_debug.c b/misc-utils/mtd_debug.c new file mode 100644 index 0000000..d6993ce --- /dev/null +++ b/misc-utils/mtd_debug.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2d3D, Inc. + * Written by Abraham vd Merwe <abraham@2d3d.co.za> + * All rights reserved. + * + * 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. + */ + +#define PROGRAM_NAME "mtd_debug" + +#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> +#include "common.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, off_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, 0666); + 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", __func__, size); + if (size != BUF_SIZE) { + size = BUF_SIZE; + fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, size, n); + perror("read()"); + goto err2; + } + err = write(outfd, buf, size); + if (err < 0) { + fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, 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 %zu bytes from address 0x%.8"PRIxoff_t" 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, off_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", __func__, size); + if (size != BUF_SIZE) { + size = BUF_SIZE; + fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, 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", __func__, 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%.8"PRIxoff_t" 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_MLCNANDFLASH: + printf("MTD_MLCNANDFLASH"); + break; + case MTD_DATAFLASH: + printf("MTD_DATAFLASH"); + break; + case MTD_UBIVOLUME: + printf("MTD_UBIVOLUME"); + 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_WRITEABLE", MTD_WRITEABLE }, + { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, + { "MTD_NO_ERASE", MTD_NO_ERASE }, + { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK }, + { NULL, -1 } + }; + for (i = 0; flags[i].name != NULL; i++) { + if (mtd.flags & flags[i].value) { + if (first) { + printf("%s", 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.writesize = "); + printsize(mtd.writesize); + + printf("\nmtd.oobsize = "); + printsize(mtd.oobsize); + + printf("\nregions = %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(void) +{ + fprintf(stderr, "usage: %1$s info <device>\n" + " %1$s read <device> <offset> <len> <dest-filename>\n" + " %1$s write <device> <offset> <len> <source-filename>\n" + " %1$s erase <device> <offset> <len>\n", + PROGRAM_NAME); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + int err = 0, fd; + int open_flag; + + enum { + OPT_INFO, + OPT_READ, + OPT_WRITE, + OPT_ERASE + } option = OPT_INFO; + + /* 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(); + + /* open device */ + open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR; + if ((fd = open(argv[2], O_SYNC | open_flag)) < 0) + errmsg_die("open()"); + + switch (option) { + case OPT_INFO: + showinfo(fd); + break; + case OPT_READ: + err = flash_to_file(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); + break; + case OPT_WRITE: + err = file_to_flash(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); + break; + case OPT_ERASE: + err = erase_flash(fd, strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0)); + break; + } + + /* close device */ + if (close(fd) < 0) + errmsg_die("close()"); + + return err; +} diff --git a/misc-utils/mtdpart.c b/misc-utils/mtdpart.c new file mode 100644 index 0000000..0016e34 --- /dev/null +++ b/misc-utils/mtdpart.c @@ -0,0 +1,194 @@ +/* + * mtdpart.c + * + * Copyright 2015 The Chromium OS Authors. + * + * 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 adds or removes a partition from an MTD device. + */ + +#define PROGRAM_NAME "mtdpart" + +#include <fcntl.h> +#include <getopt.h> +#include <limits.h> +#include <linux/blkpg.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "common.h" + +static void display_help(int status) +{ + fprintf(status == EXIT_SUCCESS ? stdout : stderr, +"Usage: %1$s add [OPTION] <MTD_DEVICE> <PART_NAME> <START> <SIZE>\n" +" %1$s del [OPTION] <MTD_DEVICE> <PART_NUMBER>\n" +"Adds a partition to an MTD device, or remove an existing partition from it.\n" +"\n" +" -h, --help Display this help and exit\n" +" --version Output version information and exit\n" +"\n" +"START location and SIZE of the partition are in bytes. They should align on\n" +"eraseblock size.\n", + PROGRAM_NAME + ); + exit(status); +} + +static void display_version(void) +{ + printf("%1$s " VERSION "\n" + "\n" + "%1$s comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of %1$s\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n", + PROGRAM_NAME); + exit(EXIT_SUCCESS); +} + +/* Command arguments */ + +typedef enum { + COMMAND_ADD, + COMMAND_DEL +} command_type; + +static command_type command; /* add or del */ +static const char *mtddev; /* mtd device name */ +static const char *part_name; /* partition name */ +static int part_no; /* partition number */ +static long long start_addr; /* start address */ +static long long length; /* partition size */ + +static void process_options(int argc, char * const argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char short_options[] = "h"; + static const struct option long_options[] = { + {"version", no_argument, 0, 0}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + display_version(); + break; + case 'h': + display_help(EXIT_SUCCESS); + break; + case '?': + error++; + break; + } + } + + if ((argc - optind) < 3 || error) + display_help(EXIT_FAILURE); + + const char *s_command = argv[optind++]; + mtddev = argv[optind++]; + + if (strcmp(s_command, "del") == 0 && (argc - optind) == 1) { + const char *s_part_no = argv[optind++]; + + long tmp = simple_strtol(s_part_no, &error); + if (tmp < 0) + errmsg_die("Can't specify negative partition number: %ld", + tmp); + if (tmp > INT_MAX) + errmsg_die("Partition number exceeds INT_MAX: %ld", + tmp); + + part_no = tmp; + command = COMMAND_DEL; + } else if (strcmp(s_command, "add") == 0 && (argc - optind) == 3) { + const char *s_start; + const char *s_length; + + part_name = argv[optind++]; + s_start = argv[optind++]; + s_length = argv[optind++]; + + if (strlen(part_name) >= BLKPG_DEVNAMELTH) + errmsg_die("Partition name (%s) should be less than %d characters", + part_name, BLKPG_DEVNAMELTH); + + start_addr = simple_strtoll(s_start, &error); + if (start_addr < 0) + errmsg_die("Can't specify negative start offset: %lld", + start_addr); + + length = simple_strtoll(s_length, &error); + if (length < 0) + errmsg_die("Can't specify negative length: %lld", + length); + + command = COMMAND_ADD; + } else + display_help(EXIT_FAILURE); + + if (error) + display_help(EXIT_FAILURE); +} + + +int main(int argc, char * const argv[]) +{ + int fd; + struct blkpg_partition part; + struct blkpg_ioctl_arg arg; + + process_options(argc, argv); + + fd = open(mtddev, O_RDWR | O_CLOEXEC); + if (fd == -1) + sys_errmsg_die("Cannot open %s", mtddev); + + memset(&part, 0, sizeof(part)); + + memset(&arg, 0, sizeof(arg)); + arg.datalen = sizeof(part); + arg.data = ∂ + + switch (command) { + case COMMAND_ADD: + part.start = start_addr; + part.length = length; + strncpy(part.devname, part_name, sizeof(part.devname)); + arg.op = BLKPG_ADD_PARTITION; + break; + case COMMAND_DEL: + part.pno = part_no; + arg.op = BLKPG_DEL_PARTITION; + break; + } + + if (ioctl(fd, BLKPG, &arg)) + sys_errmsg_die("Failed to issue BLKPG ioctl"); + + close(fd); + + /* Exit happy */ + return EXIT_SUCCESS; +} diff --git a/misc-utils/recv_image.c b/misc-utils/recv_image.c new file mode 100644 index 0000000..0093831 --- /dev/null +++ b/misc-utils/recv_image.c @@ -0,0 +1,484 @@ + +#define PROGRAM_NAME "recv_image" +#define _XOPEN_SOURCE 500 +#define _BSD_SOURCE /* struct ip_mreq */ + +#include <errno.h> +#include <stdio.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <crc32.h> +#include "mtd/mtd-user.h" +#include "mcast_image.h" + +#include "common.h" + +#define WBUF_SIZE 4096 +struct eraseblock { + uint32_t flash_offset; + unsigned char wbuf[WBUF_SIZE]; + int wbuf_ofs; + int nr_pkts; + int *pkt_indices; + uint32_t crc; +}; + +int main(int argc, char **argv) +{ + struct addrinfo *ai; + struct addrinfo hints; + struct addrinfo *runp; + int ret; + int sock; + ssize_t len; + int flfd; + struct mtd_info_user meminfo; + unsigned char *eb_buf, *decode_buf, **src_pkts; + int nr_blocks = 0; + int pkts_per_block; + int block_nr = -1; + uint32_t image_crc = 0; + int total_pkts = 0; + int ignored_pkts = 0; + loff_t mtdoffset = 0; + int badcrcs = 0; + int duplicates = 0; + int file_mode = 0; + struct fec_parms *fec = NULL; + int i; + struct eraseblock *eraseblocks = NULL; + uint32_t start_seq = 0; + struct timeval start, now; + unsigned long fec_time = 0, flash_time = 0, crc_time = 0, + rflash_time = 0, erase_time = 0, net_time = 0; + + if (argc != 4) { + fprintf(stderr, "usage: %s <host> <port> <mtddev>\n", + PROGRAM_NAME); + exit(1); + } + /* Open the device */ + flfd = open(argv[3], O_RDWR); + + if (flfd >= 0) { + /* Fill in MTD device capability structure */ + if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(flfd); + flfd = -1; + } else { + printf("Receive to MTD device %s with erasesize %d\n", + argv[3], meminfo.erasesize); + } + } + if (flfd == -1) { + /* Try again, as if it's a file */ + flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); + if (flfd < 0) { + perror("open"); + exit(1); + } + meminfo.erasesize = 131072; + file_mode = 1; + printf("Receive to file %s with (assumed) erasesize %d\n", + argv[3], meminfo.erasesize); + } + + pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; + + eb_buf = malloc(pkts_per_block * PKT_SIZE); + decode_buf = malloc(pkts_per_block * PKT_SIZE); + if (!eb_buf && !decode_buf) { + fprintf(stderr, "No memory for eraseblock buffer\n"); + exit(1); + } + src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); + if (!src_pkts) { + fprintf(stderr, "No memory for decode packet pointers\n"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(argv[1], argv[2], &hints, &ai); + if (ret) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + exit(1); + } + runp = ai; + for (runp = ai; runp; runp = runp->ai_next) { + sock = socket(runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (sock == -1) { + perror("socket"); + continue; + } + if (runp->ai_family == AF_INET && + IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { + struct ip_mreq rq; + rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; + rq.imr_interface.s_addr = INADDR_ANY; + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { + perror("IP_ADD_MEMBERSHIP"); + close(sock); + continue; + } + + } else if (runp->ai_family == AF_INET6 && + ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { + struct ipv6_mreq rq; + rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; + rq.ipv6mr_interface = 0; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { + perror("IPV6_ADD_MEMBERSHIP"); + close(sock); + continue; + } + } + if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { + perror("bind"); + close(sock); + continue; + } + break; + } + if (!runp) + exit(1); + + while (1) { + struct image_pkt thispkt; + + len = read(sock, &thispkt, sizeof(thispkt)); + + if (len < 0) { + perror("read socket"); + break; + } + if (len < sizeof(thispkt)) { + fprintf(stderr, "Wrong length %zd bytes (expected %zu)\n", + len, sizeof(thispkt)); + continue; + } + if (!eraseblocks) { + image_crc = thispkt.hdr.totcrc; + start_seq = ntohl(thispkt.hdr.pkt_sequence); + + if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { + fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", + ntohl(thispkt.hdr.blocksize), meminfo.erasesize); + exit(1); + } + nr_blocks = ntohl(thispkt.hdr.nr_blocks); + + fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); + + eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); + if (!eraseblocks) { + fprintf(stderr, "No memory for block map\n"); + exit(1); + } + for (i = 0; i < nr_blocks; i++) { + eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); + if (!eraseblocks[i].pkt_indices) { + fprintf(stderr, "Failed to allocate packet indices\n"); + exit(1); + } + eraseblocks[i].nr_pkts = 0; + if (!file_mode) { + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } +#if 1 /* Deliberately use bad blocks... test write failures */ + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); + mtdoffset += meminfo.erasesize; + } +#endif + } + eraseblocks[i].flash_offset = mtdoffset; + mtdoffset += meminfo.erasesize; + eraseblocks[i].wbuf_ofs = 0; + } + gettimeofday(&start, NULL); + } + if (image_crc != thispkt.hdr.totcrc) { + fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", + ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); + exit(1); + } + + block_nr = ntohl(thispkt.hdr.block_nr); + if (block_nr >= nr_blocks) { + fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", + block_nr, nr_blocks); + exit(1); + } + for (i=0; i<eraseblocks[block_nr].nr_pkts; i++) { + if (eraseblocks[block_nr].pkt_indices[i] == ntohs(thispkt.hdr.pkt_nr)) { +// printf("Discarding duplicate packet at %08x pkt %d\n", +// block_nr * meminfo.erasesize, eraseblocks[block_nr].pkt_indices[i]); + duplicates++; + break; + } + } + if (i < eraseblocks[block_nr].nr_pkts) { + continue; + } + + if (eraseblocks[block_nr].nr_pkts >= pkts_per_block) { + /* We have a block which we didn't really need */ + eraseblocks[block_nr].nr_pkts++; + ignored_pkts++; + continue; + } + + if (mtd_crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { + printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", + block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), + mtd_crc32(-1, thispkt.data, PKT_SIZE), + ntohl(thispkt.hdr.thiscrc)); + badcrcs++; + continue; + } + pkt_again: + eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = + ntohs(thispkt.hdr.pkt_nr); + total_pkts++; + if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { + uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; + long time_msec; + gettimeofday(&now, NULL); + + time_msec = ((now.tv_usec - start.tv_usec) / 1000) + + (now.tv_sec - start.tv_sec) * 1000; + + printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", + total_pkts, nr_blocks * pkts_per_block, + total_pkts * 100 / nr_blocks / pkts_per_block, + time_msec / 1000, + total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, + pkts_sent - total_pkts - duplicates - ignored_pkts, + (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, + duplicates + ignored_pkts); + fflush(stdout); + } + + if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { + /* New packet doesn't full the wbuf */ + memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, + thispkt.data, PKT_SIZE); + eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; + } else { + int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; + ssize_t wrotelen; + static int faked = 1; + + memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, + thispkt.data, fits); + wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, + eraseblocks[block_nr].flash_offset); + + if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { + faked = 1; + if (wrotelen < 0) + perror("\npacket write"); + else + fprintf(stderr, "\nshort write of packet wbuf\n"); + + if (!file_mode) { + struct erase_info_user erase; + /* FIXME: Perhaps we should store pkt crcs and try + to recover data from the offending eraseblock */ + + /* We have increased nr_pkts but not yet flash_offset */ + erase.start = eraseblocks[block_nr].flash_offset & + ~(meminfo.erasesize - 1); + erase.length = meminfo.erasesize; + + printf("Will erase at %08x len %08x (bad write was at %08x)\n", + erase.start, erase.length, eraseblocks[block_nr].flash_offset); + if (ioctl(flfd, MEMERASE, &erase)) { + perror("MEMERASE"); + exit(1); + } + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); + mtdoffset += meminfo.erasesize; + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + } + eraseblocks[block_nr].flash_offset = mtdoffset; + printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); + total_pkts -= eraseblocks[block_nr].nr_pkts; + eraseblocks[block_nr].nr_pkts = 0; + eraseblocks[block_nr].wbuf_ofs = 0; + mtdoffset += meminfo.erasesize; + goto pkt_again; + } + else /* Usually nothing we can do in file mode */ + exit(1); + } + eraseblocks[block_nr].flash_offset += WBUF_SIZE; + /* Copy the remainder into the wbuf */ + memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); + eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; + } + + if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { + eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); + + if (total_pkts == nr_blocks * pkts_per_block) + break; + } + } + printf("\n"); + gettimeofday(&now, NULL); + net_time = (now.tv_usec - start.tv_usec) / 1000; + net_time += (now.tv_sec - start.tv_sec) * 1000; + close(sock); + for (block_nr = 0; block_nr < nr_blocks; block_nr++) { + ssize_t rwlen; + gettimeofday(&start, NULL); + eraseblocks[block_nr].flash_offset -= meminfo.erasesize; + rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); + + gettimeofday(&now, NULL); + rflash_time += (now.tv_usec - start.tv_usec) / 1000; + rflash_time += (now.tv_sec - start.tv_sec) * 1000; + if (rwlen < 0) { + perror("read"); + /* Argh. Perhaps we could go back and try again, but if the flash is + going to fail to read back what we write to it, and the whole point + in this program is to write to it, what's the point? */ + fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); + exit(1); + } + + memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, + eraseblocks[block_nr].wbuf_ofs); + + for (i=0; i < pkts_per_block; i++) + src_pkts[i] = &eb_buf[i * PKT_SIZE]; + + gettimeofday(&start, NULL); + if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { + /* Eep. This cannot happen */ + printf("The world is broken. fec_decode() returned error\n"); + exit(1); + } + gettimeofday(&now, NULL); + fec_time += (now.tv_usec - start.tv_usec) / 1000; + fec_time += (now.tv_sec - start.tv_sec) * 1000; + + for (i=0; i < pkts_per_block; i++) + memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); + + /* Paranoia */ + gettimeofday(&start, NULL); + if (mtd_crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { + printf("\nCRC mismatch for block #%d: want %08x got %08x\n", + block_nr, eraseblocks[block_nr].crc, + mtd_crc32(-1, decode_buf, meminfo.erasesize)); + exit(1); + } + gettimeofday(&now, NULL); + crc_time += (now.tv_usec - start.tv_usec) / 1000; + crc_time += (now.tv_sec - start.tv_sec) * 1000; + start = now; + + if (!file_mode) { + struct erase_info_user erase; + + erase.start = eraseblocks[block_nr].flash_offset; + erase.length = meminfo.erasesize; + + printf("\rErasing block at %08x...", erase.start); + + if (ioctl(flfd, MEMERASE, &erase)) { + perror("MEMERASE"); + /* This block has dirty data on it. If the erase failed, we're screwed */ + fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); + exit(1); + } + gettimeofday(&now, NULL); + erase_time += (now.tv_usec - start.tv_usec) / 1000; + erase_time += (now.tv_sec - start.tv_sec) * 1000; + start = now; + } + else printf("\r"); + write_again: + rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); + if (rwlen < meminfo.erasesize) { + if (rwlen < 0) { + perror("\ndecoded data write"); + } else + fprintf(stderr, "\nshort write of decoded data\n"); + + if (!file_mode) { + struct erase_info_user erase; + erase.start = eraseblocks[block_nr].flash_offset; + erase.length = meminfo.erasesize; + + printf("Erasing failed block at %08x\n", + eraseblocks[block_nr].flash_offset); + + if (ioctl(flfd, MEMERASE, &erase)) { + perror("MEMERASE"); + exit(1); + } + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { + printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); + mtdoffset += meminfo.erasesize; + if (mtdoffset >= meminfo.size) { + fprintf(stderr, "Run out of space on flash\n"); + exit(1); + } + } + printf("Will try again at %08lx...", (long)mtdoffset); + eraseblocks[block_nr].flash_offset = mtdoffset; + + goto write_again; + } + else /* Usually nothing we can do in file mode */ + exit(1); + } + gettimeofday(&now, NULL); + flash_time += (now.tv_usec - start.tv_usec) / 1000; + flash_time += (now.tv_sec - start.tv_sec) * 1000; + + printf("wrote image block %08x (%d pkts) ", + block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); + fflush(stdout); + } + close(flfd); + printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); + printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); + printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); + printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); + printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); + printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); + + return 0; +} diff --git a/misc-utils/serve_image.c b/misc-utils/serve_image.c new file mode 100644 index 0000000..d3794ec --- /dev/null +++ b/misc-utils/serve_image.c @@ -0,0 +1,300 @@ +#define PROGRAM_NAME "serve_image" +#define _POSIX_C_SOURCE 199309 + +#include <time.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <crc32.h> +#include <inttypes.h> + +#include "mcast_image.h" + +int tx_rate = 80000; +int pkt_delay; + +#undef RANDOMDROP + +int main(int argc, char **argv) +{ + struct addrinfo *ai; + struct addrinfo hints; + struct addrinfo *runp; + int ret; + int sock; + struct image_pkt pktbuf; + int rfd; + struct stat st; + int writeerrors = 0; + uint32_t erasesize; + unsigned char *image, *blockptr = NULL; + uint32_t block_nr, pkt_nr; + int nr_blocks; + struct timeval then, now, nextpkt; + long time_msecs; + int pkts_per_block; + int total_pkts_per_block; + struct fec_parms *fec; + unsigned char *last_block; + uint32_t *block_crcs; + long tosleep; + uint32_t sequence = 0; + + if (argc == 6) { + tx_rate = atol(argv[5]) * 1024; + if (tx_rate < PKT_SIZE || tx_rate > 20000000) { + fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); + exit(1); + } + argc = 5; + } + if (argc != 5) { + fprintf(stderr, "usage: %s <host> <port> <image> <erasesize> [<tx_rate>]\n", + PROGRAM_NAME); + exit(1); + } + pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; + printf("Inter-packet delay (avg): %dµs\n", pkt_delay); + printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); + + erasesize = atol(argv[4]); + if (!erasesize) { + fprintf(stderr, "erasesize cannot be zero\n"); + exit(1); + } + + pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; + total_pkts_per_block = pkts_per_block * 3 / 2; + + /* We have to pad it with zeroes, so can't use it in-place */ + last_block = malloc(pkts_per_block * PKT_SIZE); + if (!last_block) { + fprintf(stderr, "Failed to allocate last-block buffer\n"); + exit(1); + } + + fec = fec_new(pkts_per_block, total_pkts_per_block); + if (!fec) { + fprintf(stderr, "Error initialising FEC\n"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(argv[1], argv[2], &hints, &ai); + if (ret) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + exit(1); + } + runp = ai; + for (runp = ai; runp; runp = runp->ai_next) { + sock = socket(runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (sock == -1) { + perror("socket"); + continue; + } + if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) + break; + perror("connect"); + close(sock); + } + if (!runp) + exit(1); + + rfd = open(argv[3], O_RDONLY); + if (rfd < 0) { + perror("open"); + exit(1); + } + + if (fstat(rfd, &st)) { + perror("fstat"); + exit(1); + } + + if (st.st_size % erasesize) { + fprintf(stderr, "Image size %" PRIu64 " bytes is not a multiple of erasesize %d bytes\n", + st.st_size, erasesize); + exit(1); + } + image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); + if (image == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + nr_blocks = st.st_size / erasesize; + + block_crcs = malloc(nr_blocks * sizeof(uint32_t)); + if (!block_crcs) { + fprintf(stderr, "Failed to allocate memory for CRCs\n"); + exit(1); + } + + memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); + memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); + + printf("Checking CRC...."); + fflush(stdout); + + pktbuf.hdr.resend = 0; + pktbuf.hdr.totcrc = htonl(mtd_crc32(-1, image, st.st_size)); + pktbuf.hdr.nr_blocks = htonl(nr_blocks); + pktbuf.hdr.blocksize = htonl(erasesize); + pktbuf.hdr.thislen = htonl(PKT_SIZE); + pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); + + printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); + printf("Checking block CRCs...."); + fflush(stdout); + for (block_nr=0; block_nr < nr_blocks; block_nr++) { + printf("\rChecking block CRCS.... %d/%d", + block_nr + 1, nr_blocks); + fflush(stdout); + block_crcs[block_nr] = mtd_crc32(-1, image + (block_nr * erasesize), erasesize); + } + + printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" + "Estimated transmit time per cycle: %ds\n", + (long)st.st_size / 1024, (long) st.st_size, + nr_blocks, pkts_per_block, + nr_blocks * pkts_per_block * pkt_delay / 1000000); + gettimeofday(&then, NULL); + nextpkt = then; + +#ifdef RANDOMDROP + srand((unsigned)then.tv_usec); + printf("Random seed %u\n", (unsigned)then.tv_usec); +#endif + while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { + + if (blockptr && pkt_nr == 0) { + unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); + gettimeofday(&now, NULL); + + time_msecs = (now.tv_sec - then.tv_sec) * 1000; + time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; + printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", + amt_sent / 1024, time_msecs, + amt_sent / 1024 * 1000 / time_msecs); + then = now; + } + + for (block_nr = 0; block_nr < nr_blocks; block_nr++) { + + int actualpkt; + + /* Calculating the redundant FEC blocks is expensive; + the first $pkts_per_block are cheap enough though + because they're just copies. So alternate between + simple and complex stuff, so that we don't start + to choke and fail to keep up with the expected + bitrate in the second half of the sequence */ + if (block_nr & 1) + actualpkt = pkt_nr; + else + actualpkt = total_pkts_per_block - 1 - pkt_nr; + + blockptr = image + (erasesize * block_nr); + if (block_nr == nr_blocks - 1) + blockptr = last_block; + + fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); + + pktbuf.hdr.thiscrc = htonl(mtd_crc32(-1, pktbuf.data, PKT_SIZE)); + pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); + pktbuf.hdr.block_nr = htonl(block_nr); + pktbuf.hdr.pkt_nr = htons(actualpkt); + pktbuf.hdr.pkt_sequence = htonl(sequence++); + + printf("\rSending data block %08x packet %3d/%d", + block_nr * erasesize, + pkt_nr, total_pkts_per_block); + + if (pkt_nr && !block_nr) { + unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); + + gettimeofday(&now, NULL); + + time_msecs = (now.tv_sec - then.tv_sec) * 1000; + time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; + printf(" (%ld KiB/s) ", + amt_sent / 1024 * 1000 / time_msecs); + } + + fflush(stdout); + +#ifdef RANDOMDROP + if ((rand() % 1000) < 20) { + printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); + continue; + } +#endif + gettimeofday(&now, NULL); +#if 1 + tosleep = nextpkt.tv_usec - now.tv_usec + + (1000000 * (nextpkt.tv_sec - now.tv_sec)); + + /* We need hrtimers for this to actually work */ + if (tosleep > 0) { + struct timespec req; + + req.tv_nsec = (tosleep % 1000000) * 1000; + req.tv_sec = tosleep / 1000000; + + nanosleep(&req, NULL); + } +#else + while (now.tv_sec < nextpkt.tv_sec || + (now.tv_sec == nextpkt.tv_sec && + now.tv_usec < nextpkt.tv_usec)) { + gettimeofday(&now, NULL); + } +#endif + nextpkt.tv_usec += pkt_delay; + if (nextpkt.tv_usec >= 1000000) { + nextpkt.tv_sec += nextpkt.tv_usec / 1000000; + nextpkt.tv_usec %= 1000000; + } + + /* If the time for the next packet has already + passed (by some margin), then we've lost time + Adjust our expected timings accordingly. If + we're only a little way behind, don't slip yet */ + if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + + 1000000 * (nextpkt.tv_sec - now.tv_sec))) { + nextpkt = now; + } + + if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { + perror("write"); + writeerrors++; + if (writeerrors > 10) { + fprintf(stderr, "Too many consecutive write errors\n"); + exit(1); + } + } else + writeerrors = 0; + + + + } + } + munmap(image, st.st_size); + close(rfd); + close(sock); + return 0; +} diff --git a/mkfs.jffs2.1 b/mkfs.jffs2.1 deleted file mode 100644 index 7c57ddc..0000000 --- a/mkfs.jffs2.1 +++ /dev/null @@ -1,268 +0,0 @@ -.TH MKFS.JFFS2 1 -.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 --with-xattr -] -[ -.B --with-selinux -] -[ -.B --with-posix-acl -] -[ -.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 available 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. Set according to target system's memory -management page size (NOTE: this is NOT related to NAND page size). -.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 --with-xattr -Enables xattr, stuff all xattr entries into jffs2 image file. -.TP -.B --with-selinux -Enables xattr, stuff only SELinux Labels into jffs2 image file. -.TP -.B --with-posix-acl -Enable xattr, stuff only POSIX ACL entries into jffs2 image file. -.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 available compressors and their default states. -.TP -.B -X, --enable-compressor=NAME -Enable a compressor. Use -.B -L -to see the list of the available 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 available compressors and their default priority. -Priorities are used by priority compression mode. -.TP -.B -L, --list-compressors -Show the list of the available 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 LIMITATIONS -The format and grammar of the device table file does not allow it to -create symbolic links when the symbolic links are not already present -in the root working directory. - -However, symbolic links may be specified in the device table file -using the \fIl\fR type for the purposes of setting their permissions -and ownership. -.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 deleted file mode 100644 index f09c0b2..0000000 --- a/mkfs.jffs2.c +++ /dev/null @@ -1,1805 +0,0 @@ -/* 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 - * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> - * - * 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 PROGRAM_NAME "mkfs.jffs2" - -#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> -#ifndef WITHOUT_XATTR -#include <sys/xattr.h> -#include <sys/acl.h> -#endif -#include <byteswap.h> -#include <crc32.h> -#include <inttypes.h> - -#include "rbtree.h" -#include "common.h" - -/* Do not use the weird XPG version of basename */ -#undef basename - -//#define DMALLOC -//#define mkfs_debug_msg errmsg -#define mkfs_debug_msg(a...) { } - -#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 */ - uint32_t ino; /* Inode number of this file in JFFS2 */ - 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 */ - struct rb_node hardlink_rb; -}; - -struct rb_root hardlinks; -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; - -uint32_t find_hardlink(struct filesystem_entry *e) -{ - struct filesystem_entry *f; - struct rb_node **n = &hardlinks.rb_node; - struct rb_node *parent = NULL; - - while (*n) { - parent = *n; - f = rb_entry(parent, struct filesystem_entry, hardlink_rb); - - if ((f->sb.st_dev < e->sb.st_dev) || - (f->sb.st_dev == e->sb.st_dev && - f->sb.st_ino < e->sb.st_ino)) - n = &parent->rb_left; - else if ((f->sb.st_dev > e->sb.st_dev) || - (f->sb.st_dev == e->sb.st_dev && - f->sb.st_ino > e->sb.st_ino)) { - n = &parent->rb_right; - } else - return f->ino; - } - - rb_link_node(&e->hardlink_rb, parent, n); - rb_insert_color(&e->hardlink_rb, &hardlinks); - return 0; -} - -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) { - sys_errmsg("%s:%s", PROGRAM_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) - sys_errmsg_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)) { - /* If this is the first call, and we actually want this - * directory, then return it now */ - if (strcmp(fullname, e->fullname) == 0) - return e; - - e = dir->files; - } - while (e) { - 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(const char *name, - const 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)) { - errmsg_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) { - errmsg_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_ino = sb.st_ino; - entry->sb.st_dev = sb.st_dev; - entry->sb.st_nlink = sb.st_nlink; - - 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, const char *targetpath, - const char *hostpath) -{ - int i, n; - struct stat sb; - char *hpath, *tpath; - struct dirent *dp, **namelist; - struct filesystem_entry *entry; - - - if (lstat(hostpath, &sb)) { - sys_errmsg_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) { - sys_errmsg_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; - } - - xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); - if (lstat(hpath, &sb)) { - sys_errmsg_die("%s", hpath); - } - if (strcmp(targetpath, "/") == 0) { - xasprintf(&tpath, "%s%s", targetpath, dp->d_name); - } else { - xasprintf(&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: - errmsg("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 = xmalloc(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, "/")) { - errmsg_die("Device table entries require absolute paths"); - } - - xasprintf(&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; - case 'l': - mode |= S_IFLNK; - break; - default: - errmsg_die("Unsupported file type '%c'", type); - } - entry = find_filesystem_entry(root, name, mode); - if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { - /* 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 = xstrdup(name); - dir = dirname(tmp); - parent = find_filesystem_entry(root, dir, S_IFDIR); - free(tmp); - if (parent == NULL) { - errmsg ("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 < (start + count); i++) { - xasprintf(&dname, "%s%lu", name, i); - xasprintf(&hpath, "%s/%s%lu", rootdir, name, i); - rdev = makedev(major, minor + (i - start) * increment); - 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: - errmsg_die("Unsupported file type '%c'", 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_cleanmarkers = 1; -static struct jffs2_unknown_node cleanmarker; -static int cleanmarker_size = sizeof(cleanmarker); -static unsigned char ffbuf[16] = -{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff -}; - -/* We set this at start of main() using sysconf(), -1 means we don't know */ -/* When building an fs for non-native systems, use --pagesize=SIZE option */ -int page_size = -1; - -#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) - sys_errmsg_die("write"); - - if (ret == 0) - sys_errmsg_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_cleanmarkers) { - if ((out_ofs % erase_block_size) == 0) { - full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); - pad(cleanmarker_size - sizeof(cleanmarker)); - padword(); - } - } - if ((out_ofs % erase_block_size) + req > erase_block_size) { - padblock(); - } - if (add_cleanmarkers) { - if ((out_ofs % erase_block_size) == 0) { - full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); - pad(cleanmarker_size - sizeof(cleanmarker)); - 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(mtd_crc32(0, &rd, - sizeof(struct jffs2_unknown_node) - 4)); - rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); - rd.version = cpu_to_je32(version++); - rd.ino = cpu_to_je32(e->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(mtd_crc32(0, &rd, sizeof(rd) - 8)); - rd.name_crc = cpu_to_je32(mtd_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 unsigned int 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; - unsigned int totcomp = 0; - - statbuf = &(e->sb); - if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { - errmsg("Skipping file \"%s\" too large.", e->path); - return -1; - } - fd = open(e->hostname, O_RDONLY); - if (fd == -1) { - sys_errmsg_die("%s: open file", e->hostname); - } - - e->ino = ++ino; - mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", - e->name, (unsigned long) e->ino, - (unsigned long) e->parent->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(e->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) { - sys_errmsg_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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); - ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); - - full_write(out_fd, &ri, sizeof(ri)); - totcomp += sizeof(ri); - full_write(out_fd, wbuf, space); - totcomp += 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(mtd_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(mtd_crc32(0, &ri, sizeof(ri) - 8)); - - full_write(out_fd, &ri, sizeof(ri)); - padword(); - } - free(buf); - close(fd); - return totcomp; -} - -static void write_symlink(struct filesystem_entry *e) -{ - int len; - struct stat *statbuf; - struct jffs2_raw_inode ri; - - statbuf = &(e->sb); - e->ino = ++ino; - mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", - e->name, (unsigned long) e->ino, - (unsigned long) e->parent->ino); - write_dirent(e); - - len = strlen(e->link); - if (len > JFFS2_MAX_SYMLINK_LEN) { - errmsg("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(mtd_crc32(0, - &ri, sizeof(struct jffs2_unknown_node) - 4)); - - ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); - ri.data_crc = cpu_to_je32(mtd_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); - e->ino = ++ino; - if (S_ISDIR(statbuf->st_mode)) { - mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", - e->name, (unsigned long) e->ino, - (unsigned long) (e->parent) ? e->parent->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(mtd_crc32(0, - &ri, sizeof(struct jffs2_unknown_node) - 4)); - - ri.ino = cpu_to_je32(e->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(mtd_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); - e->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(mtd_crc32(0, - &ri, sizeof(struct jffs2_unknown_node) - 4)); - - ri.ino = cpu_to_je32(e->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(mtd_crc32(0, &ri, sizeof(ri) - 8)); - ri.data_crc = cpu_to_je32(mtd_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(); -} - -#ifndef WITHOUT_XATTR -typedef struct xattr_entry { - struct xattr_entry *next; - uint32_t xid; - int xprefix; - char *xname; - char *xvalue; - int name_len; - int value_len; -} xattr_entry_t; - -#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ -static uint32_t enable_xattr = 0; -static uint32_t highest_xid = 0; -static uint32_t highest_xseqno = 0; - -static struct { - int xprefix; - const char *string; - int length; -} xprefix_tbl[] = { - { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, - { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, - { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, - { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, - { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, - { 0, NULL, 0 } -}; - -static void formalize_posix_acl(void *xvalue, int *value_len) -{ - struct posix_acl_xattr_header *pacl_header; - struct posix_acl_xattr_entry *pent, *plim; - struct jffs2_acl_header *jacl_header; - struct jffs2_acl_entry *jent; - struct jffs2_acl_entry_short *jent_s; - char buffer[XATTR_BUFFER_SIZE]; - int offset = 0; - - pacl_header = xvalue;; - pent = pacl_header->a_entries; - plim = xvalue + *value_len; - - jacl_header = (struct jffs2_acl_header *)buffer; - offset += sizeof(struct jffs2_acl_header); - jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); - - while (pent < plim) { - switch(le16_to_cpu(pent->e_tag)) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: - jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); - offset += sizeof(struct jffs2_acl_entry_short); - jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); - jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); - break; - case ACL_USER: - case ACL_GROUP: - jent = (struct jffs2_acl_entry *)(buffer + offset); - offset += sizeof(struct jffs2_acl_entry); - jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); - jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); - jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); - break; - default: - printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); - exit(1); - } - pent++; - } - if (offset > *value_len) { - printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", - offset, *value_len); - exit(1); - } - memcpy(xvalue, buffer, offset); - *value_len = offset; -} - -static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) -{ - xattr_entry_t *xe; - struct jffs2_raw_xattr rx; - int name_len; - - /* create xattr entry */ - name_len = strlen(xname); - xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); - xe->next = NULL; - xe->xid = ++highest_xid; - xe->xprefix = xprefix; - xe->xname = ((char *)xe) + sizeof(xattr_entry_t); - xe->xvalue = xe->xname + name_len + 1; - xe->name_len = name_len; - xe->value_len = value_len; - strcpy(xe->xname, xname); - memcpy(xe->xvalue, xvalue, value_len); - - /* write xattr node */ - memset(&rx, 0, sizeof(rx)); - rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); - rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); - rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); - - rx.xid = cpu_to_je32(xe->xid); - rx.version = cpu_to_je32(1); /* initial version */ - rx.xprefix = xprefix; - rx.name_len = xe->name_len; - rx.value_len = cpu_to_je16(xe->value_len); - rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); - rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4)); - - pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); - full_write(out_fd, &rx, sizeof(rx)); - full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); - padword(); - - return xe; -} - -#define XATTRENTRY_HASHSIZE 57 -static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) -{ - static xattr_entry_t **xentry_hash = NULL; - xattr_entry_t *xe; - int index, name_len; - - /* create hash table */ - if (!xentry_hash) - xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); - - if (xprefix == JFFS2_XPREFIX_ACL_ACCESS - || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) - formalize_posix_acl(xvalue, &value_len); - - name_len = strlen(xname); - index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; - for (xe = xentry_hash[index]; xe; xe = xe->next) { - if (xe->xprefix == xprefix - && xe->value_len == value_len - && !strcmp(xe->xname, xname) - && !memcmp(xe->xvalue, xvalue, value_len)) - break; - } - if (!xe) { - xe = create_xattr_entry(xprefix, xname, xvalue, value_len); - xe->next = xentry_hash[index]; - xentry_hash[index] = xe; - } - return xe; -} - -static void write_xattr_entry(struct filesystem_entry *e) -{ - struct jffs2_raw_xref ref; - struct xattr_entry *xe; - char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; - char *xname; - const char *prefix_str; - int i, xprefix, prefix_len; - int list_sz, offset, name_len, value_len; - - if (!enable_xattr) - return; - - list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); - if (list_sz < 0) { - if (verbose) - printf("llistxattr('%s') = %d : %s\n", - e->hostname, errno, strerror(errno)); - return; - } - - for (offset = 0; offset < list_sz; offset += name_len) { - xname = xlist + offset; - name_len = strlen(xname) + 1; - - for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { - prefix_str = xprefix_tbl[i].string; - prefix_len = xprefix_tbl[i].length; - if (prefix_str[prefix_len - 1] == '.') { - if (!strncmp(xname, prefix_str, prefix_len - 1)) - break; - } else { - if (!strcmp(xname, prefix_str)) - break; - } - } - if (!xprefix) { - if (verbose) - printf("%s: xattr '%s' is not supported.\n", - e->hostname, xname); - continue; - } - if ((enable_xattr & (1 << xprefix)) == 0) - continue; - - value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); - if (value_len < 0) { - if (verbose) - printf("lgetxattr('%s', '%s') = %d : %s\n", - e->hostname, xname, errno, strerror(errno)); - continue; - } - xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); - if (!xe) { - if (verbose) - printf("%s : xattr '%s' was ignored.\n", - e->hostname, xname); - continue; - } - - memset(&ref, 0, sizeof(ref)); - ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); - ref.totlen = cpu_to_je32(sizeof(ref)); - ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); - ref.ino = cpu_to_je32(e->ino); - ref.xid = cpu_to_je32(xe->xid); - ref.xseqno = cpu_to_je32(highest_xseqno += 2); - ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4)); - - pad_block_if_less_than(sizeof(ref)); - full_write(out_fd, &ref, sizeof(ref)); - padword(); - } -} - -#else /* WITHOUT_XATTR */ -#define write_xattr_entry(x) -#endif - -static void recursive_populate_directory(struct filesystem_entry *dir) -{ - struct filesystem_entry *e; - unsigned int wrote; - - if (verbose) { - printf("%s\n", dir->fullname); - } - write_xattr_entry(dir); /* for '/' */ - - e = dir->files; - while (e) { - if (e->sb.st_nlink >= 1 && - (e->ino = find_hardlink(e))) { - - write_dirent(e); - if (verbose) { - printf("\tL %04o %9lu %5d:%-3d %s\n", - e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, - (int) (e->sb.st_uid), (int) (e->sb.st_gid), - e->name); - } - } else switch (e->sb.st_mode & S_IFMT) { - case S_IFDIR: - if (verbose) { - printf("\td %04o %9" PRIdoff_t " %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); - write_xattr_entry(e); - break; - case S_IFSOCK: - if (verbose) { - printf("\ts %04o %9" PRIdoff_t " %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); - write_xattr_entry(e); - break; - case S_IFIFO: - if (verbose) { - printf("\tp %04o %9" PRIdoff_t " %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); - write_xattr_entry(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); - write_xattr_entry(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); - write_xattr_entry(e); - break; - case S_IFLNK: - if (verbose) { - printf("\tl %04o %9" PRIdoff_t " %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); - write_xattr_entry(e); - break; - case S_IFREG: - wrote = write_regular_file(e); - write_xattr_entry(e); - if (verbose) { - printf("\tf %04o %9" PRIdoff_t " (%9u) %5d:%-3d %s\n", - e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, - (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); - } - break; - default: - errmsg("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) -{ - 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(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); - - if (ino == 0) - ino = 1; - - root->ino = 1; - recursive_populate_directory(root); - - if (pad_fs_size == -1) { - padblock(); - } else { - if (pad_fs_size && add_cleanmarkers){ - padblock(); - while (out_ofs < pad_fs_size) { - full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); - pad(cleanmarker_size - sizeof(cleanmarker)); - 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-cleanmarkers", 0, NULL, 'n'}, - {"cleanmarker", 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'}, - {"enable-compressor", 1, NULL, 'X'}, - {"test-compression", 0, NULL, 't'}, - {"compressor-priority", 1, NULL, 'y'}, - {"incremental", 1, NULL, 'i'}, -#ifndef WITHOUT_XATTR - {"with-xattr", 0, NULL, 1000 }, - {"with-selinux", 0, NULL, 1001 }, - {"with-posix-acl", 0, NULL, 1002 }, -#endif - {NULL, 0, NULL, 0} -}; - -static const 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.\n" -" Set according to target system's memory management\n" -" page size (default: 4KiB)\n" -" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" -" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" -" -m, --compr-mode=MODE Select compression mode (default: priority)\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 available compressors\n" -" -t, --test-compression Call decompress and compare with the original (for test)\n" -" -n, --no-cleanmarkers Don't add a cleanmarker 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" -#ifndef WITHOUT_XATTR -" --with-xattr stuff all xattr entries into image\n" -" --with-selinux stuff only SELinux Labels into jffs2 image\n" -" --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" -#endif -" -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 const char revtext[] = "1.60"; - -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%08zx, 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%08zx, 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%08zx, 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%08zx, 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_PADDING: - if (verbose) { - printf ("%8s Padding node at 0x%08zx, 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%08zx, 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 = xmalloc(erase_block_size); - - 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; - int warn_page_size = 0; - - page_size = sysconf(_SC_PAGESIZE); - if (page_size < 0) /* System doesn't know so ... */ - page_size = 4096; /* ... we make an educated guess */ - if (page_size != 4096) - warn_page_size = 1; /* warn user if page size not 4096 */ - - 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) - sys_errmsg_die("%s", optarg); - if (sb.st_size < 10) - errmsg_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) { - errmsg_die("root directory specified more than once"); - } - rootdir = xstrdup(optarg); - break; - - case 's': - page_size = strtol(optarg, NULL, 0); - warn_page_size = 0; /* set by user, so don't need to warn */ - break; - - case 'o': - if (out_fd != -1) { - errmsg_die("output filename specified more than once"); - } - out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); - if (out_fd == -1) { - sys_errmsg_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 '?': - errmsg_die("%s", helptext); - - case 'v': - verbose = 1; - break; - - case 'V': - errmsg_die("revision %s\n", revtext); - - case 'e': { - char *next; - unsigned units = 0; - erase_block_size = strtol(optarg, &next, 0); - if (!erase_block_size) - errmsg_die("Unrecognisable erase size\n"); - - if (*next) { - if (!strcmp(next, "KiB")) { - units = 1024; - } else if (!strcmp(next, "MiB")) { - units = 1024 * 1024; - } else { - errmsg_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_cleanmarkers = 0; - break; - case 'c': - cleanmarker_size = strtol(optarg, NULL, 0); - if (cleanmarker_size < sizeof(cleanmarker)) { - errmsg_die("cleanmarker size must be >= 12"); - } - if (cleanmarker_size >= erase_block_size) { - errmsg_die("cleanmarker size must be < eraseblock size"); - } - break; - case 'm': - if (jffs2_set_compression_mode_name(optarg)) { - errmsg_die("Unknown compression mode %s", optarg); - } - break; - case 'x': - if (jffs2_disable_compressor_name(optarg)) { - errmsg_die("Unknown compressor name %s",optarg); - } - break; - case 'X': - if (jffs2_enable_compressor_name(optarg)) { - errmsg_die("Unknown compressor name %s",optarg); - } - break; - case 'L': - errmsg_die("\n%s",jffs2_list_compressors()); - break; - case 't': - jffs2_compression_check_set(1); - break; - case 'y': - compr_name = xmalloc(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 { - errmsg_die("Cannot parse %s",optarg); - } - free(compr_name); - break; - case 'i': - if (in_fd != -1) { - errmsg_die("(incremental) filename specified more than once"); - } - in_fd = open(optarg, O_RDONLY); - if (in_fd == -1) { - sys_errmsg_die("cannot open (incremental) file"); - } - break; -#ifndef WITHOUT_XATTR - case 1000: /* --with-xattr */ - enable_xattr |= (1 << JFFS2_XPREFIX_USER) - | (1 << JFFS2_XPREFIX_SECURITY) - | (1 << JFFS2_XPREFIX_ACL_ACCESS) - | (1 << JFFS2_XPREFIX_ACL_DEFAULT) - | (1 << JFFS2_XPREFIX_TRUSTED); - break; - case 1001: /* --with-selinux */ - enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); - break; - case 1002: /* --with-posix-acl */ - enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) - | (1 << JFFS2_XPREFIX_ACL_DEFAULT); - break; -#endif - } - } - if (warn_page_size) { - errmsg("Page size for this system is by default %d", page_size); - errmsg("Use the --pagesize=SIZE option if this is not what you want"); - } - if (out_fd == -1) { - if (isatty(1)) { - errmsg_die("%s", helptext); - } - out_fd = 1; - } - if (lstat(rootdir, &sb)) { - sys_errmsg_die("%s", rootdir); - } - if (chdir(rootdir)) - sys_errmsg_die("%s", rootdir); - - if (!(cwd = getcwd(0, GETCWD_SIZE))) - sys_errmsg_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/mkfs.ubifs/.gitignore b/mkfs.ubifs/.gitignore deleted file mode 100644 index 6b0e85c..0000000 --- a/mkfs.ubifs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/mkfs.ubifs diff --git a/mkfs.ubifs/COPYING b/mkfs.ubifs/COPYING deleted file mode 100644 index 60549be..0000000 --- a/mkfs.ubifs/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - 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/mkfs.ubifs/README b/mkfs.ubifs/README deleted file mode 100644 index 7e19939..0000000 --- a/mkfs.ubifs/README +++ /dev/null @@ -1,9 +0,0 @@ -UBIFS File System - Make File System program - -* crc16.h and crc16.c were copied from the linux kernel. -* crc32.h and crc32.c were copied from mtd-utils and amended. -* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. -* key.h is copied from fs/ubifs/key.h from the linux kernel. -* defs.h is a bunch of definitions to smooth things over. -* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. -* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ diff --git a/mkfs.ubifs/compr.c b/mkfs.ubifs/compr.c deleted file mode 100644 index 34b2f60..0000000 --- a/mkfs.ubifs/compr.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation. - * Copyright (C) 2008 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 version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Artem Bityutskiy - * Adrian Hunter - * Zoltan Sogor - */ - -#include <stdlib.h> -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <lzo/lzo1x.h> -#include <linux/types.h> - -#define crc32 __zlib_crc32 -#include <zlib.h> -#undef crc32 - -#include "compr.h" -#include "mkfs.ubifs.h" - -static void *lzo_mem; -static unsigned long long errcnt = 0; -static struct ubifs_info *c = &info_; - -#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION -#define DEFLATE_DEF_WINBITS 11 -#define DEFLATE_DEF_MEMLEVEL 8 - -static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, - size_t *out_len) -{ - z_stream strm; - - strm.zalloc = NULL; - strm.zfree = NULL; - - /* - * Match exactly the zlib parameters used by the Linux kernel crypto - * API. - */ - if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, - -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, - Z_DEFAULT_STRATEGY)) { - errcnt += 1; - return -1; - } - - strm.next_in = in_buf; - strm.avail_in = in_len; - strm.total_in = 0; - - strm.next_out = out_buf; - strm.avail_out = *out_len; - strm.total_out = 0; - - if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { - deflateEnd(&strm); - errcnt += 1; - return -1; - } - - if (deflateEnd(&strm) != Z_OK) { - errcnt += 1; - return -1; - } - - *out_len = strm.total_out; - - return 0; -} - -static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, - size_t *out_len) -{ - lzo_uint len; - int ret; - - len = *out_len; - ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); - *out_len = len; - - if (ret != LZO_E_OK) { - errcnt += 1; - return -1; - } - - return 0; -} - -static int no_compress(void *in_buf, size_t in_len, void *out_buf, - size_t *out_len) -{ - memcpy(out_buf, in_buf, in_len); - *out_len = in_len; - return 0; -} - -static char *zlib_buf; - -static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, - size_t *out_len, int *type) -{ - int lzo_ret, zlib_ret; - size_t lzo_len, zlib_len; - - lzo_len = zlib_len = *out_len; - lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); - zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); - - if (lzo_ret && zlib_ret) - /* Both compressors failed */ - return -1; - - if (!lzo_ret && !zlib_ret) { - double percent; - - /* Both compressors succeeded */ - if (lzo_len <= zlib_len ) - goto select_lzo; - - percent = (double)zlib_len / (double)lzo_len; - percent *= 100; - if (percent > 100 - c->favor_percent) - goto select_lzo; - goto select_zlib; - } - - if (lzo_ret) - /* Only zlib compressor succeeded */ - goto select_zlib; - - /* Only LZO compressor succeeded */ - -select_lzo: - *out_len = lzo_len; - *type = MKFS_UBIFS_COMPR_LZO; - return 0; - -select_zlib: - *out_len = zlib_len; - *type = MKFS_UBIFS_COMPR_ZLIB; - memcpy(out_buf, zlib_buf, zlib_len); - return 0; -} - -int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, - int type) -{ - int ret; - - if (in_len < UBIFS_MIN_COMPR_LEN) { - no_compress(in_buf, in_len, out_buf, out_len); - return MKFS_UBIFS_COMPR_NONE; - } - - if (c->favor_lzo) - ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); - else { - switch (type) { - case MKFS_UBIFS_COMPR_LZO: - ret = lzo_compress(in_buf, in_len, out_buf, out_len); - break; - case MKFS_UBIFS_COMPR_ZLIB: - ret = zlib_deflate(in_buf, in_len, out_buf, out_len); - break; - case MKFS_UBIFS_COMPR_NONE: - ret = 1; - break; - default: - errcnt += 1; - ret = 1; - break; - } - } - if (ret || *out_len >= in_len) { - no_compress(in_buf, in_len, out_buf, out_len); - return MKFS_UBIFS_COMPR_NONE; - } - return type; -} - -int init_compression(void) -{ - lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); - if (!lzo_mem) - return -1; - - zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); - if (!zlib_buf) { - free(lzo_mem); - return -1; - } - - return 0; -} - -void destroy_compression(void) -{ - free(zlib_buf); - free(lzo_mem); - if (errcnt) - fprintf(stderr, "%llu compression errors occurred\n", errcnt); -} diff --git a/mkfs.ubifs/compr.h b/mkfs.ubifs/compr.h deleted file mode 100644 index e3dd95c..0000000 --- a/mkfs.ubifs/compr.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation. - * Copyright (C) 2008 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 version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Artem Bityutskiy - * Adrian Hunter - * Zoltan Sogor - */ - -#ifndef __UBIFS_COMPRESS_H__ -#define __UBIFS_COMPRESS_H__ - -/* - * Compressors may end-up with more data in the output buffer than in the input - * buffer. This constant defined the worst case factor, i.e. we assume that the - * output buffer may be at max. WORST_COMPR_FACTOR times larger than input - * buffer. - */ -#define WORST_COMPR_FACTOR 4 - -enum compression_type -{ - MKFS_UBIFS_COMPR_NONE, - MKFS_UBIFS_COMPR_LZO, - MKFS_UBIFS_COMPR_ZLIB, -}; - -int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, - int type); -int init_compression(void); -void destroy_compression(void); - -#endif diff --git a/mkfs.ubifs/crc16.c b/mkfs.ubifs/crc16.c deleted file mode 100644 index a19512e..0000000 --- a/mkfs.ubifs/crc16.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This code was taken from the linux kernel. The license is GPL Version 2. - */ - -#include "crc16.h" - -/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ -uint16_t const crc16_table[256] = { - 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, - 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, - 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, - 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, - 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, - 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, - 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, - 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, - 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, - 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, - 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, - 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, - 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, - 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, - 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, - 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, - 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, - 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, - 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, - 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, - 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, - 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, - 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, - 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, - 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, - 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, - 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, - 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, - 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, - 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, - 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, - 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 -}; - -/** - * crc16 - compute the CRC-16 for the data buffer - * @crc: previous CRC value - * @buffer: data pointer - * @len: number of bytes in the buffer - * - * Returns the updated CRC value. - */ -uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) -{ - while (len--) - crc = crc16_byte(crc, *buffer++); - return crc; -} diff --git a/mkfs.ubifs/crc16.h b/mkfs.ubifs/crc16.h deleted file mode 100644 index 539d21a..0000000 --- a/mkfs.ubifs/crc16.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Implements the standard CRC-16: - * Width 16 - * Poly 0x8005 (x^16 + x^15 + x^2 + 1) - * Init 0 - * - * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> - * - * This code was taken from the linux kernel. The license is GPL Version 2. - */ - -#ifndef __CRC16_H__ -#define __CRC16_H__ - -#include <stdlib.h> -#include <stdint.h> - -extern uint16_t const crc16_table[256]; - -extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len); - -static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data) -{ - return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; -} - -#endif /* __CRC16_H__ */ diff --git a/mkfs.ubifs/defs.h b/mkfs.ubifs/defs.h deleted file mode 100644 index 1fa3316..0000000 --- a/mkfs.ubifs/defs.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Greate deal of the code was taken from the kernel UBIFS implementation, and - * this file contains some "glue" definitions. - */ - -#ifndef __UBIFS_DEFS_H__ -#define __UBIFS_DEFS_H__ - -#define t16(x) ({ \ - uint16_t __b = (x); \ - (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ -}) - -#define t32(x) ({ \ - uint32_t __b = (x); \ - (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ -}) - -#define t64(x) ({ \ - uint64_t __b = (x); \ - (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ -}) - -#define cpu_to_le16(x) ((__le16){t16(x)}) -#define cpu_to_le32(x) ((__le32){t32(x)}) -#define cpu_to_le64(x) ((__le64){t64(x)}) - -#define le16_to_cpu(x) (t16((x))) -#define le32_to_cpu(x) (t32((x))) -#define le64_to_cpu(x) (t64((x))) - -#define unlikely(x) (x) - -#define ubifs_assert(x) ({}) - -struct qstr -{ - char *name; - size_t len; -}; - -/** - * fls - find last (most-significant) bit set - * @x: the word to search - * - * This is defined the same way as ffs. - * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. - */ -static inline int fls(int x) -{ - int r = 32; - - if (!x) - return 0; - if (!(x & 0xffff0000u)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xff000000u)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xf0000000u)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xc0000000u)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000u)) { - x <<= 1; - r -= 1; - } - return r; -} - -#define do_div(n,base) ({ \ -int __res; \ -__res = ((unsigned long) n) % (unsigned) base; \ -n = ((unsigned long) n) / (unsigned) base; \ -__res; }) - -#if INT_MAX != 0x7fffffff -#error : sizeof(int) must be 4 for this program -#endif - -#if (~0ULL) != 0xffffffffffffffffULL -#error : sizeof(long long) must be 8 for this program -#endif - -#endif diff --git a/mkfs.ubifs/devtable.c b/mkfs.ubifs/devtable.c deleted file mode 100644 index dee035d..0000000 --- a/mkfs.ubifs/devtable.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation. - * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: Artem Bityutskiy - * - * Part of the device table parsing code was taken from the mkfs.jffs2 utility. - * The original author of that code is Erik Andersen, hence: - * Copyright (C) 2001, 2002 Erik Andersen <andersen@codepoet.org> - */ - -/* - * This file implemented device table support. 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) - * - * 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. - * - * Please, refer the device_table.txt file which can be found at MTD utilities - * for more information about what the device table is. - */ - -#include "mkfs.ubifs.h" -#include "hashtable/hashtable.h" -#include "hashtable/hashtable_itr.h" - -/* - * The hash table which contains paths to files/directories/device nodes - * referred to in the device table. For example, if the device table refers - * "/dev/loop0", the @path_htbl will contain "/dev" element. - */ -static struct hashtable *path_htbl; - -/* Hash function used for hash tables */ -static unsigned int r5_hash(void *s) -{ - unsigned int a = 0; - const signed char *str = s; - - while (*str) { - a += *str << 4; - a += *str >> 4; - a *= 11; - str++; - } - - return a; -} - -/* - * Check whether 2 keys of a hash table are equivalent. The keys are path/file - * names, so we simply use 'strcmp()'. - */ -static int is_equivalent(void *k1, void *k2) -{ - return !strcmp(k1, k2); -} - -/** - * separate_last - separate out the last path component - * @buf: the path to split - * @len: length of the @buf string - * @path: the beginning of path is returned here - * @name: the last path component is returned here - * - * This helper function separates out the the last component of the full path - * string. For example, "/dev/loop" would be split on "/dev" and "loop". This - * function allocates memory for @path and @name and return the result there. - * Returns zero in case of success and a negative error code in case of - * failure. - */ -static int separate_last(const char *buf, int len, char **path, char **name) -{ - int path_len = len, name_len; - const char *p = buf + len, *n; - - while (*--p != '/') - path_len -= 1; - - /* Drop the final '/' unless this is the root directory */ - name_len = len - path_len; - n = buf + path_len; - if (path_len > 1) - path_len -= 1; - - *path = malloc(path_len + 1); - if (!*path) - return err_msg("cannot allocate %d bytes of memory", - path_len + 1); - memcpy(*path, buf, path_len); - (*path)[path_len] = '\0'; - - *name = malloc(name_len + 1); - if (!*name) { - free(*path); - return err_msg("cannot allocate %d bytes of memory", - name_len + 1); - } - memcpy(*name, n, name_len + 1); - - return 0; -} - -static int interpret_table_entry(const char *line) -{ - char buf[1024], type, *path = NULL, *name = NULL; - int len; - struct path_htbl_element *ph_elt = NULL; - struct name_htbl_element *nh_elt = NULL; - unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; - unsigned int start = 0, increment = 0, count = 0; - - if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u", - buf, &type, &mode, &uid, &gid, &major, &minor, - &start, &increment, &count) < 0) - return sys_err_msg("sscanf failed"); - - dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, " - "minor %u, start %u, inc %u, cnt %u", - buf, type, mode, uid, gid, major, minor, start, - increment, count); - - len = strnlen(buf, 1024); - if (len == 1024) - return err_msg("too long path"); - - if (!strcmp(buf, "/")) - return err_msg("device table entries require absolute paths"); - if (buf[1] == '\0') - return err_msg("root directory cannot be created"); - if (strstr(buf, "//")) - return err_msg("'//' cannot be used in the path"); - if (buf[len - 1] == '/') - return err_msg("do not put '/' at the end"); - - if (strstr(buf, "/./") || strstr(buf, "/../") || - !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/..")) - return err_msg("'.' and '..' cannot be used in the path"); - - 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: - return err_msg("unsupported file type '%c'", type); - } - - if (separate_last(buf, len, &path, &name)) - return -1; - - /* - * Check if this path already exist in the path hash table and add it - * if it is not. - */ - ph_elt = hashtable_search(path_htbl, path); - if (!ph_elt) { - dbg_msg(3, "inserting '%s' into path hash table", path); - ph_elt = malloc(sizeof(struct path_htbl_element)); - if (!ph_elt) { - err_msg("cannot allocate %zd bytes of memory", - sizeof(struct path_htbl_element)); - goto out_free; - } - - if (!hashtable_insert(path_htbl, path, ph_elt)) { - err_msg("cannot insert into path hash table"); - goto out_free; - } - - ph_elt->path = path; - path = NULL; - ph_elt->name_htbl = create_hashtable(128, &r5_hash, - &is_equivalent); - if (!ph_elt->name_htbl) { - err_msg("cannot create name hash table"); - goto out_free; - } - } - - if (increment != 0 && count == 0) - return err_msg("count cannot be zero if increment is non-zero"); - - /* - * Add the file/directory/device node (last component of the path) to - * the name hashtable. The name hashtable resides in the corresponding - * path hashtable element. - */ - - if (count == 0) { - /* This entry does not require any iterating */ - nh_elt = malloc(sizeof(struct name_htbl_element)); - if (!nh_elt) { - err_msg("cannot allocate %zd bytes of memory", - sizeof(struct name_htbl_element)); - goto out_free; - } - - nh_elt->mode = mode; - nh_elt->uid = uid; - nh_elt->gid = gid; - nh_elt->dev = makedev(major, minor); - - dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", - name, major(nh_elt->dev), minor(nh_elt->dev)); - - if (hashtable_search(ph_elt->name_htbl, name)) - return err_msg("'%s' is referred twice", buf); - - nh_elt->name = name; - if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) { - err_msg("cannot insert into name hash table"); - goto out_free; - } - } else { - int i, num = start + count, len = strlen(name) + 20; - char *nm; - - for (i = start; i < num; i++) { - nh_elt = malloc(sizeof(struct name_htbl_element)); - if (!nh_elt) { - err_msg("cannot allocate %zd bytes of memory", - sizeof(struct name_htbl_element)); - goto out_free; - } - - nh_elt->mode = mode; - nh_elt->uid = uid; - nh_elt->gid = gid; - nh_elt->dev = makedev(major, minor + (i - start) * increment); - - nm = malloc(len); - if (!nm) { - err_msg("cannot allocate %d bytes of memory", len); - goto out_free; - } - - sprintf(nm, "%s%d", name, i); - nh_elt->name = nm; - - dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", - nm, major(nh_elt->dev), minor(nh_elt->dev)); - - if (hashtable_search(ph_elt->name_htbl, nm)) { - err_msg("'%s' is referred twice", buf); - free (nm); - goto out_free; - } - - if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { - err_msg("cannot insert into name hash table"); - free (nm); - goto out_free; - } - } - free(name); - name = NULL; - } - - return 0; - -out_free: - free(ph_elt); - free(nh_elt); - free(path); - free(name); - return -1; -} - -/** - * parse_devtable - parse the device table. - * @tbl_file: device table file name - * - * This function parses the device table and prepare the hash table which will - * later be used by mkfs.ubifs to create the specified files/device nodes. - * Returns zero in case of success and a negative error code in case of - * failure. - */ -int parse_devtable(const char *tbl_file) -{ - FILE *f; - char *line = NULL; - struct stat st; - size_t len; - - dbg_msg(1, "parsing device table file '%s'", tbl_file); - - path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); - if (!path_htbl) - return err_msg("cannot create path hash table"); - - f = fopen(tbl_file, "r"); - if (!f) - return sys_err_msg("cannot open '%s'", tbl_file); - - if (fstat(fileno(f), &st) < 0) { - sys_err_msg("cannot stat '%s'", tbl_file); - goto out_close; - } - - if (st.st_size < 10) { - sys_err_msg("'%s' is too short", tbl_file); - goto out_close; - } - - /* - * 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 - */ - while (getline(&line, &len, f) != -1) { - /* First trim off any white-space */ - len = strlen(line); - - /* Trim trailing white-space */ - while (len > 0 && isspace(line[len - 1])) - line[--len] = '\0'; - /* Trim leading white-space */ - 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(line)) { - err_msg("cannot parse '%s'", line); - goto out_close; - } - } - - free(line); - line = NULL; - } - - dbg_msg(1, "finished parsing"); - fclose(f); - return 0; - -out_close: - fclose(f); - free_devtable_info(); - return -1; -} - -/** - * devtbl_find_path - find a path in the path hash table. - * @path: UBIFS path to find. - * - * This looks up the path hash table. Returns the path hash table element - * reference if @path was found and %NULL if not. - */ -struct path_htbl_element *devtbl_find_path(const char *path) -{ - if (!path_htbl) - return NULL; - - return hashtable_search(path_htbl, (void *)path); -} - -/** - * devtbl_find_name - find a name in the name hash table. - * @ph_etl: path hash table element to find at - * @name: name to find - * - * This looks up the name hash table. Returns the name hash table element - * reference if @name found and %NULL if not. - */ -struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, - const char *name) -{ - if (!path_htbl) - return NULL; - - return hashtable_search(ph_elt->name_htbl, (void *)name); -} - -/** - * override_attributes - override inode attributes. - * @st: struct stat object to containing the attributes to override - * @ph_elt: path hash table element object - * @nh_elt: name hash table element object containing the new values - * - * The device table file may override attributes like UID of files. For - * example, the device table may contain a "/dev" entry, and the UBIFS FS on - * the host may contain "/dev" directory. In this case the attributes of the - * "/dev" directory inode has to be as the device table specifies. - * - * Note, the hash element is removed by this function as well. - */ -int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, - struct name_htbl_element *nh_elt) -{ - if (!path_htbl) - return 0; - - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || - S_ISFIFO(st->st_mode)) - return err_msg("%s/%s both exists at UBIFS root at host, " - "and is referred from the device table", - strcmp(ph_elt->path, "/") ? ph_elt->path : "", - nh_elt->name); - - if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) - return err_msg("%s/%s is referred from the device table also exists in " - "the UBIFS root directory at host, but the file type is " - "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", - nh_elt->name); - - dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", - nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); - - st->st_uid = nh_elt->uid; - st->st_gid = nh_elt->gid; - st->st_mode = nh_elt->mode; - - hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); - return 0; -} - -/** - * first_name_htbl_element - return first element of the name hash table. - * @ph_elt: the path hash table the name hash table belongs to - * @itr: double pointer to a 'struct hashtable_itr' object where the - * information about further iterations is stored - * - * This function implements name hash table iteration together with - * 'next_name_htbl_element()'. Returns the first name hash table element or - * %NULL if the hash table is empty. - */ -struct name_htbl_element * -first_name_htbl_element(struct path_htbl_element *ph_elt, - struct hashtable_itr **itr) -{ - if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) - return NULL; - - *itr = hashtable_iterator(ph_elt->name_htbl); - return hashtable_iterator_value(*itr); -} - -/** - * first_name_htbl_element - return next element of the name hash table. - * @ph_elt: the path hash table the name hash table belongs to - * @itr: double pointer to a 'struct hashtable_itr' object where the - * information about further iterations is stored - * - * This function implements name hash table iteration together with - * 'first_name_htbl_element()'. Returns the next name hash table element or - * %NULL if there are no more elements. - */ -struct name_htbl_element * -next_name_htbl_element(struct path_htbl_element *ph_elt, - struct hashtable_itr **itr) -{ - if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) - return NULL; - - return hashtable_iterator_value(*itr); -} - -/** - * free_devtable_info - free device table information. - * - * This function frees the path hash table and the name hash tables. - */ -void free_devtable_info(void) -{ - struct hashtable_itr *ph_itr; - struct path_htbl_element *ph_elt; - - if (!path_htbl) - return; - - if (hashtable_count(path_htbl) > 0) { - ph_itr = hashtable_iterator(path_htbl); - do { - ph_elt = hashtable_iterator_value(ph_itr); - /* - * Note, since we use the same string for the key and - * @name in the name hash table elements, we do not - * have to iterate name hash table because @name memory - * will be freed when freeing the key. - */ - hashtable_destroy(ph_elt->name_htbl, 1); - } while (hashtable_iterator_advance(ph_itr)); - } - hashtable_destroy(path_htbl, 1); -} diff --git a/mkfs.ubifs/hashtable/hashtable.c b/mkfs.ubifs/hashtable/hashtable.c deleted file mode 100644 index c1f99ed..0000000 --- a/mkfs.ubifs/hashtable/hashtable.c +++ /dev/null @@ -1,277 +0,0 @@ -/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ - -#define PROGRAM_NAME "hashtable" - -#include "common.h" -#include "hashtable.h" -#include "hashtable_private.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> - -/* -Credit for primes table: Aaron Krowne - http://br.endernet.org/~akrowne/ - http://planetmath.org/encyclopedia/GoodHashTablePrimes.html -*/ -static const unsigned int primes[] = { -53, 97, 193, 389, -769, 1543, 3079, 6151, -12289, 24593, 49157, 98317, -196613, 393241, 786433, 1572869, -3145739, 6291469, 12582917, 25165843, -50331653, 100663319, 201326611, 402653189, -805306457, 1610612741 -}; -const unsigned int prime_table_length = ARRAY_SIZE(primes); -const float max_load_factor = 0.65; - -/*****************************************************************************/ -struct hashtable * -create_hashtable(unsigned int minsize, - unsigned int (*hashf) (void*), - int (*eqf) (void*,void*)) -{ - struct hashtable *h; - unsigned int pindex, size = primes[0]; - /* Check requested hashtable isn't too large */ - if (minsize > (1u << 30)) return NULL; - /* Enforce size as prime */ - for (pindex=0; pindex < prime_table_length; pindex++) { - if (primes[pindex] > minsize) { size = primes[pindex]; break; } - } - h = (struct hashtable *)malloc(sizeof(struct hashtable)); - if (NULL == h) return NULL; /*oom*/ - h->table = (struct entry **)malloc(sizeof(struct entry*) * size); - if (NULL == h->table) { free(h); return NULL; } /*oom*/ - memset(h->table, 0, size * sizeof(struct entry *)); - h->tablelength = size; - h->primeindex = pindex; - h->entrycount = 0; - h->hashfn = hashf; - h->eqfn = eqf; - h->loadlimit = (unsigned int) ceil(size * max_load_factor); - return h; -} - -/*****************************************************************************/ -unsigned int -hash(struct hashtable *h, void *k) -{ - /* Aim to protect against poor hash functions by adding logic here - * - logic taken from java 1.4 hashtable source */ - unsigned int i = h->hashfn(k); - i += ~(i << 9); - i ^= ((i >> 14) | (i << 18)); /* >>> */ - i += (i << 4); - i ^= ((i >> 10) | (i << 22)); /* >>> */ - return i; -} - -/*****************************************************************************/ -static int -hashtable_expand(struct hashtable *h) -{ - /* Double the size of the table to accomodate more entries */ - struct entry **newtable; - struct entry *e; - struct entry **pE; - unsigned int newsize, i, index; - /* Check we're not hitting max capacity */ - if (h->primeindex == (prime_table_length - 1)) return 0; - newsize = primes[++(h->primeindex)]; - - newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); - if (NULL != newtable) - { - memset(newtable, 0, newsize * sizeof(struct entry *)); - /* This algorithm is not 'stable'. ie. it reverses the list - * when it transfers entries between the tables */ - for (i = 0; i < h->tablelength; i++) { - while (NULL != (e = h->table[i])) { - h->table[i] = e->next; - index = indexFor(newsize,e->h); - e->next = newtable[index]; - newtable[index] = e; - } - } - free(h->table); - h->table = newtable; - } - /* Plan B: realloc instead */ - else - { - newtable = (struct entry **) - realloc(h->table, newsize * sizeof(struct entry *)); - if (NULL == newtable) { (h->primeindex)--; return 0; } - h->table = newtable; - memset(newtable[h->tablelength], 0, newsize - h->tablelength); - for (i = 0; i < h->tablelength; i++) { - for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { - index = indexFor(newsize,e->h); - if (index == i) - { - pE = &(e->next); - } - else - { - *pE = e->next; - e->next = newtable[index]; - newtable[index] = e; - } - } - } - } - h->tablelength = newsize; - h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); - return -1; -} - -/*****************************************************************************/ -unsigned int -hashtable_count(struct hashtable *h) -{ - return h->entrycount; -} - -/*****************************************************************************/ -int -hashtable_insert(struct hashtable *h, void *k, void *v) -{ - /* This method allows duplicate keys - but they shouldn't be used */ - unsigned int index; - struct entry *e; - if (++(h->entrycount) > h->loadlimit) - { - /* Ignore the return value. If expand fails, we should - * still try cramming just this value into the existing table - * -- we may not have memory for a larger table, but one more - * element may be ok. Next time we insert, we'll try expanding again.*/ - hashtable_expand(h); - } - e = (struct entry *)malloc(sizeof(struct entry)); - if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ - e->h = hash(h,k); - index = indexFor(h->tablelength,e->h); - e->k = k; - e->v = v; - e->next = h->table[index]; - h->table[index] = e; - return -1; -} - -/*****************************************************************************/ -void * /* returns value associated with key */ -hashtable_search(struct hashtable *h, void *k) -{ - struct entry *e; - unsigned int hashvalue, index; - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - e = h->table[index]; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; - e = e->next; - } - return NULL; -} - -/*****************************************************************************/ -void * /* returns value associated with key */ -hashtable_remove(struct hashtable *h, void *k) -{ - /* TODO: consider compacting the table when the load factor drops enough, - * or provide a 'compact' method. */ - - struct entry *e; - struct entry **pE; - void *v; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hash(h,k)); - pE = &(h->table[index]); - e = *pE; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - { - *pE = e->next; - h->entrycount--; - v = e->v; - freekey(e->k); - free(e); - return v; - } - pE = &(e->next); - e = e->next; - } - return NULL; -} - -/*****************************************************************************/ -/* destroy */ -void -hashtable_destroy(struct hashtable *h, int free_values) -{ - unsigned int i; - struct entry *e, *f; - struct entry **table = h->table; - if (free_values) - { - for (i = 0; i < h->tablelength; i++) - { - e = table[i]; - while (NULL != e) - { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } - } - } - else - { - for (i = 0; i < h->tablelength; i++) - { - e = table[i]; - while (NULL != e) - { f = e; e = e->next; freekey(f->k); free(f); } - } - } - free(h->table); - free(h); -} - -/* - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER - * 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. -*/ diff --git a/mkfs.ubifs/hashtable/hashtable.h b/mkfs.ubifs/hashtable/hashtable.h deleted file mode 100644 index c0b0acd..0000000 --- a/mkfs.ubifs/hashtable/hashtable.h +++ /dev/null @@ -1,199 +0,0 @@ -/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ - -#ifndef __HASHTABLE_CWC22_H__ -#define __HASHTABLE_CWC22_H__ - -struct hashtable; - -/* Example of use: - * - * struct hashtable *h; - * struct some_key *k; - * struct some_value *v; - * - * static unsigned int hash_from_key_fn( void *k ); - * static int keys_equal_fn ( void *key1, void *key2 ); - * - * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); - * k = (struct some_key *) malloc(sizeof(struct some_key)); - * v = (struct some_value *) malloc(sizeof(struct some_value)); - * - * (initialise k and v to suitable values) - * - * if (! hashtable_insert(h,k,v) ) - * { exit(-1); } - * - * if (NULL == (found = hashtable_search(h,k) )) - * { printf("not found!"); } - * - * if (NULL == (found = hashtable_remove(h,k) )) - * { printf("Not found\n"); } - * - */ - -/* Macros may be used to define type-safe(r) hashtable access functions, with - * methods specialized to take known key and value types as parameters. - * - * Example: - * - * Insert this at the start of your file: - * - * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); - * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); - * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); - * - * This defines the functions 'insert_some', 'search_some' and 'remove_some'. - * These operate just like hashtable_insert etc., with the same parameters, - * but their function signatures have 'struct some_key *' rather than - * 'void *', and hence can generate compile time errors if your program is - * supplying incorrect data as a key (and similarly for value). - * - * Note that the hash and key equality functions passed to create_hashtable - * still take 'void *' parameters instead of 'some key *'. This shouldn't be - * a difficult issue as they're only defined and passed once, and the other - * functions will ensure that only valid keys are supplied to them. - * - * The cost for this checking is increased code size and runtime overhead - * - if performance is important, it may be worth switching back to the - * unsafe methods once your program has been debugged with the safe methods. - * This just requires switching to some simple alternative defines - eg: - * #define insert_some hashtable_insert - * - */ - -/***************************************************************************** - * create_hashtable - - * @name create_hashtable - * @param minsize minimum initial size of hashtable - * @param hashfunction function for hashing keys - * @param key_eq_fn function for determining key equality - * @return newly created hashtable or NULL on failure - */ - -struct hashtable * -create_hashtable(unsigned int minsize, - unsigned int (*hashfunction) (void*), - int (*key_eq_fn) (void*,void*)); - -/***************************************************************************** - * hashtable_insert - - * @name hashtable_insert - * @param h the hashtable to insert into - * @param k the key - hashtable claims ownership and will free on removal - * @param v the value - does not claim ownership - * @return non-zero for successful insertion - * - * This function will cause the table to expand if the insertion would take - * the ratio of entries to table size over the maximum load factor. - * - * This function does not check for repeated insertions with a duplicate key. - * The value returned when using a duplicate key is undefined -- when - * the hashtable changes size, the order of retrieval of duplicate key - * entries is reversed. - * If in doubt, remove before insert. - */ - -int -hashtable_insert(struct hashtable *h, void *k, void *v); - -#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ -int fnname (struct hashtable *h, keytype *k, valuetype *v) \ -{ \ - return hashtable_insert(h,k,v); \ -} - -/***************************************************************************** - * hashtable_search - - * @name hashtable_search - * @param h the hashtable to search - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ - -void * -hashtable_search(struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ -valuetype * fnname (struct hashtable *h, keytype *k) \ -{ \ - return (valuetype *) (hashtable_search(h,k)); \ -} - -/***************************************************************************** - * hashtable_remove - - * @name hashtable_remove - * @param h the hashtable to remove the item from - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ - -void * /* returns value */ -hashtable_remove(struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ -valuetype * fnname (struct hashtable *h, keytype *k) \ -{ \ - return (valuetype *) (hashtable_remove(h,k)); \ -} - - -/***************************************************************************** - * hashtable_count - - * @name hashtable_count - * @param h the hashtable - * @return the number of items stored in the hashtable - */ -unsigned int -hashtable_count(struct hashtable *h); - - -/***************************************************************************** - * hashtable_destroy - - * @name hashtable_destroy - * @param h the hashtable - * @param free_values whether to call 'free' on the remaining values - */ - -void -hashtable_destroy(struct hashtable *h, int free_values); - -#endif /* __HASHTABLE_CWC22_H__ */ - -/* - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER - * 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. -*/ diff --git a/mkfs.ubifs/hashtable/hashtable_itr.c b/mkfs.ubifs/hashtable/hashtable_itr.c deleted file mode 100644 index d102453..0000000 --- a/mkfs.ubifs/hashtable/hashtable_itr.c +++ /dev/null @@ -1,176 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ - -#include "hashtable.h" -#include "hashtable_private.h" -#include "hashtable_itr.h" -#include <stdlib.h> /* defines NULL */ - -/*****************************************************************************/ -/* hashtable_iterator - iterator constructor */ - -struct hashtable_itr * -hashtable_iterator(struct hashtable *h) -{ - unsigned int i, tablelength; - struct hashtable_itr *itr = (struct hashtable_itr *) - malloc(sizeof(struct hashtable_itr)); - if (NULL == itr) return NULL; - itr->h = h; - itr->e = NULL; - itr->parent = NULL; - tablelength = h->tablelength; - itr->index = tablelength; - if (0 == h->entrycount) return itr; - - for (i = 0; i < tablelength; i++) - { - if (NULL != h->table[i]) - { - itr->e = h->table[i]; - itr->index = i; - break; - } - } - return itr; -} - -/*****************************************************************************/ -/* advance - advance the iterator to the next element - * returns zero if advanced to end of table */ - -int -hashtable_iterator_advance(struct hashtable_itr *itr) -{ - unsigned int j,tablelength; - struct entry **table; - struct entry *next; - if (NULL == itr->e) return 0; /* stupidity check */ - - next = itr->e->next; - if (NULL != next) - { - itr->parent = itr->e; - itr->e = next; - return -1; - } - tablelength = itr->h->tablelength; - itr->parent = NULL; - if (tablelength <= (j = ++(itr->index))) - { - itr->e = NULL; - return 0; - } - table = itr->h->table; - while (NULL == (next = table[j])) - { - if (++j >= tablelength) - { - itr->index = tablelength; - itr->e = NULL; - return 0; - } - } - itr->index = j; - itr->e = next; - return -1; -} - -/*****************************************************************************/ -/* remove - remove the entry at the current iterator position - * and advance the iterator, if there is a successive - * element. - * If you want the value, read it before you remove: - * beware memory leaks if you don't. - * Returns zero if end of iteration. */ - -int -hashtable_iterator_remove(struct hashtable_itr *itr) -{ - struct entry *remember_e, *remember_parent; - int ret; - - /* Do the removal */ - if (NULL == (itr->parent)) - { - /* element is head of a chain */ - itr->h->table[itr->index] = itr->e->next; - } else { - /* element is mid-chain */ - itr->parent->next = itr->e->next; - } - /* itr->e is now outside the hashtable */ - remember_e = itr->e; - itr->h->entrycount--; - freekey(remember_e->k); - - /* Advance the iterator, correcting the parent */ - remember_parent = itr->parent; - ret = hashtable_iterator_advance(itr); - if (itr->parent == remember_e) { itr->parent = remember_parent; } - free(remember_e); - return ret; -} - -/*****************************************************************************/ -int /* returns zero if not found */ -hashtable_iterator_search(struct hashtable_itr *itr, - struct hashtable *h, void *k) -{ - struct entry *e, *parent; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - - e = h->table[index]; - parent = NULL; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - { - itr->index = index; - itr->e = e; - itr->parent = parent; - itr->h = h; - return -1; - } - parent = e; - e = e->next; - } - return 0; -} - - -/* - * Copyright (c) 2002, 2004, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER - * 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. -*/ diff --git a/mkfs.ubifs/hashtable/hashtable_itr.h b/mkfs.ubifs/hashtable/hashtable_itr.h deleted file mode 100644 index 5c94a04..0000000 --- a/mkfs.ubifs/hashtable/hashtable_itr.h +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ - -#ifndef __HASHTABLE_ITR_CWC22__ -#define __HASHTABLE_ITR_CWC22__ -#include "hashtable.h" -#include "hashtable_private.h" /* needed to enable inlining */ - -/*****************************************************************************/ -/* This struct is only concrete here to allow the inlining of two of the - * accessor functions. */ -struct hashtable_itr -{ - struct hashtable *h; - struct entry *e; - struct entry *parent; - unsigned int index; -}; - - -/*****************************************************************************/ -/* hashtable_iterator - */ - -struct hashtable_itr * -hashtable_iterator(struct hashtable *h); - -/*****************************************************************************/ -/* hashtable_iterator_key - * - return the value of the (key,value) pair at the current position */ - -static inline void * -hashtable_iterator_key(struct hashtable_itr *i) -{ - return i->e->k; -} - -/*****************************************************************************/ -/* value - return the value of the (key,value) pair at the current position */ - -static inline void * -hashtable_iterator_value(struct hashtable_itr *i) -{ - return i->e->v; -} - -/*****************************************************************************/ -/* advance - advance the iterator to the next element - * returns zero if advanced to end of table */ - -int -hashtable_iterator_advance(struct hashtable_itr *itr); - -/*****************************************************************************/ -/* remove - remove current element and advance the iterator to the next element - * NB: if you need the value to free it, read it before - * removing. ie: beware memory leaks! - * returns zero if advanced to end of table */ - -int -hashtable_iterator_remove(struct hashtable_itr *itr); - -/*****************************************************************************/ -/* search - overwrite the supplied iterator, to point to the entry - * matching the supplied key. - h points to the hashtable to be searched. - * returns zero if not found. */ -int -hashtable_iterator_search(struct hashtable_itr *itr, - struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ -int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ -{ \ - return (hashtable_iterator_search(i,h,k)); \ -} - - - -#endif /* __HASHTABLE_ITR_CWC22__*/ - -/* - * Copyright (c) 2002, 2004, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER - * 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. -*/ diff --git a/mkfs.ubifs/hashtable/hashtable_private.h b/mkfs.ubifs/hashtable/hashtable_private.h deleted file mode 100644 index 3a558e6..0000000 --- a/mkfs.ubifs/hashtable/hashtable_private.h +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ - -#ifndef __HASHTABLE_PRIVATE_CWC22_H__ -#define __HASHTABLE_PRIVATE_CWC22_H__ - -#include "hashtable.h" - -/*****************************************************************************/ -struct entry -{ - void *k, *v; - unsigned int h; - struct entry *next; -}; - -struct hashtable { - unsigned int tablelength; - struct entry **table; - unsigned int entrycount; - unsigned int loadlimit; - unsigned int primeindex; - unsigned int (*hashfn) (void *k); - int (*eqfn) (void *k1, void *k2); -}; - -/*****************************************************************************/ -unsigned int -hash(struct hashtable *h, void *k); - -/*****************************************************************************/ -/* indexFor */ -static inline unsigned int -indexFor(unsigned int tablelength, unsigned int hashvalue) { - return (hashvalue % tablelength); -}; - -/* Only works if tablelength == 2^N */ -/*static inline unsigned int -indexFor(unsigned int tablelength, unsigned int hashvalue) -{ - return (hashvalue & (tablelength - 1u)); -} -*/ - -/*****************************************************************************/ -#define freekey(X) free(X) -/*define freekey(X) ; */ - - -/*****************************************************************************/ - -#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ - -/* - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * 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. - * - * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER - * 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. -*/ diff --git a/mkfs.ubifs/key.h b/mkfs.ubifs/key.h deleted file mode 100644 index d3a02d4..0000000 --- a/mkfs.ubifs/key.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * This file is part of UBIFS. - * - * Copyright (C) 2006-2008 Nokia Corporation. - * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Artem Bityutskiy (Битюцкий Артём) - * Adrian Hunter - */ - -/* - * This header contains various key-related definitions and helper function. - * UBIFS allows several key schemes, so we access key fields only via these - * helpers. At the moment only one key scheme is supported. - * - * Simple key scheme - * ~~~~~~~~~~~~~~~~~ - * - * Keys are 64-bits long. First 32-bits are inode number (parent inode number - * in case of direntry key). Next 3 bits are node type. The last 29 bits are - * 4KiB offset in case of inode node, and direntry hash in case of a direntry - * node. We use "r5" hash borrowed from reiserfs. - */ - -#ifndef __UBIFS_KEY_H__ -#define __UBIFS_KEY_H__ - -/** - * key_mask_hash - mask a valid hash value. - * @val: value to be masked - * - * We use hash values as offset in directories, so values %0 and %1 are - * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This - * function makes sure the reserved values are not used. - */ -static inline uint32_t key_mask_hash(uint32_t hash) -{ - hash &= UBIFS_S_KEY_HASH_MASK; - if (unlikely(hash <= 2)) - hash += 3; - return hash; -} - -/** - * key_r5_hash - R5 hash function (borrowed from reiserfs). - * @s: direntry name - * @len: name length - */ -static inline uint32_t key_r5_hash(const char *s, int len) -{ - uint32_t a = 0; - const signed char *str = (const signed char *)s; - - len = len; - while (*str) { - a += *str << 4; - a += *str >> 4; - a *= 11; - str++; - } - - return key_mask_hash(a); -} - -/** - * key_test_hash - testing hash function. - * @str: direntry name - * @len: name length - */ -static inline uint32_t key_test_hash(const char *str, int len) -{ - uint32_t a = 0; - - len = min_t(uint32_t, len, 4); - memcpy(&a, str, len); - return key_mask_hash(a); -} - -/** - * ino_key_init - initialize inode key. - * @c: UBIFS file-system description object - * @key: key to initialize - * @inum: inode number - */ -static inline void ino_key_init(union ubifs_key *key, ino_t inum) -{ - key->u32[0] = inum; - key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; -} - -/** - * dent_key_init - initialize directory entry key. - * @c: UBIFS file-system description object - * @key: key to initialize - * @inum: parent inode number - * @nm: direntry name and length - */ -static inline void dent_key_init(const struct ubifs_info *c, - union ubifs_key *key, ino_t inum, - const struct qstr *nm) -{ - uint32_t hash = c->key_hash(nm->name, nm->len); - - ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); - key->u32[0] = inum; - key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); -} - -/** - * data_key_init - initialize data key. - * @c: UBIFS file-system description object - * @key: key to initialize - * @inum: inode number - * @block: block number - */ -static inline void data_key_init(union ubifs_key *key, ino_t inum, - unsigned int block) -{ - ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); - key->u32[0] = inum; - key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); -} - -/** - * key_write - transform a key from in-memory format. - * @c: UBIFS file-system description object - * @from: the key to transform - * @to: the key to store the result - */ -static inline void key_write(const union ubifs_key *from, void *to) -{ - union ubifs_key *t = to; - - t->j32[0] = cpu_to_le32(from->u32[0]); - t->j32[1] = cpu_to_le32(from->u32[1]); - memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); -} - -/** - * key_write_idx - transform a key from in-memory format for the index. - * @c: UBIFS file-system description object - * @from: the key to transform - * @to: the key to store the result - */ -static inline void key_write_idx(const union ubifs_key *from, void *to) -{ - union ubifs_key *t = to; - - t->j32[0] = cpu_to_le32(from->u32[0]); - t->j32[1] = cpu_to_le32(from->u32[1]); -} - -/** - * keys_cmp - compare keys. - * @c: UBIFS file-system description object - * @key1: the first key to compare - * @key2: the second key to compare - * - * This function compares 2 keys and returns %-1 if @key1 is less than - * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. - */ -static inline int keys_cmp(const union ubifs_key *key1, - const union ubifs_key *key2) -{ - if (key1->u32[0] < key2->u32[0]) - return -1; - if (key1->u32[0] > key2->u32[0]) - return 1; - if (key1->u32[1] < key2->u32[1]) - return -1; - if (key1->u32[1] > key2->u32[1]) - return 1; - - return 0; -} - -#endif /* !__UBIFS_KEY_H__ */ diff --git a/mkfs.ubifs/lpt.c b/mkfs.ubifs/lpt.c deleted file mode 100644 index 6aa0b88..0000000 --- a/mkfs.ubifs/lpt.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * This file is part of UBIFS. - * - * Copyright (C) 2006, 2007 Nokia Corporation. - * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Adrian Hunter - * Artem Bityutskiy - */ - -#include "mkfs.ubifs.h" - -/** - * do_calc_lpt_geom - calculate sizes for the LPT area. - * @c: the UBIFS file-system description object - * - * Calculate the sizes of LPT bit fields, nodes, and tree, based on the - * properties of the flash and whether LPT is "big" (c->big_lpt). - */ -static void do_calc_lpt_geom(struct ubifs_info *c) -{ - int n, bits, per_leb_wastage; - long long sz, tot_wastage; - - c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; - - n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; - c->nnode_cnt = n; - while (n > 1) { - n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; - c->nnode_cnt += n; - } - - c->lpt_hght = 1; - n = UBIFS_LPT_FANOUT; - while (n < c->pnode_cnt) { - c->lpt_hght += 1; - n <<= UBIFS_LPT_FANOUT_SHIFT; - } - - c->space_bits = fls(c->leb_size) - 3; - c->lpt_lnum_bits = fls(c->lpt_lebs); - c->lpt_offs_bits = fls(c->leb_size - 1); - c->lpt_spc_bits = fls(c->leb_size); - - n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; - c->pcnt_bits = fls(n - 1); - - c->lnum_bits = fls(c->max_leb_cnt - 1); - - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + - (c->big_lpt ? c->pcnt_bits : 0) + - (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; - c->pnode_sz = (bits + 7) / 8; - - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + - (c->big_lpt ? c->pcnt_bits : 0) + - (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; - c->nnode_sz = (bits + 7) / 8; - - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + - c->lpt_lebs * c->lpt_spc_bits * 2; - c->ltab_sz = (bits + 7) / 8; - - bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + - c->lnum_bits * c->lsave_cnt; - c->lsave_sz = (bits + 7) / 8; - - /* Calculate the minimum LPT size */ - c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; - c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; - c->lpt_sz += c->ltab_sz; - c->lpt_sz += c->lsave_sz; - - /* Add wastage */ - sz = c->lpt_sz; - per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); - sz += per_leb_wastage; - tot_wastage = per_leb_wastage; - while (sz > c->leb_size) { - sz += per_leb_wastage; - sz -= c->leb_size; - tot_wastage += per_leb_wastage; - } - tot_wastage += ALIGN(sz, c->min_io_size) - sz; - c->lpt_sz += tot_wastage; -} - -/** - * calc_dflt_lpt_geom - calculate default LPT geometry. - * @c: the UBIFS file-system description object - * @main_lebs: number of main area LEBs is passed and returned here - * @big_lpt: whether the LPT area is "big" is returned here - * - * The size of the LPT area depends on parameters that themselves are dependent - * on the size of the LPT area. This function, successively recalculates the LPT - * area geometry until the parameters and resultant geometry are consistent. - * - * This function returns %0 on success and a negative error code on failure. - */ -int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) -{ - int i, lebs_needed; - long long sz; - - /* Start by assuming the minimum number of LPT LEBs */ - c->lpt_lebs = UBIFS_MIN_LPT_LEBS; - c->main_lebs = *main_lebs - c->lpt_lebs; - if (c->main_lebs <= 0) - return -EINVAL; - - /* And assume we will use the small LPT model */ - c->big_lpt = 0; - - /* - * Calculate the geometry based on assumptions above and then see if it - * makes sense - */ - do_calc_lpt_geom(c); - - /* Small LPT model must have lpt_sz < leb_size */ - if (c->lpt_sz > c->leb_size) { - /* Nope, so try again using big LPT model */ - c->big_lpt = 1; - do_calc_lpt_geom(c); - } - - /* Now check there are enough LPT LEBs */ - for (i = 0; i < 64 ; i++) { - sz = c->lpt_sz * 4; /* Allow 4 times the size */ - sz += c->leb_size - 1; - do_div(sz, c->leb_size); - lebs_needed = sz; - if (lebs_needed > c->lpt_lebs) { - /* Not enough LPT LEBs so try again with more */ - c->lpt_lebs = lebs_needed; - c->main_lebs = *main_lebs - c->lpt_lebs; - if (c->main_lebs <= 0) - return -EINVAL; - do_calc_lpt_geom(c); - continue; - } - if (c->ltab_sz > c->leb_size) { - err_msg("LPT ltab too big"); - return -EINVAL; - } - *main_lebs = c->main_lebs; - *big_lpt = c->big_lpt; - return 0; - } - return -EINVAL; -} - -/** - * pack_bits - pack bit fields end-to-end. - * @addr: address at which to pack (passed and next address returned) - * @pos: bit position at which to pack (passed and next position returned) - * @val: value to pack - * @nrbits: number of bits of value to pack (1-32) - */ -static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) -{ - uint8_t *p = *addr; - int b = *pos; - - if (b) { - *p |= ((uint8_t)val) << b; - nrbits += b; - if (nrbits > 8) { - *++p = (uint8_t)(val >>= (8 - b)); - if (nrbits > 16) { - *++p = (uint8_t)(val >>= 8); - if (nrbits > 24) { - *++p = (uint8_t)(val >>= 8); - if (nrbits > 32) - *++p = (uint8_t)(val >>= 8); - } - } - } - } else { - *p = (uint8_t)val; - if (nrbits > 8) { - *++p = (uint8_t)(val >>= 8); - if (nrbits > 16) { - *++p = (uint8_t)(val >>= 8); - if (nrbits > 24) - *++p = (uint8_t)(val >>= 8); - } - } - } - b = nrbits & 7; - if (b == 0) - p++; - *addr = p; - *pos = b; -} - -/** - * pack_pnode - pack all the bit fields of a pnode. - * @c: UBIFS file-system description object - * @buf: buffer into which to pack - * @pnode: pnode to pack - */ -static void pack_pnode(struct ubifs_info *c, void *buf, - struct ubifs_pnode *pnode) -{ - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; - int i, pos = 0; - uint16_t crc; - - pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); - if (c->big_lpt) - pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); - for (i = 0; i < UBIFS_LPT_FANOUT; i++) { - pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, - c->space_bits); - pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, - c->space_bits); - if (pnode->lprops[i].flags & LPROPS_INDEX) - pack_bits(&addr, &pos, 1, 1); - else - pack_bits(&addr, &pos, 0, 1); - } - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, - c->pnode_sz - UBIFS_LPT_CRC_BYTES); - addr = buf; - pos = 0; - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); -} - -/** - * pack_nnode - pack all the bit fields of a nnode. - * @c: UBIFS file-system description object - * @buf: buffer into which to pack - * @nnode: nnode to pack - */ -static void pack_nnode(struct ubifs_info *c, void *buf, - struct ubifs_nnode *nnode) -{ - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; - int i, pos = 0; - uint16_t crc; - - pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); - if (c->big_lpt) - pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); - for (i = 0; i < UBIFS_LPT_FANOUT; i++) { - int lnum = nnode->nbranch[i].lnum; - - if (lnum == 0) - lnum = c->lpt_last + 1; - pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); - pack_bits(&addr, &pos, nnode->nbranch[i].offs, - c->lpt_offs_bits); - } - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, - c->nnode_sz - UBIFS_LPT_CRC_BYTES); - addr = buf; - pos = 0; - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); -} - -/** - * pack_ltab - pack the LPT's own lprops table. - * @c: UBIFS file-system description object - * @buf: buffer into which to pack - * @ltab: LPT's own lprops table to pack - */ -static void pack_ltab(struct ubifs_info *c, void *buf, - struct ubifs_lpt_lprops *ltab) -{ - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; - int i, pos = 0; - uint16_t crc; - - pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); - for (i = 0; i < c->lpt_lebs; i++) { - pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); - pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); - } - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, - c->ltab_sz - UBIFS_LPT_CRC_BYTES); - addr = buf; - pos = 0; - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); -} - -/** - * pack_lsave - pack the LPT's save table. - * @c: UBIFS file-system description object - * @buf: buffer into which to pack - * @lsave: LPT's save table to pack - */ -static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) -{ - uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; - int i, pos = 0; - uint16_t crc; - - pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); - for (i = 0; i < c->lsave_cnt; i++) - pack_bits(&addr, &pos, lsave[i], c->lnum_bits); - crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, - c->lsave_sz - UBIFS_LPT_CRC_BYTES); - addr = buf; - pos = 0; - pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); -} - -/** - * set_ltab - set LPT LEB properties. - * @c: UBIFS file-system description object - * @lnum: LEB number - * @free: amount of free space - * @dirty: amount of dirty space - */ -static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) -{ - dbg_msg(3, "LEB %d free %d dirty %d to %d %d", - lnum, c->ltab[lnum - c->lpt_first].free, - c->ltab[lnum - c->lpt_first].dirty, free, dirty); - c->ltab[lnum - c->lpt_first].free = free; - c->ltab[lnum - c->lpt_first].dirty = dirty; -} - -/** - * calc_nnode_num - calculate nnode number. - * @row: the row in the tree (root is zero) - * @col: the column in the row (leftmost is zero) - * - * The nnode number is a number that uniquely identifies a nnode and can be used - * easily to traverse the tree from the root to that nnode. - * - * This function calculates and returns the nnode number for the nnode at @row - * and @col. - */ -static int calc_nnode_num(int row, int col) -{ - int num, bits; - - num = 1; - while (row--) { - bits = (col & (UBIFS_LPT_FANOUT - 1)); - col >>= UBIFS_LPT_FANOUT_SHIFT; - num <<= UBIFS_LPT_FANOUT_SHIFT; - num |= bits; - } - return num; -} - -/** - * create_lpt - create LPT. - * @c: UBIFS file-system description object - * - * This function returns %0 on success and a negative error code on failure. - */ -int create_lpt(struct ubifs_info *c) -{ - int lnum, err = 0, i, j, cnt, len, alen, row; - int blnum, boffs, bsz, bcnt; - struct ubifs_pnode *pnode = NULL; - struct ubifs_nnode *nnode = NULL; - void *buf = NULL, *p; - int *lsave = NULL; - - pnode = malloc(sizeof(struct ubifs_pnode)); - nnode = malloc(sizeof(struct ubifs_nnode)); - buf = malloc(c->leb_size); - lsave = malloc(sizeof(int) * c->lsave_cnt); - if (!pnode || !nnode || !buf || !lsave) { - err = -ENOMEM; - goto out; - } - memset(pnode, 0 , sizeof(struct ubifs_pnode)); - memset(nnode, 0 , sizeof(struct ubifs_nnode)); - - c->lscan_lnum = c->main_first; - - lnum = c->lpt_first; - p = buf; - len = 0; - /* Number of leaf nodes (pnodes) */ - cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; - //printf("pnode_cnt=%d\n",cnt); - - /* - * To calculate the internal node branches, we keep information about - * the level below. - */ - blnum = lnum; /* LEB number of level below */ - boffs = 0; /* Offset of level below */ - bcnt = cnt; /* Number of nodes in level below */ - bsz = c->pnode_sz; /* Size of nodes in level below */ - - /* Add pnodes */ - for (i = 0; i < cnt; i++) { - if (len + c->pnode_sz > c->leb_size) { - alen = ALIGN(len, c->min_io_size); - set_ltab(c, lnum, c->leb_size - alen, alen - len); - memset(p, 0xff, alen - len); - err = write_leb(lnum++, alen, buf); - if (err) - goto out; - p = buf; - len = 0; - } - /* Fill in the pnode */ - for (j = 0; j < UBIFS_LPT_FANOUT; j++) { - int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; - - if (k < c->main_lebs) - pnode->lprops[j] = c->lpt[k]; - else { - pnode->lprops[j].free = c->leb_size; - pnode->lprops[j].dirty = 0; - pnode->lprops[j].flags = 0; - } - } - pack_pnode(c, p, pnode); - p += c->pnode_sz; - len += c->pnode_sz; - /* - * pnodes are simply numbered left to right starting at zero, - * which means the pnode number can be used easily to traverse - * down the tree to the corresponding pnode. - */ - pnode->num += 1; - } - - row = c->lpt_hght - 1; - /* Add all nnodes, one level at a time */ - while (1) { - /* Number of internal nodes (nnodes) at next level */ - cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; - if (cnt == 0) - cnt = 1; - for (i = 0; i < cnt; i++) { - if (len + c->nnode_sz > c->leb_size) { - alen = ALIGN(len, c->min_io_size); - set_ltab(c, lnum, c->leb_size - alen, - alen - len); - memset(p, 0xff, alen - len); - err = write_leb(lnum++, alen, buf); - if (err) - goto out; - p = buf; - len = 0; - } - /* The root is on row zero */ - if (row == 0) { - c->lpt_lnum = lnum; - c->lpt_offs = len; - } - /* Set branches to the level below */ - for (j = 0; j < UBIFS_LPT_FANOUT; j++) { - if (bcnt) { - if (boffs + bsz > c->leb_size) { - blnum += 1; - boffs = 0; - } - nnode->nbranch[j].lnum = blnum; - nnode->nbranch[j].offs = boffs; - boffs += bsz; - bcnt--; - } else { - nnode->nbranch[j].lnum = 0; - nnode->nbranch[j].offs = 0; - } - } - nnode->num = calc_nnode_num(row, i); - pack_nnode(c, p, nnode); - p += c->nnode_sz; - len += c->nnode_sz; - } - /* Row zero is the top row */ - if (row == 0) - break; - /* Update the information about the level below */ - bcnt = cnt; - bsz = c->nnode_sz; - row -= 1; - } - - if (c->big_lpt) { - /* Need to add LPT's save table */ - if (len + c->lsave_sz > c->leb_size) { - alen = ALIGN(len, c->min_io_size); - set_ltab(c, lnum, c->leb_size - alen, alen - len); - memset(p, 0xff, alen - len); - err = write_leb(lnum++, alen, buf); - if (err) - goto out; - p = buf; - len = 0; - } - - c->lsave_lnum = lnum; - c->lsave_offs = len; - - for (i = 0; i < c->lsave_cnt; i++) - lsave[i] = c->main_first + i; - - pack_lsave(c, p, lsave); - p += c->lsave_sz; - len += c->lsave_sz; - } - - /* Need to add LPT's own LEB properties table */ - if (len + c->ltab_sz > c->leb_size) { - alen = ALIGN(len, c->min_io_size); - set_ltab(c, lnum, c->leb_size - alen, alen - len); - memset(p, 0xff, alen - len); - err = write_leb(lnum++, alen, buf); - if (err) - goto out; - p = buf; - len = 0; - } - - c->ltab_lnum = lnum; - c->ltab_offs = len; - - /* Update ltab before packing it */ - len += c->ltab_sz; - alen = ALIGN(len, c->min_io_size); - set_ltab(c, lnum, c->leb_size - alen, alen - len); - - pack_ltab(c, p, c->ltab); - p += c->ltab_sz; - - /* Write remaining buffer */ - memset(p, 0xff, alen - len); - err = write_leb(lnum, alen, buf); - if (err) - goto out; - - c->nhead_lnum = lnum; - c->nhead_offs = ALIGN(len, c->min_io_size); - - dbg_msg(1, "lpt_sz: %lld", c->lpt_sz); - dbg_msg(1, "space_bits: %d", c->space_bits); - dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); - dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); - dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); - dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); - dbg_msg(1, "lnum_bits: %d", c->lnum_bits); - dbg_msg(1, "pnode_sz: %d", c->pnode_sz); - dbg_msg(1, "nnode_sz: %d", c->nnode_sz); - dbg_msg(1, "ltab_sz: %d", c->ltab_sz); - dbg_msg(1, "lsave_sz: %d", c->lsave_sz); - dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); - dbg_msg(1, "lpt_hght: %d", c->lpt_hght); - dbg_msg(1, "big_lpt: %d", c->big_lpt); - dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); - dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); - dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); - if (c->big_lpt) - dbg_msg(1, "LPT lsave is at %d:%d", - c->lsave_lnum, c->lsave_offs); -out: - free(lsave); - free(buf); - free(nnode); - free(pnode); - return err; -} diff --git a/mkfs.ubifs/lpt.h b/mkfs.ubifs/lpt.h deleted file mode 100644 index 4cde59d..0000000 --- a/mkfs.ubifs/lpt.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation. - * Copyright (C) 2008 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 version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Artem Bityutskiy - * Adrian Hunter - */ - -#ifndef __UBIFS_LPT_H__ -#define __UBIFS_LPT_H__ - -int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); -int create_lpt(struct ubifs_info *c); - -#endif diff --git a/mkfs.ubifs/mkfs.ubifs.c b/mkfs.ubifs/mkfs.ubifs.c deleted file mode 100644 index ca17e2b..0000000 --- a/mkfs.ubifs/mkfs.ubifs.c +++ /dev/null @@ -1,2324 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation. - * Copyright (C) 2008 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 version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Adrian Hunter - * Artem Bityutskiy - * Zoltan Sogor - */ - -#define _XOPEN_SOURCE 500 /* For realpath() */ - -#include "mkfs.ubifs.h" -#include <crc32.h> -#include "common.h" - -/* Size (prime number) of hash table for link counting */ -#define HASH_TABLE_SIZE 10099 - -/* The node buffer must allow for worst case compression */ -#define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + \ - UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) - -/* Default time granularity in nanoseconds */ -#define DEFAULT_TIME_GRAN 1000000000 - -/** - * struct idx_entry - index entry. - * @next: next index entry (NULL at end of list) - * @prev: previous index entry (NULL at beginning of list) - * @key: key - * @name: directory entry name used for sorting colliding keys by name - * @lnum: LEB number - * @offs: offset - * @len: length - * - * The index is recorded as a linked list which is sorted and used to create - * the bottom level of the on-flash index tree. The remaining levels of the - * index tree are each built from the level below. - */ -struct idx_entry { - struct idx_entry *next; - struct idx_entry *prev; - union ubifs_key key; - char *name; - int lnum; - int offs; - int len; -}; - -/** - * struct inum_mapping - inode number mapping for link counting. - * @next: next inum_mapping (NULL at end of list) - * @prev: previous inum_mapping (NULL at beginning of list) - * @dev: source device on which the source inode number resides - * @inum: source inode number of the file - * @use_inum: target inode number of the file - * @use_nlink: number of links - * @path_name: a path name of the file - * @st: struct stat object containing inode attributes which have to be used - * when the inode is being created (actually only UID, GID, access - * mode, major and minor device numbers) - * - * If a file has more than one hard link, then the number of hard links that - * exist in the source directory hierarchy must be counted to exclude the - * possibility that the file is linked from outside the source directory - * hierarchy. - * - * The inum_mappings are stored in a hash_table of linked lists. - */ -struct inum_mapping { - struct inum_mapping *next; - struct inum_mapping *prev; - dev_t dev; - ino_t inum; - ino_t use_inum; - unsigned int use_nlink; - char *path_name; - struct stat st; -}; - -/* - * Because we copy functions from the kernel, we use a subset of the UBIFS - * file-system description object struct ubifs_info. - */ -struct ubifs_info info_; -static struct ubifs_info *c = &info_; -static libubi_t ubi; - -/* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ -int debug_level; -int verbose; -int yes; - -static char *root; -static int root_len; -static struct stat root_st; -static char *output; -static int out_fd; -static int out_ubi; -static int squash_owner; - -/* The 'head' (position) which nodes are written */ -static int head_lnum; -static int head_offs; -static int head_flags; - -/* The index list */ -static struct idx_entry *idx_list_first; -static struct idx_entry *idx_list_last; -static size_t idx_cnt; - -/* Global buffers */ -static void *leb_buf; -static void *node_buf; -static void *block_buf; - -/* Hash table for inode link counting */ -static struct inum_mapping **hash_table; - -/* Inode creation sequence number */ -static unsigned long long creat_sqnum; - -static const char *optstring = "d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq"; - -static const struct option longopts[] = { - {"root", 1, NULL, 'r'}, - {"min-io-size", 1, NULL, 'm'}, - {"leb-size", 1, NULL, 'e'}, - {"max-leb-cnt", 1, NULL, 'c'}, - {"output", 1, NULL, 'o'}, - {"devtable", 1, NULL, 'D'}, - {"yes", 0, NULL, 'y'}, - {"help", 0, NULL, 'h'}, - {"verbose", 0, NULL, 'v'}, - {"version", 0, NULL, 'V'}, - {"debug-level", 1, NULL, 'g'}, - {"jrn-size", 1, NULL, 'j'}, - {"reserved", 1, NULL, 'R'}, - {"compr", 1, NULL, 'x'}, - {"favor-percent", 1, NULL, 'X'}, - {"fanout", 1, NULL, 'f'}, - {"space-fixup", 0, NULL, 'F'}, - {"keyhash", 1, NULL, 'k'}, - {"log-lebs", 1, NULL, 'l'}, - {"orph-lebs", 1, NULL, 'p'}, - {"squash-uids" , 0, NULL, 'U'}, - {NULL, 0, NULL, 0} -}; - -static const char *helptext = -"Usage: mkfs.ubifs [OPTIONS] target\n" -"Make a UBIFS file system image from an existing directory tree\n\n" -"Examples:\n" -"Build file system from directory /opt/img, writting the result in the ubifs.img file\n" -"\tmkfs.ubifs -m 512 -e 128KiB -c 100 -r /opt/img ubifs.img\n" -"The same, but writting directly to an UBI volume\n" -"\tmkfs.ubifs -r /opt/img /dev/ubi0_0\n" -"Creating an empty UBIFS filesystem on an UBI volume\n" -"\tmkfs.ubifs /dev/ubi0_0\n\n" -"Options:\n" -"-r, -d, --root=DIR build file system from directory DIR\n" -"-m, --min-io-size=SIZE minimum I/O unit size\n" -"-e, --leb-size=SIZE logical erase block size\n" -"-c, --max-leb-cnt=COUNT maximum logical erase block count\n" -"-o, --output=FILE output to FILE\n" -"-j, --jrn-size=SIZE journal size\n" -"-R, --reserved=SIZE how much space should be reserved for the super-user\n" -"-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" -" \"none\" (default: \"lzo\")\n" -"-X, --favor-percent may only be used with favor LZO compression and defines\n" -" how many percent better zlib should compress to make\n" -" mkfs.ubifs use zlib instead of LZO (default 20%)\n" -"-f, --fanout=NUM fanout NUM (default: 8)\n" -"-F, --space-fixup file-system free space has to be fixed up on first mount\n" -" (requires kernel version 3.0 or greater)\n" -"-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" -"-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" -"-D, --devtable=FILE use device table FILE\n" -"-U, --squash-uids squash owners making all files owned by root\n" -"-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" -" debugging)\n" -"-y, --yes assume the answer is \"yes\" for all questions\n" -"-v, --verbose verbose operation\n" -"-V, --version display version information\n" -"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" -" 2 - files, 3 - more details)\n" -"-h, --help display this help text\n\n" -"Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" -"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" -"If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" -"for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" -"really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" -"compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" -"compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" -"or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" -"\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" -"default 20%.\n\n" -"The -F parameter is used to set the \"fix up free space\" flag in the superblock,\n" -"which forces UBIFS to \"fixup\" all the free space which it is going to use. This\n" -"option is useful to work-around the problem of double free space programming: if the\n" -"flasher program which flashes the UBI image is unable to skip NAND pages containing\n" -"only 0xFF bytes, the effect is that some NAND pages are written to twice - first time\n" -"when flashing the image and the second time when UBIFS is mounted and writes useful\n" -"data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n" -"flag may make the first mount very slow, because the \"free space fixup\" procedure\n" -"takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"; - -/** - * make_path - make a path name from a directory and a name. - * @dir: directory path name - * @name: name - */ -static char *make_path(const char *dir, const char *name) -{ - char *s; - - s = malloc(strlen(dir) + strlen(name) + 2); - if (!s) - return NULL; - strcpy(s, dir); - if (dir[strlen(dir) - 1] != '/') - strcat(s, "/"); - strcat(s, name); - return s; -} - -/** - * is_contained - determine if a file is beneath a directory. - * @file: file path name - * @dir: directory path name - * - * This function returns %1 if @file is accessible from the @dir directory and - * %0 otherwise. In case of error, returns %-1. - */ -static int is_contained(const char *file, const char *dir) -{ - char *real_file = NULL; - char *real_dir = NULL; - char *file_base, *copy; - int ret = -1; - - /* Make a copy of the file path because 'dirname()' can modify it */ - copy = strdup(file); - if (!copy) - return -1; - file_base = dirname(copy); - - /* Turn the paths into the canonical form */ - real_file = malloc(PATH_MAX); - if (!real_file) - goto out_free; - - real_dir = malloc(PATH_MAX); - if (!real_dir) - goto out_free; - - if (!realpath(file_base, real_file)) { - perror("Could not canonicalize file path"); - goto out_free; - } - if (!realpath(dir, real_dir)) { - perror("Could not canonicalize directory"); - goto out_free; - } - - ret = !!strstr(real_file, real_dir); - -out_free: - free(copy); - free(real_file); - free(real_dir); - return ret; -} - -/** - * calc_min_log_lebs - calculate the minimum number of log LEBs needed. - * @max_bud_bytes: journal size (buds only) - */ -static int calc_min_log_lebs(unsigned long long max_bud_bytes) -{ - int buds, log_lebs; - unsigned long long log_size; - - buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; - log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); - log_size *= buds; - log_size += ALIGN(UBIFS_CS_NODE_SZ + - UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), - c->min_io_size); - log_lebs = (log_size + c->leb_size - 1) / c->leb_size; - log_lebs += 1; - return log_lebs; -} - -/** - * add_space_overhead - add UBIFS overhead. - * @size: flash space which should be visible to the user - * - * UBIFS has overhead, and if we need to reserve @size bytes for the user data, - * we have to reserve more flash space, to compensate the overhead. This - * function calculates and returns the amount of physical flash space which - * should be reserved to provide @size bytes for the user. - */ -static long long add_space_overhead(long long size) -{ - int divisor, factor, f, max_idx_node_sz; - - /* - * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS - * function does. - */ - max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); - f = c->fanout > 3 ? c->fanout >> 1 : 2; - divisor = UBIFS_BLOCK_SIZE; - factor = UBIFS_MAX_DATA_NODE_SZ; - factor += (max_idx_node_sz * 3) / (f - 1); - size *= factor; - return size / divisor; -} - -static int validate_options(void) -{ - int tmp; - - if (!output) - return err_msg("no output file or UBI volume specified"); - if (root) { - tmp = is_contained(output, root); - if (tmp < 0) - return err_msg("failed to perform output file root check"); - else if (tmp) - return err_msg("output file cannot be in the UBIFS root " - "directory"); - } - if (!is_power_of_2(c->min_io_size)) - return err_msg("min. I/O unit size should be power of 2"); - if (c->leb_size < c->min_io_size) - return err_msg("min. I/O unit cannot be larger than LEB size"); - if (c->leb_size < UBIFS_MIN_LEB_SZ) - return err_msg("too small LEB size %d, minimum is %d", - c->leb_size, UBIFS_MIN_LEB_SZ); - if (c->leb_size % c->min_io_size) - return err_msg("LEB should be multiple of min. I/O units"); - if (c->leb_size % 8) - return err_msg("LEB size has to be multiple of 8"); - if (c->leb_size > UBIFS_MAX_LEB_SZ) - return err_msg("too large LEB size %d, maximum is %d", - c->leb_size, UBIFS_MAX_LEB_SZ); - if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) - return err_msg("too low max. count of LEBs, minimum is %d", - UBIFS_MIN_LEB_CNT); - if (c->fanout < UBIFS_MIN_FANOUT) - return err_msg("too low fanout, minimum is %d", - UBIFS_MIN_FANOUT); - tmp = c->leb_size - UBIFS_IDX_NODE_SZ; - tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; - if (c->fanout > tmp) - return err_msg("too high fanout, maximum is %d", tmp); - if (c->log_lebs < UBIFS_MIN_LOG_LEBS) - return err_msg("too few log LEBs, minimum is %d", - UBIFS_MIN_LOG_LEBS); - if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) - return err_msg("too many log LEBs, maximum is %d", - c->max_leb_cnt - UBIFS_MIN_LEB_CNT); - if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) - return err_msg("too few orphan LEBs, minimum is %d", - UBIFS_MIN_ORPH_LEBS); - if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) - return err_msg("too many orphan LEBs, maximum is %d", - c->max_leb_cnt - UBIFS_MIN_LEB_CNT); - tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; - tmp += c->orph_lebs + 4; - if (tmp > c->max_leb_cnt) - return err_msg("too low max. count of LEBs, expected at " - "least %d", tmp); - tmp = calc_min_log_lebs(c->max_bud_bytes); - if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) - return err_msg("too few log LEBs, expected at least %d", tmp); - if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) - return err_msg("too much reserved space %lld", c->rp_size); - return 0; -} - -/** - * get_multiplier - convert size specifier to an integer multiplier. - * @str: the size specifier string - * - * This function parses the @str size specifier, which may be one of - * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive - * size multiplier in case of success and %-1 in case of failure. - */ -static int get_multiplier(const char *str) -{ - if (!str) - return 1; - - /* Remove spaces before the specifier */ - while (*str == ' ' || *str == '\t') - str += 1; - - if (!strcmp(str, "KiB")) - return 1024; - if (!strcmp(str, "MiB")) - return 1024 * 1024; - if (!strcmp(str, "GiB")) - return 1024 * 1024 * 1024; - - return -1; -} - -/** - * get_bytes - convert a string containing amount of bytes into an - * integer. - * @str: string to convert - * - * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size - * specifiers. Returns positive amount of bytes in case of success and %-1 in - * case of failure. - */ -static long long get_bytes(const char *str) -{ - char *endp; - long long bytes = strtoull(str, &endp, 0); - - if (endp == str || bytes < 0) - return err_msg("incorrect amount of bytes: \"%s\"", str); - - if (*endp != '\0') { - int mult = get_multiplier(endp); - - if (mult == -1) - return err_msg("bad size specifier: \"%s\" - " - "should be 'KiB', 'MiB' or 'GiB'", endp); - bytes *= mult; - } - - return bytes; -} -/** - * open_ubi - open the UBI volume. - * @node: name of the UBI volume character device to fetch information about - * - * Returns %0 in case of success and %-1 in case of failure - */ -static int open_ubi(const char *node) -{ - struct stat st; - - if (stat(node, &st) || !S_ISCHR(st.st_mode)) - return -1; - - ubi = libubi_open(); - if (!ubi) - return -1; - if (ubi_get_vol_info(ubi, node, &c->vi)) - return -1; - if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) - return -1; - return 0; -} - -static int get_options(int argc, char**argv) -{ - int opt, i; - const char *tbl_file = NULL; - struct stat st; - char *endp; - - c->fanout = 8; - c->orph_lebs = 1; - c->key_hash = key_r5_hash; - c->key_len = UBIFS_SK_LEN; - c->default_compr = UBIFS_COMPR_LZO; - c->favor_percent = 20; - c->lsave_cnt = 256; - c->leb_size = -1; - c->min_io_size = -1; - c->max_leb_cnt = -1; - c->max_bud_bytes = -1; - c->log_lebs = -1; - - while (1) { - opt = getopt_long(argc, argv, optstring, longopts, &i); - if (opt == -1) - break; - switch (opt) { - case 'r': - case 'd': - root_len = strlen(optarg); - root = malloc(root_len + 2); - if (!root) - return err_msg("cannot allocate memory"); - - /* - * The further code expects '/' at the end of the root - * UBIFS directory on the host. - */ - memcpy(root, optarg, root_len); - if (root[root_len - 1] != '/') - root[root_len++] = '/'; - root[root_len] = 0; - - /* Make sure the root directory exists */ - if (stat(root, &st)) - return sys_err_msg("bad root directory '%s'", - root); - break; - case 'm': - c->min_io_size = get_bytes(optarg); - if (c->min_io_size <= 0) - return err_msg("bad min. I/O size"); - break; - case 'e': - c->leb_size = get_bytes(optarg); - if (c->leb_size <= 0) - return err_msg("bad LEB size"); - break; - case 'c': - c->max_leb_cnt = get_bytes(optarg); - if (c->max_leb_cnt <= 0) - return err_msg("bad maximum LEB count"); - break; - case 'o': - output = xstrdup(optarg); - break; - case 'D': - tbl_file = optarg; - if (stat(tbl_file, &st) < 0) - return sys_err_msg("bad device table file '%s'", - tbl_file); - break; - case 'y': - yes = 1; - break; - case 'h': - case '?': - printf("%s", helptext); - exit(0); - case 'v': - verbose = 1; - break; - case 'V': - common_print_version(); - exit(0); - case 'g': - debug_level = strtol(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - debug_level < 0 || debug_level > 3) - return err_msg("bad debugging level '%s'", - optarg); - break; - case 'f': - c->fanout = strtol(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || c->fanout <= 0) - return err_msg("bad fanout %s", optarg); - break; - case 'F': - c->space_fixup = 1; - break; - case 'l': - c->log_lebs = strtol(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) - return err_msg("bad count of log LEBs '%s'", - optarg); - break; - case 'p': - c->orph_lebs = strtol(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - c->orph_lebs <= 0) - return err_msg("bad orphan LEB count '%s'", - optarg); - break; - case 'k': - if (strcmp(optarg, "r5") == 0) { - c->key_hash = key_r5_hash; - c->key_hash_type = UBIFS_KEY_HASH_R5; - } else if (strcmp(optarg, "test") == 0) { - c->key_hash = key_test_hash; - c->key_hash_type = UBIFS_KEY_HASH_TEST; - } else - return err_msg("bad key hash"); - break; - case 'x': - if (strcmp(optarg, "favor_lzo") == 0) - c->favor_lzo = 1; - else if (strcmp(optarg, "zlib") == 0) - c->default_compr = UBIFS_COMPR_ZLIB; - else if (strcmp(optarg, "none") == 0) - c->default_compr = UBIFS_COMPR_NONE; - else if (strcmp(optarg, "lzo") != 0) - return err_msg("bad compressor name"); - break; - case 'X': - c->favor_percent = strtol(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - c->favor_percent <= 0 || c->favor_percent >= 100) - return err_msg("bad favor LZO percent '%s'", - optarg); - break; - case 'j': - c->max_bud_bytes = get_bytes(optarg); - if (c->max_bud_bytes <= 0) - return err_msg("bad maximum amount of buds"); - break; - case 'R': - c->rp_size = get_bytes(optarg); - if (c->rp_size < 0) - return err_msg("bad reserved bytes count"); - break; - case 'U': - squash_owner = 1; - break; - } - } - - if (optind != argc && !output) - output = xstrdup(argv[optind]); - - if (!output) - return err_msg("not output device or file specified"); - - out_ubi = !open_ubi(output); - - if (out_ubi) { - c->min_io_size = c->di.min_io_size; - c->leb_size = c->vi.leb_size; - if (c->max_leb_cnt == -1) - c->max_leb_cnt = c->vi.rsvd_lebs; - } - - if (c->min_io_size == -1) - return err_msg("min. I/O unit was not specified " - "(use -h for help)"); - - if (c->leb_size == -1) - return err_msg("LEB size was not specified (use -h for help)"); - - if (c->max_leb_cnt == -1) - return err_msg("Maximum count of LEBs was not specified " - "(use -h for help)"); - - if (c->max_bud_bytes == -1) { - int lebs; - - lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; - lebs -= c->orph_lebs; - if (c->log_lebs != -1) - lebs -= c->log_lebs; - else - lebs -= UBIFS_MIN_LOG_LEBS; - /* - * We do not know lprops geometry so far, so assume minimum - * count of lprops LEBs. - */ - lebs -= UBIFS_MIN_LPT_LEBS; - /* Make the journal about 12.5% of main area lebs */ - c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; - /* Make the max journal size 8MiB */ - if (c->max_bud_bytes > 8 * 1024 * 1024) - c->max_bud_bytes = 8 * 1024 * 1024; - if (c->max_bud_bytes < 4 * c->leb_size) - c->max_bud_bytes = 4 * c->leb_size; - } - - if (c->log_lebs == -1) { - c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); - c->log_lebs += 2; - } - - if (c->min_io_size < 8) - c->min_io_size = 8; - c->rp_size = add_space_overhead(c->rp_size); - - if (verbose) { - printf("mkfs.ubifs\n"); - printf("\troot: %s\n", root); - printf("\tmin_io_size: %d\n", c->min_io_size); - printf("\tleb_size: %d\n", c->leb_size); - printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); - printf("\toutput: %s\n", output); - printf("\tjrn_size: %llu\n", c->max_bud_bytes); - printf("\treserved: %llu\n", c->rp_size); - switch (c->default_compr) { - case UBIFS_COMPR_LZO: - printf("\tcompr: lzo\n"); - break; - case UBIFS_COMPR_ZLIB: - printf("\tcompr: zlib\n"); - break; - case UBIFS_COMPR_NONE: - printf("\tcompr: none\n"); - break; - } - printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? - "r5" : "test"); - printf("\tfanout: %d\n", c->fanout); - printf("\torph_lebs: %d\n", c->orph_lebs); - printf("\tspace_fixup: %d\n", c->space_fixup); - } - - if (validate_options()) - return -1; - - if (tbl_file && parse_devtable(tbl_file)) - return err_msg("cannot parse device table file '%s'", tbl_file); - - return 0; -} - -/** - * prepare_node - fill in the common header. - * @node: node - * @len: node length - */ -static void prepare_node(void *node, int len) -{ - uint32_t crc; - struct ubifs_ch *ch = node; - - ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); - ch->len = cpu_to_le32(len); - ch->group_type = UBIFS_NO_NODE_GROUP; - ch->sqnum = cpu_to_le64(++c->max_sqnum); - ch->padding[0] = ch->padding[1] = 0; - crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); - ch->crc = cpu_to_le32(crc); -} - -/** - * write_leb - copy the image of a LEB to the output target. - * @lnum: LEB number - * @len: length of data in the buffer - * @buf: buffer (must be at least c->leb_size bytes) - */ -int write_leb(int lnum, int len, void *buf) -{ - off_t pos = (off_t)lnum * c->leb_size; - - dbg_msg(3, "LEB %d len %d", lnum, len); - memset(buf + len, 0xff, c->leb_size - len); - if (out_ubi) - if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size)) - return sys_err_msg("ubi_leb_change_start failed"); - - if (lseek(out_fd, pos, SEEK_SET) != pos) - return sys_err_msg("lseek failed seeking %"PRIdoff_t, pos); - - if (write(out_fd, buf, c->leb_size) != c->leb_size) - return sys_err_msg("write failed writing %d bytes at pos %"PRIdoff_t, - c->leb_size, pos); - - return 0; -} - -/** - * write_empty_leb - copy the image of an empty LEB to the output target. - * @lnum: LEB number - */ -static int write_empty_leb(int lnum) -{ - return write_leb(lnum, 0, leb_buf); -} - -/** - * do_pad - pad a buffer to the minimum I/O size. - * @buf: buffer - * @len: buffer length - */ -static int do_pad(void *buf, int len) -{ - int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); - uint32_t crc; - - memset(buf + len, 0xff, alen - len); - pad_len = wlen - alen; - dbg_msg(3, "len %d pad_len %d", len, pad_len); - buf += alen; - if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { - struct ubifs_ch *ch = buf; - struct ubifs_pad_node *pad_node = buf; - - ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); - ch->node_type = UBIFS_PAD_NODE; - ch->group_type = UBIFS_NO_NODE_GROUP; - ch->padding[0] = ch->padding[1] = 0; - ch->sqnum = cpu_to_le64(0); - ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); - - pad_len -= UBIFS_PAD_NODE_SZ; - pad_node->pad_len = cpu_to_le32(pad_len); - - crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8, - UBIFS_PAD_NODE_SZ - 8); - ch->crc = cpu_to_le32(crc); - - memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); - } else if (pad_len > 0) - memset(buf, UBIFS_PADDING_BYTE, pad_len); - - return wlen; -} - -/** - * write_node - write a node to a LEB. - * @node: node - * @len: node length - * @lnum: LEB number - */ -static int write_node(void *node, int len, int lnum) -{ - prepare_node(node, len); - - memcpy(leb_buf, node, len); - - len = do_pad(leb_buf, len); - - return write_leb(lnum, len, leb_buf); -} - -/** - * calc_dark - calculate LEB dark space size. - * @c: the UBIFS file-system description object - * @spc: amount of free and dirty space in the LEB - * - * This function calculates amount of dark space in an LEB which has @spc bytes - * of free and dirty space. Returns the calculations result. - * - * Dark space is the space which is not always usable - it depends on which - * nodes are written in which order. E.g., if an LEB has only 512 free bytes, - * it is dark space, because it cannot fit a large data node. So UBIFS cannot - * count on this LEB and treat these 512 bytes as usable because it is not true - * if, for example, only big chunks of uncompressible data will be written to - * the FS. - */ -static int calc_dark(struct ubifs_info *c, int spc) -{ - if (spc < c->dark_wm) - return spc; - - /* - * If we have slightly more space then the dark space watermark, we can - * anyway safely assume it we'll be able to write a node of the - * smallest size there. - */ - if (spc - c->dark_wm < (int)MIN_WRITE_SZ) - return spc - MIN_WRITE_SZ; - - return c->dark_wm; -} - -/** - * set_lprops - set the LEB property values for a LEB. - * @lnum: LEB number - * @offs: end offset of data in the LEB - * @flags: LEB property flags - */ -static void set_lprops(int lnum, int offs, int flags) -{ - int i = lnum - c->main_first, free, dirty; - int a = max_t(int, c->min_io_size, 8); - - free = c->leb_size - ALIGN(offs, a); - dirty = c->leb_size - free - ALIGN(offs, 8); - dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, - flags); - if (i < c->main_lebs) { - c->lpt[i].free = free; - c->lpt[i].dirty = dirty; - c->lpt[i].flags = flags; - } - c->lst.total_free += free; - c->lst.total_dirty += dirty; - if (flags & LPROPS_INDEX) - c->lst.idx_lebs += 1; - else { - int spc; - - spc = free + dirty; - if (spc < c->dead_wm) - c->lst.total_dead += spc; - else - c->lst.total_dark += calc_dark(c, spc); - c->lst.total_used += c->leb_size - spc; - } -} - -/** - * add_to_index - add a node key and position to the index. - * @key: node key - * @lnum: node LEB number - * @offs: node offset - * @len: node length - */ -static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, - int len) -{ - struct idx_entry *e; - - dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); - e = malloc(sizeof(struct idx_entry)); - if (!e) - return err_msg("out of memory"); - e->next = NULL; - e->prev = idx_list_last; - e->key = *key; - e->name = name; - e->lnum = lnum; - e->offs = offs; - e->len = len; - if (!idx_list_first) - idx_list_first = e; - if (idx_list_last) - idx_list_last->next = e; - idx_list_last = e; - idx_cnt += 1; - return 0; -} - -/** - * flush_nodes - write the current head and move the head to the next LEB. - */ -static int flush_nodes(void) -{ - int len, err; - - if (!head_offs) - return 0; - len = do_pad(leb_buf, head_offs); - err = write_leb(head_lnum, len, leb_buf); - if (err) - return err; - set_lprops(head_lnum, head_offs, head_flags); - head_lnum += 1; - head_offs = 0; - return 0; -} - -/** - * reserve_space - reserve space for a node on the head. - * @len: node length - * @lnum: LEB number is returned here - * @offs: offset is returned here - */ -static int reserve_space(int len, int *lnum, int *offs) -{ - int err; - - if (len > c->leb_size - head_offs) { - err = flush_nodes(); - if (err) - return err; - } - *lnum = head_lnum; - *offs = head_offs; - head_offs += ALIGN(len, 8); - return 0; -} - -/** - * add_node - write a node to the head. - * @key: node key - * @node: node - * @len: node length - */ -static int add_node(union ubifs_key *key, char *name, void *node, int len) -{ - int err, lnum, offs; - - prepare_node(node, len); - - err = reserve_space(len, &lnum, &offs); - if (err) - return err; - - memcpy(leb_buf + offs, node, len); - memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); - - add_to_index(key, name, lnum, offs, len); - - return 0; -} - -/** - * add_inode_with_data - write an inode. - * @st: stat information of source inode - * @inum: target inode number - * @data: inode data (for special inodes e.g. symlink path etc) - * @data_len: inode data length - * @flags: source inode flags - */ -static int add_inode_with_data(struct stat *st, ino_t inum, void *data, - unsigned int data_len, int flags) -{ - struct ubifs_ino_node *ino = node_buf; - union ubifs_key key; - int len, use_flags = 0; - - if (c->default_compr != UBIFS_COMPR_NONE) - use_flags |= UBIFS_COMPR_FL; - if (flags & FS_COMPR_FL) - use_flags |= UBIFS_COMPR_FL; - if (flags & FS_SYNC_FL) - use_flags |= UBIFS_SYNC_FL; - if (flags & FS_IMMUTABLE_FL) - use_flags |= UBIFS_IMMUTABLE_FL; - if (flags & FS_APPEND_FL) - use_flags |= UBIFS_APPEND_FL; - if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) - use_flags |= UBIFS_DIRSYNC_FL; - - memset(ino, 0, UBIFS_INO_NODE_SZ); - - ino_key_init(&key, inum); - ino->ch.node_type = UBIFS_INO_NODE; - key_write(&key, &ino->key); - ino->creat_sqnum = cpu_to_le64(creat_sqnum); - ino->size = cpu_to_le64(st->st_size); - ino->nlink = cpu_to_le32(st->st_nlink); - /* - * The time fields are updated assuming the default time granularity - * of 1 second. To support finer granularities, utime() would be needed. - */ - ino->atime_sec = cpu_to_le64(st->st_atime); - ino->ctime_sec = cpu_to_le64(st->st_ctime); - ino->mtime_sec = cpu_to_le64(st->st_mtime); - ino->atime_nsec = 0; - ino->ctime_nsec = 0; - ino->mtime_nsec = 0; - ino->uid = cpu_to_le32(st->st_uid); - ino->gid = cpu_to_le32(st->st_gid); - ino->mode = cpu_to_le32(st->st_mode); - ino->flags = cpu_to_le32(use_flags); - ino->data_len = cpu_to_le32(data_len); - ino->compr_type = cpu_to_le16(c->default_compr); - if (data_len) - memcpy(&ino->data, data, data_len); - - len = UBIFS_INO_NODE_SZ + data_len; - - return add_node(&key, NULL, ino, len); -} - -/** - * add_inode - write an inode. - * @st: stat information of source inode - * @inum: target inode number - * @flags: source inode flags - */ -static int add_inode(struct stat *st, ino_t inum, int flags) -{ - return add_inode_with_data(st, inum, NULL, 0, flags); -} - -/** - * add_dir_inode - write an inode for a directory. - * @dir: source directory - * @inum: target inode number - * @size: target directory size - * @nlink: target directory link count - * @st: struct stat object describing attributes (except size and nlink) of the - * target inode to create - * - * Note, this function may be called with %NULL @dir, when the directory which - * is being created does not exist at the host file system, but is defined by - * the device table. - */ -static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, - struct stat *st) -{ - int fd, flags = 0; - - st->st_size = size; - st->st_nlink = nlink; - - if (dir) { - fd = dirfd(dir); - if (fd == -1) - return sys_err_msg("dirfd failed"); - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) - flags = 0; - } - - return add_inode(st, inum, flags); -} - -/** - * add_dev_inode - write an inode for a character or block device. - * @st: stat information of source inode - * @inum: target inode number - * @flags: source inode flags - */ -static int add_dev_inode(struct stat *st, ino_t inum, int flags) -{ - union ubifs_dev_desc dev; - - dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); - return add_inode_with_data(st, inum, &dev, 8, flags); -} - -/** - * add_symlink_inode - write an inode for a symbolic link. - * @path_name: path name of symbolic link inode itself (not the link target) - * @st: stat information of source inode - * @inum: target inode number - * @flags: source inode flags - */ -static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, - int flags) -{ - char buf[UBIFS_MAX_INO_DATA + 2]; - ssize_t len; - - /* Take the symlink as is */ - len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); - if (len <= 0) - return sys_err_msg("readlink failed for %s", path_name); - if (len > UBIFS_MAX_INO_DATA) - return err_msg("symlink too long for %s", path_name); - - return add_inode_with_data(st, inum, buf, len, flags); -} - -/** - * add_dent_node - write a directory entry node. - * @dir_inum: target inode number of directory - * @name: directory entry name - * @inum: target inode number of the directory entry - * @type: type of the target inode - */ -static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, - unsigned char type) -{ - struct ubifs_dent_node *dent = node_buf; - union ubifs_key key; - struct qstr dname; - char *kname; - int len; - - dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum, - (unsigned int)type, (unsigned long)dir_inum); - memset(dent, 0, UBIFS_DENT_NODE_SZ); - - dname.name = (void *)name; - dname.len = strlen(name); - - dent->ch.node_type = UBIFS_DENT_NODE; - - dent_key_init(c, &key, dir_inum, &dname); - key_write(&key, dent->key); - dent->inum = cpu_to_le64(inum); - dent->padding1 = 0; - dent->type = type; - dent->nlen = cpu_to_le16(dname.len); - memcpy(dent->name, dname.name, dname.len); - dent->name[dname.len] = '\0'; - - len = UBIFS_DENT_NODE_SZ + dname.len + 1; - - kname = strdup(name); - if (!kname) - return err_msg("cannot allocate memory"); - - return add_node(&key, kname, dent, len); -} - -/** - * lookup_inum_mapping - add an inode mapping for link counting. - * @dev: source device on which source inode number resides - * @inum: source inode number - */ -static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) -{ - struct inum_mapping *im; - unsigned int k; - - k = inum % HASH_TABLE_SIZE; - im = hash_table[k]; - while (im) { - if (im->dev == dev && im->inum == inum) - return im; - im = im->next; - } - im = malloc(sizeof(struct inum_mapping)); - if (!im) - return NULL; - im->next = hash_table[k]; - im->prev = NULL; - im->dev = dev; - im->inum = inum; - im->use_inum = 0; - im->use_nlink = 0; - if (hash_table[k]) - hash_table[k]->prev = im; - hash_table[k] = im; - return im; -} - -/** - * all_zero - does a buffer contain only zero bytes. - * @buf: buffer - * @len: buffer length - */ -static int all_zero(void *buf, int len) -{ - unsigned char *p = buf; - - while (len--) - if (*p++ != 0) - return 0; - return 1; -} - -/** - * add_file - write the data of a file and its inode to the output file. - * @path_name: source path name - * @st: source inode stat information - * @inum: target inode number - * @flags: source inode flags - */ -static int add_file(const char *path_name, struct stat *st, ino_t inum, - int flags) -{ - struct ubifs_data_node *dn = node_buf; - void *buf = block_buf; - loff_t file_size = 0; - ssize_t ret, bytes_read; - union ubifs_key key; - int fd, dn_len, err, compr_type, use_compr; - unsigned int block_no = 0; - size_t out_len; - - fd = open(path_name, O_RDONLY | O_LARGEFILE); - if (fd == -1) - return sys_err_msg("failed to open file '%s'", path_name); - do { - /* Read next block */ - bytes_read = 0; - do { - ret = read(fd, buf + bytes_read, - UBIFS_BLOCK_SIZE - bytes_read); - if (ret == -1) { - sys_err_msg("failed to read file '%s'", - path_name); - close(fd); - return 1; - } - bytes_read += ret; - } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); - if (bytes_read == 0) - break; - file_size += bytes_read; - /* Skip holes */ - if (all_zero(buf, bytes_read)) { - block_no += 1; - continue; - } - /* Make data node */ - memset(dn, 0, UBIFS_DATA_NODE_SZ); - data_key_init(&key, inum, block_no++); - dn->ch.node_type = UBIFS_DATA_NODE; - key_write(&key, &dn->key); - dn->size = cpu_to_le32(bytes_read); - out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; - if (c->default_compr == UBIFS_COMPR_NONE && - (flags & FS_COMPR_FL)) - use_compr = UBIFS_COMPR_LZO; - else - use_compr = c->default_compr; - compr_type = compress_data(buf, bytes_read, &dn->data, - &out_len, use_compr); - dn->compr_type = cpu_to_le16(compr_type); - dn_len = UBIFS_DATA_NODE_SZ + out_len; - /* Add data node to file system */ - err = add_node(&key, NULL, dn, dn_len); - if (err) { - close(fd); - return err; - } - } while (ret != 0); - if (close(fd) == -1) - return sys_err_msg("failed to close file '%s'", path_name); - if (file_size != st->st_size) - return err_msg("file size changed during writing file '%s'", - path_name); - return add_inode(st, inum, flags); -} - -/** - * add_non_dir - write a non-directory to the output file. - * @path_name: source path name - * @inum: target inode number is passed and returned here (due to link counting) - * @nlink: number of links if known otherwise zero - * @type: UBIFS inode type is returned here - * @st: struct stat object containing inode attributes which should be use when - * creating the UBIFS inode - */ -static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, - unsigned char *type, struct stat *st) -{ - int fd, flags = 0; - - dbg_msg(2, "%s", path_name); - - if (S_ISREG(st->st_mode)) { - fd = open(path_name, O_RDONLY); - if (fd == -1) - return sys_err_msg("failed to open file '%s'", - path_name); - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) - flags = 0; - if (close(fd) == -1) - return sys_err_msg("failed to close file '%s'", - path_name); - *type = UBIFS_ITYPE_REG; - } else if (S_ISCHR(st->st_mode)) - *type = UBIFS_ITYPE_CHR; - else if (S_ISBLK(st->st_mode)) - *type = UBIFS_ITYPE_BLK; - else if (S_ISLNK(st->st_mode)) - *type = UBIFS_ITYPE_LNK; - else if (S_ISSOCK(st->st_mode)) - *type = UBIFS_ITYPE_SOCK; - else if (S_ISFIFO(st->st_mode)) - *type = UBIFS_ITYPE_FIFO; - else - return err_msg("file '%s' has unknown inode type", path_name); - - if (nlink) - st->st_nlink = nlink; - else if (st->st_nlink > 1) { - /* - * If the number of links is greater than 1, then add this file - * later when we know the number of links that we actually have. - * For now, we just put the inode mapping in the hash table. - */ - struct inum_mapping *im; - - im = lookup_inum_mapping(st->st_dev, st->st_ino); - if (!im) - return err_msg("out of memory"); - if (im->use_nlink == 0) { - /* New entry */ - im->use_inum = *inum; - im->use_nlink = 1; - im->path_name = malloc(strlen(path_name) + 1); - if (!im->path_name) - return err_msg("out of memory"); - strcpy(im->path_name, path_name); - } else { - /* Existing entry */ - *inum = im->use_inum; - im->use_nlink += 1; - /* Return unused inode number */ - c->highest_inum -= 1; - } - - memcpy(&im->st, st, sizeof(struct stat)); - return 0; - } else - st->st_nlink = 1; - - creat_sqnum = ++c->max_sqnum; - - if (S_ISREG(st->st_mode)) - return add_file(path_name, st, *inum, flags); - if (S_ISCHR(st->st_mode)) - return add_dev_inode(st, *inum, flags); - if (S_ISBLK(st->st_mode)) - return add_dev_inode(st, *inum, flags); - if (S_ISLNK(st->st_mode)) - return add_symlink_inode(path_name, st, *inum, flags); - if (S_ISSOCK(st->st_mode)) - return add_inode(st, *inum, flags); - if (S_ISFIFO(st->st_mode)) - return add_inode(st, *inum, flags); - - return err_msg("file '%s' has unknown inode type", path_name); -} - -/** - * add_directory - write a directory tree to the output file. - * @dir_name: directory path name - * @dir_inum: UBIFS inode number of directory - * @st: directory inode statistics - * @non_existing: non-zero if this function is called for a directory which - * does not exist on the host file-system and it is being - * created because it is defined in the device table file. - */ -static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, - int non_existing) -{ - struct dirent *entry; - DIR *dir = NULL; - int err = 0; - loff_t size = UBIFS_INO_NODE_SZ; - char *name = NULL; - unsigned int nlink = 2; - struct path_htbl_element *ph_elt; - struct name_htbl_element *nh_elt = NULL; - struct hashtable_itr *itr; - ino_t inum; - unsigned char type; - unsigned long long dir_creat_sqnum = ++c->max_sqnum; - - dbg_msg(2, "%s", dir_name); - if (!non_existing) { - dir = opendir(dir_name); - if (dir == NULL) - return sys_err_msg("cannot open directory '%s'", - dir_name); - } - - /* - * Check whether this directory contains files which should be - * added/changed because they were specified in the device table. - * @ph_elt will be non-zero if yes. - */ - ph_elt = devtbl_find_path(dir_name + root_len - 1); - - /* - * Before adding the directory itself, we have to iterate over all the - * entries the device table adds to this directory and create them. - */ - for (; !non_existing;) { - struct stat dent_st; - - errno = 0; - entry = readdir(dir); - if (!entry) { - if (errno == 0) - break; - sys_err_msg("error reading directory '%s'", dir_name); - err = -1; - break; - } - - if (strcmp(".", entry->d_name) == 0) - continue; - if (strcmp("..", entry->d_name) == 0) - continue; - - if (ph_elt) - /* - * This directory was referred to at the device table - * file. Check if this directory entry is referred at - * too. - */ - nh_elt = devtbl_find_name(ph_elt, entry->d_name); - - /* - * We are going to create the file corresponding to this - * directory entry (@entry->d_name). We use 'struct stat' - * object to pass information about file attributes (actually - * only about UID, GID, mode, major, and minor). Get attributes - * for this file from the UBIFS rootfs on the host. - */ - free(name); - name = make_path(dir_name, entry->d_name); - if (lstat(name, &dent_st) == -1) { - sys_err_msg("lstat failed for file '%s'", name); - goto out_free; - } - - if (squash_owner) - /* - * Squash UID/GID. But the device table may override - * this. - */ - dent_st.st_uid = dent_st.st_gid = 0; - - /* - * And if the device table describes the same file, override - * the attributes. However, this is not allowed for device node - * files. - */ - if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) - goto out_free; - - inum = ++c->highest_inum; - - if (S_ISDIR(dent_st.st_mode)) { - err = add_directory(name, inum, &dent_st, 0); - if (err) - goto out_free; - nlink += 1; - type = UBIFS_ITYPE_DIR; - } else { - err = add_non_dir(name, &inum, 0, &type, &dent_st); - if (err) - goto out_free; - } - - err = add_dent_node(dir_inum, entry->d_name, inum, type); - if (err) - goto out_free; - size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, - 8); - } - - /* - * OK, we have created all files in this directory (recursively), let's - * also create all files described in the device table. All t - */ - nh_elt = first_name_htbl_element(ph_elt, &itr); - while (nh_elt) { - struct stat fake_st; - - /* - * We prohibit creating regular files using the device table, - * the device table may only re-define attributes of regular - * files. - */ - if (S_ISREG(nh_elt->mode)) { - err_msg("Bad device table entry %s/%s - it is " - "prohibited to create regular files " - "via device table", - strcmp(ph_elt->path, "/") ? ph_elt->path : "", - nh_elt->name); - goto out_free; - } - - memcpy(&fake_st, &root_st, sizeof(struct stat)); - fake_st.st_uid = nh_elt->uid; - fake_st.st_uid = nh_elt->uid; - fake_st.st_mode = nh_elt->mode; - fake_st.st_rdev = nh_elt->dev; - fake_st.st_nlink = 1; - - free(name); - name = make_path(dir_name, nh_elt->name); - inum = ++c->highest_inum; - - if (S_ISDIR(nh_elt->mode)) { - err = add_directory(name, inum, &fake_st, 1); - if (err) - goto out_free; - nlink += 1; - type = UBIFS_ITYPE_DIR; - } else { - err = add_non_dir(name, &inum, 0, &type, &fake_st); - if (err) - goto out_free; - } - - err = add_dent_node(dir_inum, nh_elt->name, inum, type); - if (err) - goto out_free; - size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); - - nh_elt = next_name_htbl_element(ph_elt, &itr); - } - - creat_sqnum = dir_creat_sqnum; - - err = add_dir_inode(dir, dir_inum, size, nlink, st); - if (err) - goto out_free; - - free(name); - if (!non_existing && closedir(dir) == -1) - return sys_err_msg("error closing directory '%s'", dir_name); - - return 0; - -out_free: - free(name); - if (!non_existing) - closedir(dir); - return -1; -} - -/** - * add_multi_linked_files - write all the files for which we counted links. - */ -static int add_multi_linked_files(void) -{ - int i, err; - - for (i = 0; i < HASH_TABLE_SIZE; i++) { - struct inum_mapping *im; - unsigned char type = 0; - - for (im = hash_table[i]; im; im = im->next) { - dbg_msg(2, "%s", im->path_name); - err = add_non_dir(im->path_name, &im->use_inum, - im->use_nlink, &type, &im->st); - if (err) - return err; - } - } - return 0; -} - -/** - * write_data - write the files and directories. - */ -static int write_data(void) -{ - int err; - mode_t mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - - if (root) { - err = stat(root, &root_st); - if (err) - return sys_err_msg("bad root file-system directory '%s'", - root); - } else { - root_st.st_mtime = time(NULL); - root_st.st_atime = root_st.st_ctime = root_st.st_mtime; - root_st.st_mode = mode; - } - - head_flags = 0; - err = add_directory(root, UBIFS_ROOT_INO, &root_st, !root); - if (err) - return err; - err = add_multi_linked_files(); - if (err) - return err; - return flush_nodes(); -} - -static int namecmp(const char *name1, const char *name2) -{ - size_t len1 = strlen(name1), len2 = strlen(name2); - size_t clen = (len1 < len2) ? len1 : len2; - int cmp; - - cmp = memcmp(name1, name2, clen); - if (cmp) - return cmp; - return (len1 < len2) ? -1 : 1; -} - -static int cmp_idx(const void *a, const void *b) -{ - const struct idx_entry *e1 = *(const struct idx_entry **)a; - const struct idx_entry *e2 = *(const struct idx_entry **)b; - int cmp; - - cmp = keys_cmp(&e1->key, &e2->key); - if (cmp) - return cmp; - return namecmp(e1->name, e2->name); -} - -/** - * add_idx_node - write an index node to the head. - * @node: index node - * @child_cnt: number of children of this index node - */ -static int add_idx_node(void *node, int child_cnt) -{ - int err, lnum, offs, len; - - len = ubifs_idx_node_sz(c, child_cnt); - - prepare_node(node, len); - - err = reserve_space(len, &lnum, &offs); - if (err) - return err; - - memcpy(leb_buf + offs, node, len); - memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); - - c->old_idx_sz += ALIGN(len, 8); - - dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, - c->old_idx_sz); - - /* The last index node written will be the root */ - c->zroot.lnum = lnum; - c->zroot.offs = offs; - c->zroot.len = len; - - return 0; -} - -/** - * write_index - write out the index. - */ -static int write_index(void) -{ - size_t sz, i, cnt, idx_sz, pstep, bcnt; - struct idx_entry **idx_ptr, **p; - struct ubifs_idx_node *idx; - struct ubifs_branch *br; - int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err; - - dbg_msg(1, "leaf node count: %zd", idx_cnt); - - /* Reset the head for the index */ - head_flags = LPROPS_INDEX; - /* Allocate index node */ - idx_sz = ubifs_idx_node_sz(c, c->fanout); - idx = malloc(idx_sz); - if (!idx) - return err_msg("out of memory"); - /* Make an array of pointers to sort the index list */ - sz = idx_cnt * sizeof(struct idx_entry *); - if (sz / sizeof(struct idx_entry *) != idx_cnt) { - free(idx); - return err_msg("index is too big (%zu entries)", idx_cnt); - } - idx_ptr = malloc(sz); - if (!idx_ptr) { - free(idx); - return err_msg("out of memory - needed %zu bytes for index", - sz); - } - idx_ptr[0] = idx_list_first; - for (i = 1; i < idx_cnt; i++) - idx_ptr[i] = idx_ptr[i - 1]->next; - qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); - /* Write level 0 index nodes */ - cnt = idx_cnt / c->fanout; - if (idx_cnt % c->fanout) - cnt += 1; - p = idx_ptr; - blnum = head_lnum; - boffs = head_offs; - for (i = 0; i < cnt; i++) { - /* - * Calculate the child count. All index nodes are created full - * except for the last index node on each row. - */ - if (i == cnt - 1) { - child_cnt = idx_cnt % c->fanout; - if (child_cnt == 0) - child_cnt = c->fanout; - } else - child_cnt = c->fanout; - memset(idx, 0, idx_sz); - idx->ch.node_type = UBIFS_IDX_NODE; - idx->child_cnt = cpu_to_le16(child_cnt); - idx->level = cpu_to_le16(0); - for (j = 0; j < child_cnt; j++, p++) { - br = ubifs_idx_branch(c, idx, j); - key_write_idx(&(*p)->key, &br->key); - br->lnum = cpu_to_le32((*p)->lnum); - br->offs = cpu_to_le32((*p)->offs); - br->len = cpu_to_le32((*p)->len); - } - add_idx_node(idx, child_cnt); - } - /* Write level 1 index nodes and above */ - level = 0; - pstep = 1; - while (cnt > 1) { - /* - * 'blast_len' is the length of the last index node in the level - * below. - */ - blast_len = ubifs_idx_node_sz(c, child_cnt); - /* 'bcnt' is the number of index nodes in the level below */ - bcnt = cnt; - /* 'cnt' is the number of index nodes in this level */ - cnt = (cnt + c->fanout - 1) / c->fanout; - if (cnt == 0) - cnt = 1; - level += 1; - /* - * The key of an index node is the same as the key of its first - * child. Thus we can get the key by stepping along the bottom - * level 'p' with an increasing large step 'pstep'. - */ - p = idx_ptr; - pstep *= c->fanout; - for (i = 0; i < cnt; i++) { - /* - * Calculate the child count. All index nodes are - * created full except for the last index node on each - * row. - */ - if (i == cnt - 1) { - child_cnt = bcnt % c->fanout; - if (child_cnt == 0) - child_cnt = c->fanout; - } else - child_cnt = c->fanout; - memset(idx, 0, idx_sz); - idx->ch.node_type = UBIFS_IDX_NODE; - idx->child_cnt = cpu_to_le16(child_cnt); - idx->level = cpu_to_le16(level); - for (j = 0; j < child_cnt; j++) { - size_t bn = i * c->fanout + j; - - /* - * The length of the index node in the level - * below is 'idx_sz' except when it is the last - * node on the row. i.e. all the others on the - * row are full. - */ - if (bn == bcnt - 1) - blen = blast_len; - else - blen = idx_sz; - /* - * 'blnum' and 'boffs' hold the position of the - * index node on the level below. - */ - if (boffs + blen > c->leb_size) { - blnum += 1; - boffs = 0; - } - /* - * Fill in the branch with the key and position - * of the index node from the level below. - */ - br = ubifs_idx_branch(c, idx, j); - key_write_idx(&(*p)->key, &br->key); - br->lnum = cpu_to_le32(blnum); - br->offs = cpu_to_le32(boffs); - br->len = cpu_to_le32(blen); - /* - * Step to the next index node on the level - * below. - */ - boffs += ALIGN(blen, 8); - p += pstep; - } - add_idx_node(idx, child_cnt); - } - } - - /* Free stuff */ - for (i = 0; i < idx_cnt; i++) - free(idx_ptr[i]); - free(idx_ptr); - free(idx); - - dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, - c->zroot.len); - - /* Set the index head */ - c->ihead_lnum = head_lnum; - c->ihead_offs = ALIGN(head_offs, c->min_io_size); - dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); - - /* Flush the last index LEB */ - err = flush_nodes(); - if (err) - return err; - - return 0; -} - -/** - * set_gc_lnum - set the LEB number reserved for the garbage collector. - */ -static int set_gc_lnum(void) -{ - int err; - - c->gc_lnum = head_lnum++; - err = write_empty_leb(c->gc_lnum); - if (err) - return err; - set_lprops(c->gc_lnum, 0, 0); - c->lst.empty_lebs += 1; - return 0; -} - -/** - * finalize_leb_cnt - now that we know how many LEBs we used. - */ -static int finalize_leb_cnt(void) -{ - c->leb_cnt = head_lnum; - if (c->leb_cnt > c->max_leb_cnt) - return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); - c->main_lebs = c->leb_cnt - c->main_first; - if (verbose) { - printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); - printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); - printf("\tlog_lebs: %d\n", c->log_lebs); - printf("\tlpt_lebs: %d\n", c->lpt_lebs); - printf("\torph_lebs: %d\n", c->orph_lebs); - printf("\tmain_lebs: %d\n", c->main_lebs); - printf("\tgc lebs: %d\n", 1); - printf("\tindex lebs: %d\n", c->lst.idx_lebs); - printf("\tleb_cnt: %d\n", c->leb_cnt); - } - dbg_msg(1, "total_free: %llu", c->lst.total_free); - dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); - dbg_msg(1, "total_used: %llu", c->lst.total_used); - dbg_msg(1, "total_dead: %llu", c->lst.total_dead); - dbg_msg(1, "total_dark: %llu", c->lst.total_dark); - dbg_msg(1, "index size: %llu", c->old_idx_sz); - dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); - return 0; -} - -/** - * write_super - write the super block. - */ -static int write_super(void) -{ - struct ubifs_sb_node sup; - - memset(&sup, 0, UBIFS_SB_NODE_SZ); - - sup.ch.node_type = UBIFS_SB_NODE; - sup.key_hash = c->key_hash_type; - sup.min_io_size = cpu_to_le32(c->min_io_size); - sup.leb_size = cpu_to_le32(c->leb_size); - sup.leb_cnt = cpu_to_le32(c->leb_cnt); - sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); - sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); - sup.log_lebs = cpu_to_le32(c->log_lebs); - sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); - sup.orph_lebs = cpu_to_le32(c->orph_lebs); - sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); - sup.fanout = cpu_to_le32(c->fanout); - sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); - sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); - sup.default_compr = cpu_to_le16(c->default_compr); - sup.rp_size = cpu_to_le64(c->rp_size); - sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); - uuid_generate_random(sup.uuid); - if (verbose) { - char s[40]; - - uuid_unparse_upper(sup.uuid, s); - printf("\tUUID: %s\n", s); - } - if (c->big_lpt) - sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); - if (c->space_fixup) - sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP); - - return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); -} - -/** - * write_master - write the master node. - */ -static int write_master(void) -{ - struct ubifs_mst_node mst; - int err; - - memset(&mst, 0, UBIFS_MST_NODE_SZ); - - mst.ch.node_type = UBIFS_MST_NODE; - mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); - mst.highest_inum = cpu_to_le64(c->highest_inum); - mst.cmt_no = cpu_to_le64(0); - mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); - mst.root_lnum = cpu_to_le32(c->zroot.lnum); - mst.root_offs = cpu_to_le32(c->zroot.offs); - mst.root_len = cpu_to_le32(c->zroot.len); - mst.gc_lnum = cpu_to_le32(c->gc_lnum); - mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); - mst.ihead_offs = cpu_to_le32(c->ihead_offs); - mst.index_size = cpu_to_le64(c->old_idx_sz); - mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); - mst.lpt_offs = cpu_to_le32(c->lpt_offs); - mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); - mst.nhead_offs = cpu_to_le32(c->nhead_offs); - mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); - mst.ltab_offs = cpu_to_le32(c->ltab_offs); - mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); - mst.lsave_offs = cpu_to_le32(c->lsave_offs); - mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); - mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); - mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); - mst.total_free = cpu_to_le64(c->lst.total_free); - mst.total_dirty = cpu_to_le64(c->lst.total_dirty); - mst.total_used = cpu_to_le64(c->lst.total_used); - mst.total_dead = cpu_to_le64(c->lst.total_dead); - mst.total_dark = cpu_to_le64(c->lst.total_dark); - mst.leb_cnt = cpu_to_le32(c->leb_cnt); - - err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); - if (err) - return err; - - err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); - if (err) - return err; - - return 0; -} - -/** - * write_log - write an empty log. - */ -static int write_log(void) -{ - struct ubifs_cs_node cs; - int err, i, lnum; - - lnum = UBIFS_LOG_LNUM; - - cs.ch.node_type = UBIFS_CS_NODE; - cs.cmt_no = cpu_to_le64(0); - - err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); - if (err) - return err; - - lnum += 1; - - for (i = 1; i < c->log_lebs; i++, lnum++) { - err = write_empty_leb(lnum); - if (err) - return err; - } - - return 0; -} - -/** - * write_lpt - write the LEB properties tree. - */ -static int write_lpt(void) -{ - int err, lnum; - - err = create_lpt(c); - if (err) - return err; - - lnum = c->nhead_lnum + 1; - while (lnum <= c->lpt_last) { - err = write_empty_leb(lnum++); - if (err) - return err; - } - - return 0; -} - -/** - * write_orphan_area - write an empty orphan area. - */ -static int write_orphan_area(void) -{ - int err, i, lnum; - - lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; - for (i = 0; i < c->orph_lebs; i++, lnum++) { - err = write_empty_leb(lnum); - if (err) - return err; - } - return 0; -} - -/** - * check_volume_empty - check if the UBI volume is empty. - * - * This function checks if the UBI volume is empty by looking if its LEBs are - * mapped or not. - * - * Returns %0 in case of success, %1 is the volume is not empty, - * and a negative error code in case of failure. - */ -static int check_volume_empty(void) -{ - int lnum, err; - - for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) { - err = ubi_is_mapped(out_fd, lnum); - if (err < 0) - return err; - if (err == 1) - return 1; - } - return 0; -} - -/** - * open_target - open the output target. - * - * Open the output target. The target can be an UBI volume - * or a file. - * - * Returns %0 in case of success and %-1 in case of failure. - */ -static int open_target(void) -{ - if (out_ubi) { - out_fd = open(output, O_RDWR | O_EXCL); - - if (out_fd == -1) - return sys_err_msg("cannot open the UBI volume '%s'", - output); - if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) - return sys_err_msg("ubi_set_property failed"); - - if (!yes && check_volume_empty()) { - if (!prompt("UBI volume is not empty. Format anyways?", false)) - return err_msg("UBI volume is not empty"); - } - } else { - out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - if (out_fd == -1) - return sys_err_msg("cannot create output file '%s'", - output); - } - return 0; -} - - -/** - * close_target - close the output target. - * - * Close the output target. If the target was an UBI - * volume, also close libubi. - * - * Returns %0 in case of success and %-1 in case of failure. - */ -static int close_target(void) -{ - if (ubi) - libubi_close(ubi); - if (out_fd >= 0 && close(out_fd) == -1) - return sys_err_msg("cannot close the target '%s'", output); - if (output) - free(output); - return 0; -} - -/** - * init - initialize things. - */ -static int init(void) -{ - int err, i, main_lebs, big_lpt = 0, sz; - - c->highest_inum = UBIFS_FIRST_INO; - - c->jhead_cnt = 1; - - main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; - main_lebs -= c->log_lebs + c->orph_lebs; - - err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); - if (err) - return err; - - c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + - c->orph_lebs; - head_lnum = c->main_first; - head_offs = 0; - - c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; - c->lpt_last = c->lpt_first + c->lpt_lebs - 1; - - c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); - if (!c->lpt) - return err_msg("unable to allocate LPT"); - - c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); - if (!c->ltab) - return err_msg("unable to allocate LPT ltab"); - - /* Initialize LPT's own lprops */ - for (i = 0; i < c->lpt_lebs; i++) { - c->ltab[i].free = c->leb_size; - c->ltab[i].dirty = 0; - } - - c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); - c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); - dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); - - leb_buf = malloc(c->leb_size); - if (!leb_buf) - return err_msg("out of memory"); - - node_buf = malloc(NODE_BUFFER_SIZE); - if (!node_buf) - return err_msg("out of memory"); - - block_buf = malloc(UBIFS_BLOCK_SIZE); - if (!block_buf) - return err_msg("out of memory"); - - sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; - hash_table = malloc(sz); - if (!hash_table) - return err_msg("out of memory"); - memset(hash_table, 0, sz); - - err = init_compression(); - if (err) - return err; - - return 0; -} - -static void destroy_hash_table(void) -{ - int i; - - for (i = 0; i < HASH_TABLE_SIZE; i++) { - struct inum_mapping *im, *q; - - for (im = hash_table[i]; im; ) { - q = im; - im = im->next; - free(q->path_name); - free(q); - } - } -} - -/** - * deinit - deinitialize things. - */ -static void deinit(void) -{ - free(c->lpt); - free(c->ltab); - free(leb_buf); - free(node_buf); - free(block_buf); - destroy_hash_table(); - free(hash_table); - destroy_compression(); - free_devtable_info(); -} - -/** - * mkfs - make the file system. - * - * Each on-flash area has a corresponding function to create it. The order of - * the functions reflects what information must be known to complete each stage. - * As a consequence the output file is not written sequentially. No effort has - * been made to make efficient use of memory or to allow for the possibility of - * incremental updates to the output file. - */ -static int mkfs(void) -{ - int err = 0; - - err = init(); - if (err) - goto out; - - err = write_data(); - if (err) - goto out; - - err = set_gc_lnum(); - if (err) - goto out; - - err = write_index(); - if (err) - goto out; - - err = finalize_leb_cnt(); - if (err) - goto out; - - err = write_lpt(); - if (err) - goto out; - - err = write_super(); - if (err) - goto out; - - err = write_master(); - if (err) - goto out; - - err = write_log(); - if (err) - goto out; - - err = write_orphan_area(); - -out: - deinit(); - return err; -} - -int main(int argc, char *argv[]) -{ - int err; - - err = get_options(argc, argv); - if (err) - return err; - - err = open_target(); - if (err) - return err; - - err = mkfs(); - if (err) { - close_target(); - return err; - } - - err = close_target(); - if (err) - return err; - - if (verbose) - printf("Success!\n"); - - return 0; -} diff --git a/mkfs.ubifs/mkfs.ubifs.h b/mkfs.ubifs/mkfs.ubifs.h deleted file mode 100644 index 944a159..0000000 --- a/mkfs.ubifs/mkfs.ubifs.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation. - * Copyright (C) 2008 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 version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Artem Bityutskiy - * Adrian Hunter - * Zoltan Sogor - */ - -#ifndef __MKFS_UBIFS_H__ -#define __MKFS_UBIFS_H__ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <limits.h> -#include <string.h> -#include <stdint.h> -#include <endian.h> -#include <byteswap.h> -#include <linux/types.h> -#include <linux/fs.h> - -#include <getopt.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> -#include <dirent.h> -#include <errno.h> -#include <libgen.h> -#include <ctype.h> -#include <uuid/uuid.h> -#include <sys/file.h> - -#include <mtd/ubifs-media.h> - -/* common.h requires the PROGRAM_NAME macro */ -#define PROGRAM_NAME "mkfs.ubifs" -#include "common.h" - -#include "libubi.h" -#include "defs.h" -#include "crc16.h" -#include "ubifs.h" -#include "key.h" -#include "lpt.h" -#include "compr.h" - -/* - * Compression flags are duplicated so that compr.c can compile without ubifs.h. - * Here we make sure they are the same. - */ -#if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE -#error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE -#endif -#if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO -#error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO -#endif -#if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB -#error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB -#endif - -extern int verbose; -extern int debug_level; - -#define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ - printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ -} while(0) - -#define err_msg(fmt, ...) ({ \ - fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ - -1; \ -}) - -#define sys_err_msg(fmt, ...) ({ \ - int err_ = errno; \ - fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ - fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ - -1; \ -}) - -/** - * struct path_htbl_element - an element of the path hash table. - * @path: the UBIFS path the element describes (the key of the element) - * @name_htbl: one more (nested) hash table containing names of all - * files/directories/device nodes which should be created at this - * path - * - * See device table handling for more information. - */ -struct path_htbl_element { - const char *path; - struct hashtable *name_htbl; -}; - -/** - * struct name_htbl_element - an element in the name hash table - * @name: name of the file/directory/device node (the key of the element) - * @mode: accsess rights and file type - * @uid: user ID - * @gid: group ID - * @major: device node major number - * @minor: device node minor number - * - * This is an element of the name hash table. Name hash table sits in the path - * hash table elements and describes file names which should be created/changed - * at this path. - */ -struct name_htbl_element { - const char *name; - unsigned int mode; - unsigned int uid; - unsigned int gid; - dev_t dev; -}; - -extern struct ubifs_info info_; - -struct hashtable_itr; - -int write_leb(int lnum, int len, void *buf); -int parse_devtable(const char *tbl_file); -struct path_htbl_element *devtbl_find_path(const char *path); -struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, - const char *name); -int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, - struct name_htbl_element *nh_elt); -struct name_htbl_element * -first_name_htbl_element(struct path_htbl_element *ph_elt, - struct hashtable_itr **itr); -struct name_htbl_element * -next_name_htbl_element(struct path_htbl_element *ph_elt, - struct hashtable_itr **itr); -void free_devtable_info(void); - -#endif diff --git a/mkfs.ubifs/ubifs.h b/mkfs.ubifs/ubifs.h deleted file mode 100644 index 434b651..0000000 --- a/mkfs.ubifs/ubifs.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - * This file is part of UBIFS. - * - * Copyright (C) 2008 Nokia Corporation. - * Copyright (C) 2008 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 version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Artem Bityutskiy - * Adrian Hunter - * Zoltan Sogor - */ - -#ifndef __UBIFS_H__ -#define __UBIFS_H__ - -/* Maximum logical eraseblock size in bytes */ -#define UBIFS_MAX_LEB_SZ (2*1024*1024) - -/* Minimum amount of data UBIFS writes to the flash */ -#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) - -/* Largest key size supported in this implementation */ -#define CUR_MAX_KEY_LEN UBIFS_SK_LEN - -/* - * There is no notion of truncation key because truncation nodes do not exist - * in TNC. However, when replaying, it is handy to introduce fake "truncation" - * keys for truncation nodes because the code becomes simpler. So we define - * %UBIFS_TRUN_KEY type. - */ -#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT - -/* The below union makes it easier to deal with keys */ -union ubifs_key -{ - uint8_t u8[CUR_MAX_KEY_LEN]; - uint32_t u32[CUR_MAX_KEY_LEN/4]; - uint64_t u64[CUR_MAX_KEY_LEN/8]; - __le32 j32[CUR_MAX_KEY_LEN/4]; -}; - -/* - * LEB properties flags. - * - * LPROPS_UNCAT: not categorized - * LPROPS_DIRTY: dirty > 0, not index - * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index - * LPROPS_FREE: free > 0, not empty, not index - * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs - * LPROPS_EMPTY: LEB is empty, not taken - * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken - * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken - * LPROPS_CAT_MASK: mask for the LEB categories above - * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) - * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) - */ -enum { - LPROPS_UNCAT = 0, - LPROPS_DIRTY = 1, - LPROPS_DIRTY_IDX = 2, - LPROPS_FREE = 3, - LPROPS_HEAP_CNT = 3, - LPROPS_EMPTY = 4, - LPROPS_FREEABLE = 5, - LPROPS_FRDI_IDX = 6, - LPROPS_CAT_MASK = 15, - LPROPS_TAKEN = 16, - LPROPS_INDEX = 32, -}; - -/** - * struct ubifs_lprops - logical eraseblock properties. - * @free: amount of free space in bytes - * @dirty: amount of dirty space in bytes - * @flags: LEB properties flags (see above) - */ -struct ubifs_lprops -{ - int free; - int dirty; - int flags; -}; - -/** - * struct ubifs_lpt_lprops - LPT logical eraseblock properties. - * @free: amount of free space in bytes - * @dirty: amount of dirty space in bytes - */ -struct ubifs_lpt_lprops -{ - int free; - int dirty; -}; - -struct ubifs_nnode; - -/** - * struct ubifs_cnode - LEB Properties Tree common node. - * @parent: parent nnode - * @cnext: next cnode to commit - * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) - * @iip: index in parent - * @level: level in the tree (zero for pnodes, greater than zero for nnodes) - * @num: node number - */ -struct ubifs_cnode -{ - struct ubifs_nnode *parent; - struct ubifs_cnode *cnext; - unsigned long flags; - int iip; - int level; - int num; -}; - -/** - * struct ubifs_pnode - LEB Properties Tree leaf node. - * @parent: parent nnode - * @cnext: next cnode to commit - * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) - * @iip: index in parent - * @level: level in the tree (always zero for pnodes) - * @num: node number - * @lprops: LEB properties array - */ -struct ubifs_pnode -{ - struct ubifs_nnode *parent; - struct ubifs_cnode *cnext; - unsigned long flags; - int iip; - int level; - int num; - struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; -}; - -/** - * struct ubifs_nbranch - LEB Properties Tree internal node branch. - * @lnum: LEB number of child - * @offs: offset of child - * @nnode: nnode child - * @pnode: pnode child - * @cnode: cnode child - */ -struct ubifs_nbranch -{ - int lnum; - int offs; - union - { - struct ubifs_nnode *nnode; - struct ubifs_pnode *pnode; - struct ubifs_cnode *cnode; - }; -}; - -/** - * struct ubifs_nnode - LEB Properties Tree internal node. - * @parent: parent nnode - * @cnext: next cnode to commit - * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) - * @iip: index in parent - * @level: level in the tree (always greater than zero for nnodes) - * @num: node number - * @nbranch: branches to child nodes - */ -struct ubifs_nnode -{ - struct ubifs_nnode *parent; - struct ubifs_cnode *cnext; - unsigned long flags; - int iip; - int level; - int num; - struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; -}; - -/** - * struct ubifs_lp_stats - statistics of eraseblocks in the main area. - * @empty_lebs: number of empty LEBs - * @taken_empty_lebs: number of taken LEBs - * @idx_lebs: number of indexing LEBs - * @total_free: total free space in bytes - * @total_dirty: total dirty space in bytes - * @total_used: total used space in bytes (includes only data LEBs) - * @total_dead: total dead space in bytes (includes only data LEBs) - * @total_dark: total dark space in bytes (includes only data LEBs) - */ -struct ubifs_lp_stats { - int empty_lebs; - int taken_empty_lebs; - int idx_lebs; - long long total_free; - long long total_dirty; - long long total_used; - long long total_dead; - long long total_dark; -}; - -/** - * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. - * @key: key - * @znode: znode address in memory - * @lnum: LEB number of the indexing node - * @offs: offset of the indexing node within @lnum - * @len: target node length - */ -struct ubifs_zbranch -{ - union ubifs_key key; - struct ubifs_znode *znode; - int lnum; - int offs; - int len; -}; - -/** - * struct ubifs_znode - in-memory representation of an indexing node. - * @parent: parent znode or NULL if it is the root - * @cnext: next znode to commit - * @flags: flags - * @time: last access time (seconds) - * @level: level of the entry in the TNC tree - * @child_cnt: count of child znodes - * @iip: index in parent's zbranch array - * @alt: lower bound of key range has altered i.e. child inserted at slot 0 - * @zbranch: array of znode branches (@c->fanout elements) - */ -struct ubifs_znode -{ - struct ubifs_znode *parent; - struct ubifs_znode *cnext; - unsigned long flags; - unsigned long time; - int level; - int child_cnt; - int iip; - int alt; -#ifdef CONFIG_UBIFS_FS_DEBUG - int lnum, offs, len; -#endif - struct ubifs_zbranch zbranch[]; -}; - -/** - * struct ubifs_info - UBIFS file-system description data structure - * (per-superblock). - * - * @highest_inum: highest used inode number - * @max_sqnum: current global sequence number - * - * @jhead_cnt: count of journal heads - * @max_bud_bytes: maximum number of bytes allowed in buds - * - * @zroot: zbranch which points to the root index node and znode - * @ihead_lnum: LEB number of index head - * @ihead_offs: offset of index head - * - * @log_lebs: number of logical eraseblocks in the log - * @lpt_lebs: number of LEBs used for lprops table - * @lpt_first: first LEB of the lprops table area - * @lpt_last: last LEB of the lprops table area - * @main_lebs: count of LEBs in the main area - * @main_first: first LEB of the main area - * @default_compr: default compression type - * @favor_lzo: favor LZO compression method - * @favor_percent: lzo vs. zlib threshold used in case favor LZO - * - * @key_hash_type: type of the key hash - * @key_hash: direntry key hash function - * @key_fmt: key format - * @key_len: key length - * @fanout: fanout of the index tree (number of links per indexing node) - * - * @min_io_size: minimal input/output unit size - * @leb_size: logical eraseblock size in bytes - * @leb_cnt: count of logical eraseblocks - * @max_leb_cnt: maximum count of logical eraseblocks - * - * @old_idx_sz: size of index on flash - * @lst: lprops statistics - * - * @dead_wm: LEB dead space watermark - * @dark_wm: LEB dark space watermark - * - * @di: UBI device information - * @vi: UBI volume information - * - * @gc_lnum: LEB number used for garbage collection - * @rp_size: reserved pool size - * - * @space_bits: number of bits needed to record free or dirty space - * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT - * @lpt_offs_bits: number of bits needed to record an offset in the LPT - * @lpt_spc_bits: number of bits needed to space in the LPT - * @pcnt_bits: number of bits needed to record pnode or nnode number - * @lnum_bits: number of bits needed to record LEB number - * @nnode_sz: size of on-flash nnode - * @pnode_sz: size of on-flash pnode - * @ltab_sz: size of on-flash LPT lprops table - * @lsave_sz: size of on-flash LPT save table - * @pnode_cnt: number of pnodes - * @nnode_cnt: number of nnodes - * @lpt_hght: height of the LPT - * - * @lpt_lnum: LEB number of the root nnode of the LPT - * @lpt_offs: offset of the root nnode of the LPT - * @nhead_lnum: LEB number of LPT head - * @nhead_offs: offset of LPT head - * @big_lpt: flag that LPT is too big to write whole during commit - * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up - * @lpt_sz: LPT size - * - * @ltab_lnum: LEB number of LPT's own lprops table - * @ltab_offs: offset of LPT's own lprops table - * @lpt: lprops table - * @ltab: LPT's own lprops table - * @lsave_cnt: number of LEB numbers in LPT's save table - * @lsave_lnum: LEB number of LPT's save table - * @lsave_offs: offset of LPT's save table - * @lsave: LPT's save table - * @lscan_lnum: LEB number of last LPT scan - */ -struct ubifs_info -{ - ino_t highest_inum; - unsigned long long max_sqnum; - - int jhead_cnt; - long long max_bud_bytes; - - struct ubifs_zbranch zroot; - int ihead_lnum; - int ihead_offs; - - int log_lebs; - int lpt_lebs; - int lpt_first; - int lpt_last; - int orph_lebs; - int main_lebs; - int main_first; - int default_compr; - int favor_lzo; - int favor_percent; - - uint8_t key_hash_type; - uint32_t (*key_hash)(const char *str, int len); - int key_fmt; - int key_len; - int fanout; - - int min_io_size; - int leb_size; - int leb_cnt; - int max_leb_cnt; - - unsigned long long old_idx_sz; - struct ubifs_lp_stats lst; - - int dead_wm; - int dark_wm; - - struct ubi_dev_info di; - struct ubi_vol_info vi; - - int gc_lnum; - long long rp_size; - - int space_bits; - int lpt_lnum_bits; - int lpt_offs_bits; - int lpt_spc_bits; - int pcnt_bits; - int lnum_bits; - int nnode_sz; - int pnode_sz; - int ltab_sz; - int lsave_sz; - int pnode_cnt; - int nnode_cnt; - int lpt_hght; - - int lpt_lnum; - int lpt_offs; - int nhead_lnum; - int nhead_offs; - int big_lpt; - int space_fixup; - long long lpt_sz; - - int ltab_lnum; - int ltab_offs; - struct ubifs_lprops *lpt; - struct ubifs_lpt_lprops *ltab; - int lsave_cnt; - int lsave_lnum; - int lsave_offs; - int *lsave; - int lscan_lnum; - -}; - -/** - * ubifs_idx_node_sz - return index node size. - * @c: the UBIFS file-system description object - * @child_cnt: number of children of this index node - */ -static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) -{ - return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; -} - -/** - * ubifs_idx_branch - return pointer to an index branch. - * @c: the UBIFS file-system description object - * @idx: index node - * @bnum: branch number - */ -static inline -struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, - const struct ubifs_idx_node *idx, - int bnum) -{ - return (struct ubifs_branch *)((void *)idx->branches + - (UBIFS_BRANCH_SZ + c->key_len) * bnum); -} - -#endif /* __UBIFS_H__ */ diff --git a/mtd_debug.c b/mtd_debug.c deleted file mode 100644 index d6993ce..0000000 --- a/mtd_debug.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (c) 2d3D, Inc. - * Written by Abraham vd Merwe <abraham@2d3d.co.za> - * All rights reserved. - * - * 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. - */ - -#define PROGRAM_NAME "mtd_debug" - -#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> -#include "common.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, off_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, 0666); - 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", __func__, size); - if (size != BUF_SIZE) { - size = BUF_SIZE; - fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, size, n); - perror("read()"); - goto err2; - } - err = write(outfd, buf, size); - if (err < 0) { - fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, 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 %zu bytes from address 0x%.8"PRIxoff_t" 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, off_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", __func__, size); - if (size != BUF_SIZE) { - size = BUF_SIZE; - fprintf(stderr, "%s: trying buffer size %#x\n", __func__, 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", __func__, 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", __func__, 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%.8"PRIxoff_t" 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_MLCNANDFLASH: - printf("MTD_MLCNANDFLASH"); - break; - case MTD_DATAFLASH: - printf("MTD_DATAFLASH"); - break; - case MTD_UBIVOLUME: - printf("MTD_UBIVOLUME"); - 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_WRITEABLE", MTD_WRITEABLE }, - { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, - { "MTD_NO_ERASE", MTD_NO_ERASE }, - { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK }, - { NULL, -1 } - }; - for (i = 0; flags[i].name != NULL; i++) { - if (mtd.flags & flags[i].value) { - if (first) { - printf("%s", 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.writesize = "); - printsize(mtd.writesize); - - printf("\nmtd.oobsize = "); - printsize(mtd.oobsize); - - printf("\nregions = %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(void) -{ - fprintf(stderr, "usage: %1$s info <device>\n" - " %1$s read <device> <offset> <len> <dest-filename>\n" - " %1$s write <device> <offset> <len> <source-filename>\n" - " %1$s erase <device> <offset> <len>\n", - PROGRAM_NAME); - exit(EXIT_FAILURE); -} - -int main(int argc, char *argv[]) -{ - int err = 0, fd; - int open_flag; - - enum { - OPT_INFO, - OPT_READ, - OPT_WRITE, - OPT_ERASE - } option = OPT_INFO; - - /* 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(); - - /* open device */ - open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR; - if ((fd = open(argv[2], O_SYNC | open_flag)) < 0) - errmsg_die("open()"); - - switch (option) { - case OPT_INFO: - showinfo(fd); - break; - case OPT_READ: - err = flash_to_file(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); - break; - case OPT_WRITE: - err = file_to_flash(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); - break; - case OPT_ERASE: - err = erase_flash(fd, strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0)); - break; - } - - /* close device */ - if (close(fd) < 0) - errmsg_die("close()"); - - return err; -} diff --git a/mtdpart.c b/mtdpart.c deleted file mode 100644 index 0016e34..0000000 --- a/mtdpart.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * mtdpart.c - * - * Copyright 2015 The Chromium OS Authors. - * - * 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 adds or removes a partition from an MTD device. - */ - -#define PROGRAM_NAME "mtdpart" - -#include <fcntl.h> -#include <getopt.h> -#include <limits.h> -#include <linux/blkpg.h> -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "common.h" - -static void display_help(int status) -{ - fprintf(status == EXIT_SUCCESS ? stdout : stderr, -"Usage: %1$s add [OPTION] <MTD_DEVICE> <PART_NAME> <START> <SIZE>\n" -" %1$s del [OPTION] <MTD_DEVICE> <PART_NUMBER>\n" -"Adds a partition to an MTD device, or remove an existing partition from it.\n" -"\n" -" -h, --help Display this help and exit\n" -" --version Output version information and exit\n" -"\n" -"START location and SIZE of the partition are in bytes. They should align on\n" -"eraseblock size.\n", - PROGRAM_NAME - ); - exit(status); -} - -static void display_version(void) -{ - printf("%1$s " VERSION "\n" - "\n" - "%1$s comes with NO WARRANTY\n" - "to the extent permitted by law.\n" - "\n" - "You may redistribute copies of %1$s\n" - "under the terms of the GNU General Public Licence.\n" - "See the file `COPYING' for more information.\n", - PROGRAM_NAME); - exit(EXIT_SUCCESS); -} - -/* Command arguments */ - -typedef enum { - COMMAND_ADD, - COMMAND_DEL -} command_type; - -static command_type command; /* add or del */ -static const char *mtddev; /* mtd device name */ -static const char *part_name; /* partition name */ -static int part_no; /* partition number */ -static long long start_addr; /* start address */ -static long long length; /* partition size */ - -static void process_options(int argc, char * const argv[]) -{ - int error = 0; - - for (;;) { - int option_index = 0; - static const char short_options[] = "h"; - static const struct option long_options[] = { - {"version", no_argument, 0, 0}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0}, - }; - - int c = getopt_long(argc, argv, short_options, - long_options, &option_index); - if (c == EOF) { - break; - } - - switch (c) { - case 0: - display_version(); - break; - case 'h': - display_help(EXIT_SUCCESS); - break; - case '?': - error++; - break; - } - } - - if ((argc - optind) < 3 || error) - display_help(EXIT_FAILURE); - - const char *s_command = argv[optind++]; - mtddev = argv[optind++]; - - if (strcmp(s_command, "del") == 0 && (argc - optind) == 1) { - const char *s_part_no = argv[optind++]; - - long tmp = simple_strtol(s_part_no, &error); - if (tmp < 0) - errmsg_die("Can't specify negative partition number: %ld", - tmp); - if (tmp > INT_MAX) - errmsg_die("Partition number exceeds INT_MAX: %ld", - tmp); - - part_no = tmp; - command = COMMAND_DEL; - } else if (strcmp(s_command, "add") == 0 && (argc - optind) == 3) { - const char *s_start; - const char *s_length; - - part_name = argv[optind++]; - s_start = argv[optind++]; - s_length = argv[optind++]; - - if (strlen(part_name) >= BLKPG_DEVNAMELTH) - errmsg_die("Partition name (%s) should be less than %d characters", - part_name, BLKPG_DEVNAMELTH); - - start_addr = simple_strtoll(s_start, &error); - if (start_addr < 0) - errmsg_die("Can't specify negative start offset: %lld", - start_addr); - - length = simple_strtoll(s_length, &error); - if (length < 0) - errmsg_die("Can't specify negative length: %lld", - length); - - command = COMMAND_ADD; - } else - display_help(EXIT_FAILURE); - - if (error) - display_help(EXIT_FAILURE); -} - - -int main(int argc, char * const argv[]) -{ - int fd; - struct blkpg_partition part; - struct blkpg_ioctl_arg arg; - - process_options(argc, argv); - - fd = open(mtddev, O_RDWR | O_CLOEXEC); - if (fd == -1) - sys_errmsg_die("Cannot open %s", mtddev); - - memset(&part, 0, sizeof(part)); - - memset(&arg, 0, sizeof(arg)); - arg.datalen = sizeof(part); - arg.data = ∂ - - switch (command) { - case COMMAND_ADD: - part.start = start_addr; - part.length = length; - strncpy(part.devname, part_name, sizeof(part.devname)); - arg.op = BLKPG_ADD_PARTITION; - break; - case COMMAND_DEL: - part.pno = part_no; - arg.op = BLKPG_DEL_PARTITION; - break; - } - - if (ioctl(fd, BLKPG, &arg)) - sys_errmsg_die("Failed to issue BLKPG ioctl"); - - close(fd); - - /* Exit happy */ - return EXIT_SUCCESS; -} diff --git a/nand-utils/load_nandsim.sh b/nand-utils/load_nandsim.sh new file mode 100755 index 0000000..4d9f0cb --- /dev/null +++ b/nand-utils/load_nandsim.sh @@ -0,0 +1,127 @@ +#!/bin/sh -euf + +# +# This script inserts NAND simulator module to emulate NAND flash of specified +# size. +# +# Author: Artem Bityutskiy +# + +fatal() +{ + echo "Error: $1" 1>&2 + exit 1 +} + +usage() +{ + cat 1>&2 <<EOF +Load NAND simulator to simulate flash of a specified size. + +Usage: ${0##*/} <size in MiB> <eraseblock size in KiB> \\ + <page size (512 or 2048)> + +Only the first parameter is mandatory. Default eraseblock size +is 16KiB, default NAND page size is 512 bytes. + +Only the following combinations are supported: +-------------------------------------------------- +| size (MiB) | EB size (KiB) | Page size (bytes) | +-------------------------------------------------- +| 16 | 16 | 512 | +| 32 | 16 | 512 | +| 64 | 16 | 512 | +| 128 | 16 | 512 | +| 256 | 16 | 512 | +| 64 | 64 | 2048 | +| 64 | 128 | 2048 | +| 64 | 256 | 2048 | +| 64 | 512 | 2048 | +| 128 | 64 | 2048 | +| 128 | 128 | 2048 | +| 128 | 256 | 2048 | +| 128 | 512 | 2048 | +| 256 | 64 | 2048 | +| 256 | 128 | 2048 | +| 256 | 256 | 2048 | +| 256 | 512 | 2048 | +| 512 | 64 | 2048 | +| 512 | 128 | 2048 | +| 512 | 256 | 2048 | +| 512 | 512 | 2048 | +| 1024 | 64 | 2048 | +| 1024 | 128 | 2048 | +| 1024 | 256 | 2048 | +| 1024 | 512 | 2048 | +-------------------------------------------------- +EOF +} + +if grep -q "NAND simulator" /proc/mtd; then + fatal "nandsim is already loaded" +fi + +if [ "$#" -lt "1" ]; then + usage + exit 1 +fi + +size="$1" +eb_size="$2" +page_size="$3" +if [ "$#" = "1" ]; then + eb_size="16" + page_size="512" +elif [ "$#" = "2" ]; then + page_size="512" +fi + +if [ "$page_size" -eq 512 ] && [ "$eb_size" -ne "16" ]; then + fatal "only 16KiB eraseblocks are possible in case of 512 bytes page" +fi + +first= +second= +third= +fourth= + +if [ "$page_size" -eq "512" ]; then + first="0x20" + case "$size" in + 16) second=0x33 ;; + 32) second=0x35 ;; + 64) second=0x36 ;; + 128) second=0x78 ;; + 256) second=0x71 ;; + *) fatal "flash size ${size}MiB is not supported, try 16, 32, 64 or 256" + esac +elif [ "$page_size" -eq "2048" ]; then + case "$eb_size" in + 64) fourth="0x05" ;; + 128) fourth="0x15" ;; + 256) fourth="0x25" ;; + 512) fourth="0x35" ;; + *) fatal "eraseblock ${eb_size}KiB is not supported" + esac + + + case "$size" in + 64) first="0x20"; second="0xa2"; third="0x00 ";; + 128) first="0xec"; second="0xa1"; third="0x00 ";; + 256) first="0x20"; second="0xaa"; third="0x00 ";; + 512) first="0x20"; second="0xac"; third="0x00 ";; + 1024) first="0xec"; second="0xd3"; third="0x51 ";; + *) fatal "unable to emulate ${size}MiB flash with ${eb_size}KiB eraseblock" + esac +else + fatal "bad NAND page size ${page_size}KiB, it has to be either 512 or 2048" +fi + +first="first_id_byte=$first" +second="second_id_byte=$second" +[ -z "$third" ] || third="third_id_byte=$third" +[ -z "$fourth" ] || fourth="fourth_id_byte=$fourth" + +modprobe nandsim "$first" "$second" $third $fourth + +echo "Loaded NAND simulator (${size}MiB, ${eb_size}KiB eraseblock, $page_size bytes NAND page)" diff --git a/nand-utils/nanddump.c b/nand-utils/nanddump.c new file mode 100644 index 0000000..4ee7ed4 --- /dev/null +++ b/nand-utils/nanddump.c @@ -0,0 +1,490 @@ +/* + * nanddump.c + * + * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) + * Steven J. Hill (sjhill@realitydiluted.com) + * + * 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 PROGRAM_NAME "nanddump" + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.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> +#include "common.h" +#include <libmtd.h> + +static void display_help(int status) +{ + fprintf(status == EXIT_SUCCESS ? stdout : stderr, +"Usage: %s [OPTIONS] MTD-device\n" +"Dumps the contents of a nand mtd partition.\n" +"\n" +"-h --help Display this help and exit\n" +" --version Output version information and exit\n" +" --bb=METHOD Choose bad block handling method (see below).\n" +"-a --forcebinary Force printing of binary data to tty\n" +"-c --canonicalprint Print canonical Hex+ASCII dump\n" +"-f file --file=file Dump to file\n" +"-l length --length=length Length\n" +"-n --noecc Read without error correction\n" +" --omitoob Omit OOB data (default)\n" +"-o --oob Dump OOB data\n" +"-p --prettyprint Print nice (hexdump)\n" +"-q --quiet Don't display progress and status messages\n" +"-s addr --startaddress=addr Start address\n" +"\n" +"--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':\n" +" padbad: dump flash data, substituting 0xFF for any bad blocks\n" +" dumpbad: dump flash data, including any bad blocks\n" +" skipbad: dump good data, completely skipping any bad blocks (default)\n", + PROGRAM_NAME); + exit(status); +} + +static void display_version(void) +{ + printf("%1$s " VERSION "\n" + "\n" + "%1$s comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of %1$s\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n", + PROGRAM_NAME); + exit(EXIT_SUCCESS); +} + +// Option variables + +static bool pretty_print = false; // print nice +static bool noecc = false; // don't error correct +static bool omitoob = true; // omit oob data +static long long start_addr; // start address +static long long length; // dump length +static const char *mtddev; // mtd device name +static const char *dumpfile; // dump file name +static bool quiet = false; // suppress diagnostic output +static bool canonical = false; // print nice + ascii +static bool forcebinary = false; // force printing binary to tty + +static enum { + padbad, // dump flash data, substituting 0xFF for any bad blocks + dumpbad, // dump flash data, including any bad blocks + skipbad, // dump good data, completely skipping any bad blocks +} bb_method = skipbad; + +static void process_options(int argc, char * const argv[]) +{ + int error = 0; + bool oob_default = true; + + for (;;) { + int option_index = 0; + static const char short_options[] = "hs:f:l:opqnca"; + static const struct option long_options[] = { + {"version", no_argument, 0, 0}, + {"bb", required_argument, 0, 0}, + {"omitoob", no_argument, 0, 0}, + {"help", no_argument, 0, 'h'}, + {"forcebinary", no_argument, 0, 'a'}, + {"canonicalprint", no_argument, 0, 'c'}, + {"file", required_argument, 0, 'f'}, + {"oob", no_argument, 0, 'o'}, + {"prettyprint", no_argument, 0, 'p'}, + {"startaddress", required_argument, 0, 's'}, + {"length", required_argument, 0, 'l'}, + {"noecc", no_argument, 0, 'n'}, + {"quiet", 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_version(); + break; + case 1: + /* Handle --bb=METHOD */ + if (!strcmp(optarg, "padbad")) + bb_method = padbad; + else if (!strcmp(optarg, "dumpbad")) + bb_method = dumpbad; + else if (!strcmp(optarg, "skipbad")) + bb_method = skipbad; + else + error++; + break; + case 2: /* --omitoob */ + if (oob_default) { + oob_default = false; + omitoob = true; + } else { + errmsg_die("--oob and --oomitoob are mutually exclusive"); + } + break; + } + break; + case 's': + start_addr = simple_strtoll(optarg, &error); + break; + case 'f': + dumpfile = xstrdup(optarg); + break; + case 'l': + length = simple_strtoll(optarg, &error); + break; + case 'o': + if (oob_default) { + oob_default = false; + omitoob = false; + } else { + errmsg_die("--oob and --oomitoob are mutually exclusive"); + } + break; + case 'a': + forcebinary = true; + break; + case 'c': + canonical = true; + case 'p': + pretty_print = true; + break; + case 'q': + quiet = true; + break; + case 'n': + noecc = true; + break; + case 'h': + display_help(EXIT_SUCCESS); + break; + case '?': + error++; + break; + } + } + + if (start_addr < 0) + errmsg_die("Can't specify negative offset with option -s: %lld", + start_addr); + + if (length < 0) + errmsg_die("Can't specify negative length with option -l: %lld", length); + + if (quiet && pretty_print) { + fprintf(stderr, "The quiet and pretty print options are mutually-\n" + "exclusive. Choose one or the other.\n"); + exit(EXIT_FAILURE); + } + + if (forcebinary && pretty_print) { + fprintf(stderr, "The forcebinary and pretty print options are\n" + "mutually-exclusive. Choose one or the " + "other.\n"); + exit(EXIT_FAILURE); + } + + if ((argc - optind) != 1 || error) + display_help(EXIT_FAILURE); + + mtddev = argv[optind]; +} + +#define PRETTY_ROW_SIZE 16 +#define PRETTY_BUF_LEN 80 + +/** + * pretty_dump_to_buffer - formats a blob of data to "hex ASCII" in memory + * @buf: data blob to dump + * @len: number of bytes in the @buf + * @linebuf: where to put the converted data + * @linebuflen: total size of @linebuf, including space for terminating NULL + * @pagedump: true - dumping as page format; false - dumping as OOB format + * @ascii: dump ascii formatted data next to hexdump + * @prefix: address to print before line in a page dump, ignored if !pagedump + * + * pretty_dump_to_buffer() works on one "line" of output at a time, i.e., + * PRETTY_ROW_SIZE bytes of input data converted to hex + ASCII output. + * + * Given a buffer of unsigned char data, pretty_dump_to_buffer() converts the + * input data to a hex/ASCII dump at the supplied memory location. A prefix + * is included based on whether we are dumping page or OOB data. The converted + * output is always NULL-terminated. + * + * e.g. + * pretty_dump_to_buffer(data, data_len, prettybuf, linelen, true, + * false, 256); + * produces: + * 0x00000100: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f + * NOTE: This function was adapted from linux kernel, "lib/hexdump.c" + */ +static void pretty_dump_to_buffer(const unsigned char *buf, size_t len, + char *linebuf, size_t linebuflen, bool pagedump, bool ascii, + unsigned long long prefix) +{ + static const char hex_asc[] = "0123456789abcdef"; + unsigned char ch; + unsigned int j, lx = 0, ascii_column; + + if (pagedump) + lx += sprintf(linebuf, "0x%.8llx: ", prefix); + else + lx += sprintf(linebuf, " OOB Data: "); + + if (!len) + goto nil; + if (len > PRETTY_ROW_SIZE) /* limit to one line at a time */ + len = PRETTY_ROW_SIZE; + + for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { + ch = buf[j]; + linebuf[lx++] = hex_asc[(ch & 0xf0) >> 4]; + linebuf[lx++] = hex_asc[ch & 0x0f]; + linebuf[lx++] = ' '; + } + if (j) + lx--; + + ascii_column = 3 * PRETTY_ROW_SIZE + 14; + + if (!ascii) + goto nil; + + /* Spacing between hex and ASCII - ensure at least one space */ + lx += sprintf(linebuf + lx, "%*s", + MAX((int)MIN(linebuflen, ascii_column) - 1 - lx, 1), + " "); + + linebuf[lx++] = '|'; + for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) + linebuf[lx++] = (isascii(buf[j]) && isprint(buf[j])) ? buf[j] + : '.'; + linebuf[lx++] = '|'; +nil: + linebuf[lx++] = '\n'; + linebuf[lx++] = '\0'; +} + + +/* + * Main program + */ +int main(int argc, char * const argv[]) +{ + long long ofs, end_addr = 0; + long long blockstart = 1; + int i, fd, ofd = 0, bs, badblock = 0; + struct mtd_dev_info mtd; + char pretty_buf[PRETTY_BUF_LEN]; + int firstblock = 1; + struct mtd_ecc_stats stat1, stat2; + bool eccstats = false; + unsigned char *readbuf = NULL, *oobbuf = NULL; + libmtd_t mtd_desc; + + process_options(argc, argv); + + /* Initialize libmtd */ + mtd_desc = libmtd_open(); + if (!mtd_desc) + return errmsg("can't initialize libmtd"); + + /* Open MTD device */ + if ((fd = open(mtddev, O_RDONLY)) == -1) { + perror(mtddev); + exit(EXIT_FAILURE); + } + + /* Fill in MTD device capability structure */ + if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) + return errmsg("mtd_get_dev_info failed"); + + /* Allocate buffers */ + oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); + readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); + + if (noecc) { + if (ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW) != 0) { + perror("MTDFILEMODE"); + goto closeall; + } + } else { + /* check if we can read ecc stats */ + if (!ioctl(fd, ECCGETSTATS, &stat1)) { + eccstats = true; + if (!quiet) { + fprintf(stderr, "ECC failed: %d\n", stat1.failed); + fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); + fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); + fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); + } + } else + perror("No ECC status information available"); + } + + /* 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(dumpfile); + goto closeall; + } + + if (!pretty_print && !forcebinary && isatty(ofd)) { + fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" + "or '--forcebinary' to override.\n"); + goto closeall; + } + + /* Initialize start/end addresses and block size */ + if (start_addr & (mtd.min_io_size - 1)) { + fprintf(stderr, "the start address (-s parameter) is not page-aligned!\n" + "The pagesize of this NAND Flash is 0x%x.\n", + mtd.min_io_size); + goto closeall; + } + if (length) + end_addr = start_addr + length; + if (!length || end_addr > mtd.size) + end_addr = mtd.size; + + bs = mtd.min_io_size; + + /* Print informative message */ + if (!quiet) { + fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", + mtd.eb_size, mtd.min_io_size, mtd.oob_size); + fprintf(stderr, + "Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", + start_addr, end_addr); + } + + /* Dump the flash contents */ + for (ofs = start_addr; ofs < end_addr; ofs += bs) { + /* Check for bad block */ + if (bb_method == dumpbad) { + badblock = 0; + } else if (blockstart != (ofs & (~mtd.eb_size + 1)) || + firstblock) { + blockstart = ofs & (~mtd.eb_size + 1); + firstblock = 0; + if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { + errmsg("libmtd: mtd_is_bad"); + goto closeall; + } + } + + if (badblock) { + /* skip bad block, increase end_addr */ + if (bb_method == skipbad) { + end_addr += mtd.eb_size; + ofs += mtd.eb_size - bs; + if (end_addr > mtd.size) + end_addr = mtd.size; + continue; + } + memset(readbuf, 0xff, bs); + } else { + /* Read page data and exit on failure */ + if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { + errmsg("mtd_read"); + goto closeall; + } + } + + /* ECC stats available ? */ + if (eccstats) { + if (ioctl(fd, ECCGETSTATS, &stat2)) { + perror("ioctl(ECCGETSTATS)"); + goto closeall; + } + if (stat1.failed != stat2.failed) + fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" + " at offset 0x%08llx\n", + stat2.failed - stat1.failed, ofs); + if (stat1.corrected != stat2.corrected) + fprintf(stderr, "ECC: %d corrected bitflip(s) at" + " offset 0x%08llx\n", + stat2.corrected - stat1.corrected, ofs); + stat1 = stat2; + } + + /* Write out page data */ + if (pretty_print) { + for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { + pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, + pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); + write(ofd, pretty_buf, strlen(pretty_buf)); + } + } else + write(ofd, readbuf, bs); + + if (omitoob) + continue; + + if (badblock) { + memset(oobbuf, 0xff, mtd.oob_size); + } else { + /* Read OOB data and exit on failure */ + if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { + errmsg("libmtd: mtd_read_oob"); + goto closeall; + } + } + + /* Write out OOB data */ + if (pretty_print) { + for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { + pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, + pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); + write(ofd, pretty_buf, strlen(pretty_buf)); + } + } else + write(ofd, oobbuf, mtd.oob_size); + } + + /* Close the output file and MTD device, free memory */ + close(fd); + close(ofd); + free(oobbuf); + free(readbuf); + + /* Exit happy */ + return EXIT_SUCCESS; + +closeall: + close(fd); + close(ofd); + free(oobbuf); + free(readbuf); + exit(EXIT_FAILURE); +} diff --git a/nand-utils/nandtest.c b/nand-utils/nandtest.c new file mode 100644 index 0000000..0805387 --- /dev/null +++ b/nand-utils/nandtest.c @@ -0,0 +1,313 @@ +#define PROGRAM_NAME "nandtest" + +#include <ctype.h> +#include <errno.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" + +void usage(int status) +{ + fprintf(status ? stderr : stdout, + "usage: %s [OPTIONS] <device>\n\n" + " -h, --help Display this help output\n" + " -m, --markbad Mark blocks bad if they appear so\n" + " -s, --seed Supply random seed\n" + " -p, --passes Number of passes\n" + " -r <n>, --reads=<n> Read & check <n> times per pass\n" + " -o, --offset Start offset on flash\n" + " -l, --length Length of flash to test\n" + " -k, --keep Restore existing contents after test\n", + PROGRAM_NAME); + exit(status); +} + +struct mtd_info_user meminfo; +struct mtd_ecc_stats oldstats, newstats; +int fd; +int markbad=0; +int seed; + +int read_and_compare(loff_t ofs, unsigned char *data, unsigned char *rbuf) +{ + ssize_t len; + int i; + + len = pread(fd, rbuf, meminfo.erasesize, ofs); + if (len < meminfo.erasesize) { + printf("\n"); + if (len) + fprintf(stderr, "Short read (%zd bytes)\n", len); + else + perror("read"); + exit(1); + } + + if (ioctl(fd, ECCGETSTATS, &newstats)) { + printf("\n"); + perror("ECCGETSTATS"); + close(fd); + exit(1); + } + + if (newstats.corrected > oldstats.corrected) { + printf("\n %d bit(s) ECC corrected at %08x\n", + newstats.corrected - oldstats.corrected, + (unsigned) ofs); + oldstats.corrected = newstats.corrected; + } + if (newstats.failed > oldstats.failed) { + printf("\nECC failed at %08x\n", (unsigned) ofs); + oldstats.failed = newstats.failed; + } + + printf("\r%08x: checking...", (unsigned)ofs); + fflush(stdout); + + if (memcmp(data, rbuf, meminfo.erasesize)) { + printf("\n"); + fprintf(stderr, "compare failed. seed %d\n", seed); + for (i=0; i<meminfo.erasesize; i++) { + if (data[i] != rbuf[i]) + printf("Byte 0x%x is %02x should be %02x\n", + i, rbuf[i], data[i]); + } + return 1; + } + return 0; +} + +int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, int nr_reads) +{ + struct erase_info_user er; + ssize_t len; + int i, read_errs = 0; + + printf("\r%08x: erasing... ", (unsigned)ofs); + fflush(stdout); + + er.start = ofs; + er.length = meminfo.erasesize; + + if (ioctl(fd, MEMERASE, &er)) { + perror("MEMERASE"); + if (markbad) { + printf("Mark block bad at %08lx\n", (long)ofs); + ioctl(fd, MEMSETBADBLOCK, &ofs); + } + return 1; + } + + printf("\r%08x: writing...", (unsigned)ofs); + fflush(stdout); + + len = pwrite(fd, data, meminfo.erasesize, ofs); + if (len < 0) { + printf("\n"); + perror("write"); + if (markbad) { + printf("Mark block bad at %08lx\n", (long)ofs); + ioctl(fd, MEMSETBADBLOCK, &ofs); + } + return 1; + } + if (len < meminfo.erasesize) { + printf("\n"); + fprintf(stderr, "Short write (%zd bytes)\n", len); + exit(1); + } + + for (i=1; i<=nr_reads; i++) { + printf("\r%08x: reading (%d of %d)...", (unsigned)ofs, i, nr_reads); + fflush(stdout); + if (read_and_compare(ofs, data, rbuf)) + read_errs++; + } + if (read_errs) { + fprintf(stderr, "read/check %d of %d failed. seed %d\n", read_errs, nr_reads, seed); + return 1; + } + return 0; +} + + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int i; + unsigned char *wbuf, *rbuf, *kbuf; + int pass; + int nr_passes = 1; + int nr_reads = 4; + int keep_contents = 0; + uint32_t offset = 0; + uint32_t length = -1; + + seed = time(NULL); + + for (;;) { + static const char short_options[] = "hkl:mo:p:r:s:"; + static const struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "markbad", no_argument, 0, 'm' }, + { "seed", required_argument, 0, 's' }, + { "passes", required_argument, 0, 'p' }, + { "offset", required_argument, 0, 'o' }, + { "length", required_argument, 0, 'l' }, + { "reads", required_argument, 0, 'r' }, + { "keep", no_argument, 0, 'k' }, + {0, 0, 0, 0}, + }; + int option_index = 0; + int c = getopt_long(argc, argv, short_options, long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 'h': + usage(0); + break; + + case '?': + usage(1); + break; + + case 'm': + markbad = 1; + break; + + case 'k': + keep_contents = 1; + break; + + case 's': + seed = atol(optarg); + break; + + case 'p': + nr_passes = atol(optarg); + break; + + case 'r': + nr_reads = atol(optarg); + break; + + case 'o': + offset = atol(optarg); + break; + + case 'l': + length = strtol(optarg, NULL, 0); + break; + + } + } + if (argc - optind != 1) + usage(1); + + fd = open(argv[optind], O_RDWR); + if (fd < 0) { + perror("open"); + exit(1); + } + + if (ioctl(fd, MEMGETINFO, &meminfo)) { + perror("MEMGETINFO"); + close(fd); + exit(1); + } + + if (length == -1) + length = meminfo.size; + + if (offset % meminfo.erasesize) { + fprintf(stderr, "Offset %x not multiple of erase size %x\n", + offset, meminfo.erasesize); + exit(1); + } + if (length % meminfo.erasesize) { + fprintf(stderr, "Length %x not multiple of erase size %x\n", + length, meminfo.erasesize); + exit(1); + } + if (length + offset > meminfo.size) { + fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", + length, offset, meminfo.size); + exit(1); + } + + wbuf = malloc(meminfo.erasesize * 3); + if (!wbuf) { + fprintf(stderr, "Could not allocate %d bytes for buffer\n", + meminfo.erasesize * 2); + exit(1); + } + rbuf = wbuf + meminfo.erasesize; + kbuf = rbuf + meminfo.erasesize; + + if (ioctl(fd, ECCGETSTATS, &oldstats)) { + perror("ECCGETSTATS"); + close(fd); + exit(1); + } + + printf("ECC corrections: %d\n", oldstats.corrected); + printf("ECC failures : %d\n", oldstats.failed); + printf("Bad blocks : %d\n", oldstats.badblocks); + printf("BBT blocks : %d\n", oldstats.bbtblocks); + + srand(seed); + + for (pass = 0; pass < nr_passes; pass++) { + loff_t test_ofs; + + for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { + ssize_t len; + + seed = rand(); + srand(seed); + + if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { + printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); + continue; + } + + for (i=0; i<meminfo.erasesize; i++) + wbuf[i] = rand(); + + if (keep_contents) { + printf("\r%08x: reading... ", (unsigned)test_ofs); + fflush(stdout); + + len = pread(fd, kbuf, meminfo.erasesize, test_ofs); + if (len < meminfo.erasesize) { + printf("\n"); + if (len) + fprintf(stderr, "Short read (%zd bytes)\n", len); + else + perror("read"); + exit(1); + } + } + if (erase_and_write(test_ofs, wbuf, rbuf, nr_reads)) + continue; + if (keep_contents) + erase_and_write(test_ofs, kbuf, rbuf, 1); + } + printf("\nFinished pass %d successfully\n", pass+1); + } + /* Return happy */ + return 0; +} diff --git a/nand-utils/nandwrite.c b/nand-utils/nandwrite.c new file mode 100644 index 0000000..9c3fe8f --- /dev/null +++ b/nand-utils/nandwrite.c @@ -0,0 +1,578 @@ +/* + * nandwrite.c + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2003 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility 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 PROGRAM_NAME "nandwrite" + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.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" +#include "common.h" +#include <libmtd.h> + +static void display_help(int status) +{ + fprintf(status == EXIT_SUCCESS ? stdout : stderr, +"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" +"Writes to the specified MTD device.\n" +"\n" +" -a, --autoplace Use auto OOB layout\n" +" -m, --markbad Mark blocks bad if write fails\n" +" -n, --noecc Write without ecc\n" +" -N, --noskipbad Write without bad block skipping\n" +" -o, --oob Input contains oob data\n" +" -O, --onlyoob Input contains oob data and only write the oob part\n" +" -s addr, --start=addr Set output start address (default is 0)\n" +" -p, --pad Pad writes to page size\n" +" -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n" +" --input-skip=length Skip |length| bytes of the input file\n" +" --input-size=length Only read |length| bytes of the input file\n" +" -q, --quiet Don't display progress messages\n" +" -h, --help Display this help and exit\n" +" --version Output version information and exit\n" + ); + exit(status); +} + +static void display_version(void) +{ + printf("%1$s " VERSION "\n" + "\n" + "Copyright (C) 2003 Thomas Gleixner \n" + "\n" + "%1$s comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of %1$s\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n", + PROGRAM_NAME); + exit(EXIT_SUCCESS); +} + +static const char *standard_input = "-"; +static const char *mtd_device, *img; +static long long mtdoffset = 0; +static long long inputskip = 0; +static long long inputsize = 0; +static bool quiet = false; +static bool writeoob = false; +static bool onlyoob = false; +static bool markbad = false; +static bool noecc = false; +static bool autoplace = false; +static bool noskipbad = false; +static bool pad = false; +static int blockalign = 1; /* default to using actual block size */ + +static void process_options(int argc, char * const argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char short_options[] = "hb:mnNoOpqs:a"; + static const struct option long_options[] = { + /* Order of these args with val==0 matters; see option_index. */ + {"version", no_argument, 0, 0}, + {"input-skip", required_argument, 0, 0}, + {"input-size", required_argument, 0, 0}, + {"help", no_argument, 0, 'h'}, + {"blockalign", required_argument, 0, 'b'}, + {"markbad", no_argument, 0, 'm'}, + {"noecc", no_argument, 0, 'n'}, + {"noskipbad", no_argument, 0, 'N'}, + {"oob", no_argument, 0, 'o'}, + {"onlyoob", no_argument, 0, 'O'}, + {"pad", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"start", required_argument, 0, 's'}, + {"autoplace", no_argument, 0, 'a'}, + {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: /* --version */ + display_version(); + break; + case 1: /* --input-skip */ + inputskip = simple_strtoll(optarg, &error); + break; + case 2: /* --input-size */ + inputsize = simple_strtoll(optarg, &error); + break; + } + break; + case 'q': + quiet = true; + break; + case 'n': + noecc = true; + break; + case 'N': + noskipbad = true; + break; + case 'm': + markbad = true; + break; + case 'o': + writeoob = true; + break; + case 'O': + writeoob = true; + onlyoob = true; + break; + case 'p': + pad = true; + break; + case 's': + mtdoffset = simple_strtoll(optarg, &error); + break; + case 'b': + blockalign = atoi(optarg); + break; + case 'a': + autoplace = true; + break; + case 'h': + display_help(EXIT_SUCCESS); + break; + case '?': + error++; + break; + } + } + + if (mtdoffset < 0) + errmsg_die("Can't specify negative device offset with option" + " -s: %lld", mtdoffset); + + if (blockalign < 0) + errmsg_die("Can't specify negative blockalign with option -b:" + " %d", blockalign); + + if (autoplace && noecc) + errmsg_die("Autoplacement and no-ECC are mutually exclusive"); + + if (!onlyoob && (pad && writeoob)) + errmsg_die("Can't pad when oob data is present"); + + argc -= optind; + argv += optind; + + /* + * There must be at least the MTD device node positional + * argument remaining and, optionally, the input file. + */ + + if (argc < 1 || argc > 2 || error) + display_help(EXIT_FAILURE); + + mtd_device = argv[0]; + + /* + * Standard input may be specified either explictly as "-" or + * implicity by simply omitting the second of the two + * positional arguments. + */ + + img = ((argc == 2) ? argv[1] : standard_input); +} + +static void erase_buffer(void *buffer, size_t size) +{ + const uint8_t kEraseByte = 0xff; + + if (buffer != NULL && size > 0) + memset(buffer, kEraseByte, size); +} + +/* + * Main program + */ +int main(int argc, char * const argv[]) +{ + int fd = -1; + int ifd = -1; + int pagelen; + long long imglen = 0; + bool baderaseblock = false; + long long blockstart = -1; + struct mtd_dev_info mtd; + long long offs; + int ret; + bool failed = true; + /* contains all the data read from the file so far for the current eraseblock */ + unsigned char *filebuf = NULL; + size_t filebuf_max = 0; + size_t filebuf_len = 0; + /* points to the current page inside filebuf */ + unsigned char *writebuf = NULL; + /* points to the OOB for the current page in filebuf */ + unsigned char *oobbuf = NULL; + libmtd_t mtd_desc; + int ebsize_aligned; + uint8_t write_mode; + + process_options(argc, argv); + + /* Open the device */ + if ((fd = open(mtd_device, O_RDWR)) == -1) + sys_errmsg_die("%s", mtd_device); + + mtd_desc = libmtd_open(); + if (!mtd_desc) + errmsg_die("can't initialize libmtd"); + + /* Fill in MTD device capability structure */ + if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) + errmsg_die("mtd_get_dev_info failed"); + + /* + * Pretend erasesize is specified number of blocks - to match jffs2 + * (virtual) block size + * Use this value throughout unless otherwise necessary + */ + ebsize_aligned = mtd.eb_size * blockalign; + + if (mtdoffset & (mtd.min_io_size - 1)) + errmsg_die("The start address is not page-aligned !\n" + "The pagesize of this NAND Flash is 0x%x.\n", + mtd.min_io_size); + + /* Select OOB write mode */ + if (noecc) + write_mode = MTD_OPS_RAW; + else if (autoplace) + write_mode = MTD_OPS_AUTO_OOB; + else + write_mode = MTD_OPS_PLACE_OOB; + + if (noecc) { + ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); + if (ret) { + switch (errno) { + case ENOTTY: + errmsg_die("ioctl MTDFILEMODE is missing"); + default: + sys_errmsg_die("MTDFILEMODE"); + } + } + } + + /* Determine if we are reading from standard input or from a file. */ + if (strcmp(img, standard_input) == 0) + ifd = STDIN_FILENO; + else + ifd = open(img, O_RDONLY); + + if (ifd == -1) { + perror(img); + goto closeall; + } + + pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); + + if (ifd == STDIN_FILENO) { + imglen = inputsize ? : pagelen; + if (inputskip) { + errmsg("seeking stdin not supported"); + goto closeall; + } + } else { + if (!inputsize) { + struct stat st; + if (fstat(ifd, &st)) { + sys_errmsg("unable to stat input image"); + goto closeall; + } + imglen = st.st_size - inputskip; + } else + imglen = inputsize; + + if (inputskip && lseek(ifd, inputskip, SEEK_CUR) == -1) { + sys_errmsg("lseek input by %lld failed", inputskip); + goto closeall; + } + } + + /* Check, if file is page-aligned */ + if (!pad && (imglen % pagelen) != 0) { + fprintf(stderr, "Input file is not page-aligned. Use the padding " + "option.\n"); + goto closeall; + } + + /* Check, if length fits into device */ + if ((imglen / pagelen) * mtd.min_io_size > mtd.size - mtdoffset) { + fprintf(stderr, "Image %lld bytes, NAND page %d bytes, OOB area %d" + " bytes, device size %lld bytes\n", + imglen, pagelen, mtd.oob_size, mtd.size); + sys_errmsg("Input file does not fit into device"); + goto closeall; + } + + /* + * Allocate a buffer big enough to contain all the data (OOB included) + * for one eraseblock. The order of operations here matters; if ebsize + * and pagelen are large enough, then "ebsize_aligned * pagelen" could + * overflow a 32-bit data type. + */ + filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; + filebuf = xmalloc(filebuf_max); + erase_buffer(filebuf, filebuf_max); + + /* + * Get data from input and write to the device while there is + * still input to read and we are still within the device + * bounds. Note that in the case of standard input, the input + * length is simply a quasi-boolean flag whose values are page + * length or zero. + */ + while ((imglen > 0 || writebuf < filebuf + filebuf_len) + && mtdoffset < mtd.size) { + /* + * New eraseblock, check for bad block(s) + * Stay in the loop to be sure that, if mtdoffset changes because + * of a bad block, the next block that will be written to + * is also checked. Thus, we avoid errors if the block(s) after the + * skipped block(s) is also bad (number of blocks depending on + * the blockalign). + */ + while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { + blockstart = mtdoffset & (~ebsize_aligned + 1); + offs = blockstart; + + /* + * if writebuf == filebuf, we are rewinding so we must + * not reset the buffer but just replay it + */ + if (writebuf != filebuf) { + erase_buffer(filebuf, filebuf_len); + filebuf_len = 0; + writebuf = filebuf; + } + + baderaseblock = false; + if (!quiet) + fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", + blockstart / ebsize_aligned, blockstart); + + /* Check all the blocks in an erase block for bad blocks */ + if (noskipbad) + continue; + + do { + ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned); + if (ret < 0) { + sys_errmsg("%s: MTD get bad block failed", mtd_device); + goto closeall; + } else if (ret == 1) { + baderaseblock = true; + if (!quiet) + fprintf(stderr, "Bad block at %llx, %u block(s) " + "from %llx will be skipped\n", + offs, blockalign, blockstart); + } + + if (baderaseblock) { + mtdoffset = blockstart + ebsize_aligned; + + if (mtdoffset > mtd.size) { + errmsg("too many bad blocks, cannot complete request"); + goto closeall; + } + } + + offs += ebsize_aligned / blockalign; + } while (offs < blockstart + ebsize_aligned); + + } + + /* Read more data from the input if there isn't enough in the buffer */ + if (writebuf + mtd.min_io_size > filebuf + filebuf_len) { + size_t readlen = mtd.min_io_size; + size_t alreadyread = (filebuf + filebuf_len) - writebuf; + size_t tinycnt = alreadyread; + ssize_t cnt = 0; + + while (tinycnt < readlen) { + cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); + if (cnt == 0) { /* EOF */ + break; + } else if (cnt < 0) { + perror("File I/O error on input"); + goto closeall; + } + tinycnt += cnt; + } + + /* No padding needed - we are done */ + if (tinycnt == 0) { + /* + * For standard input, set imglen to 0 to signal + * the end of the "file". For nonstandard input, + * leave it as-is to detect an early EOF. + */ + if (ifd == STDIN_FILENO) + imglen = 0; + + break; + } + + /* Padding */ + if (tinycnt < readlen) { + if (!pad) { + fprintf(stderr, "Unexpected EOF. Expecting at least " + "%zu more bytes. Use the padding option.\n", + readlen - tinycnt); + goto closeall; + } + erase_buffer(writebuf + tinycnt, readlen - tinycnt); + } + + filebuf_len += readlen - alreadyread; + if (ifd != STDIN_FILENO) { + imglen -= tinycnt - alreadyread; + } else if (cnt == 0) { + /* No more bytes - we are done after writing the remaining bytes */ + imglen = 0; + } + } + + if (writeoob) { + oobbuf = writebuf + mtd.min_io_size; + + /* Read more data for the OOB from the input if there isn't enough in the buffer */ + if (oobbuf + mtd.oob_size > filebuf + filebuf_len) { + size_t readlen = mtd.oob_size; + size_t alreadyread = (filebuf + filebuf_len) - oobbuf; + size_t tinycnt = alreadyread; + ssize_t cnt; + + while (tinycnt < readlen) { + cnt = read(ifd, oobbuf + tinycnt, readlen - tinycnt); + if (cnt == 0) { /* EOF */ + break; + } else if (cnt < 0) { + perror("File I/O error on input"); + goto closeall; + } + tinycnt += cnt; + } + + if (tinycnt < readlen) { + fprintf(stderr, "Unexpected EOF. Expecting at least " + "%zu more bytes for OOB\n", readlen - tinycnt); + goto closeall; + } + + filebuf_len += readlen - alreadyread; + if (ifd != STDIN_FILENO) { + imglen -= tinycnt - alreadyread; + } else if (cnt == 0) { + /* No more bytes - we are done after writing the remaining bytes */ + imglen = 0; + } + } + } + + /* Write out data */ + ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, + mtdoffset % mtd.eb_size, + onlyoob ? NULL : writebuf, + onlyoob ? 0 : mtd.min_io_size, + writeoob ? oobbuf : NULL, + writeoob ? mtd.oob_size : 0, + write_mode); + if (ret) { + long long i; + if (errno != EIO) { + sys_errmsg("%s: MTD write failure", mtd_device); + goto closeall; + } + + /* Must rewind to blockstart if we can */ + writebuf = filebuf; + + fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", + blockstart, blockstart + ebsize_aligned - 1); + for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { + if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { + int errno_tmp = errno; + sys_errmsg("%s: MTD Erase failure", mtd_device); + if (errno_tmp != EIO) + goto closeall; + } + } + + if (markbad) { + fprintf(stderr, "Marking block at %08llx bad\n", + mtdoffset & (~mtd.eb_size + 1)); + if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { + sys_errmsg("%s: MTD Mark bad block failure", mtd_device); + goto closeall; + } + } + mtdoffset = blockstart + ebsize_aligned; + + continue; + } + mtdoffset += mtd.min_io_size; + writebuf += pagelen; + } + + failed = false; + +closeall: + close(ifd); + libmtd_close(mtd_desc); + free(filebuf); + close(fd); + + if (failed || (ifd != STDIN_FILENO && imglen > 0) + || (writebuf < filebuf + filebuf_len)) + sys_errmsg_die("Data was only partially written due to error"); + + /* Return happy */ + return EXIT_SUCCESS; +} diff --git a/nand-utils/nftl_format.c b/nand-utils/nftl_format.c new file mode 100644 index 0000000..1fc3b36 --- /dev/null +++ b/nand-utils/nftl_format.c @@ -0,0 +1,422 @@ +/* + * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device + * + * 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 PROGRAM_NAME "nftl_format" + +#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> +#include <mtd_swab.h> + +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("%s: failed to read BBT, errno=%d\n", PROGRAM_NAME, 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: %s [-ib] <mtddevice> [<start offset> [<size>]]\n", PROGRAM_NAME); + 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; + const char *nftl; + int c, do_inftl = 0, do_bbt = 0; + + + printf("version 1.24 2005/11/07 11:15:13 gleixner\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/nand-utils/nftldump.c b/nand-utils/nftldump.c new file mode 100644 index 0000000..32f4f2f --- /dev/null +++ b/nand-utils/nftldump.c @@ -0,0 +1,278 @@ +/* + * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" + * + * 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 PROGRAM_NAME "nftldump" + +#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> +#include <mtd_swab.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]; + +#define SWAP16(x) do { x = le16_to_cpu(x); } while(0) +#define SWAP32(x) do { x = le32_to_cpu(x); } while(0) + +/* 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", PROGRAM_NAME); + 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/nanddump.c b/nanddump.c deleted file mode 100644 index 4ee7ed4..0000000 --- a/nanddump.c +++ /dev/null @@ -1,490 +0,0 @@ -/* - * nanddump.c - * - * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) - * Steven J. Hill (sjhill@realitydiluted.com) - * - * 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 PROGRAM_NAME "nanddump" - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <stdbool.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> -#include "common.h" -#include <libmtd.h> - -static void display_help(int status) -{ - fprintf(status == EXIT_SUCCESS ? stdout : stderr, -"Usage: %s [OPTIONS] MTD-device\n" -"Dumps the contents of a nand mtd partition.\n" -"\n" -"-h --help Display this help and exit\n" -" --version Output version information and exit\n" -" --bb=METHOD Choose bad block handling method (see below).\n" -"-a --forcebinary Force printing of binary data to tty\n" -"-c --canonicalprint Print canonical Hex+ASCII dump\n" -"-f file --file=file Dump to file\n" -"-l length --length=length Length\n" -"-n --noecc Read without error correction\n" -" --omitoob Omit OOB data (default)\n" -"-o --oob Dump OOB data\n" -"-p --prettyprint Print nice (hexdump)\n" -"-q --quiet Don't display progress and status messages\n" -"-s addr --startaddress=addr Start address\n" -"\n" -"--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':\n" -" padbad: dump flash data, substituting 0xFF for any bad blocks\n" -" dumpbad: dump flash data, including any bad blocks\n" -" skipbad: dump good data, completely skipping any bad blocks (default)\n", - PROGRAM_NAME); - exit(status); -} - -static void display_version(void) -{ - printf("%1$s " VERSION "\n" - "\n" - "%1$s comes with NO WARRANTY\n" - "to the extent permitted by law.\n" - "\n" - "You may redistribute copies of %1$s\n" - "under the terms of the GNU General Public Licence.\n" - "See the file `COPYING' for more information.\n", - PROGRAM_NAME); - exit(EXIT_SUCCESS); -} - -// Option variables - -static bool pretty_print = false; // print nice -static bool noecc = false; // don't error correct -static bool omitoob = true; // omit oob data -static long long start_addr; // start address -static long long length; // dump length -static const char *mtddev; // mtd device name -static const char *dumpfile; // dump file name -static bool quiet = false; // suppress diagnostic output -static bool canonical = false; // print nice + ascii -static bool forcebinary = false; // force printing binary to tty - -static enum { - padbad, // dump flash data, substituting 0xFF for any bad blocks - dumpbad, // dump flash data, including any bad blocks - skipbad, // dump good data, completely skipping any bad blocks -} bb_method = skipbad; - -static void process_options(int argc, char * const argv[]) -{ - int error = 0; - bool oob_default = true; - - for (;;) { - int option_index = 0; - static const char short_options[] = "hs:f:l:opqnca"; - static const struct option long_options[] = { - {"version", no_argument, 0, 0}, - {"bb", required_argument, 0, 0}, - {"omitoob", no_argument, 0, 0}, - {"help", no_argument, 0, 'h'}, - {"forcebinary", no_argument, 0, 'a'}, - {"canonicalprint", no_argument, 0, 'c'}, - {"file", required_argument, 0, 'f'}, - {"oob", no_argument, 0, 'o'}, - {"prettyprint", no_argument, 0, 'p'}, - {"startaddress", required_argument, 0, 's'}, - {"length", required_argument, 0, 'l'}, - {"noecc", no_argument, 0, 'n'}, - {"quiet", 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_version(); - break; - case 1: - /* Handle --bb=METHOD */ - if (!strcmp(optarg, "padbad")) - bb_method = padbad; - else if (!strcmp(optarg, "dumpbad")) - bb_method = dumpbad; - else if (!strcmp(optarg, "skipbad")) - bb_method = skipbad; - else - error++; - break; - case 2: /* --omitoob */ - if (oob_default) { - oob_default = false; - omitoob = true; - } else { - errmsg_die("--oob and --oomitoob are mutually exclusive"); - } - break; - } - break; - case 's': - start_addr = simple_strtoll(optarg, &error); - break; - case 'f': - dumpfile = xstrdup(optarg); - break; - case 'l': - length = simple_strtoll(optarg, &error); - break; - case 'o': - if (oob_default) { - oob_default = false; - omitoob = false; - } else { - errmsg_die("--oob and --oomitoob are mutually exclusive"); - } - break; - case 'a': - forcebinary = true; - break; - case 'c': - canonical = true; - case 'p': - pretty_print = true; - break; - case 'q': - quiet = true; - break; - case 'n': - noecc = true; - break; - case 'h': - display_help(EXIT_SUCCESS); - break; - case '?': - error++; - break; - } - } - - if (start_addr < 0) - errmsg_die("Can't specify negative offset with option -s: %lld", - start_addr); - - if (length < 0) - errmsg_die("Can't specify negative length with option -l: %lld", length); - - if (quiet && pretty_print) { - fprintf(stderr, "The quiet and pretty print options are mutually-\n" - "exclusive. Choose one or the other.\n"); - exit(EXIT_FAILURE); - } - - if (forcebinary && pretty_print) { - fprintf(stderr, "The forcebinary and pretty print options are\n" - "mutually-exclusive. Choose one or the " - "other.\n"); - exit(EXIT_FAILURE); - } - - if ((argc - optind) != 1 || error) - display_help(EXIT_FAILURE); - - mtddev = argv[optind]; -} - -#define PRETTY_ROW_SIZE 16 -#define PRETTY_BUF_LEN 80 - -/** - * pretty_dump_to_buffer - formats a blob of data to "hex ASCII" in memory - * @buf: data blob to dump - * @len: number of bytes in the @buf - * @linebuf: where to put the converted data - * @linebuflen: total size of @linebuf, including space for terminating NULL - * @pagedump: true - dumping as page format; false - dumping as OOB format - * @ascii: dump ascii formatted data next to hexdump - * @prefix: address to print before line in a page dump, ignored if !pagedump - * - * pretty_dump_to_buffer() works on one "line" of output at a time, i.e., - * PRETTY_ROW_SIZE bytes of input data converted to hex + ASCII output. - * - * Given a buffer of unsigned char data, pretty_dump_to_buffer() converts the - * input data to a hex/ASCII dump at the supplied memory location. A prefix - * is included based on whether we are dumping page or OOB data. The converted - * output is always NULL-terminated. - * - * e.g. - * pretty_dump_to_buffer(data, data_len, prettybuf, linelen, true, - * false, 256); - * produces: - * 0x00000100: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f - * NOTE: This function was adapted from linux kernel, "lib/hexdump.c" - */ -static void pretty_dump_to_buffer(const unsigned char *buf, size_t len, - char *linebuf, size_t linebuflen, bool pagedump, bool ascii, - unsigned long long prefix) -{ - static const char hex_asc[] = "0123456789abcdef"; - unsigned char ch; - unsigned int j, lx = 0, ascii_column; - - if (pagedump) - lx += sprintf(linebuf, "0x%.8llx: ", prefix); - else - lx += sprintf(linebuf, " OOB Data: "); - - if (!len) - goto nil; - if (len > PRETTY_ROW_SIZE) /* limit to one line at a time */ - len = PRETTY_ROW_SIZE; - - for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { - ch = buf[j]; - linebuf[lx++] = hex_asc[(ch & 0xf0) >> 4]; - linebuf[lx++] = hex_asc[ch & 0x0f]; - linebuf[lx++] = ' '; - } - if (j) - lx--; - - ascii_column = 3 * PRETTY_ROW_SIZE + 14; - - if (!ascii) - goto nil; - - /* Spacing between hex and ASCII - ensure at least one space */ - lx += sprintf(linebuf + lx, "%*s", - MAX((int)MIN(linebuflen, ascii_column) - 1 - lx, 1), - " "); - - linebuf[lx++] = '|'; - for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) - linebuf[lx++] = (isascii(buf[j]) && isprint(buf[j])) ? buf[j] - : '.'; - linebuf[lx++] = '|'; -nil: - linebuf[lx++] = '\n'; - linebuf[lx++] = '\0'; -} - - -/* - * Main program - */ -int main(int argc, char * const argv[]) -{ - long long ofs, end_addr = 0; - long long blockstart = 1; - int i, fd, ofd = 0, bs, badblock = 0; - struct mtd_dev_info mtd; - char pretty_buf[PRETTY_BUF_LEN]; - int firstblock = 1; - struct mtd_ecc_stats stat1, stat2; - bool eccstats = false; - unsigned char *readbuf = NULL, *oobbuf = NULL; - libmtd_t mtd_desc; - - process_options(argc, argv); - - /* Initialize libmtd */ - mtd_desc = libmtd_open(); - if (!mtd_desc) - return errmsg("can't initialize libmtd"); - - /* Open MTD device */ - if ((fd = open(mtddev, O_RDONLY)) == -1) { - perror(mtddev); - exit(EXIT_FAILURE); - } - - /* Fill in MTD device capability structure */ - if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) - return errmsg("mtd_get_dev_info failed"); - - /* Allocate buffers */ - oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); - readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); - - if (noecc) { - if (ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW) != 0) { - perror("MTDFILEMODE"); - goto closeall; - } - } else { - /* check if we can read ecc stats */ - if (!ioctl(fd, ECCGETSTATS, &stat1)) { - eccstats = true; - if (!quiet) { - fprintf(stderr, "ECC failed: %d\n", stat1.failed); - fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); - fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); - fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); - } - } else - perror("No ECC status information available"); - } - - /* 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(dumpfile); - goto closeall; - } - - if (!pretty_print && !forcebinary && isatty(ofd)) { - fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" - "or '--forcebinary' to override.\n"); - goto closeall; - } - - /* Initialize start/end addresses and block size */ - if (start_addr & (mtd.min_io_size - 1)) { - fprintf(stderr, "the start address (-s parameter) is not page-aligned!\n" - "The pagesize of this NAND Flash is 0x%x.\n", - mtd.min_io_size); - goto closeall; - } - if (length) - end_addr = start_addr + length; - if (!length || end_addr > mtd.size) - end_addr = mtd.size; - - bs = mtd.min_io_size; - - /* Print informative message */ - if (!quiet) { - fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", - mtd.eb_size, mtd.min_io_size, mtd.oob_size); - fprintf(stderr, - "Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", - start_addr, end_addr); - } - - /* Dump the flash contents */ - for (ofs = start_addr; ofs < end_addr; ofs += bs) { - /* Check for bad block */ - if (bb_method == dumpbad) { - badblock = 0; - } else if (blockstart != (ofs & (~mtd.eb_size + 1)) || - firstblock) { - blockstart = ofs & (~mtd.eb_size + 1); - firstblock = 0; - if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { - errmsg("libmtd: mtd_is_bad"); - goto closeall; - } - } - - if (badblock) { - /* skip bad block, increase end_addr */ - if (bb_method == skipbad) { - end_addr += mtd.eb_size; - ofs += mtd.eb_size - bs; - if (end_addr > mtd.size) - end_addr = mtd.size; - continue; - } - memset(readbuf, 0xff, bs); - } else { - /* Read page data and exit on failure */ - if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { - errmsg("mtd_read"); - goto closeall; - } - } - - /* ECC stats available ? */ - if (eccstats) { - if (ioctl(fd, ECCGETSTATS, &stat2)) { - perror("ioctl(ECCGETSTATS)"); - goto closeall; - } - if (stat1.failed != stat2.failed) - fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" - " at offset 0x%08llx\n", - stat2.failed - stat1.failed, ofs); - if (stat1.corrected != stat2.corrected) - fprintf(stderr, "ECC: %d corrected bitflip(s) at" - " offset 0x%08llx\n", - stat2.corrected - stat1.corrected, ofs); - stat1 = stat2; - } - - /* Write out page data */ - if (pretty_print) { - for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { - pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, - pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); - write(ofd, pretty_buf, strlen(pretty_buf)); - } - } else - write(ofd, readbuf, bs); - - if (omitoob) - continue; - - if (badblock) { - memset(oobbuf, 0xff, mtd.oob_size); - } else { - /* Read OOB data and exit on failure */ - if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { - errmsg("libmtd: mtd_read_oob"); - goto closeall; - } - } - - /* Write out OOB data */ - if (pretty_print) { - for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { - pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, - pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); - write(ofd, pretty_buf, strlen(pretty_buf)); - } - } else - write(ofd, oobbuf, mtd.oob_size); - } - - /* Close the output file and MTD device, free memory */ - close(fd); - close(ofd); - free(oobbuf); - free(readbuf); - - /* Exit happy */ - return EXIT_SUCCESS; - -closeall: - close(fd); - close(ofd); - free(oobbuf); - free(readbuf); - exit(EXIT_FAILURE); -} diff --git a/nandtest.c b/nandtest.c deleted file mode 100644 index 0805387..0000000 --- a/nandtest.c +++ /dev/null @@ -1,313 +0,0 @@ -#define PROGRAM_NAME "nandtest" - -#include <ctype.h> -#include <errno.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" - -void usage(int status) -{ - fprintf(status ? stderr : stdout, - "usage: %s [OPTIONS] <device>\n\n" - " -h, --help Display this help output\n" - " -m, --markbad Mark blocks bad if they appear so\n" - " -s, --seed Supply random seed\n" - " -p, --passes Number of passes\n" - " -r <n>, --reads=<n> Read & check <n> times per pass\n" - " -o, --offset Start offset on flash\n" - " -l, --length Length of flash to test\n" - " -k, --keep Restore existing contents after test\n", - PROGRAM_NAME); - exit(status); -} - -struct mtd_info_user meminfo; -struct mtd_ecc_stats oldstats, newstats; -int fd; -int markbad=0; -int seed; - -int read_and_compare(loff_t ofs, unsigned char *data, unsigned char *rbuf) -{ - ssize_t len; - int i; - - len = pread(fd, rbuf, meminfo.erasesize, ofs); - if (len < meminfo.erasesize) { - printf("\n"); - if (len) - fprintf(stderr, "Short read (%zd bytes)\n", len); - else - perror("read"); - exit(1); - } - - if (ioctl(fd, ECCGETSTATS, &newstats)) { - printf("\n"); - perror("ECCGETSTATS"); - close(fd); - exit(1); - } - - if (newstats.corrected > oldstats.corrected) { - printf("\n %d bit(s) ECC corrected at %08x\n", - newstats.corrected - oldstats.corrected, - (unsigned) ofs); - oldstats.corrected = newstats.corrected; - } - if (newstats.failed > oldstats.failed) { - printf("\nECC failed at %08x\n", (unsigned) ofs); - oldstats.failed = newstats.failed; - } - - printf("\r%08x: checking...", (unsigned)ofs); - fflush(stdout); - - if (memcmp(data, rbuf, meminfo.erasesize)) { - printf("\n"); - fprintf(stderr, "compare failed. seed %d\n", seed); - for (i=0; i<meminfo.erasesize; i++) { - if (data[i] != rbuf[i]) - printf("Byte 0x%x is %02x should be %02x\n", - i, rbuf[i], data[i]); - } - return 1; - } - return 0; -} - -int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, int nr_reads) -{ - struct erase_info_user er; - ssize_t len; - int i, read_errs = 0; - - printf("\r%08x: erasing... ", (unsigned)ofs); - fflush(stdout); - - er.start = ofs; - er.length = meminfo.erasesize; - - if (ioctl(fd, MEMERASE, &er)) { - perror("MEMERASE"); - if (markbad) { - printf("Mark block bad at %08lx\n", (long)ofs); - ioctl(fd, MEMSETBADBLOCK, &ofs); - } - return 1; - } - - printf("\r%08x: writing...", (unsigned)ofs); - fflush(stdout); - - len = pwrite(fd, data, meminfo.erasesize, ofs); - if (len < 0) { - printf("\n"); - perror("write"); - if (markbad) { - printf("Mark block bad at %08lx\n", (long)ofs); - ioctl(fd, MEMSETBADBLOCK, &ofs); - } - return 1; - } - if (len < meminfo.erasesize) { - printf("\n"); - fprintf(stderr, "Short write (%zd bytes)\n", len); - exit(1); - } - - for (i=1; i<=nr_reads; i++) { - printf("\r%08x: reading (%d of %d)...", (unsigned)ofs, i, nr_reads); - fflush(stdout); - if (read_and_compare(ofs, data, rbuf)) - read_errs++; - } - if (read_errs) { - fprintf(stderr, "read/check %d of %d failed. seed %d\n", read_errs, nr_reads, seed); - return 1; - } - return 0; -} - - -/* - * Main program - */ -int main(int argc, char **argv) -{ - int i; - unsigned char *wbuf, *rbuf, *kbuf; - int pass; - int nr_passes = 1; - int nr_reads = 4; - int keep_contents = 0; - uint32_t offset = 0; - uint32_t length = -1; - - seed = time(NULL); - - for (;;) { - static const char short_options[] = "hkl:mo:p:r:s:"; - static const struct option long_options[] = { - { "help", no_argument, 0, 'h' }, - { "markbad", no_argument, 0, 'm' }, - { "seed", required_argument, 0, 's' }, - { "passes", required_argument, 0, 'p' }, - { "offset", required_argument, 0, 'o' }, - { "length", required_argument, 0, 'l' }, - { "reads", required_argument, 0, 'r' }, - { "keep", no_argument, 0, 'k' }, - {0, 0, 0, 0}, - }; - int option_index = 0; - int c = getopt_long(argc, argv, short_options, long_options, &option_index); - if (c == EOF) - break; - - switch (c) { - case 'h': - usage(0); - break; - - case '?': - usage(1); - break; - - case 'm': - markbad = 1; - break; - - case 'k': - keep_contents = 1; - break; - - case 's': - seed = atol(optarg); - break; - - case 'p': - nr_passes = atol(optarg); - break; - - case 'r': - nr_reads = atol(optarg); - break; - - case 'o': - offset = atol(optarg); - break; - - case 'l': - length = strtol(optarg, NULL, 0); - break; - - } - } - if (argc - optind != 1) - usage(1); - - fd = open(argv[optind], O_RDWR); - if (fd < 0) { - perror("open"); - exit(1); - } - - if (ioctl(fd, MEMGETINFO, &meminfo)) { - perror("MEMGETINFO"); - close(fd); - exit(1); - } - - if (length == -1) - length = meminfo.size; - - if (offset % meminfo.erasesize) { - fprintf(stderr, "Offset %x not multiple of erase size %x\n", - offset, meminfo.erasesize); - exit(1); - } - if (length % meminfo.erasesize) { - fprintf(stderr, "Length %x not multiple of erase size %x\n", - length, meminfo.erasesize); - exit(1); - } - if (length + offset > meminfo.size) { - fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", - length, offset, meminfo.size); - exit(1); - } - - wbuf = malloc(meminfo.erasesize * 3); - if (!wbuf) { - fprintf(stderr, "Could not allocate %d bytes for buffer\n", - meminfo.erasesize * 2); - exit(1); - } - rbuf = wbuf + meminfo.erasesize; - kbuf = rbuf + meminfo.erasesize; - - if (ioctl(fd, ECCGETSTATS, &oldstats)) { - perror("ECCGETSTATS"); - close(fd); - exit(1); - } - - printf("ECC corrections: %d\n", oldstats.corrected); - printf("ECC failures : %d\n", oldstats.failed); - printf("Bad blocks : %d\n", oldstats.badblocks); - printf("BBT blocks : %d\n", oldstats.bbtblocks); - - srand(seed); - - for (pass = 0; pass < nr_passes; pass++) { - loff_t test_ofs; - - for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { - ssize_t len; - - seed = rand(); - srand(seed); - - if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { - printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); - continue; - } - - for (i=0; i<meminfo.erasesize; i++) - wbuf[i] = rand(); - - if (keep_contents) { - printf("\r%08x: reading... ", (unsigned)test_ofs); - fflush(stdout); - - len = pread(fd, kbuf, meminfo.erasesize, test_ofs); - if (len < meminfo.erasesize) { - printf("\n"); - if (len) - fprintf(stderr, "Short read (%zd bytes)\n", len); - else - perror("read"); - exit(1); - } - } - if (erase_and_write(test_ofs, wbuf, rbuf, nr_reads)) - continue; - if (keep_contents) - erase_and_write(test_ofs, kbuf, rbuf, 1); - } - printf("\nFinished pass %d successfully\n", pass+1); - } - /* Return happy */ - return 0; -} diff --git a/nandwrite.c b/nandwrite.c deleted file mode 100644 index 9c3fe8f..0000000 --- a/nandwrite.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * nandwrite.c - * - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * 2003 Thomas Gleixner (tglx@linutronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This utility 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 PROGRAM_NAME "nandwrite" - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.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" -#include "common.h" -#include <libmtd.h> - -static void display_help(int status) -{ - fprintf(status == EXIT_SUCCESS ? stdout : stderr, -"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" -"Writes to the specified MTD device.\n" -"\n" -" -a, --autoplace Use auto OOB layout\n" -" -m, --markbad Mark blocks bad if write fails\n" -" -n, --noecc Write without ecc\n" -" -N, --noskipbad Write without bad block skipping\n" -" -o, --oob Input contains oob data\n" -" -O, --onlyoob Input contains oob data and only write the oob part\n" -" -s addr, --start=addr Set output start address (default is 0)\n" -" -p, --pad Pad writes to page size\n" -" -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n" -" --input-skip=length Skip |length| bytes of the input file\n" -" --input-size=length Only read |length| bytes of the input file\n" -" -q, --quiet Don't display progress messages\n" -" -h, --help Display this help and exit\n" -" --version Output version information and exit\n" - ); - exit(status); -} - -static void display_version(void) -{ - printf("%1$s " VERSION "\n" - "\n" - "Copyright (C) 2003 Thomas Gleixner \n" - "\n" - "%1$s comes with NO WARRANTY\n" - "to the extent permitted by law.\n" - "\n" - "You may redistribute copies of %1$s\n" - "under the terms of the GNU General Public Licence.\n" - "See the file `COPYING' for more information.\n", - PROGRAM_NAME); - exit(EXIT_SUCCESS); -} - -static const char *standard_input = "-"; -static const char *mtd_device, *img; -static long long mtdoffset = 0; -static long long inputskip = 0; -static long long inputsize = 0; -static bool quiet = false; -static bool writeoob = false; -static bool onlyoob = false; -static bool markbad = false; -static bool noecc = false; -static bool autoplace = false; -static bool noskipbad = false; -static bool pad = false; -static int blockalign = 1; /* default to using actual block size */ - -static void process_options(int argc, char * const argv[]) -{ - int error = 0; - - for (;;) { - int option_index = 0; - static const char short_options[] = "hb:mnNoOpqs:a"; - static const struct option long_options[] = { - /* Order of these args with val==0 matters; see option_index. */ - {"version", no_argument, 0, 0}, - {"input-skip", required_argument, 0, 0}, - {"input-size", required_argument, 0, 0}, - {"help", no_argument, 0, 'h'}, - {"blockalign", required_argument, 0, 'b'}, - {"markbad", no_argument, 0, 'm'}, - {"noecc", no_argument, 0, 'n'}, - {"noskipbad", no_argument, 0, 'N'}, - {"oob", no_argument, 0, 'o'}, - {"onlyoob", no_argument, 0, 'O'}, - {"pad", no_argument, 0, 'p'}, - {"quiet", no_argument, 0, 'q'}, - {"start", required_argument, 0, 's'}, - {"autoplace", no_argument, 0, 'a'}, - {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: /* --version */ - display_version(); - break; - case 1: /* --input-skip */ - inputskip = simple_strtoll(optarg, &error); - break; - case 2: /* --input-size */ - inputsize = simple_strtoll(optarg, &error); - break; - } - break; - case 'q': - quiet = true; - break; - case 'n': - noecc = true; - break; - case 'N': - noskipbad = true; - break; - case 'm': - markbad = true; - break; - case 'o': - writeoob = true; - break; - case 'O': - writeoob = true; - onlyoob = true; - break; - case 'p': - pad = true; - break; - case 's': - mtdoffset = simple_strtoll(optarg, &error); - break; - case 'b': - blockalign = atoi(optarg); - break; - case 'a': - autoplace = true; - break; - case 'h': - display_help(EXIT_SUCCESS); - break; - case '?': - error++; - break; - } - } - - if (mtdoffset < 0) - errmsg_die("Can't specify negative device offset with option" - " -s: %lld", mtdoffset); - - if (blockalign < 0) - errmsg_die("Can't specify negative blockalign with option -b:" - " %d", blockalign); - - if (autoplace && noecc) - errmsg_die("Autoplacement and no-ECC are mutually exclusive"); - - if (!onlyoob && (pad && writeoob)) - errmsg_die("Can't pad when oob data is present"); - - argc -= optind; - argv += optind; - - /* - * There must be at least the MTD device node positional - * argument remaining and, optionally, the input file. - */ - - if (argc < 1 || argc > 2 || error) - display_help(EXIT_FAILURE); - - mtd_device = argv[0]; - - /* - * Standard input may be specified either explictly as "-" or - * implicity by simply omitting the second of the two - * positional arguments. - */ - - img = ((argc == 2) ? argv[1] : standard_input); -} - -static void erase_buffer(void *buffer, size_t size) -{ - const uint8_t kEraseByte = 0xff; - - if (buffer != NULL && size > 0) - memset(buffer, kEraseByte, size); -} - -/* - * Main program - */ -int main(int argc, char * const argv[]) -{ - int fd = -1; - int ifd = -1; - int pagelen; - long long imglen = 0; - bool baderaseblock = false; - long long blockstart = -1; - struct mtd_dev_info mtd; - long long offs; - int ret; - bool failed = true; - /* contains all the data read from the file so far for the current eraseblock */ - unsigned char *filebuf = NULL; - size_t filebuf_max = 0; - size_t filebuf_len = 0; - /* points to the current page inside filebuf */ - unsigned char *writebuf = NULL; - /* points to the OOB for the current page in filebuf */ - unsigned char *oobbuf = NULL; - libmtd_t mtd_desc; - int ebsize_aligned; - uint8_t write_mode; - - process_options(argc, argv); - - /* Open the device */ - if ((fd = open(mtd_device, O_RDWR)) == -1) - sys_errmsg_die("%s", mtd_device); - - mtd_desc = libmtd_open(); - if (!mtd_desc) - errmsg_die("can't initialize libmtd"); - - /* Fill in MTD device capability structure */ - if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) - errmsg_die("mtd_get_dev_info failed"); - - /* - * Pretend erasesize is specified number of blocks - to match jffs2 - * (virtual) block size - * Use this value throughout unless otherwise necessary - */ - ebsize_aligned = mtd.eb_size * blockalign; - - if (mtdoffset & (mtd.min_io_size - 1)) - errmsg_die("The start address is not page-aligned !\n" - "The pagesize of this NAND Flash is 0x%x.\n", - mtd.min_io_size); - - /* Select OOB write mode */ - if (noecc) - write_mode = MTD_OPS_RAW; - else if (autoplace) - write_mode = MTD_OPS_AUTO_OOB; - else - write_mode = MTD_OPS_PLACE_OOB; - - if (noecc) { - ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); - if (ret) { - switch (errno) { - case ENOTTY: - errmsg_die("ioctl MTDFILEMODE is missing"); - default: - sys_errmsg_die("MTDFILEMODE"); - } - } - } - - /* Determine if we are reading from standard input or from a file. */ - if (strcmp(img, standard_input) == 0) - ifd = STDIN_FILENO; - else - ifd = open(img, O_RDONLY); - - if (ifd == -1) { - perror(img); - goto closeall; - } - - pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); - - if (ifd == STDIN_FILENO) { - imglen = inputsize ? : pagelen; - if (inputskip) { - errmsg("seeking stdin not supported"); - goto closeall; - } - } else { - if (!inputsize) { - struct stat st; - if (fstat(ifd, &st)) { - sys_errmsg("unable to stat input image"); - goto closeall; - } - imglen = st.st_size - inputskip; - } else - imglen = inputsize; - - if (inputskip && lseek(ifd, inputskip, SEEK_CUR) == -1) { - sys_errmsg("lseek input by %lld failed", inputskip); - goto closeall; - } - } - - /* Check, if file is page-aligned */ - if (!pad && (imglen % pagelen) != 0) { - fprintf(stderr, "Input file is not page-aligned. Use the padding " - "option.\n"); - goto closeall; - } - - /* Check, if length fits into device */ - if ((imglen / pagelen) * mtd.min_io_size > mtd.size - mtdoffset) { - fprintf(stderr, "Image %lld bytes, NAND page %d bytes, OOB area %d" - " bytes, device size %lld bytes\n", - imglen, pagelen, mtd.oob_size, mtd.size); - sys_errmsg("Input file does not fit into device"); - goto closeall; - } - - /* - * Allocate a buffer big enough to contain all the data (OOB included) - * for one eraseblock. The order of operations here matters; if ebsize - * and pagelen are large enough, then "ebsize_aligned * pagelen" could - * overflow a 32-bit data type. - */ - filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; - filebuf = xmalloc(filebuf_max); - erase_buffer(filebuf, filebuf_max); - - /* - * Get data from input and write to the device while there is - * still input to read and we are still within the device - * bounds. Note that in the case of standard input, the input - * length is simply a quasi-boolean flag whose values are page - * length or zero. - */ - while ((imglen > 0 || writebuf < filebuf + filebuf_len) - && mtdoffset < mtd.size) { - /* - * New eraseblock, check for bad block(s) - * Stay in the loop to be sure that, if mtdoffset changes because - * of a bad block, the next block that will be written to - * is also checked. Thus, we avoid errors if the block(s) after the - * skipped block(s) is also bad (number of blocks depending on - * the blockalign). - */ - while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { - blockstart = mtdoffset & (~ebsize_aligned + 1); - offs = blockstart; - - /* - * if writebuf == filebuf, we are rewinding so we must - * not reset the buffer but just replay it - */ - if (writebuf != filebuf) { - erase_buffer(filebuf, filebuf_len); - filebuf_len = 0; - writebuf = filebuf; - } - - baderaseblock = false; - if (!quiet) - fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", - blockstart / ebsize_aligned, blockstart); - - /* Check all the blocks in an erase block for bad blocks */ - if (noskipbad) - continue; - - do { - ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned); - if (ret < 0) { - sys_errmsg("%s: MTD get bad block failed", mtd_device); - goto closeall; - } else if (ret == 1) { - baderaseblock = true; - if (!quiet) - fprintf(stderr, "Bad block at %llx, %u block(s) " - "from %llx will be skipped\n", - offs, blockalign, blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart + ebsize_aligned; - - if (mtdoffset > mtd.size) { - errmsg("too many bad blocks, cannot complete request"); - goto closeall; - } - } - - offs += ebsize_aligned / blockalign; - } while (offs < blockstart + ebsize_aligned); - - } - - /* Read more data from the input if there isn't enough in the buffer */ - if (writebuf + mtd.min_io_size > filebuf + filebuf_len) { - size_t readlen = mtd.min_io_size; - size_t alreadyread = (filebuf + filebuf_len) - writebuf; - size_t tinycnt = alreadyread; - ssize_t cnt = 0; - - while (tinycnt < readlen) { - cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); - if (cnt == 0) { /* EOF */ - break; - } else if (cnt < 0) { - perror("File I/O error on input"); - goto closeall; - } - tinycnt += cnt; - } - - /* No padding needed - we are done */ - if (tinycnt == 0) { - /* - * For standard input, set imglen to 0 to signal - * the end of the "file". For nonstandard input, - * leave it as-is to detect an early EOF. - */ - if (ifd == STDIN_FILENO) - imglen = 0; - - break; - } - - /* Padding */ - if (tinycnt < readlen) { - if (!pad) { - fprintf(stderr, "Unexpected EOF. Expecting at least " - "%zu more bytes. Use the padding option.\n", - readlen - tinycnt); - goto closeall; - } - erase_buffer(writebuf + tinycnt, readlen - tinycnt); - } - - filebuf_len += readlen - alreadyread; - if (ifd != STDIN_FILENO) { - imglen -= tinycnt - alreadyread; - } else if (cnt == 0) { - /* No more bytes - we are done after writing the remaining bytes */ - imglen = 0; - } - } - - if (writeoob) { - oobbuf = writebuf + mtd.min_io_size; - - /* Read more data for the OOB from the input if there isn't enough in the buffer */ - if (oobbuf + mtd.oob_size > filebuf + filebuf_len) { - size_t readlen = mtd.oob_size; - size_t alreadyread = (filebuf + filebuf_len) - oobbuf; - size_t tinycnt = alreadyread; - ssize_t cnt; - - while (tinycnt < readlen) { - cnt = read(ifd, oobbuf + tinycnt, readlen - tinycnt); - if (cnt == 0) { /* EOF */ - break; - } else if (cnt < 0) { - perror("File I/O error on input"); - goto closeall; - } - tinycnt += cnt; - } - - if (tinycnt < readlen) { - fprintf(stderr, "Unexpected EOF. Expecting at least " - "%zu more bytes for OOB\n", readlen - tinycnt); - goto closeall; - } - - filebuf_len += readlen - alreadyread; - if (ifd != STDIN_FILENO) { - imglen -= tinycnt - alreadyread; - } else if (cnt == 0) { - /* No more bytes - we are done after writing the remaining bytes */ - imglen = 0; - } - } - } - - /* Write out data */ - ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, - mtdoffset % mtd.eb_size, - onlyoob ? NULL : writebuf, - onlyoob ? 0 : mtd.min_io_size, - writeoob ? oobbuf : NULL, - writeoob ? mtd.oob_size : 0, - write_mode); - if (ret) { - long long i; - if (errno != EIO) { - sys_errmsg("%s: MTD write failure", mtd_device); - goto closeall; - } - - /* Must rewind to blockstart if we can */ - writebuf = filebuf; - - fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", - blockstart, blockstart + ebsize_aligned - 1); - for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { - if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { - int errno_tmp = errno; - sys_errmsg("%s: MTD Erase failure", mtd_device); - if (errno_tmp != EIO) - goto closeall; - } - } - - if (markbad) { - fprintf(stderr, "Marking block at %08llx bad\n", - mtdoffset & (~mtd.eb_size + 1)); - if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { - sys_errmsg("%s: MTD Mark bad block failure", mtd_device); - goto closeall; - } - } - mtdoffset = blockstart + ebsize_aligned; - - continue; - } - mtdoffset += mtd.min_io_size; - writebuf += pagelen; - } - - failed = false; - -closeall: - close(ifd); - libmtd_close(mtd_desc); - free(filebuf); - close(fd); - - if (failed || (ifd != STDIN_FILENO && imglen > 0) - || (writebuf < filebuf + filebuf_len)) - sys_errmsg_die("Data was only partially written due to error"); - - /* Return happy */ - return EXIT_SUCCESS; -} diff --git a/nftl_format.c b/nftl_format.c deleted file mode 100644 index 1fc3b36..0000000 --- a/nftl_format.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device - * - * 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 PROGRAM_NAME "nftl_format" - -#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> -#include <mtd_swab.h> - -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("%s: failed to read BBT, errno=%d\n", PROGRAM_NAME, 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: %s [-ib] <mtddevice> [<start offset> [<size>]]\n", PROGRAM_NAME); - 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; - const char *nftl; - int c, do_inftl = 0, do_bbt = 0; - - - printf("version 1.24 2005/11/07 11:15:13 gleixner\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 deleted file mode 100644 index 32f4f2f..0000000 --- a/nftldump.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" - * - * 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 PROGRAM_NAME "nftldump" - -#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> -#include <mtd_swab.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]; - -#define SWAP16(x) do { x = le16_to_cpu(x); } while(0) -#define SWAP32(x) do { x = le32_to_cpu(x); } while(0) - -/* 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", PROGRAM_NAME); - 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/nor-utils/rfddump.c b/nor-utils/rfddump.c new file mode 100644 index 0000000..0375bac --- /dev/null +++ b/nor-utils/rfddump.c @@ -0,0 +1,337 @@ +/* + * 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. + */ + +#define PROGRAM_NAME "rfddump" +#define VERSION "$Revision 1.0 $" + +#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 <mtd_swab.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; + uint16_t *header; + int sector_count; + int *sector_map; + const char *mtd_filename; + const char *out_filename; + int verbose; +}; + +void display_help(void) +{ + printf("Usage: %s [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", + PROGRAM_NAME); + exit(0); +} + +void display_version(void) +{ + printf("%s " 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", + PROGRAM_NAME); + + 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++) { + uint16_t 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; + uint8_t 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(uint16_t) + 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(uint16_t); + + rfd.header = malloc(rfd.header_size); + if (!rfd.header) { + perror(PROGRAM_NAME); + close(fd); + return 2; + } + rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); + if (!rfd.sector_map) { + perror(PROGRAM_NAME); + 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/nor-utils/rfdformat.c b/nor-utils/rfdformat.c new file mode 100644 index 0000000..17d9d2d --- /dev/null +++ b/nor-utils/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. + * + * This is very easy: just erase all the blocks and put the magic at + * the beginning of each block. + */ + +#define PROGRAM_NAME "rfdformat" +#define VERSION "$Revision 1.0 $" + +#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> + +void display_help(void) +{ + printf("Usage: %s [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", + PROGRAM_NAME); + exit(0); +} + +void display_version(void) +{ + printf("%s " 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", + PROGRAM_NAME); + + 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 uint8_t 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/rbtree.c b/rbtree.c deleted file mode 100644 index 329e098..0000000 --- a/rbtree.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli <andrea@suse.de> - (C) 2002 David Woodhouse <dwmw2@infradead.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. - - 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 - - linux/lib/rbtree.c -*/ - -#include <stdlib.h> -#include "rbtree.h" - -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *right = node->rb_right; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_right = right->rb_left)) - rb_set_parent(right->rb_left, node); - right->rb_left = node; - - rb_set_parent(right, parent); - - if (parent) - { - if (node == parent->rb_left) - parent->rb_left = right; - else - parent->rb_right = right; - } - else - root->rb_node = right; - rb_set_parent(node, right); -} - -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *left = node->rb_left; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_left = left->rb_right)) - rb_set_parent(left->rb_right, node); - left->rb_right = node; - - rb_set_parent(left, parent); - - if (parent) - { - if (node == parent->rb_right) - parent->rb_right = left; - else - parent->rb_left = left; - } - else - root->rb_node = left; - rb_set_parent(node, left); -} - -void rb_insert_color(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *parent, *gparent; - - while ((parent = rb_parent(node)) && rb_is_red(parent)) - { - gparent = rb_parent(parent); - - if (parent == gparent->rb_left) - { - { - register struct rb_node *uncle = gparent->rb_right; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_right == node) - { - register struct rb_node *tmp; - __rb_rotate_left(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_right(gparent, root); - } else { - { - register struct rb_node *uncle = gparent->rb_left; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } - } - - if (parent->rb_left == node) - { - register struct rb_node *tmp; - __rb_rotate_right(parent, root); - tmp = parent; - parent = node; - node = tmp; - } - - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_left(gparent, root); - } - } - - rb_set_black(root->rb_node); -} - -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, - struct rb_root *root) -{ - struct rb_node *other; - - while ((!node || rb_is_black(node)) && node != root->rb_node) - { - if (parent->rb_left == node) - { - other = parent->rb_right; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_left(parent, root); - other = parent->rb_right; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_right || rb_is_black(other->rb_right)) - { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); - rb_set_red(other); - __rb_rotate_right(other, root); - other = parent->rb_right; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); - __rb_rotate_left(parent, root); - node = root->rb_node; - break; - } - } - else - { - other = parent->rb_left; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_right(parent, root); - other = parent->rb_left; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); - } - else - { - if (!other->rb_left || rb_is_black(other->rb_left)) - { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); - rb_set_red(other); - __rb_rotate_left(other, root); - other = parent->rb_left; - } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); - __rb_rotate_right(parent, root); - node = root->rb_node; - break; - } - } - } - if (node) - rb_set_black(node); -} - -void rb_erase(struct rb_node *node, struct rb_root *root) -{ - struct rb_node *child, *parent; - int color; - - if (!node->rb_left) - child = node->rb_right; - else if (!node->rb_right) - child = node->rb_left; - else - { - struct rb_node *old = node, *left; - - node = node->rb_right; - while ((left = node->rb_left) != NULL) - node = left; - child = node->rb_right; - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent == old) { - parent->rb_right = child; - parent = node; - } else - parent->rb_left = child; - - node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; - node->rb_left = old->rb_left; - - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); - goto color; - } - - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent) - { - if (parent->rb_left == node) - parent->rb_left = child; - else - parent->rb_right = child; - } - else - root->rb_node = child; - - color: - if (color == RB_BLACK) - __rb_erase_color(child, parent, root); -} - -/* - * This function returns the first node (in sort order) of the tree. - */ -struct rb_node *rb_first(struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_left) - n = n->rb_left; - return n; -} - -struct rb_node *rb_last(struct rb_root *root) -{ - struct rb_node *n; - - n = root->rb_node; - if (!n) - return NULL; - while (n->rb_right) - n = n->rb_right; - return n; -} - -struct rb_node *rb_next(struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a right-hand child, go down and then left as far - as we can. */ - if (node->rb_right) { - node = node->rb_right; - while (node->rb_left) - node=node->rb_left; - return node; - } - - /* No right-hand children. Everything down and left is - smaller than us, so any 'next' node must be in the general - direction of our parent. Go up the tree; any time the - ancestor is a right-hand child of its parent, keep going - up. First time it's a left-hand child of its parent, said - parent is our 'next' node. */ - while ((parent = rb_parent(node)) && node == parent->rb_right) - node = parent; - - return parent; -} - -struct rb_node *rb_prev(struct rb_node *node) -{ - struct rb_node *parent; - - if (rb_parent(node) == node) - return NULL; - - /* If we have a left-hand child, go down and then right as far - as we can. */ - if (node->rb_left) { - node = node->rb_left; - while (node->rb_right) - node=node->rb_right; - return node; - } - - /* No left-hand children. Go up till we find an ancestor which - is a right-hand child of its parent */ - while ((parent = rb_parent(node)) && node == parent->rb_left) - node = parent; - - return parent; -} - -void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root) -{ - struct rb_node *parent = rb_parent(victim); - - /* Set the surrounding nodes to point to the replacement */ - if (parent) { - if (victim == parent->rb_left) - parent->rb_left = new; - else - parent->rb_right = new; - } else { - root->rb_node = new; - } - if (victim->rb_left) - rb_set_parent(victim->rb_left, new); - if (victim->rb_right) - rb_set_parent(victim->rb_right, new); - - /* Copy the pointers/colour from the victim to the replacement */ - *new = *victim; -} diff --git a/rbtree.h b/rbtree.h deleted file mode 100644 index 0d77b65..0000000 --- a/rbtree.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - Red Black Trees - (C) 1999 Andrea Arcangeli <andrea@suse.de> - - 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 - - linux/include/linux/rbtree.h - - To use rbtrees you'll have to implement your own insert and search cores. - This will avoid us to use callbacks and to drop drammatically performances. - I know it's not the cleaner way, but in C (not in C++) to get - performances and genericity... - - Some example of insert and search follows here. The search is a plain - normal search over an ordered tree. The insert instead must be implemented - int two steps: as first thing the code must insert the element in - order as a red leaf in the tree, then the support library function - rb_insert_color() must be called. Such function will do the - not trivial work to rebalance the rbtree if necessary. - ------------------------------------------------------------------------ -static inline struct page * rb_search_page_cache(struct inode * inode, - unsigned long offset) -{ - struct rb_node * n = inode->i_rb_page_cache.rb_node; - struct page * page; - - while (n) - { - page = rb_entry(n, struct page, rb_page_cache); - - if (offset < page->offset) - n = n->rb_left; - else if (offset > page->offset) - n = n->rb_right; - else - return page; - } - return NULL; -} - -static inline struct page * __rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct rb_node ** p = &inode->i_rb_page_cache.rb_node; - struct rb_node * parent = NULL; - struct page * page; - - while (*p) - { - parent = *p; - page = rb_entry(parent, struct page, rb_page_cache); - - if (offset < page->offset) - p = &(*p)->rb_left; - else if (offset > page->offset) - p = &(*p)->rb_right; - else - return page; - } - - rb_link_node(node, parent, p); - - return NULL; -} - -static inline struct page * rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct page * ret; - if ((ret = __rb_insert_page_cache(inode, offset, node))) - goto out; - rb_insert_color(node, &inode->i_rb_page_cache); - out: - return ret; -} ------------------------------------------------------------------------ -*/ - -#ifndef _LINUX_RBTREE_H -#define _LINUX_RBTREE_H - -#include <linux/kernel.h> -#include <linux/stddef.h> - -struct rb_node -{ - unsigned long rb_parent_color; -#define RB_RED 0 -#define RB_BLACK 1 - struct rb_node *rb_right; - struct rb_node *rb_left; -} __attribute__((aligned(sizeof(long)))); - /* The alignment might seem pointless, but allegedly CRIS needs it */ - -struct rb_root -{ - struct rb_node *rb_node; -}; - - -#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) -#define rb_color(r) ((r)->rb_parent_color & 1) -#define rb_is_red(r) (!rb_color(r)) -#define rb_is_black(r) rb_color(r) -#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) -#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) - -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) -{ - rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; -} -static inline void rb_set_color(struct rb_node *rb, int color) -{ - rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; -} - -#define RB_ROOT (struct rb_root) { NULL, } - -/* Newer gcc versions take care of exporting this */ -#ifndef offsetof -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) -#endif - -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - -#define rb_entry(ptr, type, member) container_of(ptr, type, member) - -#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -#define RB_EMPTY_NODE(node) (rb_parent(node) == node) -#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) - -extern void rb_insert_color(struct rb_node *, struct rb_root *); -extern void rb_erase(struct rb_node *, struct rb_root *); - -/* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(struct rb_node *); -extern struct rb_node *rb_prev(struct rb_node *); -extern struct rb_node *rb_first(struct rb_root *); -extern struct rb_node *rb_last(struct rb_root *); - -/* Fast replacement of a single node without remove/rebalance/add/rebalance */ -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, - struct rb_root *root); - -static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, - struct rb_node ** rb_link) -{ - node->rb_parent_color = (unsigned long )parent; - node->rb_left = node->rb_right = NULL; - - *rb_link = node; -} - -#endif /* _LINUX_RBTREE_H */ diff --git a/recv_image.c b/recv_image.c deleted file mode 100644 index 0093831..0000000 --- a/recv_image.c +++ /dev/null @@ -1,484 +0,0 @@ - -#define PROGRAM_NAME "recv_image" -#define _XOPEN_SOURCE 500 -#define _BSD_SOURCE /* struct ip_mreq */ - -#include <errno.h> -#include <stdio.h> -#include <netdb.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <crc32.h> -#include "mtd/mtd-user.h" -#include "mcast_image.h" - -#include "common.h" - -#define WBUF_SIZE 4096 -struct eraseblock { - uint32_t flash_offset; - unsigned char wbuf[WBUF_SIZE]; - int wbuf_ofs; - int nr_pkts; - int *pkt_indices; - uint32_t crc; -}; - -int main(int argc, char **argv) -{ - struct addrinfo *ai; - struct addrinfo hints; - struct addrinfo *runp; - int ret; - int sock; - ssize_t len; - int flfd; - struct mtd_info_user meminfo; - unsigned char *eb_buf, *decode_buf, **src_pkts; - int nr_blocks = 0; - int pkts_per_block; - int block_nr = -1; - uint32_t image_crc = 0; - int total_pkts = 0; - int ignored_pkts = 0; - loff_t mtdoffset = 0; - int badcrcs = 0; - int duplicates = 0; - int file_mode = 0; - struct fec_parms *fec = NULL; - int i; - struct eraseblock *eraseblocks = NULL; - uint32_t start_seq = 0; - struct timeval start, now; - unsigned long fec_time = 0, flash_time = 0, crc_time = 0, - rflash_time = 0, erase_time = 0, net_time = 0; - - if (argc != 4) { - fprintf(stderr, "usage: %s <host> <port> <mtddev>\n", - PROGRAM_NAME); - exit(1); - } - /* Open the device */ - flfd = open(argv[3], O_RDWR); - - if (flfd >= 0) { - /* Fill in MTD device capability structure */ - if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { - perror("MEMGETINFO"); - close(flfd); - flfd = -1; - } else { - printf("Receive to MTD device %s with erasesize %d\n", - argv[3], meminfo.erasesize); - } - } - if (flfd == -1) { - /* Try again, as if it's a file */ - flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); - if (flfd < 0) { - perror("open"); - exit(1); - } - meminfo.erasesize = 131072; - file_mode = 1; - printf("Receive to file %s with (assumed) erasesize %d\n", - argv[3], meminfo.erasesize); - } - - pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; - - eb_buf = malloc(pkts_per_block * PKT_SIZE); - decode_buf = malloc(pkts_per_block * PKT_SIZE); - if (!eb_buf && !decode_buf) { - fprintf(stderr, "No memory for eraseblock buffer\n"); - exit(1); - } - src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); - if (!src_pkts) { - fprintf(stderr, "No memory for decode packet pointers\n"); - exit(1); - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG; - hints.ai_socktype = SOCK_DGRAM; - - ret = getaddrinfo(argv[1], argv[2], &hints, &ai); - if (ret) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); - exit(1); - } - runp = ai; - for (runp = ai; runp; runp = runp->ai_next) { - sock = socket(runp->ai_family, runp->ai_socktype, - runp->ai_protocol); - if (sock == -1) { - perror("socket"); - continue; - } - if (runp->ai_family == AF_INET && - IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { - struct ip_mreq rq; - rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; - rq.imr_interface.s_addr = INADDR_ANY; - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { - perror("IP_ADD_MEMBERSHIP"); - close(sock); - continue; - } - - } else if (runp->ai_family == AF_INET6 && - ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { - struct ipv6_mreq rq; - rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; - rq.ipv6mr_interface = 0; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { - perror("IPV6_ADD_MEMBERSHIP"); - close(sock); - continue; - } - } - if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { - perror("bind"); - close(sock); - continue; - } - break; - } - if (!runp) - exit(1); - - while (1) { - struct image_pkt thispkt; - - len = read(sock, &thispkt, sizeof(thispkt)); - - if (len < 0) { - perror("read socket"); - break; - } - if (len < sizeof(thispkt)) { - fprintf(stderr, "Wrong length %zd bytes (expected %zu)\n", - len, sizeof(thispkt)); - continue; - } - if (!eraseblocks) { - image_crc = thispkt.hdr.totcrc; - start_seq = ntohl(thispkt.hdr.pkt_sequence); - - if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { - fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", - ntohl(thispkt.hdr.blocksize), meminfo.erasesize); - exit(1); - } - nr_blocks = ntohl(thispkt.hdr.nr_blocks); - - fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); - - eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); - if (!eraseblocks) { - fprintf(stderr, "No memory for block map\n"); - exit(1); - } - for (i = 0; i < nr_blocks; i++) { - eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); - if (!eraseblocks[i].pkt_indices) { - fprintf(stderr, "Failed to allocate packet indices\n"); - exit(1); - } - eraseblocks[i].nr_pkts = 0; - if (!file_mode) { - if (mtdoffset >= meminfo.size) { - fprintf(stderr, "Run out of space on flash\n"); - exit(1); - } -#if 1 /* Deliberately use bad blocks... test write failures */ - while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { - printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); - mtdoffset += meminfo.erasesize; - } -#endif - } - eraseblocks[i].flash_offset = mtdoffset; - mtdoffset += meminfo.erasesize; - eraseblocks[i].wbuf_ofs = 0; - } - gettimeofday(&start, NULL); - } - if (image_crc != thispkt.hdr.totcrc) { - fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", - ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); - exit(1); - } - - block_nr = ntohl(thispkt.hdr.block_nr); - if (block_nr >= nr_blocks) { - fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", - block_nr, nr_blocks); - exit(1); - } - for (i=0; i<eraseblocks[block_nr].nr_pkts; i++) { - if (eraseblocks[block_nr].pkt_indices[i] == ntohs(thispkt.hdr.pkt_nr)) { -// printf("Discarding duplicate packet at %08x pkt %d\n", -// block_nr * meminfo.erasesize, eraseblocks[block_nr].pkt_indices[i]); - duplicates++; - break; - } - } - if (i < eraseblocks[block_nr].nr_pkts) { - continue; - } - - if (eraseblocks[block_nr].nr_pkts >= pkts_per_block) { - /* We have a block which we didn't really need */ - eraseblocks[block_nr].nr_pkts++; - ignored_pkts++; - continue; - } - - if (mtd_crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { - printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", - block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), - mtd_crc32(-1, thispkt.data, PKT_SIZE), - ntohl(thispkt.hdr.thiscrc)); - badcrcs++; - continue; - } - pkt_again: - eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = - ntohs(thispkt.hdr.pkt_nr); - total_pkts++; - if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { - uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; - long time_msec; - gettimeofday(&now, NULL); - - time_msec = ((now.tv_usec - start.tv_usec) / 1000) + - (now.tv_sec - start.tv_sec) * 1000; - - printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", - total_pkts, nr_blocks * pkts_per_block, - total_pkts * 100 / nr_blocks / pkts_per_block, - time_msec / 1000, - total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, - pkts_sent - total_pkts - duplicates - ignored_pkts, - (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, - duplicates + ignored_pkts); - fflush(stdout); - } - - if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { - /* New packet doesn't full the wbuf */ - memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, - thispkt.data, PKT_SIZE); - eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; - } else { - int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; - ssize_t wrotelen; - static int faked = 1; - - memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, - thispkt.data, fits); - wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, - eraseblocks[block_nr].flash_offset); - - if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { - faked = 1; - if (wrotelen < 0) - perror("\npacket write"); - else - fprintf(stderr, "\nshort write of packet wbuf\n"); - - if (!file_mode) { - struct erase_info_user erase; - /* FIXME: Perhaps we should store pkt crcs and try - to recover data from the offending eraseblock */ - - /* We have increased nr_pkts but not yet flash_offset */ - erase.start = eraseblocks[block_nr].flash_offset & - ~(meminfo.erasesize - 1); - erase.length = meminfo.erasesize; - - printf("Will erase at %08x len %08x (bad write was at %08x)\n", - erase.start, erase.length, eraseblocks[block_nr].flash_offset); - if (ioctl(flfd, MEMERASE, &erase)) { - perror("MEMERASE"); - exit(1); - } - if (mtdoffset >= meminfo.size) { - fprintf(stderr, "Run out of space on flash\n"); - exit(1); - } - while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { - printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); - mtdoffset += meminfo.erasesize; - if (mtdoffset >= meminfo.size) { - fprintf(stderr, "Run out of space on flash\n"); - exit(1); - } - } - eraseblocks[block_nr].flash_offset = mtdoffset; - printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); - total_pkts -= eraseblocks[block_nr].nr_pkts; - eraseblocks[block_nr].nr_pkts = 0; - eraseblocks[block_nr].wbuf_ofs = 0; - mtdoffset += meminfo.erasesize; - goto pkt_again; - } - else /* Usually nothing we can do in file mode */ - exit(1); - } - eraseblocks[block_nr].flash_offset += WBUF_SIZE; - /* Copy the remainder into the wbuf */ - memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); - eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; - } - - if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { - eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); - - if (total_pkts == nr_blocks * pkts_per_block) - break; - } - } - printf("\n"); - gettimeofday(&now, NULL); - net_time = (now.tv_usec - start.tv_usec) / 1000; - net_time += (now.tv_sec - start.tv_sec) * 1000; - close(sock); - for (block_nr = 0; block_nr < nr_blocks; block_nr++) { - ssize_t rwlen; - gettimeofday(&start, NULL); - eraseblocks[block_nr].flash_offset -= meminfo.erasesize; - rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); - - gettimeofday(&now, NULL); - rflash_time += (now.tv_usec - start.tv_usec) / 1000; - rflash_time += (now.tv_sec - start.tv_sec) * 1000; - if (rwlen < 0) { - perror("read"); - /* Argh. Perhaps we could go back and try again, but if the flash is - going to fail to read back what we write to it, and the whole point - in this program is to write to it, what's the point? */ - fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); - exit(1); - } - - memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, - eraseblocks[block_nr].wbuf_ofs); - - for (i=0; i < pkts_per_block; i++) - src_pkts[i] = &eb_buf[i * PKT_SIZE]; - - gettimeofday(&start, NULL); - if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { - /* Eep. This cannot happen */ - printf("The world is broken. fec_decode() returned error\n"); - exit(1); - } - gettimeofday(&now, NULL); - fec_time += (now.tv_usec - start.tv_usec) / 1000; - fec_time += (now.tv_sec - start.tv_sec) * 1000; - - for (i=0; i < pkts_per_block; i++) - memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); - - /* Paranoia */ - gettimeofday(&start, NULL); - if (mtd_crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { - printf("\nCRC mismatch for block #%d: want %08x got %08x\n", - block_nr, eraseblocks[block_nr].crc, - mtd_crc32(-1, decode_buf, meminfo.erasesize)); - exit(1); - } - gettimeofday(&now, NULL); - crc_time += (now.tv_usec - start.tv_usec) / 1000; - crc_time += (now.tv_sec - start.tv_sec) * 1000; - start = now; - - if (!file_mode) { - struct erase_info_user erase; - - erase.start = eraseblocks[block_nr].flash_offset; - erase.length = meminfo.erasesize; - - printf("\rErasing block at %08x...", erase.start); - - if (ioctl(flfd, MEMERASE, &erase)) { - perror("MEMERASE"); - /* This block has dirty data on it. If the erase failed, we're screwed */ - fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); - exit(1); - } - gettimeofday(&now, NULL); - erase_time += (now.tv_usec - start.tv_usec) / 1000; - erase_time += (now.tv_sec - start.tv_sec) * 1000; - start = now; - } - else printf("\r"); - write_again: - rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); - if (rwlen < meminfo.erasesize) { - if (rwlen < 0) { - perror("\ndecoded data write"); - } else - fprintf(stderr, "\nshort write of decoded data\n"); - - if (!file_mode) { - struct erase_info_user erase; - erase.start = eraseblocks[block_nr].flash_offset; - erase.length = meminfo.erasesize; - - printf("Erasing failed block at %08x\n", - eraseblocks[block_nr].flash_offset); - - if (ioctl(flfd, MEMERASE, &erase)) { - perror("MEMERASE"); - exit(1); - } - if (mtdoffset >= meminfo.size) { - fprintf(stderr, "Run out of space on flash\n"); - exit(1); - } - while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { - printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); - mtdoffset += meminfo.erasesize; - if (mtdoffset >= meminfo.size) { - fprintf(stderr, "Run out of space on flash\n"); - exit(1); - } - } - printf("Will try again at %08lx...", (long)mtdoffset); - eraseblocks[block_nr].flash_offset = mtdoffset; - - goto write_again; - } - else /* Usually nothing we can do in file mode */ - exit(1); - } - gettimeofday(&now, NULL); - flash_time += (now.tv_usec - start.tv_usec) / 1000; - flash_time += (now.tv_sec - start.tv_sec) * 1000; - - printf("wrote image block %08x (%d pkts) ", - block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); - fflush(stdout); - } - close(flfd); - printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); - printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); - printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); - printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); - printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); - printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); - - return 0; -} diff --git a/rfddump.c b/rfddump.c deleted file mode 100644 index 0375bac..0000000 --- a/rfddump.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * 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. - */ - -#define PROGRAM_NAME "rfddump" -#define VERSION "$Revision 1.0 $" - -#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 <mtd_swab.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; - uint16_t *header; - int sector_count; - int *sector_map; - const char *mtd_filename; - const char *out_filename; - int verbose; -}; - -void display_help(void) -{ - printf("Usage: %s [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", - PROGRAM_NAME); - exit(0); -} - -void display_version(void) -{ - printf("%s " 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", - PROGRAM_NAME); - - 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++) { - uint16_t 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; - uint8_t 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(uint16_t) + 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(uint16_t); - - rfd.header = malloc(rfd.header_size); - if (!rfd.header) { - perror(PROGRAM_NAME); - close(fd); - return 2; - } - rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); - if (!rfd.sector_map) { - perror(PROGRAM_NAME); - 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 deleted file mode 100644 index 17d9d2d..0000000 --- a/rfdformat.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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. - * - * This is very easy: just erase all the blocks and put the magic at - * the beginning of each block. - */ - -#define PROGRAM_NAME "rfdformat" -#define VERSION "$Revision 1.0 $" - -#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> - -void display_help(void) -{ - printf("Usage: %s [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", - PROGRAM_NAME); - exit(0); -} - -void display_version(void) -{ - printf("%s " 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", - PROGRAM_NAME); - - 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 uint8_t 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/serve_image.c b/serve_image.c deleted file mode 100644 index d3794ec..0000000 --- a/serve_image.c +++ /dev/null @@ -1,300 +0,0 @@ -#define PROGRAM_NAME "serve_image" -#define _POSIX_C_SOURCE 199309 - -#include <time.h> -#include <errno.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/mman.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <crc32.h> -#include <inttypes.h> - -#include "mcast_image.h" - -int tx_rate = 80000; -int pkt_delay; - -#undef RANDOMDROP - -int main(int argc, char **argv) -{ - struct addrinfo *ai; - struct addrinfo hints; - struct addrinfo *runp; - int ret; - int sock; - struct image_pkt pktbuf; - int rfd; - struct stat st; - int writeerrors = 0; - uint32_t erasesize; - unsigned char *image, *blockptr = NULL; - uint32_t block_nr, pkt_nr; - int nr_blocks; - struct timeval then, now, nextpkt; - long time_msecs; - int pkts_per_block; - int total_pkts_per_block; - struct fec_parms *fec; - unsigned char *last_block; - uint32_t *block_crcs; - long tosleep; - uint32_t sequence = 0; - - if (argc == 6) { - tx_rate = atol(argv[5]) * 1024; - if (tx_rate < PKT_SIZE || tx_rate > 20000000) { - fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); - exit(1); - } - argc = 5; - } - if (argc != 5) { - fprintf(stderr, "usage: %s <host> <port> <image> <erasesize> [<tx_rate>]\n", - PROGRAM_NAME); - exit(1); - } - pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; - printf("Inter-packet delay (avg): %dµs\n", pkt_delay); - printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); - - erasesize = atol(argv[4]); - if (!erasesize) { - fprintf(stderr, "erasesize cannot be zero\n"); - exit(1); - } - - pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; - total_pkts_per_block = pkts_per_block * 3 / 2; - - /* We have to pad it with zeroes, so can't use it in-place */ - last_block = malloc(pkts_per_block * PKT_SIZE); - if (!last_block) { - fprintf(stderr, "Failed to allocate last-block buffer\n"); - exit(1); - } - - fec = fec_new(pkts_per_block, total_pkts_per_block); - if (!fec) { - fprintf(stderr, "Error initialising FEC\n"); - exit(1); - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG; - hints.ai_socktype = SOCK_DGRAM; - - ret = getaddrinfo(argv[1], argv[2], &hints, &ai); - if (ret) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); - exit(1); - } - runp = ai; - for (runp = ai; runp; runp = runp->ai_next) { - sock = socket(runp->ai_family, runp->ai_socktype, - runp->ai_protocol); - if (sock == -1) { - perror("socket"); - continue; - } - if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) - break; - perror("connect"); - close(sock); - } - if (!runp) - exit(1); - - rfd = open(argv[3], O_RDONLY); - if (rfd < 0) { - perror("open"); - exit(1); - } - - if (fstat(rfd, &st)) { - perror("fstat"); - exit(1); - } - - if (st.st_size % erasesize) { - fprintf(stderr, "Image size %" PRIu64 " bytes is not a multiple of erasesize %d bytes\n", - st.st_size, erasesize); - exit(1); - } - image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); - if (image == MAP_FAILED) { - perror("mmap"); - exit(1); - } - - nr_blocks = st.st_size / erasesize; - - block_crcs = malloc(nr_blocks * sizeof(uint32_t)); - if (!block_crcs) { - fprintf(stderr, "Failed to allocate memory for CRCs\n"); - exit(1); - } - - memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); - memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); - - printf("Checking CRC...."); - fflush(stdout); - - pktbuf.hdr.resend = 0; - pktbuf.hdr.totcrc = htonl(mtd_crc32(-1, image, st.st_size)); - pktbuf.hdr.nr_blocks = htonl(nr_blocks); - pktbuf.hdr.blocksize = htonl(erasesize); - pktbuf.hdr.thislen = htonl(PKT_SIZE); - pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); - - printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); - printf("Checking block CRCs...."); - fflush(stdout); - for (block_nr=0; block_nr < nr_blocks; block_nr++) { - printf("\rChecking block CRCS.... %d/%d", - block_nr + 1, nr_blocks); - fflush(stdout); - block_crcs[block_nr] = mtd_crc32(-1, image + (block_nr * erasesize), erasesize); - } - - printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" - "Estimated transmit time per cycle: %ds\n", - (long)st.st_size / 1024, (long) st.st_size, - nr_blocks, pkts_per_block, - nr_blocks * pkts_per_block * pkt_delay / 1000000); - gettimeofday(&then, NULL); - nextpkt = then; - -#ifdef RANDOMDROP - srand((unsigned)then.tv_usec); - printf("Random seed %u\n", (unsigned)then.tv_usec); -#endif - while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { - - if (blockptr && pkt_nr == 0) { - unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); - gettimeofday(&now, NULL); - - time_msecs = (now.tv_sec - then.tv_sec) * 1000; - time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; - printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", - amt_sent / 1024, time_msecs, - amt_sent / 1024 * 1000 / time_msecs); - then = now; - } - - for (block_nr = 0; block_nr < nr_blocks; block_nr++) { - - int actualpkt; - - /* Calculating the redundant FEC blocks is expensive; - the first $pkts_per_block are cheap enough though - because they're just copies. So alternate between - simple and complex stuff, so that we don't start - to choke and fail to keep up with the expected - bitrate in the second half of the sequence */ - if (block_nr & 1) - actualpkt = pkt_nr; - else - actualpkt = total_pkts_per_block - 1 - pkt_nr; - - blockptr = image + (erasesize * block_nr); - if (block_nr == nr_blocks - 1) - blockptr = last_block; - - fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); - - pktbuf.hdr.thiscrc = htonl(mtd_crc32(-1, pktbuf.data, PKT_SIZE)); - pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); - pktbuf.hdr.block_nr = htonl(block_nr); - pktbuf.hdr.pkt_nr = htons(actualpkt); - pktbuf.hdr.pkt_sequence = htonl(sequence++); - - printf("\rSending data block %08x packet %3d/%d", - block_nr * erasesize, - pkt_nr, total_pkts_per_block); - - if (pkt_nr && !block_nr) { - unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); - - gettimeofday(&now, NULL); - - time_msecs = (now.tv_sec - then.tv_sec) * 1000; - time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; - printf(" (%ld KiB/s) ", - amt_sent / 1024 * 1000 / time_msecs); - } - - fflush(stdout); - -#ifdef RANDOMDROP - if ((rand() % 1000) < 20) { - printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); - continue; - } -#endif - gettimeofday(&now, NULL); -#if 1 - tosleep = nextpkt.tv_usec - now.tv_usec + - (1000000 * (nextpkt.tv_sec - now.tv_sec)); - - /* We need hrtimers for this to actually work */ - if (tosleep > 0) { - struct timespec req; - - req.tv_nsec = (tosleep % 1000000) * 1000; - req.tv_sec = tosleep / 1000000; - - nanosleep(&req, NULL); - } -#else - while (now.tv_sec < nextpkt.tv_sec || - (now.tv_sec == nextpkt.tv_sec && - now.tv_usec < nextpkt.tv_usec)) { - gettimeofday(&now, NULL); - } -#endif - nextpkt.tv_usec += pkt_delay; - if (nextpkt.tv_usec >= 1000000) { - nextpkt.tv_sec += nextpkt.tv_usec / 1000000; - nextpkt.tv_usec %= 1000000; - } - - /* If the time for the next packet has already - passed (by some margin), then we've lost time - Adjust our expected timings accordingly. If - we're only a little way behind, don't slip yet */ - if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + - 1000000 * (nextpkt.tv_sec - now.tv_sec))) { - nextpkt = now; - } - - if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { - perror("write"); - writeerrors++; - if (writeerrors > 10) { - fprintf(stderr, "Too many consecutive write errors\n"); - exit(1); - } - } else - writeerrors = 0; - - - - } - } - munmap(image, st.st_size); - close(rfd); - close(sock); - return 0; -} diff --git a/summary.h b/summary.h deleted file mode 100644 index e9d95a5..0000000 --- a/summary.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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. - */ - -#ifndef JFFS2_SUMMARY_H -#define JFFS2_SUMMARY_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)) -#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) -#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) - -/* 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)); - -struct jffs2_sum_xattr_flash -{ - jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ - jint32_t xid; /* xattr identifier */ - jint32_t version; /* version number */ - jint32_t offset; /* offset on jeb */ - jint32_t totlen; /* node length */ -} __attribute__((packed)); - -struct jffs2_sum_xref_flash -{ - jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ - jint32_t offset; /* offset on jeb */ -} __attribute__((packed)); - -union jffs2_sum_flash -{ - struct jffs2_sum_unknown_flash u; - struct jffs2_sum_inode_flash i; - struct jffs2_sum_dirent_flash d; - struct jffs2_sum_xattr_flash x; - struct jffs2_sum_xref_flash r; -}; - -/* 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)); - -struct jffs2_sum_xattr_mem -{ - union jffs2_sum_mem *next; - jint16_t nodetype; - jint32_t xid; - jint32_t version; - jint32_t offset; - jint32_t totlen; -} __attribute__((packed)); - -struct jffs2_sum_xref_mem -{ - union jffs2_sum_mem *next; - jint16_t nodetype; - jint32_t offset; -} __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_sum_xattr_mem x; - struct jffs2_sum_xref_mem r; -}; - -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 deleted file mode 100644 index 886b545..0000000 --- a/sumtool.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * sumtool.c - * - * Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>, - * Ferenc Havasi <havasi@inf.u-szeged.hu> - * University of Szeged, Hungary - * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> - * - * 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 - * - */ - -#define PROGRAM_NAME "sumtool" - -#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" -#include "common.h" - -#define PAD(x) (((x)+3)&~3) - -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 const 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 const 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 full_write(void *target_buff, const void *buf, int len); - -void setup_cleanmarker(void) -{ - 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(mtd_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) - errmsg_die("output filename specified more than once"); - out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); - if (out_fd == -1) - sys_errmsg_die("open output file"); - break; - - case 'i': - if (in_fd != -1) - errmsg_die("input filename specified more than once"); - in_fd = open(optarg, O_RDONLY); - if (in_fd == -1) - sys_errmsg_die("open input file"); - break; - case 'b': - target_endian = __BIG_ENDIAN; - break; - case 'l': - target_endian = __LITTLE_ENDIAN; - break; - case 'h': - case '?': - errmsg_die("%s", helptext); - case 'v': - verbose = 1; - break; - - case 'V': - errmsg_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) - errmsg_die("Unrecognisable erase size\n"); - - if (*next) { - if (!strcmp(next, "KiB")) { - units = 1024; - } else if (!strcmp(next, "MiB")) { - units = 1024 * 1024; - } else { - errmsg_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) { - warnmsg("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)) { - errmsg_die("cleanmarker size must be >= 12"); - } - if (cleanmarker_size >= erase_block_size) { - errmsg_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(void) -{ - data_buffer = xmalloc(erase_block_size); - file_buffer = xmalloc(erase_block_size); -} - -void init_sumlist(void) -{ - sum_collected = xzalloc(sizeof(*sum_collected)); -} - -void clean_buffers(void) -{ - free(data_buffer); - free(file_buffer); -} - -void clean_sumlist(void) -{ - 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) - warnmsg("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); - - free(sum_collected); - } -} - -int load_next_block(void) -{ - int ret; - ret = read(in_fd, file_buffer, erase_block_size); - file_ofs = 0; - - bareverbose(verbose, "Load next block : %d bytes read\n", ret); - - return ret; -} - -void write_buff_to_file(void) -{ - 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) - sys_errmsg_die("write"); - - if (ret == 0) - sys_errmsg_die("write returned zero"); - - len -= ret; - buf += ret; - } - - data_ofs = 0; -} - -void dump_sum_records(void) -{ - - 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 = xmalloc(datasize); - - 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(mtd_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; - } - - case JFFS2_NODETYPE_XATTR: { - struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; - - sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; - sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; - sxattr_ptr->version = sum_collected->sum_list_head->x.version; - sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; - sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; - - wpage += JFFS2_SUMMARY_XATTR_SIZE; - break; - } - - case JFFS2_NODETYPE_XREF: { - struct jffs2_sum_xref_flash *sxref_ptr = wpage; - - sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; - sxref_ptr->offset = sum_collected->sum_list_head->r.offset; - - wpage += JFFS2_SUMMARY_XREF_SIZE; - break; - } - - default : { - warnmsg("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(mtd_crc32(0, tpage, datasize)); - isum.node_crc = cpu_to_je32(mtd_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(void) -{ - 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(void) -{ - - 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; - - case JFFS2_NODETYPE_XATTR: - sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; - sum_collected->sum_num++; - break; - - case JFFS2_NODETYPE_XREF: - sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; - sum_collected->sum_num++; - break; - - default: - errmsg_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 = xmalloc(sizeof(*temp)); - - 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 = xmalloc(sizeof(*temp) + node->d.nsize); - - 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 add_sum_xattr_mem(union jffs2_node_union *node) -{ - struct jffs2_sum_xattr_mem *temp = xmalloc(sizeof(*temp)); - - temp->nodetype = node->x.nodetype; - temp->xid = node->x.xid; - temp->version = node->x.version; - temp->offset = cpu_to_je32(data_ofs); - temp->totlen = node->x.totlen; - temp->next = NULL; - - add_sum_mem((union jffs2_sum_mem *) temp); -} - -void add_sum_xref_mem(union jffs2_node_union *node) -{ - struct jffs2_sum_xref_mem *temp = xmalloc(sizeof(*temp)); - - temp->nodetype = node->r.nodetype; - temp->offset = cpu_to_je32(data_ofs); - temp->next = NULL; - - 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 write_xattr_to_buff(union jffs2_node_union *node) -{ - pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); - add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ - full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); - padword(); -} - -void write_xref_to_buff(union jffs2_node_union *node) -{ - pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); - add_sum_xref_mem(node); /* Add xref summary mem to summary list */ - full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); - padword(); -} - -void create_summed_image(int inp_size) -{ - uint8_t *p = file_buffer; - union jffs2_node_union *node; - uint32_t crc, length; - 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++) - warnmsg("Wrong bitmask at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); - if (crc != je32_to_cpu (node->u.hdr_crc)) { - warnmsg("Wrong hdr_crc at 0x%08zx, 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: - bareverbose(verbose, - "%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", - obsolete ? "Obsolete" : "", - p - 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); - if (crc != je32_to_cpu (node->i.node_crc)) { - warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); - if (crc != je32_to_cpu(node->i.data_crc)) { - warnmsg("Wrong data_crc at 0x%08zx, 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; - - bareverbose(verbose, - "%8s Dirent node at 0x%08zx, 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 = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); - if (crc != je32_to_cpu (node->d.node_crc)) { - warnmsg("Wrong node_crc at 0x%08zx, 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 = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); - if (crc != je32_to_cpu(node->d.name_crc)) { - warnmsg("Wrong name_crc at 0x%08zx, 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_XATTR: - if (je32_to_cpu(node->x.node_crc) == 0xffffffff) - obsolete = 1; - bareverbose(verbose, - "%8s Xdatum node at 0x%08zx, totlen 0x%08x, #xid %5u, version %5u\n", - obsolete ? "Obsolete" : "", - p - file_buffer, je32_to_cpu (node->x.totlen), - je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); - crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); - if (crc != je32_to_cpu(node->x.node_crc)) { - warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", - p - file_buffer, je32_to_cpu(node->x.node_crc), crc); - p += PAD(je32_to_cpu (node->x.totlen)); - continue; - } - length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); - crc = mtd_crc32(0, node->x.data, length); - if (crc != je32_to_cpu(node->x.data_crc)) { - warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", - p - file_buffer, je32_to_cpu(node->x.data_crc), crc); - p += PAD(je32_to_cpu (node->x.totlen)); - continue; - } - - write_xattr_to_buff(node); - p += PAD(je32_to_cpu (node->x.totlen)); - break; - - case JFFS2_NODETYPE_XREF: - if (je32_to_cpu(node->r.node_crc) == 0xffffffff) - obsolete = 1; - bareverbose(verbose, - "%8s Xref node at 0x%08zx, totlen 0x%08x, #ino %5u, xid %5u\n", - obsolete ? "Obsolete" : "", - p - file_buffer, je32_to_cpu(node->r.totlen), - je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); - crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); - if (crc != je32_to_cpu(node->r.node_crc)) { - warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", - p - file_buffer, je32_to_cpu(node->r.node_crc), crc); - p += PAD(je32_to_cpu (node->r.totlen)); - continue; - } - - write_xref_to_buff(node); - p += PAD(je32_to_cpu (node->r.totlen)); - break; - - case JFFS2_NODETYPE_CLEANMARKER: - bareverbose(verbose, - "%8s Cleanmarker at 0x%08zx, 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: - bareverbose(verbose, - "%8s Padding node at 0x%08zx, 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: - bareverbose(verbose, - "%8s Unknown node at 0x%08zx, 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, "%s", helptext); - errmsg_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; -} diff --git a/ubifs-utils/mkfs.ubifs/.gitignore b/ubifs-utils/mkfs.ubifs/.gitignore new file mode 100644 index 0000000..6b0e85c --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/.gitignore @@ -0,0 +1 @@ +/mkfs.ubifs diff --git a/ubifs-utils/mkfs.ubifs/COPYING b/ubifs-utils/mkfs.ubifs/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/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/ubifs-utils/mkfs.ubifs/README b/ubifs-utils/mkfs.ubifs/README new file mode 100644 index 0000000..7e19939 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/README @@ -0,0 +1,9 @@ +UBIFS File System - Make File System program + +* crc16.h and crc16.c were copied from the linux kernel. +* crc32.h and crc32.c were copied from mtd-utils and amended. +* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. +* key.h is copied from fs/ubifs/key.h from the linux kernel. +* defs.h is a bunch of definitions to smooth things over. +* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. +* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ diff --git a/ubifs-utils/mkfs.ubifs/compr.c b/ubifs-utils/mkfs.ubifs/compr.c new file mode 100644 index 0000000..34b2f60 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/compr.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 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 version 2 as published by + * the Free Software Foundation. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <lzo/lzo1x.h> +#include <linux/types.h> + +#define crc32 __zlib_crc32 +#include <zlib.h> +#undef crc32 + +#include "compr.h" +#include "mkfs.ubifs.h" + +static void *lzo_mem; +static unsigned long long errcnt = 0; +static struct ubifs_info *c = &info_; + +#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION +#define DEFLATE_DEF_WINBITS 11 +#define DEFLATE_DEF_MEMLEVEL 8 + +static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len) +{ + z_stream strm; + + strm.zalloc = NULL; + strm.zfree = NULL; + + /* + * Match exactly the zlib parameters used by the Linux kernel crypto + * API. + */ + if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, + -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, + Z_DEFAULT_STRATEGY)) { + errcnt += 1; + return -1; + } + + strm.next_in = in_buf; + strm.avail_in = in_len; + strm.total_in = 0; + + strm.next_out = out_buf; + strm.avail_out = *out_len; + strm.total_out = 0; + + if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { + deflateEnd(&strm); + errcnt += 1; + return -1; + } + + if (deflateEnd(&strm) != Z_OK) { + errcnt += 1; + return -1; + } + + *out_len = strm.total_out; + + return 0; +} + +static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len) +{ + lzo_uint len; + int ret; + + len = *out_len; + ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); + *out_len = len; + + if (ret != LZO_E_OK) { + errcnt += 1; + return -1; + } + + return 0; +} + +static int no_compress(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len) +{ + memcpy(out_buf, in_buf, in_len); + *out_len = in_len; + return 0; +} + +static char *zlib_buf; + +static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len, int *type) +{ + int lzo_ret, zlib_ret; + size_t lzo_len, zlib_len; + + lzo_len = zlib_len = *out_len; + lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); + zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); + + if (lzo_ret && zlib_ret) + /* Both compressors failed */ + return -1; + + if (!lzo_ret && !zlib_ret) { + double percent; + + /* Both compressors succeeded */ + if (lzo_len <= zlib_len ) + goto select_lzo; + + percent = (double)zlib_len / (double)lzo_len; + percent *= 100; + if (percent > 100 - c->favor_percent) + goto select_lzo; + goto select_zlib; + } + + if (lzo_ret) + /* Only zlib compressor succeeded */ + goto select_zlib; + + /* Only LZO compressor succeeded */ + +select_lzo: + *out_len = lzo_len; + *type = MKFS_UBIFS_COMPR_LZO; + return 0; + +select_zlib: + *out_len = zlib_len; + *type = MKFS_UBIFS_COMPR_ZLIB; + memcpy(out_buf, zlib_buf, zlib_len); + return 0; +} + +int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, + int type) +{ + int ret; + + if (in_len < UBIFS_MIN_COMPR_LEN) { + no_compress(in_buf, in_len, out_buf, out_len); + return MKFS_UBIFS_COMPR_NONE; + } + + if (c->favor_lzo) + ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); + else { + switch (type) { + case MKFS_UBIFS_COMPR_LZO: + ret = lzo_compress(in_buf, in_len, out_buf, out_len); + break; + case MKFS_UBIFS_COMPR_ZLIB: + ret = zlib_deflate(in_buf, in_len, out_buf, out_len); + break; + case MKFS_UBIFS_COMPR_NONE: + ret = 1; + break; + default: + errcnt += 1; + ret = 1; + break; + } + } + if (ret || *out_len >= in_len) { + no_compress(in_buf, in_len, out_buf, out_len); + return MKFS_UBIFS_COMPR_NONE; + } + return type; +} + +int init_compression(void) +{ + lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); + if (!lzo_mem) + return -1; + + zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); + if (!zlib_buf) { + free(lzo_mem); + return -1; + } + + return 0; +} + +void destroy_compression(void) +{ + free(zlib_buf); + free(lzo_mem); + if (errcnt) + fprintf(stderr, "%llu compression errors occurred\n", errcnt); +} diff --git a/ubifs-utils/mkfs.ubifs/compr.h b/ubifs-utils/mkfs.ubifs/compr.h new file mode 100644 index 0000000..e3dd95c --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/compr.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 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 version 2 as published by + * the Free Software Foundation. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#ifndef __UBIFS_COMPRESS_H__ +#define __UBIFS_COMPRESS_H__ + +/* + * Compressors may end-up with more data in the output buffer than in the input + * buffer. This constant defined the worst case factor, i.e. we assume that the + * output buffer may be at max. WORST_COMPR_FACTOR times larger than input + * buffer. + */ +#define WORST_COMPR_FACTOR 4 + +enum compression_type +{ + MKFS_UBIFS_COMPR_NONE, + MKFS_UBIFS_COMPR_LZO, + MKFS_UBIFS_COMPR_ZLIB, +}; + +int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, + int type); +int init_compression(void); +void destroy_compression(void); + +#endif diff --git a/ubifs-utils/mkfs.ubifs/crc16.c b/ubifs-utils/mkfs.ubifs/crc16.c new file mode 100644 index 0000000..a19512e --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/crc16.c @@ -0,0 +1,56 @@ +/* + * This code was taken from the linux kernel. The license is GPL Version 2. + */ + +#include "crc16.h" + +/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ +uint16_t const crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * crc16 - compute the CRC-16 for the data buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer + * + * Returns the updated CRC value. + */ +uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) +{ + while (len--) + crc = crc16_byte(crc, *buffer++); + return crc; +} diff --git a/ubifs-utils/mkfs.ubifs/crc16.h b/ubifs-utils/mkfs.ubifs/crc16.h new file mode 100644 index 0000000..539d21a --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/crc16.h @@ -0,0 +1,27 @@ +/* + * Implements the standard CRC-16: + * Width 16 + * Poly 0x8005 (x^16 + x^15 + x^2 + 1) + * Init 0 + * + * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> + * + * This code was taken from the linux kernel. The license is GPL Version 2. + */ + +#ifndef __CRC16_H__ +#define __CRC16_H__ + +#include <stdlib.h> +#include <stdint.h> + +extern uint16_t const crc16_table[256]; + +extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len); + +static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data) +{ + return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; +} + +#endif /* __CRC16_H__ */ diff --git a/ubifs-utils/mkfs.ubifs/defs.h b/ubifs-utils/mkfs.ubifs/defs.h new file mode 100644 index 0000000..1fa3316 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/defs.h @@ -0,0 +1,92 @@ +/* + * Greate deal of the code was taken from the kernel UBIFS implementation, and + * this file contains some "glue" definitions. + */ + +#ifndef __UBIFS_DEFS_H__ +#define __UBIFS_DEFS_H__ + +#define t16(x) ({ \ + uint16_t __b = (x); \ + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ +}) + +#define t32(x) ({ \ + uint32_t __b = (x); \ + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ +}) + +#define t64(x) ({ \ + uint64_t __b = (x); \ + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ +}) + +#define cpu_to_le16(x) ((__le16){t16(x)}) +#define cpu_to_le32(x) ((__le32){t32(x)}) +#define cpu_to_le64(x) ((__le64){t64(x)}) + +#define le16_to_cpu(x) (t16((x))) +#define le32_to_cpu(x) (t32((x))) +#define le64_to_cpu(x) (t64((x))) + +#define unlikely(x) (x) + +#define ubifs_assert(x) ({}) + +struct qstr +{ + char *name; + size_t len; +}; + +/** + * fls - find last (most-significant) bit set + * @x: the word to search + * + * This is defined the same way as ffs. + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ +static inline int fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +#if INT_MAX != 0x7fffffff +#error : sizeof(int) must be 4 for this program +#endif + +#if (~0ULL) != 0xffffffffffffffffULL +#error : sizeof(long long) must be 8 for this program +#endif + +#endif diff --git a/ubifs-utils/mkfs.ubifs/devtable.c b/ubifs-utils/mkfs.ubifs/devtable.c new file mode 100644 index 0000000..dee035d --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/devtable.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * + * 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. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Artem Bityutskiy + * + * Part of the device table parsing code was taken from the mkfs.jffs2 utility. + * The original author of that code is Erik Andersen, hence: + * Copyright (C) 2001, 2002 Erik Andersen <andersen@codepoet.org> + */ + +/* + * This file implemented device table support. 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) + * + * 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. + * + * Please, refer the device_table.txt file which can be found at MTD utilities + * for more information about what the device table is. + */ + +#include "mkfs.ubifs.h" +#include "hashtable/hashtable.h" +#include "hashtable/hashtable_itr.h" + +/* + * The hash table which contains paths to files/directories/device nodes + * referred to in the device table. For example, if the device table refers + * "/dev/loop0", the @path_htbl will contain "/dev" element. + */ +static struct hashtable *path_htbl; + +/* Hash function used for hash tables */ +static unsigned int r5_hash(void *s) +{ + unsigned int a = 0; + const signed char *str = s; + + while (*str) { + a += *str << 4; + a += *str >> 4; + a *= 11; + str++; + } + + return a; +} + +/* + * Check whether 2 keys of a hash table are equivalent. The keys are path/file + * names, so we simply use 'strcmp()'. + */ +static int is_equivalent(void *k1, void *k2) +{ + return !strcmp(k1, k2); +} + +/** + * separate_last - separate out the last path component + * @buf: the path to split + * @len: length of the @buf string + * @path: the beginning of path is returned here + * @name: the last path component is returned here + * + * This helper function separates out the the last component of the full path + * string. For example, "/dev/loop" would be split on "/dev" and "loop". This + * function allocates memory for @path and @name and return the result there. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +static int separate_last(const char *buf, int len, char **path, char **name) +{ + int path_len = len, name_len; + const char *p = buf + len, *n; + + while (*--p != '/') + path_len -= 1; + + /* Drop the final '/' unless this is the root directory */ + name_len = len - path_len; + n = buf + path_len; + if (path_len > 1) + path_len -= 1; + + *path = malloc(path_len + 1); + if (!*path) + return err_msg("cannot allocate %d bytes of memory", + path_len + 1); + memcpy(*path, buf, path_len); + (*path)[path_len] = '\0'; + + *name = malloc(name_len + 1); + if (!*name) { + free(*path); + return err_msg("cannot allocate %d bytes of memory", + name_len + 1); + } + memcpy(*name, n, name_len + 1); + + return 0; +} + +static int interpret_table_entry(const char *line) +{ + char buf[1024], type, *path = NULL, *name = NULL; + int len; + struct path_htbl_element *ph_elt = NULL; + struct name_htbl_element *nh_elt = NULL; + unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; + unsigned int start = 0, increment = 0, count = 0; + + if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u", + buf, &type, &mode, &uid, &gid, &major, &minor, + &start, &increment, &count) < 0) + return sys_err_msg("sscanf failed"); + + dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, " + "minor %u, start %u, inc %u, cnt %u", + buf, type, mode, uid, gid, major, minor, start, + increment, count); + + len = strnlen(buf, 1024); + if (len == 1024) + return err_msg("too long path"); + + if (!strcmp(buf, "/")) + return err_msg("device table entries require absolute paths"); + if (buf[1] == '\0') + return err_msg("root directory cannot be created"); + if (strstr(buf, "//")) + return err_msg("'//' cannot be used in the path"); + if (buf[len - 1] == '/') + return err_msg("do not put '/' at the end"); + + if (strstr(buf, "/./") || strstr(buf, "/../") || + !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/..")) + return err_msg("'.' and '..' cannot be used in the path"); + + 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: + return err_msg("unsupported file type '%c'", type); + } + + if (separate_last(buf, len, &path, &name)) + return -1; + + /* + * Check if this path already exist in the path hash table and add it + * if it is not. + */ + ph_elt = hashtable_search(path_htbl, path); + if (!ph_elt) { + dbg_msg(3, "inserting '%s' into path hash table", path); + ph_elt = malloc(sizeof(struct path_htbl_element)); + if (!ph_elt) { + err_msg("cannot allocate %zd bytes of memory", + sizeof(struct path_htbl_element)); + goto out_free; + } + + if (!hashtable_insert(path_htbl, path, ph_elt)) { + err_msg("cannot insert into path hash table"); + goto out_free; + } + + ph_elt->path = path; + path = NULL; + ph_elt->name_htbl = create_hashtable(128, &r5_hash, + &is_equivalent); + if (!ph_elt->name_htbl) { + err_msg("cannot create name hash table"); + goto out_free; + } + } + + if (increment != 0 && count == 0) + return err_msg("count cannot be zero if increment is non-zero"); + + /* + * Add the file/directory/device node (last component of the path) to + * the name hashtable. The name hashtable resides in the corresponding + * path hashtable element. + */ + + if (count == 0) { + /* This entry does not require any iterating */ + nh_elt = malloc(sizeof(struct name_htbl_element)); + if (!nh_elt) { + err_msg("cannot allocate %zd bytes of memory", + sizeof(struct name_htbl_element)); + goto out_free; + } + + nh_elt->mode = mode; + nh_elt->uid = uid; + nh_elt->gid = gid; + nh_elt->dev = makedev(major, minor); + + dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", + name, major(nh_elt->dev), minor(nh_elt->dev)); + + if (hashtable_search(ph_elt->name_htbl, name)) + return err_msg("'%s' is referred twice", buf); + + nh_elt->name = name; + if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) { + err_msg("cannot insert into name hash table"); + goto out_free; + } + } else { + int i, num = start + count, len = strlen(name) + 20; + char *nm; + + for (i = start; i < num; i++) { + nh_elt = malloc(sizeof(struct name_htbl_element)); + if (!nh_elt) { + err_msg("cannot allocate %zd bytes of memory", + sizeof(struct name_htbl_element)); + goto out_free; + } + + nh_elt->mode = mode; + nh_elt->uid = uid; + nh_elt->gid = gid; + nh_elt->dev = makedev(major, minor + (i - start) * increment); + + nm = malloc(len); + if (!nm) { + err_msg("cannot allocate %d bytes of memory", len); + goto out_free; + } + + sprintf(nm, "%s%d", name, i); + nh_elt->name = nm; + + dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", + nm, major(nh_elt->dev), minor(nh_elt->dev)); + + if (hashtable_search(ph_elt->name_htbl, nm)) { + err_msg("'%s' is referred twice", buf); + free (nm); + goto out_free; + } + + if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { + err_msg("cannot insert into name hash table"); + free (nm); + goto out_free; + } + } + free(name); + name = NULL; + } + + return 0; + +out_free: + free(ph_elt); + free(nh_elt); + free(path); + free(name); + return -1; +} + +/** + * parse_devtable - parse the device table. + * @tbl_file: device table file name + * + * This function parses the device table and prepare the hash table which will + * later be used by mkfs.ubifs to create the specified files/device nodes. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int parse_devtable(const char *tbl_file) +{ + FILE *f; + char *line = NULL; + struct stat st; + size_t len; + + dbg_msg(1, "parsing device table file '%s'", tbl_file); + + path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); + if (!path_htbl) + return err_msg("cannot create path hash table"); + + f = fopen(tbl_file, "r"); + if (!f) + return sys_err_msg("cannot open '%s'", tbl_file); + + if (fstat(fileno(f), &st) < 0) { + sys_err_msg("cannot stat '%s'", tbl_file); + goto out_close; + } + + if (st.st_size < 10) { + sys_err_msg("'%s' is too short", tbl_file); + goto out_close; + } + + /* + * 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 + */ + while (getline(&line, &len, f) != -1) { + /* First trim off any white-space */ + len = strlen(line); + + /* Trim trailing white-space */ + while (len > 0 && isspace(line[len - 1])) + line[--len] = '\0'; + /* Trim leading white-space */ + 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(line)) { + err_msg("cannot parse '%s'", line); + goto out_close; + } + } + + free(line); + line = NULL; + } + + dbg_msg(1, "finished parsing"); + fclose(f); + return 0; + +out_close: + fclose(f); + free_devtable_info(); + return -1; +} + +/** + * devtbl_find_path - find a path in the path hash table. + * @path: UBIFS path to find. + * + * This looks up the path hash table. Returns the path hash table element + * reference if @path was found and %NULL if not. + */ +struct path_htbl_element *devtbl_find_path(const char *path) +{ + if (!path_htbl) + return NULL; + + return hashtable_search(path_htbl, (void *)path); +} + +/** + * devtbl_find_name - find a name in the name hash table. + * @ph_etl: path hash table element to find at + * @name: name to find + * + * This looks up the name hash table. Returns the name hash table element + * reference if @name found and %NULL if not. + */ +struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, + const char *name) +{ + if (!path_htbl) + return NULL; + + return hashtable_search(ph_elt->name_htbl, (void *)name); +} + +/** + * override_attributes - override inode attributes. + * @st: struct stat object to containing the attributes to override + * @ph_elt: path hash table element object + * @nh_elt: name hash table element object containing the new values + * + * The device table file may override attributes like UID of files. For + * example, the device table may contain a "/dev" entry, and the UBIFS FS on + * the host may contain "/dev" directory. In this case the attributes of the + * "/dev" directory inode has to be as the device table specifies. + * + * Note, the hash element is removed by this function as well. + */ +int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, + struct name_htbl_element *nh_elt) +{ + if (!path_htbl) + return 0; + + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || + S_ISFIFO(st->st_mode)) + return err_msg("%s/%s both exists at UBIFS root at host, " + "and is referred from the device table", + strcmp(ph_elt->path, "/") ? ph_elt->path : "", + nh_elt->name); + + if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) + return err_msg("%s/%s is referred from the device table also exists in " + "the UBIFS root directory at host, but the file type is " + "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", + nh_elt->name); + + dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", + nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); + + st->st_uid = nh_elt->uid; + st->st_gid = nh_elt->gid; + st->st_mode = nh_elt->mode; + + hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); + return 0; +} + +/** + * first_name_htbl_element - return first element of the name hash table. + * @ph_elt: the path hash table the name hash table belongs to + * @itr: double pointer to a 'struct hashtable_itr' object where the + * information about further iterations is stored + * + * This function implements name hash table iteration together with + * 'next_name_htbl_element()'. Returns the first name hash table element or + * %NULL if the hash table is empty. + */ +struct name_htbl_element * +first_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr) +{ + if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) + return NULL; + + *itr = hashtable_iterator(ph_elt->name_htbl); + return hashtable_iterator_value(*itr); +} + +/** + * first_name_htbl_element - return next element of the name hash table. + * @ph_elt: the path hash table the name hash table belongs to + * @itr: double pointer to a 'struct hashtable_itr' object where the + * information about further iterations is stored + * + * This function implements name hash table iteration together with + * 'first_name_htbl_element()'. Returns the next name hash table element or + * %NULL if there are no more elements. + */ +struct name_htbl_element * +next_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr) +{ + if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) + return NULL; + + return hashtable_iterator_value(*itr); +} + +/** + * free_devtable_info - free device table information. + * + * This function frees the path hash table and the name hash tables. + */ +void free_devtable_info(void) +{ + struct hashtable_itr *ph_itr; + struct path_htbl_element *ph_elt; + + if (!path_htbl) + return; + + if (hashtable_count(path_htbl) > 0) { + ph_itr = hashtable_iterator(path_htbl); + do { + ph_elt = hashtable_iterator_value(ph_itr); + /* + * Note, since we use the same string for the key and + * @name in the name hash table elements, we do not + * have to iterate name hash table because @name memory + * will be freed when freeing the key. + */ + hashtable_destroy(ph_elt->name_htbl, 1); + } while (hashtable_iterator_advance(ph_itr)); + } + hashtable_destroy(path_htbl, 1); +} diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c new file mode 100644 index 0000000..c1f99ed --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.c @@ -0,0 +1,277 @@ +/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ + +#define PROGRAM_NAME "hashtable" + +#include "common.h" +#include "hashtable.h" +#include "hashtable_private.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +/* +Credit for primes table: Aaron Krowne + http://br.endernet.org/~akrowne/ + http://planetmath.org/encyclopedia/GoodHashTablePrimes.html +*/ +static const unsigned int primes[] = { +53, 97, 193, 389, +769, 1543, 3079, 6151, +12289, 24593, 49157, 98317, +196613, 393241, 786433, 1572869, +3145739, 6291469, 12582917, 25165843, +50331653, 100663319, 201326611, 402653189, +805306457, 1610612741 +}; +const unsigned int prime_table_length = ARRAY_SIZE(primes); +const float max_load_factor = 0.65; + +/*****************************************************************************/ +struct hashtable * +create_hashtable(unsigned int minsize, + unsigned int (*hashf) (void*), + int (*eqf) (void*,void*)) +{ + struct hashtable *h; + unsigned int pindex, size = primes[0]; + /* Check requested hashtable isn't too large */ + if (minsize > (1u << 30)) return NULL; + /* Enforce size as prime */ + for (pindex=0; pindex < prime_table_length; pindex++) { + if (primes[pindex] > minsize) { size = primes[pindex]; break; } + } + h = (struct hashtable *)malloc(sizeof(struct hashtable)); + if (NULL == h) return NULL; /*oom*/ + h->table = (struct entry **)malloc(sizeof(struct entry*) * size); + if (NULL == h->table) { free(h); return NULL; } /*oom*/ + memset(h->table, 0, size * sizeof(struct entry *)); + h->tablelength = size; + h->primeindex = pindex; + h->entrycount = 0; + h->hashfn = hashf; + h->eqfn = eqf; + h->loadlimit = (unsigned int) ceil(size * max_load_factor); + return h; +} + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k) +{ + /* Aim to protect against poor hash functions by adding logic here + * - logic taken from java 1.4 hashtable source */ + unsigned int i = h->hashfn(k); + i += ~(i << 9); + i ^= ((i >> 14) | (i << 18)); /* >>> */ + i += (i << 4); + i ^= ((i >> 10) | (i << 22)); /* >>> */ + return i; +} + +/*****************************************************************************/ +static int +hashtable_expand(struct hashtable *h) +{ + /* Double the size of the table to accomodate more entries */ + struct entry **newtable; + struct entry *e; + struct entry **pE; + unsigned int newsize, i, index; + /* Check we're not hitting max capacity */ + if (h->primeindex == (prime_table_length - 1)) return 0; + newsize = primes[++(h->primeindex)]; + + newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); + if (NULL != newtable) + { + memset(newtable, 0, newsize * sizeof(struct entry *)); + /* This algorithm is not 'stable'. ie. it reverses the list + * when it transfers entries between the tables */ + for (i = 0; i < h->tablelength; i++) { + while (NULL != (e = h->table[i])) { + h->table[i] = e->next; + index = indexFor(newsize,e->h); + e->next = newtable[index]; + newtable[index] = e; + } + } + free(h->table); + h->table = newtable; + } + /* Plan B: realloc instead */ + else + { + newtable = (struct entry **) + realloc(h->table, newsize * sizeof(struct entry *)); + if (NULL == newtable) { (h->primeindex)--; return 0; } + h->table = newtable; + memset(newtable[h->tablelength], 0, newsize - h->tablelength); + for (i = 0; i < h->tablelength; i++) { + for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { + index = indexFor(newsize,e->h); + if (index == i) + { + pE = &(e->next); + } + else + { + *pE = e->next; + e->next = newtable[index]; + newtable[index] = e; + } + } + } + } + h->tablelength = newsize; + h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); + return -1; +} + +/*****************************************************************************/ +unsigned int +hashtable_count(struct hashtable *h) +{ + return h->entrycount; +} + +/*****************************************************************************/ +int +hashtable_insert(struct hashtable *h, void *k, void *v) +{ + /* This method allows duplicate keys - but they shouldn't be used */ + unsigned int index; + struct entry *e; + if (++(h->entrycount) > h->loadlimit) + { + /* Ignore the return value. If expand fails, we should + * still try cramming just this value into the existing table + * -- we may not have memory for a larger table, but one more + * element may be ok. Next time we insert, we'll try expanding again.*/ + hashtable_expand(h); + } + e = (struct entry *)malloc(sizeof(struct entry)); + if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ + e->h = hash(h,k); + index = indexFor(h->tablelength,e->h); + e->k = k; + e->v = v; + e->next = h->table[index]; + h->table[index] = e; + return -1; +} + +/*****************************************************************************/ +void * /* returns value associated with key */ +hashtable_search(struct hashtable *h, void *k) +{ + struct entry *e; + unsigned int hashvalue, index; + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + e = h->table[index]; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +void * /* returns value associated with key */ +hashtable_remove(struct hashtable *h, void *k) +{ + /* TODO: consider compacting the table when the load factor drops enough, + * or provide a 'compact' method. */ + + struct entry *e; + struct entry **pE; + void *v; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hash(h,k)); + pE = &(h->table[index]); + e = *pE; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + *pE = e->next; + h->entrycount--; + v = e->v; + freekey(e->k); + free(e); + return v; + } + pE = &(e->next); + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +/* destroy */ +void +hashtable_destroy(struct hashtable *h, int free_values) +{ + unsigned int i; + struct entry *e, *f; + struct entry **table = h->table; + if (free_values) + { + for (i = 0; i < h->tablelength; i++) + { + e = table[i]; + while (NULL != e) + { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } + } + } + else + { + for (i = 0; i < h->tablelength; i++) + { + e = table[i]; + while (NULL != e) + { f = e; e = e->next; freekey(f->k); free(f); } + } + } + free(h->table); + free(h); +} + +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER + * 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. +*/ diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h new file mode 100644 index 0000000..c0b0acd --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable.h @@ -0,0 +1,199 @@ +/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ + +#ifndef __HASHTABLE_CWC22_H__ +#define __HASHTABLE_CWC22_H__ + +struct hashtable; + +/* Example of use: + * + * struct hashtable *h; + * struct some_key *k; + * struct some_value *v; + * + * static unsigned int hash_from_key_fn( void *k ); + * static int keys_equal_fn ( void *key1, void *key2 ); + * + * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); + * k = (struct some_key *) malloc(sizeof(struct some_key)); + * v = (struct some_value *) malloc(sizeof(struct some_value)); + * + * (initialise k and v to suitable values) + * + * if (! hashtable_insert(h,k,v) ) + * { exit(-1); } + * + * if (NULL == (found = hashtable_search(h,k) )) + * { printf("not found!"); } + * + * if (NULL == (found = hashtable_remove(h,k) )) + * { printf("Not found\n"); } + * + */ + +/* Macros may be used to define type-safe(r) hashtable access functions, with + * methods specialized to take known key and value types as parameters. + * + * Example: + * + * Insert this at the start of your file: + * + * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); + * + * This defines the functions 'insert_some', 'search_some' and 'remove_some'. + * These operate just like hashtable_insert etc., with the same parameters, + * but their function signatures have 'struct some_key *' rather than + * 'void *', and hence can generate compile time errors if your program is + * supplying incorrect data as a key (and similarly for value). + * + * Note that the hash and key equality functions passed to create_hashtable + * still take 'void *' parameters instead of 'some key *'. This shouldn't be + * a difficult issue as they're only defined and passed once, and the other + * functions will ensure that only valid keys are supplied to them. + * + * The cost for this checking is increased code size and runtime overhead + * - if performance is important, it may be worth switching back to the + * unsafe methods once your program has been debugged with the safe methods. + * This just requires switching to some simple alternative defines - eg: + * #define insert_some hashtable_insert + * + */ + +/***************************************************************************** + * create_hashtable + + * @name create_hashtable + * @param minsize minimum initial size of hashtable + * @param hashfunction function for hashing keys + * @param key_eq_fn function for determining key equality + * @return newly created hashtable or NULL on failure + */ + +struct hashtable * +create_hashtable(unsigned int minsize, + unsigned int (*hashfunction) (void*), + int (*key_eq_fn) (void*,void*)); + +/***************************************************************************** + * hashtable_insert + + * @name hashtable_insert + * @param h the hashtable to insert into + * @param k the key - hashtable claims ownership and will free on removal + * @param v the value - does not claim ownership + * @return non-zero for successful insertion + * + * This function will cause the table to expand if the insertion would take + * the ratio of entries to table size over the maximum load factor. + * + * This function does not check for repeated insertions with a duplicate key. + * The value returned when using a duplicate key is undefined -- when + * the hashtable changes size, the order of retrieval of duplicate key + * entries is reversed. + * If in doubt, remove before insert. + */ + +int +hashtable_insert(struct hashtable *h, void *k, void *v); + +#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ +int fnname (struct hashtable *h, keytype *k, valuetype *v) \ +{ \ + return hashtable_insert(h,k,v); \ +} + +/***************************************************************************** + * hashtable_search + + * @name hashtable_search + * @param h the hashtable to search + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +void * +hashtable_search(struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ +valuetype * fnname (struct hashtable *h, keytype *k) \ +{ \ + return (valuetype *) (hashtable_search(h,k)); \ +} + +/***************************************************************************** + * hashtable_remove + + * @name hashtable_remove + * @param h the hashtable to remove the item from + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +void * /* returns value */ +hashtable_remove(struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ +valuetype * fnname (struct hashtable *h, keytype *k) \ +{ \ + return (valuetype *) (hashtable_remove(h,k)); \ +} + + +/***************************************************************************** + * hashtable_count + + * @name hashtable_count + * @param h the hashtable + * @return the number of items stored in the hashtable + */ +unsigned int +hashtable_count(struct hashtable *h); + + +/***************************************************************************** + * hashtable_destroy + + * @name hashtable_destroy + * @param h the hashtable + * @param free_values whether to call 'free' on the remaining values + */ + +void +hashtable_destroy(struct hashtable *h, int free_values); + +#endif /* __HASHTABLE_CWC22_H__ */ + +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER + * 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. +*/ diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c new file mode 100644 index 0000000..d102453 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c @@ -0,0 +1,176 @@ +/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ + +#include "hashtable.h" +#include "hashtable_private.h" +#include "hashtable_itr.h" +#include <stdlib.h> /* defines NULL */ + +/*****************************************************************************/ +/* hashtable_iterator - iterator constructor */ + +struct hashtable_itr * +hashtable_iterator(struct hashtable *h) +{ + unsigned int i, tablelength; + struct hashtable_itr *itr = (struct hashtable_itr *) + malloc(sizeof(struct hashtable_itr)); + if (NULL == itr) return NULL; + itr->h = h; + itr->e = NULL; + itr->parent = NULL; + tablelength = h->tablelength; + itr->index = tablelength; + if (0 == h->entrycount) return itr; + + for (i = 0; i < tablelength; i++) + { + if (NULL != h->table[i]) + { + itr->e = h->table[i]; + itr->index = i; + break; + } + } + return itr; +} + +/*****************************************************************************/ +/* advance - advance the iterator to the next element + * returns zero if advanced to end of table */ + +int +hashtable_iterator_advance(struct hashtable_itr *itr) +{ + unsigned int j,tablelength; + struct entry **table; + struct entry *next; + if (NULL == itr->e) return 0; /* stupidity check */ + + next = itr->e->next; + if (NULL != next) + { + itr->parent = itr->e; + itr->e = next; + return -1; + } + tablelength = itr->h->tablelength; + itr->parent = NULL; + if (tablelength <= (j = ++(itr->index))) + { + itr->e = NULL; + return 0; + } + table = itr->h->table; + while (NULL == (next = table[j])) + { + if (++j >= tablelength) + { + itr->index = tablelength; + itr->e = NULL; + return 0; + } + } + itr->index = j; + itr->e = next; + return -1; +} + +/*****************************************************************************/ +/* remove - remove the entry at the current iterator position + * and advance the iterator, if there is a successive + * element. + * If you want the value, read it before you remove: + * beware memory leaks if you don't. + * Returns zero if end of iteration. */ + +int +hashtable_iterator_remove(struct hashtable_itr *itr) +{ + struct entry *remember_e, *remember_parent; + int ret; + + /* Do the removal */ + if (NULL == (itr->parent)) + { + /* element is head of a chain */ + itr->h->table[itr->index] = itr->e->next; + } else { + /* element is mid-chain */ + itr->parent->next = itr->e->next; + } + /* itr->e is now outside the hashtable */ + remember_e = itr->e; + itr->h->entrycount--; + freekey(remember_e->k); + + /* Advance the iterator, correcting the parent */ + remember_parent = itr->parent; + ret = hashtable_iterator_advance(itr); + if (itr->parent == remember_e) { itr->parent = remember_parent; } + free(remember_e); + return ret; +} + +/*****************************************************************************/ +int /* returns zero if not found */ +hashtable_iterator_search(struct hashtable_itr *itr, + struct hashtable *h, void *k) +{ + struct entry *e, *parent; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + + e = h->table[index]; + parent = NULL; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + itr->index = index; + itr->e = e; + itr->parent = parent; + itr->h = h; + return -1; + } + parent = e; + e = e->next; + } + return 0; +} + + +/* + * Copyright (c) 2002, 2004, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER + * 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. +*/ diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h new file mode 100644 index 0000000..5c94a04 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ + +#ifndef __HASHTABLE_ITR_CWC22__ +#define __HASHTABLE_ITR_CWC22__ +#include "hashtable.h" +#include "hashtable_private.h" /* needed to enable inlining */ + +/*****************************************************************************/ +/* This struct is only concrete here to allow the inlining of two of the + * accessor functions. */ +struct hashtable_itr +{ + struct hashtable *h; + struct entry *e; + struct entry *parent; + unsigned int index; +}; + + +/*****************************************************************************/ +/* hashtable_iterator + */ + +struct hashtable_itr * +hashtable_iterator(struct hashtable *h); + +/*****************************************************************************/ +/* hashtable_iterator_key + * - return the value of the (key,value) pair at the current position */ + +static inline void * +hashtable_iterator_key(struct hashtable_itr *i) +{ + return i->e->k; +} + +/*****************************************************************************/ +/* value - return the value of the (key,value) pair at the current position */ + +static inline void * +hashtable_iterator_value(struct hashtable_itr *i) +{ + return i->e->v; +} + +/*****************************************************************************/ +/* advance - advance the iterator to the next element + * returns zero if advanced to end of table */ + +int +hashtable_iterator_advance(struct hashtable_itr *itr); + +/*****************************************************************************/ +/* remove - remove current element and advance the iterator to the next element + * NB: if you need the value to free it, read it before + * removing. ie: beware memory leaks! + * returns zero if advanced to end of table */ + +int +hashtable_iterator_remove(struct hashtable_itr *itr); + +/*****************************************************************************/ +/* search - overwrite the supplied iterator, to point to the entry + * matching the supplied key. + h points to the hashtable to be searched. + * returns zero if not found. */ +int +hashtable_iterator_search(struct hashtable_itr *itr, + struct hashtable *h, void *k); + +#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ +int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ +{ \ + return (hashtable_iterator_search(i,h,k)); \ +} + + + +#endif /* __HASHTABLE_ITR_CWC22__*/ + +/* + * Copyright (c) 2002, 2004, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER + * 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. +*/ diff --git a/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h new file mode 100644 index 0000000..3a558e6 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */ + +#ifndef __HASHTABLE_PRIVATE_CWC22_H__ +#define __HASHTABLE_PRIVATE_CWC22_H__ + +#include "hashtable.h" + +/*****************************************************************************/ +struct entry +{ + void *k, *v; + unsigned int h; + struct entry *next; +}; + +struct hashtable { + unsigned int tablelength; + struct entry **table; + unsigned int entrycount; + unsigned int loadlimit; + unsigned int primeindex; + unsigned int (*hashfn) (void *k); + int (*eqfn) (void *k1, void *k2); +}; + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k); + +/*****************************************************************************/ +/* indexFor */ +static inline unsigned int +indexFor(unsigned int tablelength, unsigned int hashvalue) { + return (hashvalue % tablelength); +}; + +/* Only works if tablelength == 2^N */ +/*static inline unsigned int +indexFor(unsigned int tablelength, unsigned int hashvalue) +{ + return (hashvalue & (tablelength - 1u)); +} +*/ + +/*****************************************************************************/ +#define freekey(X) free(X) +/*define freekey(X) ; */ + + +/*****************************************************************************/ + +#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ + +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of the original author; nor the names of any 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 COPYRIGHT OWNER + * 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. +*/ diff --git a/ubifs-utils/mkfs.ubifs/key.h b/ubifs-utils/mkfs.ubifs/key.h new file mode 100644 index 0000000..d3a02d4 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/key.h @@ -0,0 +1,189 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2006-2008 Nokia Corporation. + * + * 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. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Adrian Hunter + */ + +/* + * This header contains various key-related definitions and helper function. + * UBIFS allows several key schemes, so we access key fields only via these + * helpers. At the moment only one key scheme is supported. + * + * Simple key scheme + * ~~~~~~~~~~~~~~~~~ + * + * Keys are 64-bits long. First 32-bits are inode number (parent inode number + * in case of direntry key). Next 3 bits are node type. The last 29 bits are + * 4KiB offset in case of inode node, and direntry hash in case of a direntry + * node. We use "r5" hash borrowed from reiserfs. + */ + +#ifndef __UBIFS_KEY_H__ +#define __UBIFS_KEY_H__ + +/** + * key_mask_hash - mask a valid hash value. + * @val: value to be masked + * + * We use hash values as offset in directories, so values %0 and %1 are + * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This + * function makes sure the reserved values are not used. + */ +static inline uint32_t key_mask_hash(uint32_t hash) +{ + hash &= UBIFS_S_KEY_HASH_MASK; + if (unlikely(hash <= 2)) + hash += 3; + return hash; +} + +/** + * key_r5_hash - R5 hash function (borrowed from reiserfs). + * @s: direntry name + * @len: name length + */ +static inline uint32_t key_r5_hash(const char *s, int len) +{ + uint32_t a = 0; + const signed char *str = (const signed char *)s; + + len = len; + while (*str) { + a += *str << 4; + a += *str >> 4; + a *= 11; + str++; + } + + return key_mask_hash(a); +} + +/** + * key_test_hash - testing hash function. + * @str: direntry name + * @len: name length + */ +static inline uint32_t key_test_hash(const char *str, int len) +{ + uint32_t a = 0; + + len = min_t(uint32_t, len, 4); + memcpy(&a, str, len); + return key_mask_hash(a); +} + +/** + * ino_key_init - initialize inode key. + * @c: UBIFS file-system description object + * @key: key to initialize + * @inum: inode number + */ +static inline void ino_key_init(union ubifs_key *key, ino_t inum) +{ + key->u32[0] = inum; + key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; +} + +/** + * dent_key_init - initialize directory entry key. + * @c: UBIFS file-system description object + * @key: key to initialize + * @inum: parent inode number + * @nm: direntry name and length + */ +static inline void dent_key_init(const struct ubifs_info *c, + union ubifs_key *key, ino_t inum, + const struct qstr *nm) +{ + uint32_t hash = c->key_hash(nm->name, nm->len); + + ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); + key->u32[0] = inum; + key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); +} + +/** + * data_key_init - initialize data key. + * @c: UBIFS file-system description object + * @key: key to initialize + * @inum: inode number + * @block: block number + */ +static inline void data_key_init(union ubifs_key *key, ino_t inum, + unsigned int block) +{ + ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); + key->u32[0] = inum; + key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); +} + +/** + * key_write - transform a key from in-memory format. + * @c: UBIFS file-system description object + * @from: the key to transform + * @to: the key to store the result + */ +static inline void key_write(const union ubifs_key *from, void *to) +{ + union ubifs_key *t = to; + + t->j32[0] = cpu_to_le32(from->u32[0]); + t->j32[1] = cpu_to_le32(from->u32[1]); + memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); +} + +/** + * key_write_idx - transform a key from in-memory format for the index. + * @c: UBIFS file-system description object + * @from: the key to transform + * @to: the key to store the result + */ +static inline void key_write_idx(const union ubifs_key *from, void *to) +{ + union ubifs_key *t = to; + + t->j32[0] = cpu_to_le32(from->u32[0]); + t->j32[1] = cpu_to_le32(from->u32[1]); +} + +/** + * keys_cmp - compare keys. + * @c: UBIFS file-system description object + * @key1: the first key to compare + * @key2: the second key to compare + * + * This function compares 2 keys and returns %-1 if @key1 is less than + * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. + */ +static inline int keys_cmp(const union ubifs_key *key1, + const union ubifs_key *key2) +{ + if (key1->u32[0] < key2->u32[0]) + return -1; + if (key1->u32[0] > key2->u32[0]) + return 1; + if (key1->u32[1] < key2->u32[1]) + return -1; + if (key1->u32[1] > key2->u32[1]) + return 1; + + return 0; +} + +#endif /* !__UBIFS_KEY_H__ */ diff --git a/ubifs-utils/mkfs.ubifs/lpt.c b/ubifs-utils/mkfs.ubifs/lpt.c new file mode 100644 index 0000000..6aa0b88 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/lpt.c @@ -0,0 +1,578 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2006, 2007 Nokia Corporation. + * + * 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. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Adrian Hunter + * Artem Bityutskiy + */ + +#include "mkfs.ubifs.h" + +/** + * do_calc_lpt_geom - calculate sizes for the LPT area. + * @c: the UBIFS file-system description object + * + * Calculate the sizes of LPT bit fields, nodes, and tree, based on the + * properties of the flash and whether LPT is "big" (c->big_lpt). + */ +static void do_calc_lpt_geom(struct ubifs_info *c) +{ + int n, bits, per_leb_wastage; + long long sz, tot_wastage; + + c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + + n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + c->nnode_cnt = n; + while (n > 1) { + n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + c->nnode_cnt += n; + } + + c->lpt_hght = 1; + n = UBIFS_LPT_FANOUT; + while (n < c->pnode_cnt) { + c->lpt_hght += 1; + n <<= UBIFS_LPT_FANOUT_SHIFT; + } + + c->space_bits = fls(c->leb_size) - 3; + c->lpt_lnum_bits = fls(c->lpt_lebs); + c->lpt_offs_bits = fls(c->leb_size - 1); + c->lpt_spc_bits = fls(c->leb_size); + + n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + c->pcnt_bits = fls(n - 1); + + c->lnum_bits = fls(c->max_leb_cnt - 1); + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + (c->big_lpt ? c->pcnt_bits : 0) + + (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; + c->pnode_sz = (bits + 7) / 8; + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + (c->big_lpt ? c->pcnt_bits : 0) + + (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; + c->nnode_sz = (bits + 7) / 8; + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + c->lpt_lebs * c->lpt_spc_bits * 2; + c->ltab_sz = (bits + 7) / 8; + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + c->lnum_bits * c->lsave_cnt; + c->lsave_sz = (bits + 7) / 8; + + /* Calculate the minimum LPT size */ + c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; + c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; + c->lpt_sz += c->ltab_sz; + c->lpt_sz += c->lsave_sz; + + /* Add wastage */ + sz = c->lpt_sz; + per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); + sz += per_leb_wastage; + tot_wastage = per_leb_wastage; + while (sz > c->leb_size) { + sz += per_leb_wastage; + sz -= c->leb_size; + tot_wastage += per_leb_wastage; + } + tot_wastage += ALIGN(sz, c->min_io_size) - sz; + c->lpt_sz += tot_wastage; +} + +/** + * calc_dflt_lpt_geom - calculate default LPT geometry. + * @c: the UBIFS file-system description object + * @main_lebs: number of main area LEBs is passed and returned here + * @big_lpt: whether the LPT area is "big" is returned here + * + * The size of the LPT area depends on parameters that themselves are dependent + * on the size of the LPT area. This function, successively recalculates the LPT + * area geometry until the parameters and resultant geometry are consistent. + * + * This function returns %0 on success and a negative error code on failure. + */ +int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) +{ + int i, lebs_needed; + long long sz; + + /* Start by assuming the minimum number of LPT LEBs */ + c->lpt_lebs = UBIFS_MIN_LPT_LEBS; + c->main_lebs = *main_lebs - c->lpt_lebs; + if (c->main_lebs <= 0) + return -EINVAL; + + /* And assume we will use the small LPT model */ + c->big_lpt = 0; + + /* + * Calculate the geometry based on assumptions above and then see if it + * makes sense + */ + do_calc_lpt_geom(c); + + /* Small LPT model must have lpt_sz < leb_size */ + if (c->lpt_sz > c->leb_size) { + /* Nope, so try again using big LPT model */ + c->big_lpt = 1; + do_calc_lpt_geom(c); + } + + /* Now check there are enough LPT LEBs */ + for (i = 0; i < 64 ; i++) { + sz = c->lpt_sz * 4; /* Allow 4 times the size */ + sz += c->leb_size - 1; + do_div(sz, c->leb_size); + lebs_needed = sz; + if (lebs_needed > c->lpt_lebs) { + /* Not enough LPT LEBs so try again with more */ + c->lpt_lebs = lebs_needed; + c->main_lebs = *main_lebs - c->lpt_lebs; + if (c->main_lebs <= 0) + return -EINVAL; + do_calc_lpt_geom(c); + continue; + } + if (c->ltab_sz > c->leb_size) { + err_msg("LPT ltab too big"); + return -EINVAL; + } + *main_lebs = c->main_lebs; + *big_lpt = c->big_lpt; + return 0; + } + return -EINVAL; +} + +/** + * pack_bits - pack bit fields end-to-end. + * @addr: address at which to pack (passed and next address returned) + * @pos: bit position at which to pack (passed and next position returned) + * @val: value to pack + * @nrbits: number of bits of value to pack (1-32) + */ +static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) +{ + uint8_t *p = *addr; + int b = *pos; + + if (b) { + *p |= ((uint8_t)val) << b; + nrbits += b; + if (nrbits > 8) { + *++p = (uint8_t)(val >>= (8 - b)); + if (nrbits > 16) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 24) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 32) + *++p = (uint8_t)(val >>= 8); + } + } + } + } else { + *p = (uint8_t)val; + if (nrbits > 8) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 16) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 24) + *++p = (uint8_t)(val >>= 8); + } + } + } + b = nrbits & 7; + if (b == 0) + p++; + *addr = p; + *pos = b; +} + +/** + * pack_pnode - pack all the bit fields of a pnode. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @pnode: pnode to pack + */ +static void pack_pnode(struct ubifs_info *c, void *buf, + struct ubifs_pnode *pnode) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); + if (c->big_lpt) + pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, + c->space_bits); + pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, + c->space_bits); + if (pnode->lprops[i].flags & LPROPS_INDEX) + pack_bits(&addr, &pos, 1, 1); + else + pack_bits(&addr, &pos, 0, 1); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->pnode_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * pack_nnode - pack all the bit fields of a nnode. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @nnode: nnode to pack + */ +static void pack_nnode(struct ubifs_info *c, void *buf, + struct ubifs_nnode *nnode) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); + if (c->big_lpt) + pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + int lnum = nnode->nbranch[i].lnum; + + if (lnum == 0) + lnum = c->lpt_last + 1; + pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); + pack_bits(&addr, &pos, nnode->nbranch[i].offs, + c->lpt_offs_bits); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->nnode_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * pack_ltab - pack the LPT's own lprops table. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @ltab: LPT's own lprops table to pack + */ +static void pack_ltab(struct ubifs_info *c, void *buf, + struct ubifs_lpt_lprops *ltab) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); + for (i = 0; i < c->lpt_lebs; i++) { + pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); + pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->ltab_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * pack_lsave - pack the LPT's save table. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @lsave: LPT's save table to pack + */ +static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); + for (i = 0; i < c->lsave_cnt; i++) + pack_bits(&addr, &pos, lsave[i], c->lnum_bits); + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->lsave_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * set_ltab - set LPT LEB properties. + * @c: UBIFS file-system description object + * @lnum: LEB number + * @free: amount of free space + * @dirty: amount of dirty space + */ +static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) +{ + dbg_msg(3, "LEB %d free %d dirty %d to %d %d", + lnum, c->ltab[lnum - c->lpt_first].free, + c->ltab[lnum - c->lpt_first].dirty, free, dirty); + c->ltab[lnum - c->lpt_first].free = free; + c->ltab[lnum - c->lpt_first].dirty = dirty; +} + +/** + * calc_nnode_num - calculate nnode number. + * @row: the row in the tree (root is zero) + * @col: the column in the row (leftmost is zero) + * + * The nnode number is a number that uniquely identifies a nnode and can be used + * easily to traverse the tree from the root to that nnode. + * + * This function calculates and returns the nnode number for the nnode at @row + * and @col. + */ +static int calc_nnode_num(int row, int col) +{ + int num, bits; + + num = 1; + while (row--) { + bits = (col & (UBIFS_LPT_FANOUT - 1)); + col >>= UBIFS_LPT_FANOUT_SHIFT; + num <<= UBIFS_LPT_FANOUT_SHIFT; + num |= bits; + } + return num; +} + +/** + * create_lpt - create LPT. + * @c: UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +int create_lpt(struct ubifs_info *c) +{ + int lnum, err = 0, i, j, cnt, len, alen, row; + int blnum, boffs, bsz, bcnt; + struct ubifs_pnode *pnode = NULL; + struct ubifs_nnode *nnode = NULL; + void *buf = NULL, *p; + int *lsave = NULL; + + pnode = malloc(sizeof(struct ubifs_pnode)); + nnode = malloc(sizeof(struct ubifs_nnode)); + buf = malloc(c->leb_size); + lsave = malloc(sizeof(int) * c->lsave_cnt); + if (!pnode || !nnode || !buf || !lsave) { + err = -ENOMEM; + goto out; + } + memset(pnode, 0 , sizeof(struct ubifs_pnode)); + memset(nnode, 0 , sizeof(struct ubifs_nnode)); + + c->lscan_lnum = c->main_first; + + lnum = c->lpt_first; + p = buf; + len = 0; + /* Number of leaf nodes (pnodes) */ + cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; + //printf("pnode_cnt=%d\n",cnt); + + /* + * To calculate the internal node branches, we keep information about + * the level below. + */ + blnum = lnum; /* LEB number of level below */ + boffs = 0; /* Offset of level below */ + bcnt = cnt; /* Number of nodes in level below */ + bsz = c->pnode_sz; /* Size of nodes in level below */ + + /* Add pnodes */ + for (i = 0; i < cnt; i++) { + if (len + c->pnode_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + /* Fill in the pnode */ + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { + int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; + + if (k < c->main_lebs) + pnode->lprops[j] = c->lpt[k]; + else { + pnode->lprops[j].free = c->leb_size; + pnode->lprops[j].dirty = 0; + pnode->lprops[j].flags = 0; + } + } + pack_pnode(c, p, pnode); + p += c->pnode_sz; + len += c->pnode_sz; + /* + * pnodes are simply numbered left to right starting at zero, + * which means the pnode number can be used easily to traverse + * down the tree to the corresponding pnode. + */ + pnode->num += 1; + } + + row = c->lpt_hght - 1; + /* Add all nnodes, one level at a time */ + while (1) { + /* Number of internal nodes (nnodes) at next level */ + cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + if (cnt == 0) + cnt = 1; + for (i = 0; i < cnt; i++) { + if (len + c->nnode_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, + alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + /* The root is on row zero */ + if (row == 0) { + c->lpt_lnum = lnum; + c->lpt_offs = len; + } + /* Set branches to the level below */ + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { + if (bcnt) { + if (boffs + bsz > c->leb_size) { + blnum += 1; + boffs = 0; + } + nnode->nbranch[j].lnum = blnum; + nnode->nbranch[j].offs = boffs; + boffs += bsz; + bcnt--; + } else { + nnode->nbranch[j].lnum = 0; + nnode->nbranch[j].offs = 0; + } + } + nnode->num = calc_nnode_num(row, i); + pack_nnode(c, p, nnode); + p += c->nnode_sz; + len += c->nnode_sz; + } + /* Row zero is the top row */ + if (row == 0) + break; + /* Update the information about the level below */ + bcnt = cnt; + bsz = c->nnode_sz; + row -= 1; + } + + if (c->big_lpt) { + /* Need to add LPT's save table */ + if (len + c->lsave_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + + c->lsave_lnum = lnum; + c->lsave_offs = len; + + for (i = 0; i < c->lsave_cnt; i++) + lsave[i] = c->main_first + i; + + pack_lsave(c, p, lsave); + p += c->lsave_sz; + len += c->lsave_sz; + } + + /* Need to add LPT's own LEB properties table */ + if (len + c->ltab_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + + c->ltab_lnum = lnum; + c->ltab_offs = len; + + /* Update ltab before packing it */ + len += c->ltab_sz; + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + + pack_ltab(c, p, c->ltab); + p += c->ltab_sz; + + /* Write remaining buffer */ + memset(p, 0xff, alen - len); + err = write_leb(lnum, alen, buf); + if (err) + goto out; + + c->nhead_lnum = lnum; + c->nhead_offs = ALIGN(len, c->min_io_size); + + dbg_msg(1, "lpt_sz: %lld", c->lpt_sz); + dbg_msg(1, "space_bits: %d", c->space_bits); + dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); + dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); + dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); + dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); + dbg_msg(1, "lnum_bits: %d", c->lnum_bits); + dbg_msg(1, "pnode_sz: %d", c->pnode_sz); + dbg_msg(1, "nnode_sz: %d", c->nnode_sz); + dbg_msg(1, "ltab_sz: %d", c->ltab_sz); + dbg_msg(1, "lsave_sz: %d", c->lsave_sz); + dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); + dbg_msg(1, "lpt_hght: %d", c->lpt_hght); + dbg_msg(1, "big_lpt: %d", c->big_lpt); + dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); + dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); + dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); + if (c->big_lpt) + dbg_msg(1, "LPT lsave is at %d:%d", + c->lsave_lnum, c->lsave_offs); +out: + free(lsave); + free(buf); + free(nnode); + free(pnode); + return err; +} diff --git a/ubifs-utils/mkfs.ubifs/lpt.h b/ubifs-utils/mkfs.ubifs/lpt.h new file mode 100644 index 0000000..4cde59d --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/lpt.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 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 version 2 as published by + * the Free Software Foundation. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + */ + +#ifndef __UBIFS_LPT_H__ +#define __UBIFS_LPT_H__ + +int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); +int create_lpt(struct ubifs_info *c); + +#endif diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c new file mode 100644 index 0000000..ca17e2b --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c @@ -0,0 +1,2324 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 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 version 2 as published by + * the Free Software Foundation. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Adrian Hunter + * Artem Bityutskiy + * Zoltan Sogor + */ + +#define _XOPEN_SOURCE 500 /* For realpath() */ + +#include "mkfs.ubifs.h" +#include <crc32.h> +#include "common.h" + +/* Size (prime number) of hash table for link counting */ +#define HASH_TABLE_SIZE 10099 + +/* The node buffer must allow for worst case compression */ +#define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + \ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) + +/* Default time granularity in nanoseconds */ +#define DEFAULT_TIME_GRAN 1000000000 + +/** + * struct idx_entry - index entry. + * @next: next index entry (NULL at end of list) + * @prev: previous index entry (NULL at beginning of list) + * @key: key + * @name: directory entry name used for sorting colliding keys by name + * @lnum: LEB number + * @offs: offset + * @len: length + * + * The index is recorded as a linked list which is sorted and used to create + * the bottom level of the on-flash index tree. The remaining levels of the + * index tree are each built from the level below. + */ +struct idx_entry { + struct idx_entry *next; + struct idx_entry *prev; + union ubifs_key key; + char *name; + int lnum; + int offs; + int len; +}; + +/** + * struct inum_mapping - inode number mapping for link counting. + * @next: next inum_mapping (NULL at end of list) + * @prev: previous inum_mapping (NULL at beginning of list) + * @dev: source device on which the source inode number resides + * @inum: source inode number of the file + * @use_inum: target inode number of the file + * @use_nlink: number of links + * @path_name: a path name of the file + * @st: struct stat object containing inode attributes which have to be used + * when the inode is being created (actually only UID, GID, access + * mode, major and minor device numbers) + * + * If a file has more than one hard link, then the number of hard links that + * exist in the source directory hierarchy must be counted to exclude the + * possibility that the file is linked from outside the source directory + * hierarchy. + * + * The inum_mappings are stored in a hash_table of linked lists. + */ +struct inum_mapping { + struct inum_mapping *next; + struct inum_mapping *prev; + dev_t dev; + ino_t inum; + ino_t use_inum; + unsigned int use_nlink; + char *path_name; + struct stat st; +}; + +/* + * Because we copy functions from the kernel, we use a subset of the UBIFS + * file-system description object struct ubifs_info. + */ +struct ubifs_info info_; +static struct ubifs_info *c = &info_; +static libubi_t ubi; + +/* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ +int debug_level; +int verbose; +int yes; + +static char *root; +static int root_len; +static struct stat root_st; +static char *output; +static int out_fd; +static int out_ubi; +static int squash_owner; + +/* The 'head' (position) which nodes are written */ +static int head_lnum; +static int head_offs; +static int head_flags; + +/* The index list */ +static struct idx_entry *idx_list_first; +static struct idx_entry *idx_list_last; +static size_t idx_cnt; + +/* Global buffers */ +static void *leb_buf; +static void *node_buf; +static void *block_buf; + +/* Hash table for inode link counting */ +static struct inum_mapping **hash_table; + +/* Inode creation sequence number */ +static unsigned long long creat_sqnum; + +static const char *optstring = "d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq"; + +static const struct option longopts[] = { + {"root", 1, NULL, 'r'}, + {"min-io-size", 1, NULL, 'm'}, + {"leb-size", 1, NULL, 'e'}, + {"max-leb-cnt", 1, NULL, 'c'}, + {"output", 1, NULL, 'o'}, + {"devtable", 1, NULL, 'D'}, + {"yes", 0, NULL, 'y'}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"version", 0, NULL, 'V'}, + {"debug-level", 1, NULL, 'g'}, + {"jrn-size", 1, NULL, 'j'}, + {"reserved", 1, NULL, 'R'}, + {"compr", 1, NULL, 'x'}, + {"favor-percent", 1, NULL, 'X'}, + {"fanout", 1, NULL, 'f'}, + {"space-fixup", 0, NULL, 'F'}, + {"keyhash", 1, NULL, 'k'}, + {"log-lebs", 1, NULL, 'l'}, + {"orph-lebs", 1, NULL, 'p'}, + {"squash-uids" , 0, NULL, 'U'}, + {NULL, 0, NULL, 0} +}; + +static const char *helptext = +"Usage: mkfs.ubifs [OPTIONS] target\n" +"Make a UBIFS file system image from an existing directory tree\n\n" +"Examples:\n" +"Build file system from directory /opt/img, writting the result in the ubifs.img file\n" +"\tmkfs.ubifs -m 512 -e 128KiB -c 100 -r /opt/img ubifs.img\n" +"The same, but writting directly to an UBI volume\n" +"\tmkfs.ubifs -r /opt/img /dev/ubi0_0\n" +"Creating an empty UBIFS filesystem on an UBI volume\n" +"\tmkfs.ubifs /dev/ubi0_0\n\n" +"Options:\n" +"-r, -d, --root=DIR build file system from directory DIR\n" +"-m, --min-io-size=SIZE minimum I/O unit size\n" +"-e, --leb-size=SIZE logical erase block size\n" +"-c, --max-leb-cnt=COUNT maximum logical erase block count\n" +"-o, --output=FILE output to FILE\n" +"-j, --jrn-size=SIZE journal size\n" +"-R, --reserved=SIZE how much space should be reserved for the super-user\n" +"-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" +" \"none\" (default: \"lzo\")\n" +"-X, --favor-percent may only be used with favor LZO compression and defines\n" +" how many percent better zlib should compress to make\n" +" mkfs.ubifs use zlib instead of LZO (default 20%)\n" +"-f, --fanout=NUM fanout NUM (default: 8)\n" +"-F, --space-fixup file-system free space has to be fixed up on first mount\n" +" (requires kernel version 3.0 or greater)\n" +"-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" +"-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" +"-D, --devtable=FILE use device table FILE\n" +"-U, --squash-uids squash owners making all files owned by root\n" +"-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" +" debugging)\n" +"-y, --yes assume the answer is \"yes\" for all questions\n" +"-v, --verbose verbose operation\n" +"-V, --version display version information\n" +"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" +" 2 - files, 3 - more details)\n" +"-h, --help display this help text\n\n" +"Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" +"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" +"If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" +"for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" +"really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" +"compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" +"compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" +"or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" +"\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" +"default 20%.\n\n" +"The -F parameter is used to set the \"fix up free space\" flag in the superblock,\n" +"which forces UBIFS to \"fixup\" all the free space which it is going to use. This\n" +"option is useful to work-around the problem of double free space programming: if the\n" +"flasher program which flashes the UBI image is unable to skip NAND pages containing\n" +"only 0xFF bytes, the effect is that some NAND pages are written to twice - first time\n" +"when flashing the image and the second time when UBIFS is mounted and writes useful\n" +"data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n" +"flag may make the first mount very slow, because the \"free space fixup\" procedure\n" +"takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"; + +/** + * make_path - make a path name from a directory and a name. + * @dir: directory path name + * @name: name + */ +static char *make_path(const char *dir, const char *name) +{ + char *s; + + s = malloc(strlen(dir) + strlen(name) + 2); + if (!s) + return NULL; + strcpy(s, dir); + if (dir[strlen(dir) - 1] != '/') + strcat(s, "/"); + strcat(s, name); + return s; +} + +/** + * is_contained - determine if a file is beneath a directory. + * @file: file path name + * @dir: directory path name + * + * This function returns %1 if @file is accessible from the @dir directory and + * %0 otherwise. In case of error, returns %-1. + */ +static int is_contained(const char *file, const char *dir) +{ + char *real_file = NULL; + char *real_dir = NULL; + char *file_base, *copy; + int ret = -1; + + /* Make a copy of the file path because 'dirname()' can modify it */ + copy = strdup(file); + if (!copy) + return -1; + file_base = dirname(copy); + + /* Turn the paths into the canonical form */ + real_file = malloc(PATH_MAX); + if (!real_file) + goto out_free; + + real_dir = malloc(PATH_MAX); + if (!real_dir) + goto out_free; + + if (!realpath(file_base, real_file)) { + perror("Could not canonicalize file path"); + goto out_free; + } + if (!realpath(dir, real_dir)) { + perror("Could not canonicalize directory"); + goto out_free; + } + + ret = !!strstr(real_file, real_dir); + +out_free: + free(copy); + free(real_file); + free(real_dir); + return ret; +} + +/** + * calc_min_log_lebs - calculate the minimum number of log LEBs needed. + * @max_bud_bytes: journal size (buds only) + */ +static int calc_min_log_lebs(unsigned long long max_bud_bytes) +{ + int buds, log_lebs; + unsigned long long log_size; + + buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; + log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); + log_size *= buds; + log_size += ALIGN(UBIFS_CS_NODE_SZ + + UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), + c->min_io_size); + log_lebs = (log_size + c->leb_size - 1) / c->leb_size; + log_lebs += 1; + return log_lebs; +} + +/** + * add_space_overhead - add UBIFS overhead. + * @size: flash space which should be visible to the user + * + * UBIFS has overhead, and if we need to reserve @size bytes for the user data, + * we have to reserve more flash space, to compensate the overhead. This + * function calculates and returns the amount of physical flash space which + * should be reserved to provide @size bytes for the user. + */ +static long long add_space_overhead(long long size) +{ + int divisor, factor, f, max_idx_node_sz; + + /* + * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS + * function does. + */ + max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); + f = c->fanout > 3 ? c->fanout >> 1 : 2; + divisor = UBIFS_BLOCK_SIZE; + factor = UBIFS_MAX_DATA_NODE_SZ; + factor += (max_idx_node_sz * 3) / (f - 1); + size *= factor; + return size / divisor; +} + +static int validate_options(void) +{ + int tmp; + + if (!output) + return err_msg("no output file or UBI volume specified"); + if (root) { + tmp = is_contained(output, root); + if (tmp < 0) + return err_msg("failed to perform output file root check"); + else if (tmp) + return err_msg("output file cannot be in the UBIFS root " + "directory"); + } + if (!is_power_of_2(c->min_io_size)) + return err_msg("min. I/O unit size should be power of 2"); + if (c->leb_size < c->min_io_size) + return err_msg("min. I/O unit cannot be larger than LEB size"); + if (c->leb_size < UBIFS_MIN_LEB_SZ) + return err_msg("too small LEB size %d, minimum is %d", + c->leb_size, UBIFS_MIN_LEB_SZ); + if (c->leb_size % c->min_io_size) + return err_msg("LEB should be multiple of min. I/O units"); + if (c->leb_size % 8) + return err_msg("LEB size has to be multiple of 8"); + if (c->leb_size > UBIFS_MAX_LEB_SZ) + return err_msg("too large LEB size %d, maximum is %d", + c->leb_size, UBIFS_MAX_LEB_SZ); + if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) + return err_msg("too low max. count of LEBs, minimum is %d", + UBIFS_MIN_LEB_CNT); + if (c->fanout < UBIFS_MIN_FANOUT) + return err_msg("too low fanout, minimum is %d", + UBIFS_MIN_FANOUT); + tmp = c->leb_size - UBIFS_IDX_NODE_SZ; + tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; + if (c->fanout > tmp) + return err_msg("too high fanout, maximum is %d", tmp); + if (c->log_lebs < UBIFS_MIN_LOG_LEBS) + return err_msg("too few log LEBs, minimum is %d", + UBIFS_MIN_LOG_LEBS); + if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) + return err_msg("too many log LEBs, maximum is %d", + c->max_leb_cnt - UBIFS_MIN_LEB_CNT); + if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) + return err_msg("too few orphan LEBs, minimum is %d", + UBIFS_MIN_ORPH_LEBS); + if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) + return err_msg("too many orphan LEBs, maximum is %d", + c->max_leb_cnt - UBIFS_MIN_LEB_CNT); + tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; + tmp += c->orph_lebs + 4; + if (tmp > c->max_leb_cnt) + return err_msg("too low max. count of LEBs, expected at " + "least %d", tmp); + tmp = calc_min_log_lebs(c->max_bud_bytes); + if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) + return err_msg("too few log LEBs, expected at least %d", tmp); + if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) + return err_msg("too much reserved space %lld", c->rp_size); + return 0; +} + +/** + * get_multiplier - convert size specifier to an integer multiplier. + * @str: the size specifier string + * + * This function parses the @str size specifier, which may be one of + * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive + * size multiplier in case of success and %-1 in case of failure. + */ +static int get_multiplier(const char *str) +{ + if (!str) + return 1; + + /* Remove spaces before the specifier */ + while (*str == ' ' || *str == '\t') + str += 1; + + if (!strcmp(str, "KiB")) + return 1024; + if (!strcmp(str, "MiB")) + return 1024 * 1024; + if (!strcmp(str, "GiB")) + return 1024 * 1024 * 1024; + + return -1; +} + +/** + * get_bytes - convert a string containing amount of bytes into an + * integer. + * @str: string to convert + * + * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size + * specifiers. Returns positive amount of bytes in case of success and %-1 in + * case of failure. + */ +static long long get_bytes(const char *str) +{ + char *endp; + long long bytes = strtoull(str, &endp, 0); + + if (endp == str || bytes < 0) + return err_msg("incorrect amount of bytes: \"%s\"", str); + + if (*endp != '\0') { + int mult = get_multiplier(endp); + + if (mult == -1) + return err_msg("bad size specifier: \"%s\" - " + "should be 'KiB', 'MiB' or 'GiB'", endp); + bytes *= mult; + } + + return bytes; +} +/** + * open_ubi - open the UBI volume. + * @node: name of the UBI volume character device to fetch information about + * + * Returns %0 in case of success and %-1 in case of failure + */ +static int open_ubi(const char *node) +{ + struct stat st; + + if (stat(node, &st) || !S_ISCHR(st.st_mode)) + return -1; + + ubi = libubi_open(); + if (!ubi) + return -1; + if (ubi_get_vol_info(ubi, node, &c->vi)) + return -1; + if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) + return -1; + return 0; +} + +static int get_options(int argc, char**argv) +{ + int opt, i; + const char *tbl_file = NULL; + struct stat st; + char *endp; + + c->fanout = 8; + c->orph_lebs = 1; + c->key_hash = key_r5_hash; + c->key_len = UBIFS_SK_LEN; + c->default_compr = UBIFS_COMPR_LZO; + c->favor_percent = 20; + c->lsave_cnt = 256; + c->leb_size = -1; + c->min_io_size = -1; + c->max_leb_cnt = -1; + c->max_bud_bytes = -1; + c->log_lebs = -1; + + while (1) { + opt = getopt_long(argc, argv, optstring, longopts, &i); + if (opt == -1) + break; + switch (opt) { + case 'r': + case 'd': + root_len = strlen(optarg); + root = malloc(root_len + 2); + if (!root) + return err_msg("cannot allocate memory"); + + /* + * The further code expects '/' at the end of the root + * UBIFS directory on the host. + */ + memcpy(root, optarg, root_len); + if (root[root_len - 1] != '/') + root[root_len++] = '/'; + root[root_len] = 0; + + /* Make sure the root directory exists */ + if (stat(root, &st)) + return sys_err_msg("bad root directory '%s'", + root); + break; + case 'm': + c->min_io_size = get_bytes(optarg); + if (c->min_io_size <= 0) + return err_msg("bad min. I/O size"); + break; + case 'e': + c->leb_size = get_bytes(optarg); + if (c->leb_size <= 0) + return err_msg("bad LEB size"); + break; + case 'c': + c->max_leb_cnt = get_bytes(optarg); + if (c->max_leb_cnt <= 0) + return err_msg("bad maximum LEB count"); + break; + case 'o': + output = xstrdup(optarg); + break; + case 'D': + tbl_file = optarg; + if (stat(tbl_file, &st) < 0) + return sys_err_msg("bad device table file '%s'", + tbl_file); + break; + case 'y': + yes = 1; + break; + case 'h': + case '?': + printf("%s", helptext); + exit(0); + case 'v': + verbose = 1; + break; + case 'V': + common_print_version(); + exit(0); + case 'g': + debug_level = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + debug_level < 0 || debug_level > 3) + return err_msg("bad debugging level '%s'", + optarg); + break; + case 'f': + c->fanout = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || c->fanout <= 0) + return err_msg("bad fanout %s", optarg); + break; + case 'F': + c->space_fixup = 1; + break; + case 'l': + c->log_lebs = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) + return err_msg("bad count of log LEBs '%s'", + optarg); + break; + case 'p': + c->orph_lebs = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + c->orph_lebs <= 0) + return err_msg("bad orphan LEB count '%s'", + optarg); + break; + case 'k': + if (strcmp(optarg, "r5") == 0) { + c->key_hash = key_r5_hash; + c->key_hash_type = UBIFS_KEY_HASH_R5; + } else if (strcmp(optarg, "test") == 0) { + c->key_hash = key_test_hash; + c->key_hash_type = UBIFS_KEY_HASH_TEST; + } else + return err_msg("bad key hash"); + break; + case 'x': + if (strcmp(optarg, "favor_lzo") == 0) + c->favor_lzo = 1; + else if (strcmp(optarg, "zlib") == 0) + c->default_compr = UBIFS_COMPR_ZLIB; + else if (strcmp(optarg, "none") == 0) + c->default_compr = UBIFS_COMPR_NONE; + else if (strcmp(optarg, "lzo") != 0) + return err_msg("bad compressor name"); + break; + case 'X': + c->favor_percent = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + c->favor_percent <= 0 || c->favor_percent >= 100) + return err_msg("bad favor LZO percent '%s'", + optarg); + break; + case 'j': + c->max_bud_bytes = get_bytes(optarg); + if (c->max_bud_bytes <= 0) + return err_msg("bad maximum amount of buds"); + break; + case 'R': + c->rp_size = get_bytes(optarg); + if (c->rp_size < 0) + return err_msg("bad reserved bytes count"); + break; + case 'U': + squash_owner = 1; + break; + } + } + + if (optind != argc && !output) + output = xstrdup(argv[optind]); + + if (!output) + return err_msg("not output device or file specified"); + + out_ubi = !open_ubi(output); + + if (out_ubi) { + c->min_io_size = c->di.min_io_size; + c->leb_size = c->vi.leb_size; + if (c->max_leb_cnt == -1) + c->max_leb_cnt = c->vi.rsvd_lebs; + } + + if (c->min_io_size == -1) + return err_msg("min. I/O unit was not specified " + "(use -h for help)"); + + if (c->leb_size == -1) + return err_msg("LEB size was not specified (use -h for help)"); + + if (c->max_leb_cnt == -1) + return err_msg("Maximum count of LEBs was not specified " + "(use -h for help)"); + + if (c->max_bud_bytes == -1) { + int lebs; + + lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; + lebs -= c->orph_lebs; + if (c->log_lebs != -1) + lebs -= c->log_lebs; + else + lebs -= UBIFS_MIN_LOG_LEBS; + /* + * We do not know lprops geometry so far, so assume minimum + * count of lprops LEBs. + */ + lebs -= UBIFS_MIN_LPT_LEBS; + /* Make the journal about 12.5% of main area lebs */ + c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; + /* Make the max journal size 8MiB */ + if (c->max_bud_bytes > 8 * 1024 * 1024) + c->max_bud_bytes = 8 * 1024 * 1024; + if (c->max_bud_bytes < 4 * c->leb_size) + c->max_bud_bytes = 4 * c->leb_size; + } + + if (c->log_lebs == -1) { + c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); + c->log_lebs += 2; + } + + if (c->min_io_size < 8) + c->min_io_size = 8; + c->rp_size = add_space_overhead(c->rp_size); + + if (verbose) { + printf("mkfs.ubifs\n"); + printf("\troot: %s\n", root); + printf("\tmin_io_size: %d\n", c->min_io_size); + printf("\tleb_size: %d\n", c->leb_size); + printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); + printf("\toutput: %s\n", output); + printf("\tjrn_size: %llu\n", c->max_bud_bytes); + printf("\treserved: %llu\n", c->rp_size); + switch (c->default_compr) { + case UBIFS_COMPR_LZO: + printf("\tcompr: lzo\n"); + break; + case UBIFS_COMPR_ZLIB: + printf("\tcompr: zlib\n"); + break; + case UBIFS_COMPR_NONE: + printf("\tcompr: none\n"); + break; + } + printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? + "r5" : "test"); + printf("\tfanout: %d\n", c->fanout); + printf("\torph_lebs: %d\n", c->orph_lebs); + printf("\tspace_fixup: %d\n", c->space_fixup); + } + + if (validate_options()) + return -1; + + if (tbl_file && parse_devtable(tbl_file)) + return err_msg("cannot parse device table file '%s'", tbl_file); + + return 0; +} + +/** + * prepare_node - fill in the common header. + * @node: node + * @len: node length + */ +static void prepare_node(void *node, int len) +{ + uint32_t crc; + struct ubifs_ch *ch = node; + + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); + ch->len = cpu_to_le32(len); + ch->group_type = UBIFS_NO_NODE_GROUP; + ch->sqnum = cpu_to_le64(++c->max_sqnum); + ch->padding[0] = ch->padding[1] = 0; + crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); + ch->crc = cpu_to_le32(crc); +} + +/** + * write_leb - copy the image of a LEB to the output target. + * @lnum: LEB number + * @len: length of data in the buffer + * @buf: buffer (must be at least c->leb_size bytes) + */ +int write_leb(int lnum, int len, void *buf) +{ + off_t pos = (off_t)lnum * c->leb_size; + + dbg_msg(3, "LEB %d len %d", lnum, len); + memset(buf + len, 0xff, c->leb_size - len); + if (out_ubi) + if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size)) + return sys_err_msg("ubi_leb_change_start failed"); + + if (lseek(out_fd, pos, SEEK_SET) != pos) + return sys_err_msg("lseek failed seeking %"PRIdoff_t, pos); + + if (write(out_fd, buf, c->leb_size) != c->leb_size) + return sys_err_msg("write failed writing %d bytes at pos %"PRIdoff_t, + c->leb_size, pos); + + return 0; +} + +/** + * write_empty_leb - copy the image of an empty LEB to the output target. + * @lnum: LEB number + */ +static int write_empty_leb(int lnum) +{ + return write_leb(lnum, 0, leb_buf); +} + +/** + * do_pad - pad a buffer to the minimum I/O size. + * @buf: buffer + * @len: buffer length + */ +static int do_pad(void *buf, int len) +{ + int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); + uint32_t crc; + + memset(buf + len, 0xff, alen - len); + pad_len = wlen - alen; + dbg_msg(3, "len %d pad_len %d", len, pad_len); + buf += alen; + if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { + struct ubifs_ch *ch = buf; + struct ubifs_pad_node *pad_node = buf; + + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); + ch->node_type = UBIFS_PAD_NODE; + ch->group_type = UBIFS_NO_NODE_GROUP; + ch->padding[0] = ch->padding[1] = 0; + ch->sqnum = cpu_to_le64(0); + ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); + + pad_len -= UBIFS_PAD_NODE_SZ; + pad_node->pad_len = cpu_to_le32(pad_len); + + crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8, + UBIFS_PAD_NODE_SZ - 8); + ch->crc = cpu_to_le32(crc); + + memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); + } else if (pad_len > 0) + memset(buf, UBIFS_PADDING_BYTE, pad_len); + + return wlen; +} + +/** + * write_node - write a node to a LEB. + * @node: node + * @len: node length + * @lnum: LEB number + */ +static int write_node(void *node, int len, int lnum) +{ + prepare_node(node, len); + + memcpy(leb_buf, node, len); + + len = do_pad(leb_buf, len); + + return write_leb(lnum, len, leb_buf); +} + +/** + * calc_dark - calculate LEB dark space size. + * @c: the UBIFS file-system description object + * @spc: amount of free and dirty space in the LEB + * + * This function calculates amount of dark space in an LEB which has @spc bytes + * of free and dirty space. Returns the calculations result. + * + * Dark space is the space which is not always usable - it depends on which + * nodes are written in which order. E.g., if an LEB has only 512 free bytes, + * it is dark space, because it cannot fit a large data node. So UBIFS cannot + * count on this LEB and treat these 512 bytes as usable because it is not true + * if, for example, only big chunks of uncompressible data will be written to + * the FS. + */ +static int calc_dark(struct ubifs_info *c, int spc) +{ + if (spc < c->dark_wm) + return spc; + + /* + * If we have slightly more space then the dark space watermark, we can + * anyway safely assume it we'll be able to write a node of the + * smallest size there. + */ + if (spc - c->dark_wm < (int)MIN_WRITE_SZ) + return spc - MIN_WRITE_SZ; + + return c->dark_wm; +} + +/** + * set_lprops - set the LEB property values for a LEB. + * @lnum: LEB number + * @offs: end offset of data in the LEB + * @flags: LEB property flags + */ +static void set_lprops(int lnum, int offs, int flags) +{ + int i = lnum - c->main_first, free, dirty; + int a = max_t(int, c->min_io_size, 8); + + free = c->leb_size - ALIGN(offs, a); + dirty = c->leb_size - free - ALIGN(offs, 8); + dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, + flags); + if (i < c->main_lebs) { + c->lpt[i].free = free; + c->lpt[i].dirty = dirty; + c->lpt[i].flags = flags; + } + c->lst.total_free += free; + c->lst.total_dirty += dirty; + if (flags & LPROPS_INDEX) + c->lst.idx_lebs += 1; + else { + int spc; + + spc = free + dirty; + if (spc < c->dead_wm) + c->lst.total_dead += spc; + else + c->lst.total_dark += calc_dark(c, spc); + c->lst.total_used += c->leb_size - spc; + } +} + +/** + * add_to_index - add a node key and position to the index. + * @key: node key + * @lnum: node LEB number + * @offs: node offset + * @len: node length + */ +static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, + int len) +{ + struct idx_entry *e; + + dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); + e = malloc(sizeof(struct idx_entry)); + if (!e) + return err_msg("out of memory"); + e->next = NULL; + e->prev = idx_list_last; + e->key = *key; + e->name = name; + e->lnum = lnum; + e->offs = offs; + e->len = len; + if (!idx_list_first) + idx_list_first = e; + if (idx_list_last) + idx_list_last->next = e; + idx_list_last = e; + idx_cnt += 1; + return 0; +} + +/** + * flush_nodes - write the current head and move the head to the next LEB. + */ +static int flush_nodes(void) +{ + int len, err; + + if (!head_offs) + return 0; + len = do_pad(leb_buf, head_offs); + err = write_leb(head_lnum, len, leb_buf); + if (err) + return err; + set_lprops(head_lnum, head_offs, head_flags); + head_lnum += 1; + head_offs = 0; + return 0; +} + +/** + * reserve_space - reserve space for a node on the head. + * @len: node length + * @lnum: LEB number is returned here + * @offs: offset is returned here + */ +static int reserve_space(int len, int *lnum, int *offs) +{ + int err; + + if (len > c->leb_size - head_offs) { + err = flush_nodes(); + if (err) + return err; + } + *lnum = head_lnum; + *offs = head_offs; + head_offs += ALIGN(len, 8); + return 0; +} + +/** + * add_node - write a node to the head. + * @key: node key + * @node: node + * @len: node length + */ +static int add_node(union ubifs_key *key, char *name, void *node, int len) +{ + int err, lnum, offs; + + prepare_node(node, len); + + err = reserve_space(len, &lnum, &offs); + if (err) + return err; + + memcpy(leb_buf + offs, node, len); + memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); + + add_to_index(key, name, lnum, offs, len); + + return 0; +} + +/** + * add_inode_with_data - write an inode. + * @st: stat information of source inode + * @inum: target inode number + * @data: inode data (for special inodes e.g. symlink path etc) + * @data_len: inode data length + * @flags: source inode flags + */ +static int add_inode_with_data(struct stat *st, ino_t inum, void *data, + unsigned int data_len, int flags) +{ + struct ubifs_ino_node *ino = node_buf; + union ubifs_key key; + int len, use_flags = 0; + + if (c->default_compr != UBIFS_COMPR_NONE) + use_flags |= UBIFS_COMPR_FL; + if (flags & FS_COMPR_FL) + use_flags |= UBIFS_COMPR_FL; + if (flags & FS_SYNC_FL) + use_flags |= UBIFS_SYNC_FL; + if (flags & FS_IMMUTABLE_FL) + use_flags |= UBIFS_IMMUTABLE_FL; + if (flags & FS_APPEND_FL) + use_flags |= UBIFS_APPEND_FL; + if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) + use_flags |= UBIFS_DIRSYNC_FL; + + memset(ino, 0, UBIFS_INO_NODE_SZ); + + ino_key_init(&key, inum); + ino->ch.node_type = UBIFS_INO_NODE; + key_write(&key, &ino->key); + ino->creat_sqnum = cpu_to_le64(creat_sqnum); + ino->size = cpu_to_le64(st->st_size); + ino->nlink = cpu_to_le32(st->st_nlink); + /* + * The time fields are updated assuming the default time granularity + * of 1 second. To support finer granularities, utime() would be needed. + */ + ino->atime_sec = cpu_to_le64(st->st_atime); + ino->ctime_sec = cpu_to_le64(st->st_ctime); + ino->mtime_sec = cpu_to_le64(st->st_mtime); + ino->atime_nsec = 0; + ino->ctime_nsec = 0; + ino->mtime_nsec = 0; + ino->uid = cpu_to_le32(st->st_uid); + ino->gid = cpu_to_le32(st->st_gid); + ino->mode = cpu_to_le32(st->st_mode); + ino->flags = cpu_to_le32(use_flags); + ino->data_len = cpu_to_le32(data_len); + ino->compr_type = cpu_to_le16(c->default_compr); + if (data_len) + memcpy(&ino->data, data, data_len); + + len = UBIFS_INO_NODE_SZ + data_len; + + return add_node(&key, NULL, ino, len); +} + +/** + * add_inode - write an inode. + * @st: stat information of source inode + * @inum: target inode number + * @flags: source inode flags + */ +static int add_inode(struct stat *st, ino_t inum, int flags) +{ + return add_inode_with_data(st, inum, NULL, 0, flags); +} + +/** + * add_dir_inode - write an inode for a directory. + * @dir: source directory + * @inum: target inode number + * @size: target directory size + * @nlink: target directory link count + * @st: struct stat object describing attributes (except size and nlink) of the + * target inode to create + * + * Note, this function may be called with %NULL @dir, when the directory which + * is being created does not exist at the host file system, but is defined by + * the device table. + */ +static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, + struct stat *st) +{ + int fd, flags = 0; + + st->st_size = size; + st->st_nlink = nlink; + + if (dir) { + fd = dirfd(dir); + if (fd == -1) + return sys_err_msg("dirfd failed"); + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) + flags = 0; + } + + return add_inode(st, inum, flags); +} + +/** + * add_dev_inode - write an inode for a character or block device. + * @st: stat information of source inode + * @inum: target inode number + * @flags: source inode flags + */ +static int add_dev_inode(struct stat *st, ino_t inum, int flags) +{ + union ubifs_dev_desc dev; + + dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); + return add_inode_with_data(st, inum, &dev, 8, flags); +} + +/** + * add_symlink_inode - write an inode for a symbolic link. + * @path_name: path name of symbolic link inode itself (not the link target) + * @st: stat information of source inode + * @inum: target inode number + * @flags: source inode flags + */ +static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, + int flags) +{ + char buf[UBIFS_MAX_INO_DATA + 2]; + ssize_t len; + + /* Take the symlink as is */ + len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); + if (len <= 0) + return sys_err_msg("readlink failed for %s", path_name); + if (len > UBIFS_MAX_INO_DATA) + return err_msg("symlink too long for %s", path_name); + + return add_inode_with_data(st, inum, buf, len, flags); +} + +/** + * add_dent_node - write a directory entry node. + * @dir_inum: target inode number of directory + * @name: directory entry name + * @inum: target inode number of the directory entry + * @type: type of the target inode + */ +static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, + unsigned char type) +{ + struct ubifs_dent_node *dent = node_buf; + union ubifs_key key; + struct qstr dname; + char *kname; + int len; + + dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum, + (unsigned int)type, (unsigned long)dir_inum); + memset(dent, 0, UBIFS_DENT_NODE_SZ); + + dname.name = (void *)name; + dname.len = strlen(name); + + dent->ch.node_type = UBIFS_DENT_NODE; + + dent_key_init(c, &key, dir_inum, &dname); + key_write(&key, dent->key); + dent->inum = cpu_to_le64(inum); + dent->padding1 = 0; + dent->type = type; + dent->nlen = cpu_to_le16(dname.len); + memcpy(dent->name, dname.name, dname.len); + dent->name[dname.len] = '\0'; + + len = UBIFS_DENT_NODE_SZ + dname.len + 1; + + kname = strdup(name); + if (!kname) + return err_msg("cannot allocate memory"); + + return add_node(&key, kname, dent, len); +} + +/** + * lookup_inum_mapping - add an inode mapping for link counting. + * @dev: source device on which source inode number resides + * @inum: source inode number + */ +static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) +{ + struct inum_mapping *im; + unsigned int k; + + k = inum % HASH_TABLE_SIZE; + im = hash_table[k]; + while (im) { + if (im->dev == dev && im->inum == inum) + return im; + im = im->next; + } + im = malloc(sizeof(struct inum_mapping)); + if (!im) + return NULL; + im->next = hash_table[k]; + im->prev = NULL; + im->dev = dev; + im->inum = inum; + im->use_inum = 0; + im->use_nlink = 0; + if (hash_table[k]) + hash_table[k]->prev = im; + hash_table[k] = im; + return im; +} + +/** + * all_zero - does a buffer contain only zero bytes. + * @buf: buffer + * @len: buffer length + */ +static int all_zero(void *buf, int len) +{ + unsigned char *p = buf; + + while (len--) + if (*p++ != 0) + return 0; + return 1; +} + +/** + * add_file - write the data of a file and its inode to the output file. + * @path_name: source path name + * @st: source inode stat information + * @inum: target inode number + * @flags: source inode flags + */ +static int add_file(const char *path_name, struct stat *st, ino_t inum, + int flags) +{ + struct ubifs_data_node *dn = node_buf; + void *buf = block_buf; + loff_t file_size = 0; + ssize_t ret, bytes_read; + union ubifs_key key; + int fd, dn_len, err, compr_type, use_compr; + unsigned int block_no = 0; + size_t out_len; + + fd = open(path_name, O_RDONLY | O_LARGEFILE); + if (fd == -1) + return sys_err_msg("failed to open file '%s'", path_name); + do { + /* Read next block */ + bytes_read = 0; + do { + ret = read(fd, buf + bytes_read, + UBIFS_BLOCK_SIZE - bytes_read); + if (ret == -1) { + sys_err_msg("failed to read file '%s'", + path_name); + close(fd); + return 1; + } + bytes_read += ret; + } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); + if (bytes_read == 0) + break; + file_size += bytes_read; + /* Skip holes */ + if (all_zero(buf, bytes_read)) { + block_no += 1; + continue; + } + /* Make data node */ + memset(dn, 0, UBIFS_DATA_NODE_SZ); + data_key_init(&key, inum, block_no++); + dn->ch.node_type = UBIFS_DATA_NODE; + key_write(&key, &dn->key); + dn->size = cpu_to_le32(bytes_read); + out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; + if (c->default_compr == UBIFS_COMPR_NONE && + (flags & FS_COMPR_FL)) + use_compr = UBIFS_COMPR_LZO; + else + use_compr = c->default_compr; + compr_type = compress_data(buf, bytes_read, &dn->data, + &out_len, use_compr); + dn->compr_type = cpu_to_le16(compr_type); + dn_len = UBIFS_DATA_NODE_SZ + out_len; + /* Add data node to file system */ + err = add_node(&key, NULL, dn, dn_len); + if (err) { + close(fd); + return err; + } + } while (ret != 0); + if (close(fd) == -1) + return sys_err_msg("failed to close file '%s'", path_name); + if (file_size != st->st_size) + return err_msg("file size changed during writing file '%s'", + path_name); + return add_inode(st, inum, flags); +} + +/** + * add_non_dir - write a non-directory to the output file. + * @path_name: source path name + * @inum: target inode number is passed and returned here (due to link counting) + * @nlink: number of links if known otherwise zero + * @type: UBIFS inode type is returned here + * @st: struct stat object containing inode attributes which should be use when + * creating the UBIFS inode + */ +static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, + unsigned char *type, struct stat *st) +{ + int fd, flags = 0; + + dbg_msg(2, "%s", path_name); + + if (S_ISREG(st->st_mode)) { + fd = open(path_name, O_RDONLY); + if (fd == -1) + return sys_err_msg("failed to open file '%s'", + path_name); + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) + flags = 0; + if (close(fd) == -1) + return sys_err_msg("failed to close file '%s'", + path_name); + *type = UBIFS_ITYPE_REG; + } else if (S_ISCHR(st->st_mode)) + *type = UBIFS_ITYPE_CHR; + else if (S_ISBLK(st->st_mode)) + *type = UBIFS_ITYPE_BLK; + else if (S_ISLNK(st->st_mode)) + *type = UBIFS_ITYPE_LNK; + else if (S_ISSOCK(st->st_mode)) + *type = UBIFS_ITYPE_SOCK; + else if (S_ISFIFO(st->st_mode)) + *type = UBIFS_ITYPE_FIFO; + else + return err_msg("file '%s' has unknown inode type", path_name); + + if (nlink) + st->st_nlink = nlink; + else if (st->st_nlink > 1) { + /* + * If the number of links is greater than 1, then add this file + * later when we know the number of links that we actually have. + * For now, we just put the inode mapping in the hash table. + */ + struct inum_mapping *im; + + im = lookup_inum_mapping(st->st_dev, st->st_ino); + if (!im) + return err_msg("out of memory"); + if (im->use_nlink == 0) { + /* New entry */ + im->use_inum = *inum; + im->use_nlink = 1; + im->path_name = malloc(strlen(path_name) + 1); + if (!im->path_name) + return err_msg("out of memory"); + strcpy(im->path_name, path_name); + } else { + /* Existing entry */ + *inum = im->use_inum; + im->use_nlink += 1; + /* Return unused inode number */ + c->highest_inum -= 1; + } + + memcpy(&im->st, st, sizeof(struct stat)); + return 0; + } else + st->st_nlink = 1; + + creat_sqnum = ++c->max_sqnum; + + if (S_ISREG(st->st_mode)) + return add_file(path_name, st, *inum, flags); + if (S_ISCHR(st->st_mode)) + return add_dev_inode(st, *inum, flags); + if (S_ISBLK(st->st_mode)) + return add_dev_inode(st, *inum, flags); + if (S_ISLNK(st->st_mode)) + return add_symlink_inode(path_name, st, *inum, flags); + if (S_ISSOCK(st->st_mode)) + return add_inode(st, *inum, flags); + if (S_ISFIFO(st->st_mode)) + return add_inode(st, *inum, flags); + + return err_msg("file '%s' has unknown inode type", path_name); +} + +/** + * add_directory - write a directory tree to the output file. + * @dir_name: directory path name + * @dir_inum: UBIFS inode number of directory + * @st: directory inode statistics + * @non_existing: non-zero if this function is called for a directory which + * does not exist on the host file-system and it is being + * created because it is defined in the device table file. + */ +static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, + int non_existing) +{ + struct dirent *entry; + DIR *dir = NULL; + int err = 0; + loff_t size = UBIFS_INO_NODE_SZ; + char *name = NULL; + unsigned int nlink = 2; + struct path_htbl_element *ph_elt; + struct name_htbl_element *nh_elt = NULL; + struct hashtable_itr *itr; + ino_t inum; + unsigned char type; + unsigned long long dir_creat_sqnum = ++c->max_sqnum; + + dbg_msg(2, "%s", dir_name); + if (!non_existing) { + dir = opendir(dir_name); + if (dir == NULL) + return sys_err_msg("cannot open directory '%s'", + dir_name); + } + + /* + * Check whether this directory contains files which should be + * added/changed because they were specified in the device table. + * @ph_elt will be non-zero if yes. + */ + ph_elt = devtbl_find_path(dir_name + root_len - 1); + + /* + * Before adding the directory itself, we have to iterate over all the + * entries the device table adds to this directory and create them. + */ + for (; !non_existing;) { + struct stat dent_st; + + errno = 0; + entry = readdir(dir); + if (!entry) { + if (errno == 0) + break; + sys_err_msg("error reading directory '%s'", dir_name); + err = -1; + break; + } + + if (strcmp(".", entry->d_name) == 0) + continue; + if (strcmp("..", entry->d_name) == 0) + continue; + + if (ph_elt) + /* + * This directory was referred to at the device table + * file. Check if this directory entry is referred at + * too. + */ + nh_elt = devtbl_find_name(ph_elt, entry->d_name); + + /* + * We are going to create the file corresponding to this + * directory entry (@entry->d_name). We use 'struct stat' + * object to pass information about file attributes (actually + * only about UID, GID, mode, major, and minor). Get attributes + * for this file from the UBIFS rootfs on the host. + */ + free(name); + name = make_path(dir_name, entry->d_name); + if (lstat(name, &dent_st) == -1) { + sys_err_msg("lstat failed for file '%s'", name); + goto out_free; + } + + if (squash_owner) + /* + * Squash UID/GID. But the device table may override + * this. + */ + dent_st.st_uid = dent_st.st_gid = 0; + + /* + * And if the device table describes the same file, override + * the attributes. However, this is not allowed for device node + * files. + */ + if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) + goto out_free; + + inum = ++c->highest_inum; + + if (S_ISDIR(dent_st.st_mode)) { + err = add_directory(name, inum, &dent_st, 0); + if (err) + goto out_free; + nlink += 1; + type = UBIFS_ITYPE_DIR; + } else { + err = add_non_dir(name, &inum, 0, &type, &dent_st); + if (err) + goto out_free; + } + + err = add_dent_node(dir_inum, entry->d_name, inum, type); + if (err) + goto out_free; + size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, + 8); + } + + /* + * OK, we have created all files in this directory (recursively), let's + * also create all files described in the device table. All t + */ + nh_elt = first_name_htbl_element(ph_elt, &itr); + while (nh_elt) { + struct stat fake_st; + + /* + * We prohibit creating regular files using the device table, + * the device table may only re-define attributes of regular + * files. + */ + if (S_ISREG(nh_elt->mode)) { + err_msg("Bad device table entry %s/%s - it is " + "prohibited to create regular files " + "via device table", + strcmp(ph_elt->path, "/") ? ph_elt->path : "", + nh_elt->name); + goto out_free; + } + + memcpy(&fake_st, &root_st, sizeof(struct stat)); + fake_st.st_uid = nh_elt->uid; + fake_st.st_uid = nh_elt->uid; + fake_st.st_mode = nh_elt->mode; + fake_st.st_rdev = nh_elt->dev; + fake_st.st_nlink = 1; + + free(name); + name = make_path(dir_name, nh_elt->name); + inum = ++c->highest_inum; + + if (S_ISDIR(nh_elt->mode)) { + err = add_directory(name, inum, &fake_st, 1); + if (err) + goto out_free; + nlink += 1; + type = UBIFS_ITYPE_DIR; + } else { + err = add_non_dir(name, &inum, 0, &type, &fake_st); + if (err) + goto out_free; + } + + err = add_dent_node(dir_inum, nh_elt->name, inum, type); + if (err) + goto out_free; + size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); + + nh_elt = next_name_htbl_element(ph_elt, &itr); + } + + creat_sqnum = dir_creat_sqnum; + + err = add_dir_inode(dir, dir_inum, size, nlink, st); + if (err) + goto out_free; + + free(name); + if (!non_existing && closedir(dir) == -1) + return sys_err_msg("error closing directory '%s'", dir_name); + + return 0; + +out_free: + free(name); + if (!non_existing) + closedir(dir); + return -1; +} + +/** + * add_multi_linked_files - write all the files for which we counted links. + */ +static int add_multi_linked_files(void) +{ + int i, err; + + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct inum_mapping *im; + unsigned char type = 0; + + for (im = hash_table[i]; im; im = im->next) { + dbg_msg(2, "%s", im->path_name); + err = add_non_dir(im->path_name, &im->use_inum, + im->use_nlink, &type, &im->st); + if (err) + return err; + } + } + return 0; +} + +/** + * write_data - write the files and directories. + */ +static int write_data(void) +{ + int err; + mode_t mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + + if (root) { + err = stat(root, &root_st); + if (err) + return sys_err_msg("bad root file-system directory '%s'", + root); + } else { + root_st.st_mtime = time(NULL); + root_st.st_atime = root_st.st_ctime = root_st.st_mtime; + root_st.st_mode = mode; + } + + head_flags = 0; + err = add_directory(root, UBIFS_ROOT_INO, &root_st, !root); + if (err) + return err; + err = add_multi_linked_files(); + if (err) + return err; + return flush_nodes(); +} + +static int namecmp(const char *name1, const char *name2) +{ + size_t len1 = strlen(name1), len2 = strlen(name2); + size_t clen = (len1 < len2) ? len1 : len2; + int cmp; + + cmp = memcmp(name1, name2, clen); + if (cmp) + return cmp; + return (len1 < len2) ? -1 : 1; +} + +static int cmp_idx(const void *a, const void *b) +{ + const struct idx_entry *e1 = *(const struct idx_entry **)a; + const struct idx_entry *e2 = *(const struct idx_entry **)b; + int cmp; + + cmp = keys_cmp(&e1->key, &e2->key); + if (cmp) + return cmp; + return namecmp(e1->name, e2->name); +} + +/** + * add_idx_node - write an index node to the head. + * @node: index node + * @child_cnt: number of children of this index node + */ +static int add_idx_node(void *node, int child_cnt) +{ + int err, lnum, offs, len; + + len = ubifs_idx_node_sz(c, child_cnt); + + prepare_node(node, len); + + err = reserve_space(len, &lnum, &offs); + if (err) + return err; + + memcpy(leb_buf + offs, node, len); + memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); + + c->old_idx_sz += ALIGN(len, 8); + + dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, + c->old_idx_sz); + + /* The last index node written will be the root */ + c->zroot.lnum = lnum; + c->zroot.offs = offs; + c->zroot.len = len; + + return 0; +} + +/** + * write_index - write out the index. + */ +static int write_index(void) +{ + size_t sz, i, cnt, idx_sz, pstep, bcnt; + struct idx_entry **idx_ptr, **p; + struct ubifs_idx_node *idx; + struct ubifs_branch *br; + int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err; + + dbg_msg(1, "leaf node count: %zd", idx_cnt); + + /* Reset the head for the index */ + head_flags = LPROPS_INDEX; + /* Allocate index node */ + idx_sz = ubifs_idx_node_sz(c, c->fanout); + idx = malloc(idx_sz); + if (!idx) + return err_msg("out of memory"); + /* Make an array of pointers to sort the index list */ + sz = idx_cnt * sizeof(struct idx_entry *); + if (sz / sizeof(struct idx_entry *) != idx_cnt) { + free(idx); + return err_msg("index is too big (%zu entries)", idx_cnt); + } + idx_ptr = malloc(sz); + if (!idx_ptr) { + free(idx); + return err_msg("out of memory - needed %zu bytes for index", + sz); + } + idx_ptr[0] = idx_list_first; + for (i = 1; i < idx_cnt; i++) + idx_ptr[i] = idx_ptr[i - 1]->next; + qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); + /* Write level 0 index nodes */ + cnt = idx_cnt / c->fanout; + if (idx_cnt % c->fanout) + cnt += 1; + p = idx_ptr; + blnum = head_lnum; + boffs = head_offs; + for (i = 0; i < cnt; i++) { + /* + * Calculate the child count. All index nodes are created full + * except for the last index node on each row. + */ + if (i == cnt - 1) { + child_cnt = idx_cnt % c->fanout; + if (child_cnt == 0) + child_cnt = c->fanout; + } else + child_cnt = c->fanout; + memset(idx, 0, idx_sz); + idx->ch.node_type = UBIFS_IDX_NODE; + idx->child_cnt = cpu_to_le16(child_cnt); + idx->level = cpu_to_le16(0); + for (j = 0; j < child_cnt; j++, p++) { + br = ubifs_idx_branch(c, idx, j); + key_write_idx(&(*p)->key, &br->key); + br->lnum = cpu_to_le32((*p)->lnum); + br->offs = cpu_to_le32((*p)->offs); + br->len = cpu_to_le32((*p)->len); + } + add_idx_node(idx, child_cnt); + } + /* Write level 1 index nodes and above */ + level = 0; + pstep = 1; + while (cnt > 1) { + /* + * 'blast_len' is the length of the last index node in the level + * below. + */ + blast_len = ubifs_idx_node_sz(c, child_cnt); + /* 'bcnt' is the number of index nodes in the level below */ + bcnt = cnt; + /* 'cnt' is the number of index nodes in this level */ + cnt = (cnt + c->fanout - 1) / c->fanout; + if (cnt == 0) + cnt = 1; + level += 1; + /* + * The key of an index node is the same as the key of its first + * child. Thus we can get the key by stepping along the bottom + * level 'p' with an increasing large step 'pstep'. + */ + p = idx_ptr; + pstep *= c->fanout; + for (i = 0; i < cnt; i++) { + /* + * Calculate the child count. All index nodes are + * created full except for the last index node on each + * row. + */ + if (i == cnt - 1) { + child_cnt = bcnt % c->fanout; + if (child_cnt == 0) + child_cnt = c->fanout; + } else + child_cnt = c->fanout; + memset(idx, 0, idx_sz); + idx->ch.node_type = UBIFS_IDX_NODE; + idx->child_cnt = cpu_to_le16(child_cnt); + idx->level = cpu_to_le16(level); + for (j = 0; j < child_cnt; j++) { + size_t bn = i * c->fanout + j; + + /* + * The length of the index node in the level + * below is 'idx_sz' except when it is the last + * node on the row. i.e. all the others on the + * row are full. + */ + if (bn == bcnt - 1) + blen = blast_len; + else + blen = idx_sz; + /* + * 'blnum' and 'boffs' hold the position of the + * index node on the level below. + */ + if (boffs + blen > c->leb_size) { + blnum += 1; + boffs = 0; + } + /* + * Fill in the branch with the key and position + * of the index node from the level below. + */ + br = ubifs_idx_branch(c, idx, j); + key_write_idx(&(*p)->key, &br->key); + br->lnum = cpu_to_le32(blnum); + br->offs = cpu_to_le32(boffs); + br->len = cpu_to_le32(blen); + /* + * Step to the next index node on the level + * below. + */ + boffs += ALIGN(blen, 8); + p += pstep; + } + add_idx_node(idx, child_cnt); + } + } + + /* Free stuff */ + for (i = 0; i < idx_cnt; i++) + free(idx_ptr[i]); + free(idx_ptr); + free(idx); + + dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, + c->zroot.len); + + /* Set the index head */ + c->ihead_lnum = head_lnum; + c->ihead_offs = ALIGN(head_offs, c->min_io_size); + dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); + + /* Flush the last index LEB */ + err = flush_nodes(); + if (err) + return err; + + return 0; +} + +/** + * set_gc_lnum - set the LEB number reserved for the garbage collector. + */ +static int set_gc_lnum(void) +{ + int err; + + c->gc_lnum = head_lnum++; + err = write_empty_leb(c->gc_lnum); + if (err) + return err; + set_lprops(c->gc_lnum, 0, 0); + c->lst.empty_lebs += 1; + return 0; +} + +/** + * finalize_leb_cnt - now that we know how many LEBs we used. + */ +static int finalize_leb_cnt(void) +{ + c->leb_cnt = head_lnum; + if (c->leb_cnt > c->max_leb_cnt) + return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); + c->main_lebs = c->leb_cnt - c->main_first; + if (verbose) { + printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); + printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); + printf("\tlog_lebs: %d\n", c->log_lebs); + printf("\tlpt_lebs: %d\n", c->lpt_lebs); + printf("\torph_lebs: %d\n", c->orph_lebs); + printf("\tmain_lebs: %d\n", c->main_lebs); + printf("\tgc lebs: %d\n", 1); + printf("\tindex lebs: %d\n", c->lst.idx_lebs); + printf("\tleb_cnt: %d\n", c->leb_cnt); + } + dbg_msg(1, "total_free: %llu", c->lst.total_free); + dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); + dbg_msg(1, "total_used: %llu", c->lst.total_used); + dbg_msg(1, "total_dead: %llu", c->lst.total_dead); + dbg_msg(1, "total_dark: %llu", c->lst.total_dark); + dbg_msg(1, "index size: %llu", c->old_idx_sz); + dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); + return 0; +} + +/** + * write_super - write the super block. + */ +static int write_super(void) +{ + struct ubifs_sb_node sup; + + memset(&sup, 0, UBIFS_SB_NODE_SZ); + + sup.ch.node_type = UBIFS_SB_NODE; + sup.key_hash = c->key_hash_type; + sup.min_io_size = cpu_to_le32(c->min_io_size); + sup.leb_size = cpu_to_le32(c->leb_size); + sup.leb_cnt = cpu_to_le32(c->leb_cnt); + sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); + sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); + sup.log_lebs = cpu_to_le32(c->log_lebs); + sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); + sup.orph_lebs = cpu_to_le32(c->orph_lebs); + sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); + sup.fanout = cpu_to_le32(c->fanout); + sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); + sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); + sup.default_compr = cpu_to_le16(c->default_compr); + sup.rp_size = cpu_to_le64(c->rp_size); + sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); + uuid_generate_random(sup.uuid); + if (verbose) { + char s[40]; + + uuid_unparse_upper(sup.uuid, s); + printf("\tUUID: %s\n", s); + } + if (c->big_lpt) + sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); + if (c->space_fixup) + sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP); + + return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); +} + +/** + * write_master - write the master node. + */ +static int write_master(void) +{ + struct ubifs_mst_node mst; + int err; + + memset(&mst, 0, UBIFS_MST_NODE_SZ); + + mst.ch.node_type = UBIFS_MST_NODE; + mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); + mst.highest_inum = cpu_to_le64(c->highest_inum); + mst.cmt_no = cpu_to_le64(0); + mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); + mst.root_lnum = cpu_to_le32(c->zroot.lnum); + mst.root_offs = cpu_to_le32(c->zroot.offs); + mst.root_len = cpu_to_le32(c->zroot.len); + mst.gc_lnum = cpu_to_le32(c->gc_lnum); + mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); + mst.ihead_offs = cpu_to_le32(c->ihead_offs); + mst.index_size = cpu_to_le64(c->old_idx_sz); + mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); + mst.lpt_offs = cpu_to_le32(c->lpt_offs); + mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); + mst.nhead_offs = cpu_to_le32(c->nhead_offs); + mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); + mst.ltab_offs = cpu_to_le32(c->ltab_offs); + mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); + mst.lsave_offs = cpu_to_le32(c->lsave_offs); + mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); + mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); + mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); + mst.total_free = cpu_to_le64(c->lst.total_free); + mst.total_dirty = cpu_to_le64(c->lst.total_dirty); + mst.total_used = cpu_to_le64(c->lst.total_used); + mst.total_dead = cpu_to_le64(c->lst.total_dead); + mst.total_dark = cpu_to_le64(c->lst.total_dark); + mst.leb_cnt = cpu_to_le32(c->leb_cnt); + + err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); + if (err) + return err; + + err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); + if (err) + return err; + + return 0; +} + +/** + * write_log - write an empty log. + */ +static int write_log(void) +{ + struct ubifs_cs_node cs; + int err, i, lnum; + + lnum = UBIFS_LOG_LNUM; + + cs.ch.node_type = UBIFS_CS_NODE; + cs.cmt_no = cpu_to_le64(0); + + err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); + if (err) + return err; + + lnum += 1; + + for (i = 1; i < c->log_lebs; i++, lnum++) { + err = write_empty_leb(lnum); + if (err) + return err; + } + + return 0; +} + +/** + * write_lpt - write the LEB properties tree. + */ +static int write_lpt(void) +{ + int err, lnum; + + err = create_lpt(c); + if (err) + return err; + + lnum = c->nhead_lnum + 1; + while (lnum <= c->lpt_last) { + err = write_empty_leb(lnum++); + if (err) + return err; + } + + return 0; +} + +/** + * write_orphan_area - write an empty orphan area. + */ +static int write_orphan_area(void) +{ + int err, i, lnum; + + lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; + for (i = 0; i < c->orph_lebs; i++, lnum++) { + err = write_empty_leb(lnum); + if (err) + return err; + } + return 0; +} + +/** + * check_volume_empty - check if the UBI volume is empty. + * + * This function checks if the UBI volume is empty by looking if its LEBs are + * mapped or not. + * + * Returns %0 in case of success, %1 is the volume is not empty, + * and a negative error code in case of failure. + */ +static int check_volume_empty(void) +{ + int lnum, err; + + for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) { + err = ubi_is_mapped(out_fd, lnum); + if (err < 0) + return err; + if (err == 1) + return 1; + } + return 0; +} + +/** + * open_target - open the output target. + * + * Open the output target. The target can be an UBI volume + * or a file. + * + * Returns %0 in case of success and %-1 in case of failure. + */ +static int open_target(void) +{ + if (out_ubi) { + out_fd = open(output, O_RDWR | O_EXCL); + + if (out_fd == -1) + return sys_err_msg("cannot open the UBI volume '%s'", + output); + if (ubi_set_property(out_fd, UBI_VOL_PROP_DIRECT_WRITE, 1)) + return sys_err_msg("ubi_set_property failed"); + + if (!yes && check_volume_empty()) { + if (!prompt("UBI volume is not empty. Format anyways?", false)) + return err_msg("UBI volume is not empty"); + } + } else { + out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (out_fd == -1) + return sys_err_msg("cannot create output file '%s'", + output); + } + return 0; +} + + +/** + * close_target - close the output target. + * + * Close the output target. If the target was an UBI + * volume, also close libubi. + * + * Returns %0 in case of success and %-1 in case of failure. + */ +static int close_target(void) +{ + if (ubi) + libubi_close(ubi); + if (out_fd >= 0 && close(out_fd) == -1) + return sys_err_msg("cannot close the target '%s'", output); + if (output) + free(output); + return 0; +} + +/** + * init - initialize things. + */ +static int init(void) +{ + int err, i, main_lebs, big_lpt = 0, sz; + + c->highest_inum = UBIFS_FIRST_INO; + + c->jhead_cnt = 1; + + main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; + main_lebs -= c->log_lebs + c->orph_lebs; + + err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); + if (err) + return err; + + c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + + c->orph_lebs; + head_lnum = c->main_first; + head_offs = 0; + + c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; + c->lpt_last = c->lpt_first + c->lpt_lebs - 1; + + c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); + if (!c->lpt) + return err_msg("unable to allocate LPT"); + + c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); + if (!c->ltab) + return err_msg("unable to allocate LPT ltab"); + + /* Initialize LPT's own lprops */ + for (i = 0; i < c->lpt_lebs; i++) { + c->ltab[i].free = c->leb_size; + c->ltab[i].dirty = 0; + } + + c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); + c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); + dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); + + leb_buf = malloc(c->leb_size); + if (!leb_buf) + return err_msg("out of memory"); + + node_buf = malloc(NODE_BUFFER_SIZE); + if (!node_buf) + return err_msg("out of memory"); + + block_buf = malloc(UBIFS_BLOCK_SIZE); + if (!block_buf) + return err_msg("out of memory"); + + sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; + hash_table = malloc(sz); + if (!hash_table) + return err_msg("out of memory"); + memset(hash_table, 0, sz); + + err = init_compression(); + if (err) + return err; + + return 0; +} + +static void destroy_hash_table(void) +{ + int i; + + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct inum_mapping *im, *q; + + for (im = hash_table[i]; im; ) { + q = im; + im = im->next; + free(q->path_name); + free(q); + } + } +} + +/** + * deinit - deinitialize things. + */ +static void deinit(void) +{ + free(c->lpt); + free(c->ltab); + free(leb_buf); + free(node_buf); + free(block_buf); + destroy_hash_table(); + free(hash_table); + destroy_compression(); + free_devtable_info(); +} + +/** + * mkfs - make the file system. + * + * Each on-flash area has a corresponding function to create it. The order of + * the functions reflects what information must be known to complete each stage. + * As a consequence the output file is not written sequentially. No effort has + * been made to make efficient use of memory or to allow for the possibility of + * incremental updates to the output file. + */ +static int mkfs(void) +{ + int err = 0; + + err = init(); + if (err) + goto out; + + err = write_data(); + if (err) + goto out; + + err = set_gc_lnum(); + if (err) + goto out; + + err = write_index(); + if (err) + goto out; + + err = finalize_leb_cnt(); + if (err) + goto out; + + err = write_lpt(); + if (err) + goto out; + + err = write_super(); + if (err) + goto out; + + err = write_master(); + if (err) + goto out; + + err = write_log(); + if (err) + goto out; + + err = write_orphan_area(); + +out: + deinit(); + return err; +} + +int main(int argc, char *argv[]) +{ + int err; + + err = get_options(argc, argv); + if (err) + return err; + + err = open_target(); + if (err) + return err; + + err = mkfs(); + if (err) { + close_target(); + return err; + } + + err = close_target(); + if (err) + return err; + + if (verbose) + printf("Success!\n"); + + return 0; +} diff --git a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h new file mode 100644 index 0000000..944a159 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 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 version 2 as published by + * the Free Software Foundation. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#ifndef __MKFS_UBIFS_H__ +#define __MKFS_UBIFS_H__ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <string.h> +#include <stdint.h> +#include <endian.h> +#include <byteswap.h> +#include <linux/types.h> +#include <linux/fs.h> + +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <libgen.h> +#include <ctype.h> +#include <uuid/uuid.h> +#include <sys/file.h> + +#include <mtd/ubifs-media.h> + +/* common.h requires the PROGRAM_NAME macro */ +#define PROGRAM_NAME "mkfs.ubifs" +#include "common.h" + +#include "libubi.h" +#include "defs.h" +#include "crc16.h" +#include "ubifs.h" +#include "key.h" +#include "lpt.h" +#include "compr.h" + +/* + * Compression flags are duplicated so that compr.c can compile without ubifs.h. + * Here we make sure they are the same. + */ +#if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE +#error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE +#endif +#if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO +#error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO +#endif +#if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB +#error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB +#endif + +extern int verbose; +extern int debug_level; + +#define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ + printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ +} while(0) + +#define err_msg(fmt, ...) ({ \ + fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ + -1; \ +}) + +#define sys_err_msg(fmt, ...) ({ \ + int err_ = errno; \ + fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ + fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ + -1; \ +}) + +/** + * struct path_htbl_element - an element of the path hash table. + * @path: the UBIFS path the element describes (the key of the element) + * @name_htbl: one more (nested) hash table containing names of all + * files/directories/device nodes which should be created at this + * path + * + * See device table handling for more information. + */ +struct path_htbl_element { + const char *path; + struct hashtable *name_htbl; +}; + +/** + * struct name_htbl_element - an element in the name hash table + * @name: name of the file/directory/device node (the key of the element) + * @mode: accsess rights and file type + * @uid: user ID + * @gid: group ID + * @major: device node major number + * @minor: device node minor number + * + * This is an element of the name hash table. Name hash table sits in the path + * hash table elements and describes file names which should be created/changed + * at this path. + */ +struct name_htbl_element { + const char *name; + unsigned int mode; + unsigned int uid; + unsigned int gid; + dev_t dev; +}; + +extern struct ubifs_info info_; + +struct hashtable_itr; + +int write_leb(int lnum, int len, void *buf); +int parse_devtable(const char *tbl_file); +struct path_htbl_element *devtbl_find_path(const char *path); +struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, + const char *name); +int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, + struct name_htbl_element *nh_elt); +struct name_htbl_element * +first_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr); +struct name_htbl_element * +next_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr); +void free_devtable_info(void); + +#endif diff --git a/ubifs-utils/mkfs.ubifs/ubifs.h b/ubifs-utils/mkfs.ubifs/ubifs.h new file mode 100644 index 0000000..434b651 --- /dev/null +++ b/ubifs-utils/mkfs.ubifs/ubifs.h @@ -0,0 +1,441 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 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 version 2 as published by + * the Free Software Foundation. + * + * 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., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#ifndef __UBIFS_H__ +#define __UBIFS_H__ + +/* Maximum logical eraseblock size in bytes */ +#define UBIFS_MAX_LEB_SZ (2*1024*1024) + +/* Minimum amount of data UBIFS writes to the flash */ +#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) + +/* Largest key size supported in this implementation */ +#define CUR_MAX_KEY_LEN UBIFS_SK_LEN + +/* + * There is no notion of truncation key because truncation nodes do not exist + * in TNC. However, when replaying, it is handy to introduce fake "truncation" + * keys for truncation nodes because the code becomes simpler. So we define + * %UBIFS_TRUN_KEY type. + */ +#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT + +/* The below union makes it easier to deal with keys */ +union ubifs_key +{ + uint8_t u8[CUR_MAX_KEY_LEN]; + uint32_t u32[CUR_MAX_KEY_LEN/4]; + uint64_t u64[CUR_MAX_KEY_LEN/8]; + __le32 j32[CUR_MAX_KEY_LEN/4]; +}; + +/* + * LEB properties flags. + * + * LPROPS_UNCAT: not categorized + * LPROPS_DIRTY: dirty > 0, not index + * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index + * LPROPS_FREE: free > 0, not empty, not index + * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs + * LPROPS_EMPTY: LEB is empty, not taken + * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken + * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken + * LPROPS_CAT_MASK: mask for the LEB categories above + * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) + * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) + */ +enum { + LPROPS_UNCAT = 0, + LPROPS_DIRTY = 1, + LPROPS_DIRTY_IDX = 2, + LPROPS_FREE = 3, + LPROPS_HEAP_CNT = 3, + LPROPS_EMPTY = 4, + LPROPS_FREEABLE = 5, + LPROPS_FRDI_IDX = 6, + LPROPS_CAT_MASK = 15, + LPROPS_TAKEN = 16, + LPROPS_INDEX = 32, +}; + +/** + * struct ubifs_lprops - logical eraseblock properties. + * @free: amount of free space in bytes + * @dirty: amount of dirty space in bytes + * @flags: LEB properties flags (see above) + */ +struct ubifs_lprops +{ + int free; + int dirty; + int flags; +}; + +/** + * struct ubifs_lpt_lprops - LPT logical eraseblock properties. + * @free: amount of free space in bytes + * @dirty: amount of dirty space in bytes + */ +struct ubifs_lpt_lprops +{ + int free; + int dirty; +}; + +struct ubifs_nnode; + +/** + * struct ubifs_cnode - LEB Properties Tree common node. + * @parent: parent nnode + * @cnext: next cnode to commit + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) + * @iip: index in parent + * @level: level in the tree (zero for pnodes, greater than zero for nnodes) + * @num: node number + */ +struct ubifs_cnode +{ + struct ubifs_nnode *parent; + struct ubifs_cnode *cnext; + unsigned long flags; + int iip; + int level; + int num; +}; + +/** + * struct ubifs_pnode - LEB Properties Tree leaf node. + * @parent: parent nnode + * @cnext: next cnode to commit + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) + * @iip: index in parent + * @level: level in the tree (always zero for pnodes) + * @num: node number + * @lprops: LEB properties array + */ +struct ubifs_pnode +{ + struct ubifs_nnode *parent; + struct ubifs_cnode *cnext; + unsigned long flags; + int iip; + int level; + int num; + struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; +}; + +/** + * struct ubifs_nbranch - LEB Properties Tree internal node branch. + * @lnum: LEB number of child + * @offs: offset of child + * @nnode: nnode child + * @pnode: pnode child + * @cnode: cnode child + */ +struct ubifs_nbranch +{ + int lnum; + int offs; + union + { + struct ubifs_nnode *nnode; + struct ubifs_pnode *pnode; + struct ubifs_cnode *cnode; + }; +}; + +/** + * struct ubifs_nnode - LEB Properties Tree internal node. + * @parent: parent nnode + * @cnext: next cnode to commit + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) + * @iip: index in parent + * @level: level in the tree (always greater than zero for nnodes) + * @num: node number + * @nbranch: branches to child nodes + */ +struct ubifs_nnode +{ + struct ubifs_nnode *parent; + struct ubifs_cnode *cnext; + unsigned long flags; + int iip; + int level; + int num; + struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; +}; + +/** + * struct ubifs_lp_stats - statistics of eraseblocks in the main area. + * @empty_lebs: number of empty LEBs + * @taken_empty_lebs: number of taken LEBs + * @idx_lebs: number of indexing LEBs + * @total_free: total free space in bytes + * @total_dirty: total dirty space in bytes + * @total_used: total used space in bytes (includes only data LEBs) + * @total_dead: total dead space in bytes (includes only data LEBs) + * @total_dark: total dark space in bytes (includes only data LEBs) + */ +struct ubifs_lp_stats { + int empty_lebs; + int taken_empty_lebs; + int idx_lebs; + long long total_free; + long long total_dirty; + long long total_used; + long long total_dead; + long long total_dark; +}; + +/** + * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. + * @key: key + * @znode: znode address in memory + * @lnum: LEB number of the indexing node + * @offs: offset of the indexing node within @lnum + * @len: target node length + */ +struct ubifs_zbranch +{ + union ubifs_key key; + struct ubifs_znode *znode; + int lnum; + int offs; + int len; +}; + +/** + * struct ubifs_znode - in-memory representation of an indexing node. + * @parent: parent znode or NULL if it is the root + * @cnext: next znode to commit + * @flags: flags + * @time: last access time (seconds) + * @level: level of the entry in the TNC tree + * @child_cnt: count of child znodes + * @iip: index in parent's zbranch array + * @alt: lower bound of key range has altered i.e. child inserted at slot 0 + * @zbranch: array of znode branches (@c->fanout elements) + */ +struct ubifs_znode +{ + struct ubifs_znode *parent; + struct ubifs_znode *cnext; + unsigned long flags; + unsigned long time; + int level; + int child_cnt; + int iip; + int alt; +#ifdef CONFIG_UBIFS_FS_DEBUG + int lnum, offs, len; +#endif + struct ubifs_zbranch zbranch[]; +}; + +/** + * struct ubifs_info - UBIFS file-system description data structure + * (per-superblock). + * + * @highest_inum: highest used inode number + * @max_sqnum: current global sequence number + * + * @jhead_cnt: count of journal heads + * @max_bud_bytes: maximum number of bytes allowed in buds + * + * @zroot: zbranch which points to the root index node and znode + * @ihead_lnum: LEB number of index head + * @ihead_offs: offset of index head + * + * @log_lebs: number of logical eraseblocks in the log + * @lpt_lebs: number of LEBs used for lprops table + * @lpt_first: first LEB of the lprops table area + * @lpt_last: last LEB of the lprops table area + * @main_lebs: count of LEBs in the main area + * @main_first: first LEB of the main area + * @default_compr: default compression type + * @favor_lzo: favor LZO compression method + * @favor_percent: lzo vs. zlib threshold used in case favor LZO + * + * @key_hash_type: type of the key hash + * @key_hash: direntry key hash function + * @key_fmt: key format + * @key_len: key length + * @fanout: fanout of the index tree (number of links per indexing node) + * + * @min_io_size: minimal input/output unit size + * @leb_size: logical eraseblock size in bytes + * @leb_cnt: count of logical eraseblocks + * @max_leb_cnt: maximum count of logical eraseblocks + * + * @old_idx_sz: size of index on flash + * @lst: lprops statistics + * + * @dead_wm: LEB dead space watermark + * @dark_wm: LEB dark space watermark + * + * @di: UBI device information + * @vi: UBI volume information + * + * @gc_lnum: LEB number used for garbage collection + * @rp_size: reserved pool size + * + * @space_bits: number of bits needed to record free or dirty space + * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT + * @lpt_offs_bits: number of bits needed to record an offset in the LPT + * @lpt_spc_bits: number of bits needed to space in the LPT + * @pcnt_bits: number of bits needed to record pnode or nnode number + * @lnum_bits: number of bits needed to record LEB number + * @nnode_sz: size of on-flash nnode + * @pnode_sz: size of on-flash pnode + * @ltab_sz: size of on-flash LPT lprops table + * @lsave_sz: size of on-flash LPT save table + * @pnode_cnt: number of pnodes + * @nnode_cnt: number of nnodes + * @lpt_hght: height of the LPT + * + * @lpt_lnum: LEB number of the root nnode of the LPT + * @lpt_offs: offset of the root nnode of the LPT + * @nhead_lnum: LEB number of LPT head + * @nhead_offs: offset of LPT head + * @big_lpt: flag that LPT is too big to write whole during commit + * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up + * @lpt_sz: LPT size + * + * @ltab_lnum: LEB number of LPT's own lprops table + * @ltab_offs: offset of LPT's own lprops table + * @lpt: lprops table + * @ltab: LPT's own lprops table + * @lsave_cnt: number of LEB numbers in LPT's save table + * @lsave_lnum: LEB number of LPT's save table + * @lsave_offs: offset of LPT's save table + * @lsave: LPT's save table + * @lscan_lnum: LEB number of last LPT scan + */ +struct ubifs_info +{ + ino_t highest_inum; + unsigned long long max_sqnum; + + int jhead_cnt; + long long max_bud_bytes; + + struct ubifs_zbranch zroot; + int ihead_lnum; + int ihead_offs; + + int log_lebs; + int lpt_lebs; + int lpt_first; + int lpt_last; + int orph_lebs; + int main_lebs; + int main_first; + int default_compr; + int favor_lzo; + int favor_percent; + + uint8_t key_hash_type; + uint32_t (*key_hash)(const char *str, int len); + int key_fmt; + int key_len; + int fanout; + + int min_io_size; + int leb_size; + int leb_cnt; + int max_leb_cnt; + + unsigned long long old_idx_sz; + struct ubifs_lp_stats lst; + + int dead_wm; + int dark_wm; + + struct ubi_dev_info di; + struct ubi_vol_info vi; + + int gc_lnum; + long long rp_size; + + int space_bits; + int lpt_lnum_bits; + int lpt_offs_bits; + int lpt_spc_bits; + int pcnt_bits; + int lnum_bits; + int nnode_sz; + int pnode_sz; + int ltab_sz; + int lsave_sz; + int pnode_cnt; + int nnode_cnt; + int lpt_hght; + + int lpt_lnum; + int lpt_offs; + int nhead_lnum; + int nhead_offs; + int big_lpt; + int space_fixup; + long long lpt_sz; + + int ltab_lnum; + int ltab_offs; + struct ubifs_lprops *lpt; + struct ubifs_lpt_lprops *ltab; + int lsave_cnt; + int lsave_lnum; + int lsave_offs; + int *lsave; + int lscan_lnum; + +}; + +/** + * ubifs_idx_node_sz - return index node size. + * @c: the UBIFS file-system description object + * @child_cnt: number of children of this index node + */ +static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) +{ + return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; +} + +/** + * ubifs_idx_branch - return pointer to an index branch. + * @c: the UBIFS file-system description object + * @idx: index node + * @bnum: branch number + */ +static inline +struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, + const struct ubifs_idx_node *idx, + int bnum) +{ + return (struct ubifs_branch *)((void *)idx->branches + + (UBIFS_BRANCH_SZ + c->key_len) * bnum); +} + +#endif /* __UBIFS_H__ */
* There is no code modification in this commit, only moving * the files to proper place. The user tools looks a little messy as we place almost the all tools in the root directory of mtd-utils. To make it more clear, I propose to introduce the following structure for our source code. mtd-utils/ |-- lib |-- include |-- misc-utils |-- flash-utils |-- jffsX-utils |-- nand-utils |-- nor-utils |-- ubi-utils |-- ubifs-utils `-- tests Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> --- MAKEDEV | 41 - Makefile | 66 +- compr.c | 538 ----- compr.h | 119 - compr_lzo.c | 135 -- compr_rtime.c | 119 - compr_zlib.c | 148 -- device_table.txt | 128 -- doc_loadbios.c | 150 -- docfdisk.c | 318 --- fectest.c | 91 - flash-utils/flash_erase.c | 295 +++ flash-utils/flash_eraseall | 4 + flash-utils/flash_lock.c | 8 + flash-utils/flash_otp_dump.c | 56 + flash-utils/flash_otp_info.c | 65 + flash-utils/flash_otp_lock.c | 72 + flash-utils/flash_otp_write.c | 122 + flash-utils/flash_unlock.c | 90 + flash-utils/flashcp.c | 389 ++++ flash_erase.c | 295 --- flash_eraseall | 4 - flash_lock.c | 8 - flash_otp_dump.c | 56 - flash_otp_info.c | 65 - flash_otp_lock.c | 72 - flash_otp_write.c | 122 - flash_unlock.c | 90 - flashcp.c | 389 ---- ftl_check.c | 217 -- ftl_format.c | 324 --- jffs-dump.c | 359 --- jffs2dump.c | 805 ------- jffs2reader.c | 918 -------- jffsX-utils/compr.c | 538 +++++ jffsX-utils/compr.h | 119 + jffsX-utils/compr_lzo.c | 135 ++ jffsX-utils/compr_rtime.c | 119 + jffsX-utils/compr_zlib.c | 148 ++ jffsX-utils/device_table.txt | 128 ++ jffsX-utils/jffs-dump.c | 359 +++ jffsX-utils/jffs2dump.c | 805 +++++++ jffsX-utils/jffs2reader.c | 918 ++++++++ jffsX-utils/mkfs.jffs2.1 | 268 +++ jffsX-utils/mkfs.jffs2.c | 1805 +++++++++++++++ jffsX-utils/rbtree.c | 390 ++++ jffsX-utils/rbtree.h | 171 ++ jffsX-utils/summary.h | 177 ++ jffsX-utils/sumtool.c | 872 ++++++++ load_nandsim.sh | 127 -- mcast_image.h | 54 - misc-utils/MAKEDEV | 41 + misc-utils/doc_loadbios.c | 150 ++ misc-utils/docfdisk.c | 318 +++ misc-utils/fectest.c | 91 + misc-utils/ftl_check.c | 217 ++ misc-utils/ftl_format.c | 324 +++ misc-utils/mcast_image.h | 54 + misc-utils/mtd_debug.c | 397 ++++ misc-utils/mtdpart.c | 194 ++ misc-utils/recv_image.c | 484 ++++ misc-utils/serve_image.c | 300 +++ mkfs.jffs2.1 | 268 --- mkfs.jffs2.c | 1805 --------------- mkfs.ubifs/.gitignore | 1 - mkfs.ubifs/COPYING | 340 --- mkfs.ubifs/README | 9 - mkfs.ubifs/compr.c | 219 -- mkfs.ubifs/compr.h | 46 - mkfs.ubifs/crc16.c | 56 - mkfs.ubifs/crc16.h | 27 - mkfs.ubifs/defs.h | 92 - mkfs.ubifs/devtable.c | 524 ----- mkfs.ubifs/hashtable/hashtable.c | 277 --- mkfs.ubifs/hashtable/hashtable.h | 199 -- mkfs.ubifs/hashtable/hashtable_itr.c | 176 -- mkfs.ubifs/hashtable/hashtable_itr.h | 112 - mkfs.ubifs/hashtable/hashtable_private.h | 85 - mkfs.ubifs/key.h | 189 -- mkfs.ubifs/lpt.c | 578 ----- mkfs.ubifs/lpt.h | 28 - mkfs.ubifs/mkfs.ubifs.c | 2324 -------------------- mkfs.ubifs/mkfs.ubifs.h | 150 -- mkfs.ubifs/ubifs.h | 441 ---- mtd_debug.c | 397 ---- mtdpart.c | 194 -- nand-utils/load_nandsim.sh | 127 ++ nand-utils/nanddump.c | 490 +++++ nand-utils/nandtest.c | 313 +++ nand-utils/nandwrite.c | 578 +++++ nand-utils/nftl_format.c | 422 ++++ nand-utils/nftldump.c | 278 +++ nanddump.c | 490 ----- nandtest.c | 313 --- nandwrite.c | 578 ----- nftl_format.c | 422 ---- nftldump.c | 278 --- nor-utils/rfddump.c | 337 +++ nor-utils/rfdformat.c | 160 ++ rbtree.c | 390 ---- rbtree.h | 171 -- recv_image.c | 484 ---- rfddump.c | 337 --- rfdformat.c | 160 -- serve_image.c | 300 --- summary.h | 177 -- sumtool.c | 872 -------- ubifs-utils/mkfs.ubifs/.gitignore | 1 + ubifs-utils/mkfs.ubifs/COPYING | 340 +++ ubifs-utils/mkfs.ubifs/README | 9 + ubifs-utils/mkfs.ubifs/compr.c | 219 ++ ubifs-utils/mkfs.ubifs/compr.h | 46 + ubifs-utils/mkfs.ubifs/crc16.c | 56 + ubifs-utils/mkfs.ubifs/crc16.h | 27 + ubifs-utils/mkfs.ubifs/defs.h | 92 + ubifs-utils/mkfs.ubifs/devtable.c | 524 +++++ ubifs-utils/mkfs.ubifs/hashtable/hashtable.c | 277 +++ ubifs-utils/mkfs.ubifs/hashtable/hashtable.h | 199 ++ ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c | 176 ++ ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h | 112 + .../mkfs.ubifs/hashtable/hashtable_private.h | 85 + ubifs-utils/mkfs.ubifs/key.h | 189 ++ ubifs-utils/mkfs.ubifs/lpt.c | 578 +++++ ubifs-utils/mkfs.ubifs/lpt.h | 28 + ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 2324 ++++++++++++++++++++ ubifs-utils/mkfs.ubifs/mkfs.ubifs.h | 150 ++ ubifs-utils/mkfs.ubifs/ubifs.h | 441 ++++ 127 files changed, 19240 insertions(+), 19228 deletions(-) delete mode 100755 MAKEDEV delete mode 100644 compr.c delete mode 100644 compr.h delete mode 100644 compr_lzo.c delete mode 100644 compr_rtime.c delete mode 100644 compr_zlib.c delete mode 100644 device_table.txt delete mode 100644 doc_loadbios.c delete mode 100644 docfdisk.c delete mode 100644 fectest.c create mode 100644 flash-utils/flash_erase.c create mode 100755 flash-utils/flash_eraseall create mode 100644 flash-utils/flash_lock.c create mode 100644 flash-utils/flash_otp_dump.c create mode 100644 flash-utils/flash_otp_info.c create mode 100644 flash-utils/flash_otp_lock.c create mode 100644 flash-utils/flash_otp_write.c create mode 100644 flash-utils/flash_unlock.c create mode 100644 flash-utils/flashcp.c delete mode 100644 flash_erase.c delete mode 100755 flash_eraseall delete mode 100644 flash_lock.c delete mode 100644 flash_otp_dump.c delete mode 100644 flash_otp_info.c delete mode 100644 flash_otp_lock.c delete mode 100644 flash_otp_write.c delete mode 100644 flash_unlock.c delete mode 100644 flashcp.c delete mode 100644 ftl_check.c delete mode 100644 ftl_format.c delete mode 100644 jffs-dump.c delete mode 100644 jffs2dump.c delete mode 100644 jffs2reader.c create mode 100644 jffsX-utils/compr.c create mode 100644 jffsX-utils/compr.h create mode 100644 jffsX-utils/compr_lzo.c create mode 100644 jffsX-utils/compr_rtime.c create mode 100644 jffsX-utils/compr_zlib.c create mode 100644 jffsX-utils/device_table.txt create mode 100644 jffsX-utils/jffs-dump.c create mode 100644 jffsX-utils/jffs2dump.c create mode 100644 jffsX-utils/jffs2reader.c create mode 100644 jffsX-utils/mkfs.jffs2.1 create mode 100644 jffsX-utils/mkfs.jffs2.c create mode 100644 jffsX-utils/rbtree.c create mode 100644 jffsX-utils/rbtree.h create mode 100644 jffsX-utils/summary.h create mode 100644 jffsX-utils/sumtool.c delete mode 100755 load_nandsim.sh delete mode 100644 mcast_image.h create mode 100755 misc-utils/MAKEDEV create mode 100644 misc-utils/doc_loadbios.c create mode 100644 misc-utils/docfdisk.c create mode 100644 misc-utils/fectest.c create mode 100644 misc-utils/ftl_check.c create mode 100644 misc-utils/ftl_format.c create mode 100644 misc-utils/mcast_image.h create mode 100644 misc-utils/mtd_debug.c create mode 100644 misc-utils/mtdpart.c create mode 100644 misc-utils/recv_image.c create mode 100644 misc-utils/serve_image.c delete mode 100644 mkfs.jffs2.1 delete mode 100644 mkfs.jffs2.c delete mode 100644 mkfs.ubifs/.gitignore delete mode 100644 mkfs.ubifs/COPYING delete mode 100644 mkfs.ubifs/README delete mode 100644 mkfs.ubifs/compr.c delete mode 100644 mkfs.ubifs/compr.h delete mode 100644 mkfs.ubifs/crc16.c delete mode 100644 mkfs.ubifs/crc16.h delete mode 100644 mkfs.ubifs/defs.h delete mode 100644 mkfs.ubifs/devtable.c delete mode 100644 mkfs.ubifs/hashtable/hashtable.c delete mode 100644 mkfs.ubifs/hashtable/hashtable.h delete mode 100644 mkfs.ubifs/hashtable/hashtable_itr.c delete mode 100644 mkfs.ubifs/hashtable/hashtable_itr.h delete mode 100644 mkfs.ubifs/hashtable/hashtable_private.h delete mode 100644 mkfs.ubifs/key.h delete mode 100644 mkfs.ubifs/lpt.c delete mode 100644 mkfs.ubifs/lpt.h delete mode 100644 mkfs.ubifs/mkfs.ubifs.c delete mode 100644 mkfs.ubifs/mkfs.ubifs.h delete mode 100644 mkfs.ubifs/ubifs.h delete mode 100644 mtd_debug.c delete mode 100644 mtdpart.c create mode 100755 nand-utils/load_nandsim.sh create mode 100644 nand-utils/nanddump.c create mode 100644 nand-utils/nandtest.c create mode 100644 nand-utils/nandwrite.c create mode 100644 nand-utils/nftl_format.c create mode 100644 nand-utils/nftldump.c delete mode 100644 nanddump.c delete mode 100644 nandtest.c delete mode 100644 nandwrite.c delete mode 100644 nftl_format.c delete mode 100644 nftldump.c create mode 100644 nor-utils/rfddump.c create mode 100644 nor-utils/rfdformat.c delete mode 100644 rbtree.c delete mode 100644 rbtree.h delete mode 100644 recv_image.c delete mode 100644 rfddump.c delete mode 100644 rfdformat.c delete mode 100644 serve_image.c delete mode 100644 summary.h delete mode 100644 sumtool.c create mode 100644 ubifs-utils/mkfs.ubifs/.gitignore create mode 100644 ubifs-utils/mkfs.ubifs/COPYING create mode 100644 ubifs-utils/mkfs.ubifs/README create mode 100644 ubifs-utils/mkfs.ubifs/compr.c create mode 100644 ubifs-utils/mkfs.ubifs/compr.h create mode 100644 ubifs-utils/mkfs.ubifs/crc16.c create mode 100644 ubifs-utils/mkfs.ubifs/crc16.h create mode 100644 ubifs-utils/mkfs.ubifs/defs.h create mode 100644 ubifs-utils/mkfs.ubifs/devtable.c create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable.c create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable.h create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.c create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable_itr.h create mode 100644 ubifs-utils/mkfs.ubifs/hashtable/hashtable_private.h create mode 100644 ubifs-utils/mkfs.ubifs/key.h create mode 100644 ubifs-utils/mkfs.ubifs/lpt.c create mode 100644 ubifs-utils/mkfs.ubifs/lpt.h create mode 100644 ubifs-utils/mkfs.ubifs/mkfs.ubifs.c create mode 100644 ubifs-utils/mkfs.ubifs/mkfs.ubifs.h create mode 100644 ubifs-utils/mkfs.ubifs/ubifs.h