Project import
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3fb6713
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,117 @@
+#
+# Copyright (c) 2010-2011 Nest, Inc.
+# All rights reserved.
+#
+# This document is the property of Nest. It is considered
+# confidential and proprietary information.
+#
+# This document may not be reproduced or transmitted in any form,
+# in whole or in part, without the express written permission of
+# Nest.
+#
+# Description:
+# This file is the makefile for the Memory Technology Devices
+# (MTD) utilities.
+#
+
+BuildConfigSpecialized := No
+BuildProductSpecialized := No
+
+include pre.mak
+
+PackageName := mtd-utils
+
+PackageExtension := tar.bz2
+PackageSeparator := -
+
+PackagePatchArgs := -p1
+
+PackageArchive := $(PackageName).$(PackageExtension)
+PackageSourceDir := $(PackageName)$(PackageSeparator)$(PackageVersion)
+
+PackageBuildMakefile = $(call GenerateBuildPaths,Makefile)
+
+CleanPaths += $(PackageLicenseFile)
+
+LzoDir := sw/tps/lzo
+LzoIncDir = $(call GenerateResultPaths,$(LzoDir),usr/include)
+LzoLibDir = $(call GenerateResultPaths,$(LzoDir),usr/lib)
+
+UUIDDir := sw/tps/e2fsprogs
+UUIDIncDir = $(call GenerateResultPaths,$(UUIDDir),usr/include)
+UUIDLibDir = $(call GenerateResultPaths,$(UUIDDir),usr/lib)
+
+ZlibDir := sw/tps/zlib
+ZlibIncDir = $(call GenerateResultPaths,$(ZlibDir),usr/include)
+ZlibLibDir = $(call GenerateResultPaths,$(ZlibDir),usr/lib)
+
+all: $(PackageDefaultGoal)
+
+# Generate the package license contents.
+
+$(PackageSourceDir)/COPYING: source
+
+$(PackageLicenseFile): $(PackageSourceDir)/COPYING
+ $(copy-result)
+
+# Extract the source from the archive and apply patches, if any.
+
+$(PackageSourceDir): $(PackageArchive) $(PackagePatchPaths)
+ $(expand-and-patch-package)
+
+# Prepare the sources.
+
+.PHONY: source
+source: | $(PackageSourceDir)
+
+# Patch the sources, if necessary.
+
+.PHONY: patch
+patch: source
+
+# Configure the source for building.
+
+.PHONY: configure
+configure: source
+
+# Build the source.
+
+.PHONY: build
+build: configure
+ $(Verbose)cd $(PackageSourceDir) && \
+ $(MAKE) \
+ WITHOUT_XATTR=1 CC="$(CC)" \
+ LZOCPPFLAGS=-I$(LzoIncDir) \
+ LZOLDFLAGS=-L$(LzoLibDir) \
+ UUIDCPPFLAGS=-I$(UUIDIncDir) \
+ UUIDLDFLAGS=-L$(UUIDLibDir) \
+ ZLIBCPPFLAGS=-I$(ZlibIncDir) \
+ ZLIBLDFLAGS=-L$(ZlibLibDir) \
+ BUILDDIR=$(CURDIR)/$(BuildDirectory) \
+ INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+ all
+
+# Stage the build to a temporary installation area.
+
+.PHONY: stage
+stage: build | $(ResultDirectory)
+ $(Verbose)cd $(PackageSourceDir) && \
+ $(MAKE) \
+ WITHOUT_XATTR=1 CC="$(CC)" \
+ LZOCFLAGS=-I$(LzoIncDir) \
+ LZOLDFLAGS=-L$(LzoLibDir) \
+ UUIDCPPFLAGS=-I$(UUIDIncDir) \
+ UUIDLDFLAGS=-L$(UUIDLibDir) \
+ ZLIBCFLAGS=-I$(ZlibIncDir) \
+ ZLIBLDFLAGS=-L$(ZlibLibDir) \
+ BUILDDIR=$(CURDIR)/$(BuildDirectory) \
+ INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+ DESTDIR=$(ResultDirectory) \
+ install
+
+clean:
+ $(Verbose)$(RM) $(RMFLAGS) -r $(PackageSourceDir)
+ $(Verbose)$(RM) $(RMFLAGS) -r $(BuildDirectory)
+ $(Verbose)$(RM) $(RMFLAGS) -r $(ResultDirectory)
+
+include post.mak
diff --git a/mtd-utils-1.3.1/COPYING b/mtd-utils-1.3.1/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/MAKEDEV b/mtd-utils-1.3.1/MAKEDEV
new file mode 100755
index 0000000..b31e61f
--- /dev/null
+++ b/mtd-utils-1.3.1/MAKEDEV
@@ -0,0 +1,42 @@
+#!/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/mtd-utils-1.3.1/Makefile b/mtd-utils-1.3.1/Makefile
new file mode 100644
index 0000000..666537b
--- /dev/null
+++ b/mtd-utils-1.3.1/Makefile
@@ -0,0 +1,72 @@
+
+# -*- sh -*-
+
+PREFIX=/usr
+EXEC_PREFIX=$(PREFIX)
+SBINDIR=$(EXEC_PREFIX)/sbin
+MANDIR=$(PREFIX)/man
+INCLUDEDIR=$(PREFIX)/include
+
+CPPFLAGS += -I./include $(ZLIBCPPFLAGS) $(LZOCPPFLAGS)
+
+ifeq ($(WITHOUT_XATTR), 1)
+ CPPFLAGS += -DWITHOUT_XATTR
+endif
+
+SUBDIRS = ubi-utils mkfs.ubifs
+
+TARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \
+ ftl_check mkfs.jffs2 flash_lock flash_unlock flash_info \
+ flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite nandtest \
+ jffs2dump \
+ nftldump nftl_format docfdisk \
+ rfddump rfdformat \
+ serve_image recv_image \
+ sumtool #jffs2reader
+
+SYMLINKS =
+
+include common.mk
+
+clean::
+ -rm -f $(SYMLINKS)
+ifneq ($(BUILDDIR)/.git,)
+ifneq ($(BUILDDIR),.)
+ifneq ($(BUILDDIR),$(PWD))
+ rm -rf $(BUILDDIR)
+endif
+endif
+endif
+
+$(SYMLINKS):
+ ln -sf ../fs/jffs2/$@ $@
+
+$(BUILDDIR)/mkfs.jffs2: $(addprefix $(BUILDDIR)/,\
+ crc32.o compr_rtime.o mkfs.jffs2.o compr_zlib.o compr_lzo.o \
+ compr.o rbtree.o)
+LDFLAGS_mkfs.jffs2 = $(ZLIBLDFLAGS) $(LZOLDFLAGS)
+LDLIBS_mkfs.jffs2 = -lz -llzo2
+
+$(BUILDDIR)/flash_eraseall: $(BUILDDIR)/crc32.o $(BUILDDIR)/flash_eraseall.o
+
+$(BUILDDIR)/jffs2reader: $(BUILDDIR)/jffs2reader.o
+LDFLAGS_jffs2reader = $(ZLIBLDFLAGS) $(LZOLDFLAGS)
+LDLIBS_jffs2reader = -lz -llzo2
+
+$(BUILDDIR)/jffs2dump: $(BUILDDIR)/jffs2dump.o $(BUILDDIR)/crc32.o
+
+$(BUILDDIR)/sumtool: $(BUILDDIR)/sumtool.o $(BUILDDIR)/crc32.o
+
+$(BUILDDIR)/serve_image: $(BUILDDIR)/serve_image.o $(BUILDDIR)/crc32.o $(BUILDDIR)/fec.o
+
+$(BUILDDIR)/recv_image: $(BUILDDIR)/recv_image.o $(BUILDDIR)/crc32.o $(BUILDDIR)/fec.o
+
+$(BUILDDIR)/fectest: $(BUILDDIR)/fectest.o $(BUILDDIR)/crc32.o $(BUILDDIR)/fec.o
+
+
+
+install:: ${TARGETS}
+ mkdir -p ${DESTDIR}/${SBINDIR}
+ install -m 0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
+ mkdir -p ${DESTDIR}/${MANDIR}/man1
+ gzip -9c mkfs.jffs2.1 > ${DESTDIR}/${MANDIR}/man1/mkfs.jffs2.1.gz
diff --git a/mtd-utils-1.3.1/common.mk b/mtd-utils-1.3.1/common.mk
new file mode 100644
index 0000000..9893704
--- /dev/null
+++ b/mtd-utils-1.3.1/common.mk
@@ -0,0 +1,73 @@
+.NOTPARALLEL:
+
+CC := $(CROSS)gcc
+AR := $(CROSS)ar
+RANLIB := $(CROSS)ranlib
+
+# Stolen from Linux build system
+try-run = $(shell set -e; ($(1)) >/dev/null 2>&1 && echo "$(2)" || echo "$(3)")
+cc-option = $(call try-run, $(CC) $(1) -c -xc /dev/null -o /dev/null,$(1),$(2))
+
+CFLAGS ?= -O2 -g
+WFLAGS := -Wall \
+ $(call cc-option,-Wextra) \
+ $(call cc-option,-Wwrite-strings) \
+ $(call cc-option,-Wno-sign-compare)
+CFLAGS += $(WFLAGS)
+CPPFLAGS += -D_FILE_OFFSET_BITS=64
+
+DESTDIR ?= /usr/local
+PREFIX=/usr
+EXEC_PREFIX=$(PREFIX)
+SBINDIR=$(EXEC_PREFIX)/sbin
+MANDIR=$(PREFIX)/share/man
+INCLUDEDIR=$(PREFIX)/include
+
+ifndef BUILDDIR
+ifeq ($(origin CROSS),undefined)
+ BUILDDIR := $(PWD)
+else
+# Remove the trailing slash to make the directory name
+ BUILDDIR := $(PWD)/$(CROSS:-=)
+endif
+endif
+override BUILDDIR := $(patsubst %/,%,$(BUILDDIR))
+
+override TARGETS := $(addprefix $(BUILDDIR)/,$(TARGETS))
+
+SUBDIRS_ALL = $(patsubst %,subdirs_%_all,$(SUBDIRS))
+SUBDIRS_CLEAN = $(patsubst %,subdirs_%_clean,$(SUBDIRS))
+SUBDIRS_INSTALL = $(patsubst %,subdirs_%_install,$(SUBDIRS))
+
+all:: $(TARGETS) $(SUBDIRS_ALL)
+
+clean:: $(SUBDIRS_CLEAN)
+ rm -f $(BUILDDIR)/*.o $(TARGETS) $(BUILDDIR)/.*.c.dep
+
+install:: $(TARGETS) $(SUBDIRS_INSTALL)
+
+%: %.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_$(notdir $@)) -g -o $@ $^ $(LDLIBS) $(LDLIBS_$(notdir $@))
+
+$(BUILDDIR)/%.a:
+ $(AR) crv $@ $^
+ $(RANLIB) $@
+
+$(BUILDDIR)/%.o: %.c
+ifneq ($(BUILDDIR),$(CURDIR))
+ mkdir -p $(dir $@)
+endif
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< -g -Wp,-MD,$(BUILDDIR)/.$(<F).dep
+
+subdirs_%:
+ d=$(patsubst subdirs_%,%,$@); \
+ t=`echo $$d | sed s:.*_::` d=`echo $$d | sed s:_.*::`; \
+ $(MAKE) BUILDDIR=$(BUILDDIR)/$$d -C $$d $$t
+
+.SUFFIXES:
+
+IGNORE=${wildcard $(BUILDDIR)/.*.c.dep}
+-include ${IGNORE}
+
+PHONY += all clean install
+.PHONY: $(PHONY)
diff --git a/mtd-utils-1.3.1/compr.c b/mtd-utils-1.3.1/compr.c
new file mode 100644
index 0000000..7028c93
--- /dev/null
+++ b/mtd-utils-1.3.1/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,NULL)) {
+ fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name);
+ jffs2_error_cnt++;
+ }
+ /* validate decompression */
+ else {
+ for (i=0;i<datalen;i++) {
+ if (data_in[i]!=jffs2_compression_check_buf[i]) {
+ fprintf(stderr,"JFFS2 compression check: data mismatch at %s (pos %d).\n", compr->name, i);
+ jffs2_error_cnt++;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * 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, NULL);
+ this->usecount--;
+ if (!compr_ret) {
+ ret = this->compr;
+ this->stat_compr_blocks++;
+ this->stat_compr_orig_size += *datalen;
+ this->stat_compr_new_size += *cdatalen;
+ if (jffs2_compression_check)
+ jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen);
+ break;
+ }
+ }
+ if (ret == JFFS2_COMPR_NONE) free(output_buf);
+ break;
+ case JFFS2_COMPR_MODE_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, NULL);
+ this->usecount--;
+ if (!compr_ret) {
+ if (jffs2_compression_check)
+ jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size);
+ if (((!best_dlen) || 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: unknow compression mode.\n");
+ }
+out:
+ if (ret == JFFS2_COMPR_NONE) {
+ *cpage_out = data_in;
+ *datalen = *cdatalen;
+ none_stat_compr_blocks++;
+ none_stat_compr_size += *datalen;
+ }
+ else {
+ *cpage_out = output_buf;
+ }
+ return ret;
+}
+
+
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+ struct jffs2_compressor *this;
+
+ if (!comp->name) {
+ fprintf(stderr,"NULL compressor name at registering JFFS2 compressor. Failed.\n");
+ return -1;
+ }
+ comp->compr_buf_size=0;
+ comp->compr_buf=NULL;
+ comp->usecount=0;
+ comp->stat_compr_orig_size=0;
+ comp->stat_compr_new_size=0;
+ comp->stat_compr_blocks=0;
+ comp->stat_decompr_blocks=0;
+
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (this->priority < comp->priority) {
+ list_add(&comp->list, this->list.prev);
+ goto out;
+ }
+ }
+ list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+ return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+
+ if (comp->usecount) {
+ fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n");
+ return -1;
+ }
+ list_del(&comp->list);
+
+ return 0;
+}
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+ struct jffs2_compressor *this;
+ char *buf, *act_buf;
+
+ act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+ if ((this->disabled)||(!this->compress))
+ act_buf += sprintf(act_buf,"disabled");
+ else
+ act_buf += sprintf(act_buf,"enabled");
+ act_buf += sprintf(act_buf,"\n");
+ }
+ return buf;
+}
+
+char *jffs2_stats(void)
+{
+ struct jffs2_compressor *this;
+ char *buf, *act_buf;
+
+ act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE);
+
+ act_buf += sprintf(act_buf,"Compression mode: ");
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_NONE:
+ act_buf += sprintf(act_buf,"none");
+ break;
+ case JFFS2_COMPR_MODE_PRIORITY:
+ act_buf += sprintf(act_buf,"priority");
+ break;
+ case JFFS2_COMPR_MODE_SIZE:
+ act_buf += sprintf(act_buf,"size");
+ break;
+ case JFFS2_COMPR_MODE_FAVOURLZO:
+ act_buf += sprintf(act_buf, "favourlzo");
+ break;
+ default:
+ act_buf += sprintf(act_buf,"unkown");
+ break;
+ }
+ act_buf += sprintf(act_buf,"\nCompressors:\n");
+ act_buf += sprintf(act_buf,"%10s ","none");
+ act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks,
+ none_stat_compr_size, none_stat_decompr_blocks);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority);
+ if ((this->disabled)||(!this->compress))
+ act_buf += sprintf(act_buf,"- ");
+ else
+ act_buf += sprintf(act_buf,"+ ");
+ act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks,
+ this->stat_compr_new_size, this->stat_compr_orig_size,
+ this->stat_decompr_blocks);
+ act_buf += sprintf(act_buf,"\n");
+ }
+ return buf;
+}
+
+int jffs2_set_compression_mode_name(const char *name)
+{
+ if (!strcmp("none",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+ return 0;
+ }
+ if (!strcmp("priority",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+ return 0;
+ }
+ if (!strcmp("size",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+ return 0;
+ }
+ 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/mtd-utils-1.3.1/compr.h b/mtd-utils-1.3.1/compr.h
new file mode 100644
index 0000000..51bf0dd
--- /dev/null
+++ b/mtd-utils-1.3.1/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 */
+ char *name;
+ char compr; /* JFFS2_COMPR_XXX */
+ int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *srclen, uint32_t *destlen, void *model);
+ int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+ uint32_t cdatalen, uint32_t datalen, void *model);
+ int usecount;
+ int disabled; /* if seted the compressor won't compress */
+ unsigned char *compr_buf; /* used by size compr. mode */
+ uint32_t compr_buf_size; /* used by size compr. mode */
+ uint32_t stat_compr_orig_size;
+ uint32_t stat_compr_new_size;
+ uint32_t stat_compr_blocks;
+ uint32_t stat_decompr_blocks;
+};
+
+int jffs2_register_compressor(struct jffs2_compressor *comp);
+int jffs2_unregister_compressor(struct jffs2_compressor *comp);
+
+int jffs2_compressors_init(void);
+int jffs2_compressors_exit(void);
+
+uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
+ uint32_t *datalen, uint32_t *cdatalen);
+
+/* If it is setted, a decompress will be called after every compress */
+void jffs2_compression_check_set(int yesno);
+int jffs2_compression_check_get(void);
+int jffs2_compression_check_errorcnt_get(void);
+
+char *jffs2_list_compressors(void);
+char *jffs2_stats(void);
+
+/* Compressor modules */
+
+/* These functions will be called by jffs2_compressors_init/exit */
+#ifdef CONFIG_JFFS2_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
+
+#endif /* __JFFS2_COMPR_H__ */
diff --git a/mtd-utils-1.3.1/compr_lzo.c b/mtd-utils-1.3.1/compr_lzo.c
new file mode 100644
index 0000000..3d7dad7
--- /dev/null
+++ b/mtd-utils-1.3.1/compr_lzo.c
@@ -0,0 +1,120 @@
+/*
+ * 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>
+#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, void *model)
+{
+ 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, void *model)
+{
+ 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);
+}
diff --git a/mtd-utils-1.3.1/compr_rtime.c b/mtd-utils-1.3.1/compr_rtime.c
new file mode 100644
index 0000000..131536c
--- /dev/null
+++ b/mtd-utils-1.3.1/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, void *model)
+{
+ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+ memset(positions,0,sizeof(positions));
+
+ while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
+ int backpos, runlen=0;
+ unsigned char value;
+
+ value = data_in[pos];
+
+ cpage_out[outpos++] = data_in[pos++];
+
+ backpos = positions[value];
+ positions[value]=pos;
+
+ while ((backpos < pos) && (pos < (*sourcelen)) &&
+ (data_in[pos]==data_in[backpos++]) && (runlen<255)) {
+ pos++;
+ runlen++;
+ }
+ cpage_out[outpos++] = runlen;
+ }
+
+ if (outpos >= pos) {
+ /* We failed */
+ return -1;
+ }
+
+ /* Tell the caller how much we managed to compress, and how much space it took */
+ *sourcelen = pos;
+ *dstlen = outpos;
+ return 0;
+}
+
+
+static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
+{
+ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+ memset(positions,0,sizeof(positions));
+
+ while (outpos<destlen) {
+ unsigned char value;
+ int backoffs;
+ int repeat;
+
+ value = data_in[pos++];
+ cpage_out[outpos++] = value; /* first the verbatim copied byte */
+ repeat = data_in[pos++];
+ backoffs = positions[value];
+
+ positions[value]=outpos;
+ if (repeat) {
+ if (backoffs + repeat >= outpos) {
+ while(repeat) {
+ cpage_out[outpos++] = cpage_out[backoffs++];
+ repeat--;
+ }
+ } else {
+ memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat);
+ outpos+=repeat;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static struct jffs2_compressor jffs2_rtime_comp = {
+ .priority = JFFS2_RTIME_PRIORITY,
+ .name = "rtime",
+ .disabled = 0,
+ .compr = JFFS2_COMPR_RTIME,
+ .compress = &jffs2_rtime_compress,
+ .decompress = &jffs2_rtime_decompress,
+};
+
+int jffs2_rtime_init(void)
+{
+ return jffs2_register_compressor(&jffs2_rtime_comp);
+}
+
+void jffs2_rtime_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
diff --git a/mtd-utils-1.3.1/compr_zlib.c b/mtd-utils-1.3.1/compr_zlib.c
new file mode 100644
index 0000000..400b18a
--- /dev/null
+++ b/mtd-utils-1.3.1/compr_zlib.c
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#include <zlib.h>
+#include <stdio.h>
+#include <asm/types.h>
+#include <linux/jffs2.h>
+#include "compr.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+
+/* Plan: call deflate() with avail_in == *sourcelen,
+ avail_out = *dstlen - 12 and flush == Z_FINISH.
+ If it doesn't manage to finish, call it again with
+ avail_in == 0 and avail_out set to the remaining 12
+ bytes for it to clean up.
+Q: Is 12 bytes sufficient?
+ */
+#define STREAM_END_SPACE 12
+
+int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
+{
+ z_stream strm;
+ int ret;
+
+ if (*dstlen <= STREAM_END_SPACE)
+ return -1;
+
+ strm.zalloc = (void *)0;
+ strm.zfree = (void *)0;
+
+ if (Z_OK != deflateInit(&strm, 3)) {
+ return -1;
+ }
+ strm.next_in = data_in;
+ strm.total_in = 0;
+
+ strm.next_out = cpage_out;
+ strm.total_out = 0;
+
+ while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
+ strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
+ strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
+ ret = deflate(&strm, Z_PARTIAL_FLUSH);
+ if (ret != Z_OK) {
+ deflateEnd(&strm);
+ return -1;
+ }
+ }
+ strm.avail_out += STREAM_END_SPACE;
+ strm.avail_in = 0;
+ ret = deflate(&strm, Z_FINISH);
+ if (ret != Z_STREAM_END) {
+ deflateEnd(&strm);
+ return -1;
+ }
+ deflateEnd(&strm);
+
+ if (strm.total_out >= strm.total_in)
+ return -1;
+
+
+ *dstlen = strm.total_out;
+ *sourcelen = strm.total_in;
+ return 0;
+}
+
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
+{
+ z_stream strm;
+ int ret;
+
+ strm.zalloc = (void *)0;
+ strm.zfree = (void *)0;
+
+ if (Z_OK != inflateInit(&strm)) {
+ return 1;
+ }
+ strm.next_in = data_in;
+ strm.avail_in = srclen;
+ strm.total_in = 0;
+
+ strm.next_out = cpage_out;
+ strm.avail_out = destlen;
+ strm.total_out = 0;
+
+ while((ret = inflate(&strm, Z_FINISH)) == Z_OK)
+ ;
+
+ inflateEnd(&strm);
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+ .priority = JFFS2_ZLIB_PRIORITY,
+ .name = "zlib",
+ .disabled = 0,
+ .compr = JFFS2_COMPR_ZLIB,
+ .compress = &jffs2_zlib_compress,
+ .decompress = &jffs2_zlib_decompress,
+};
+
+int jffs2_zlib_init(void)
+{
+ return jffs2_register_compressor(&jffs2_zlib_comp);
+}
+
+void jffs2_zlib_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_zlib_comp);
+}
diff --git a/mtd-utils-1.3.1/crc32.c b/mtd-utils-1.3.1/crc32.c
new file mode 100644
index 0000000..6b1e50c
--- /dev/null
+++ b/mtd-utils-1.3.1/crc32.c
@@ -0,0 +1,95 @@
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
diff --git a/mtd-utils-1.3.1/crc32.h b/mtd-utils-1.3.1/crc32.h
new file mode 100644
index 0000000..ee3145b
--- /dev/null
+++ b/mtd-utils-1.3.1/crc32.h
@@ -0,0 +1,19 @@
+#ifndef CRC32_H
+#define CRC32_H
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+ static inline uint32_t
+crc32(uint32_t val, const void *ss, int len)
+{
+ const unsigned char *s = ss;
+ while (--len >= 0)
+ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+ return val;
+}
+
+#endif
diff --git a/mtd-utils-1.3.1/device_table.txt b/mtd-utils-1.3.1/device_table.txt
new file mode 100644
index 0000000..74fdc56
--- /dev/null
+++ b/mtd-utils-1.3.1/device_table.txt
@@ -0,0 +1,129 @@
+# This is a sample device table file for use with mkfs.jffs2. You can
+# do all sorts of interesting things with a device table file. For
+# example, if you want to adjust the permissions on a particular file
+# you can just add an entry like:
+# /sbin/foobar f 2755 0 0 - - - - -
+# and (assuming the file /sbin/foobar exists) it will be made setuid
+# root (regardless of what its permissions are on the host filesystem.
+#
+# Device table entries take the form of:
+# <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+# where name is the file name, type can be one of:
+# f A regular file
+# d Directory
+# c Character special device file
+# b Block special device file
+# p Fifo (named pipe)
+# uid is the user id for the target file, gid is the group id for the
+# target file. The rest of the entried apply only to device special
+# file.
+
+# When building a target filesystem, it is desirable to not have to
+# become root and then run 'mknod' a thousand times. Using a device
+# table you can create device nodes and directories "on the fly".
+# Furthermore, you can use a single table entry to create a many device
+# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15]
+# I could just use the following two table entries:
+# /dev/hda b 640 0 0 3 0 0 0 -
+# /dev/hda b 640 0 0 3 1 1 1 15
+#
+# Have fun
+# -Erik Andersen <andersen@codepoet.org>
+#
+
+#<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+/dev d 755 0 0 - - - - -
+/dev/mem c 640 0 0 1 1 0 0 -
+/dev/kmem c 640 0 0 1 2 0 0 -
+/dev/null c 640 0 0 1 3 0 0 -
+/dev/zero c 640 0 0 1 5 0 0 -
+/dev/random c 640 0 0 1 8 0 0 -
+/dev/urandom c 640 0 0 1 9 0 0 -
+/dev/tty c 666 0 0 5 0 0 0 -
+/dev/tty c 666 0 0 4 0 0 1 6
+/dev/console c 640 0 0 5 1 0 0 -
+/dev/ram b 640 0 0 1 1 0 0 -
+/dev/ram b 640 0 0 1 0 0 1 4
+/dev/loop b 640 0 0 7 0 0 1 2
+/dev/ptmx c 666 0 0 5 2 0 0 -
+#/dev/ttyS c 640 0 0 4 64 0 1 4
+#/dev/psaux c 640 0 0 10 1 0 0 -
+#/dev/rtc c 640 0 0 10 135 0 0 -
+
+# Adjust permissions on some normal files
+#/etc/shadow f 600 0 0 - - - - -
+#/bin/tinylogin f 4755 0 0 - - - - -
+
+# User-mode Linux stuff
+/dev/ubda b 640 0 0 98 0 0 0 -
+/dev/ubda b 640 0 0 98 1 1 1 15
+
+# IDE Devices
+/dev/hda b 640 0 0 3 0 0 0 -
+/dev/hda b 640 0 0 3 1 1 1 15
+/dev/hdb b 640 0 0 3 64 0 0 -
+/dev/hdb b 640 0 0 3 65 1 1 15
+#/dev/hdc b 640 0 0 22 0 0 0 -
+#/dev/hdc b 640 0 0 22 1 1 1 15
+#/dev/hdd b 640 0 0 22 64 0 0 -
+#/dev/hdd b 640 0 0 22 65 1 1 15
+#/dev/hde b 640 0 0 33 0 0 0 -
+#/dev/hde b 640 0 0 33 1 1 1 15
+#/dev/hdf b 640 0 0 33 64 0 0 -
+#/dev/hdf b 640 0 0 33 65 1 1 15
+#/dev/hdg b 640 0 0 34 0 0 0 -
+#/dev/hdg b 640 0 0 34 1 1 1 15
+#/dev/hdh b 640 0 0 34 64 0 0 -
+#/dev/hdh b 640 0 0 34 65 1 1 15
+
+# SCSI Devices
+#/dev/sda b 640 0 0 8 0 0 0 -
+#/dev/sda b 640 0 0 8 1 1 1 15
+#/dev/sdb b 640 0 0 8 16 0 0 -
+#/dev/sdb b 640 0 0 8 17 1 1 15
+#/dev/sdc b 640 0 0 8 32 0 0 -
+#/dev/sdc b 640 0 0 8 33 1 1 15
+#/dev/sdd b 640 0 0 8 48 0 0 -
+#/dev/sdd b 640 0 0 8 49 1 1 15
+#/dev/sde b 640 0 0 8 64 0 0 -
+#/dev/sde b 640 0 0 8 65 1 1 15
+#/dev/sdf b 640 0 0 8 80 0 0 -
+#/dev/sdf b 640 0 0 8 81 1 1 15
+#/dev/sdg b 640 0 0 8 96 0 0 -
+#/dev/sdg b 640 0 0 8 97 1 1 15
+#/dev/sdh b 640 0 0 8 112 0 0 -
+#/dev/sdh b 640 0 0 8 113 1 1 15
+#/dev/sg c 640 0 0 21 0 0 1 15
+#/dev/scd b 640 0 0 11 0 0 1 15
+#/dev/st c 640 0 0 9 0 0 1 8
+#/dev/nst c 640 0 0 9 128 0 1 8
+#/dev/st c 640 0 0 9 32 1 1 4
+#/dev/st c 640 0 0 9 64 1 1 4
+#/dev/st c 640 0 0 9 96 1 1 4
+
+# Floppy disk devices
+#/dev/fd b 640 0 0 2 0 0 1 2
+#/dev/fd0d360 b 640 0 0 2 4 0 0 -
+#/dev/fd1d360 b 640 0 0 2 5 0 0 -
+#/dev/fd0h1200 b 640 0 0 2 8 0 0 -
+#/dev/fd1h1200 b 640 0 0 2 9 0 0 -
+#/dev/fd0u1440 b 640 0 0 2 28 0 0 -
+#/dev/fd1u1440 b 640 0 0 2 29 0 0 -
+#/dev/fd0u2880 b 640 0 0 2 32 0 0 -
+#/dev/fd1u2880 b 640 0 0 2 33 0 0 -
+
+# All the proprietary cdrom devices in the world
+#/dev/aztcd b 640 0 0 29 0 0 0 -
+#/dev/bpcd b 640 0 0 41 0 0 0 -
+#/dev/capi20 c 640 0 0 68 0 0 1 2
+#/dev/cdu31a b 640 0 0 15 0 0 0 -
+#/dev/cdu535 b 640 0 0 24 0 0 0 -
+#/dev/cm206cd b 640 0 0 32 0 0 0 -
+#/dev/sjcd b 640 0 0 18 0 0 0 -
+#/dev/sonycd b 640 0 0 15 0 0 0 -
+#/dev/gscd b 640 0 0 16 0 0 0 -
+#/dev/sbpcd b 640 0 0 25 0 0 0 -
+#/dev/sbpcd b 640 0 0 25 0 0 1 4
+#/dev/mcd b 640 0 0 23 0 0 0 -
+#/dev/optcd b 640 0 0 17 0 0 0 -
+
diff --git a/mtd-utils-1.3.1/doc_loadbios.c b/mtd-utils-1.3.1/doc_loadbios.c
new file mode 100644
index 0000000..0a11fd2
--- /dev/null
+++ b/mtd-utils-1.3.1/doc_loadbios.c
@@ -0,0 +1,148 @@
+#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/mtd-utils-1.3.1/docfdisk.c b/mtd-utils-1.3.1/docfdisk.c
new file mode 100644
index 0000000..56fffc4
--- /dev/null
+++ b/mtd-utils-1.3.1/docfdisk.c
@@ -0,0 +1,317 @@
+/*
+ * 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 _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",
+ argv[0]);
+ return 1;
+ }
+
+ npart = argc - 2;
+ if (npart > 4) {
+ printf("Max 4 partitions allowed.\n");
+ return 1;
+ }
+
+ for (i = 0; i < npart; i++) {
+ nblocks[i] = strtoul(argv[2+i], NULL, 0);
+ if (i && !nblocks[i-1]) {
+ printf("No sizes allowed after 0\n");
+ return 1;
+ }
+ }
+
+ // Open and size the device
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ perror("Open flash device");
+ return 1;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ return 1;
+ }
+
+ printf("Device size is %d bytes. Erasesize is %d bytes.\n",
+ meminfo.size, meminfo.erasesize);
+
+ buf = malloc(meminfo.erasesize);
+ oobbuf = malloc((meminfo.erasesize / meminfo.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/mtd-utils-1.3.1/feature-removal-schedule.txt b/mtd-utils-1.3.1/feature-removal-schedule.txt
new file mode 100644
index 0000000..d0116f8
--- /dev/null
+++ b/mtd-utils-1.3.1/feature-removal-schedule.txt
@@ -0,0 +1,9 @@
+The following is a list of files and features that are going to be
+removed in the mtd-utils source tree. Every entry should contain what
+exactly is going away, why it is happening, and who is going to be doing
+the work. When the feature is removed from the utils, it should also
+be removed from this file.
+
+---------------------------
+
+---------------------------
diff --git a/mtd-utils-1.3.1/fec.c b/mtd-utils-1.3.1/fec.c
new file mode 100644
index 0000000..6d9020f
--- /dev/null
+++ b/mtd-utils-1.3.1/fec.c
@@ -0,0 +1,904 @@
+/*
+ * fec.c -- forward error correction based on Vandermonde matrices
+ * 980624
+ * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
+ *
+ * Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
+ * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
+ * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS
+ * 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.
+ */
+
+/*
+ * The following parameter defines how many bits are used for
+ * field elements. The code supports any value from 2 to 16
+ * but fastest operation is achieved with 8 bit elements
+ * This is the only parameter you may want to change.
+ */
+#ifndef GF_BITS
+#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * stuff used for testing purposes only
+ */
+
+#ifdef TEST
+#define DEB(x)
+#define DDB(x) x
+#define DEBUG 0 /* minimal debugging */
+#ifdef MSDOS
+#include <time.h>
+struct timeval {
+ unsigned long ticks;
+};
+#define gettimeofday(x, dummy) { (x)->ticks = clock() ; }
+#define DIFF_T(a,b) (1+ 1000000*(a.ticks - b.ticks) / CLOCKS_PER_SEC )
+typedef unsigned long u_long ;
+typedef unsigned short u_short ;
+#else /* typically, unix systems */
+#include <sys/time.h>
+#define DIFF_T(a,b) \
+ (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) )
+#endif
+
+#define TICK(t) \
+ {struct timeval x ; \
+ gettimeofday(&x, NULL) ; \
+ t = x.tv_usec + 1000000* (x.tv_sec & 0xff ) ; \
+ }
+#define TOCK(t) \
+ { u_long t1 ; TICK(t1) ; \
+ if (t1 < t) t = 256000000 + t1 - t ; \
+ else t = t1 - t ; \
+ if (t == 0) t = 1 ;}
+
+u_long ticks[10]; /* vars for timekeeping */
+#else
+#define DEB(x)
+#define DDB(x)
+#define TICK(x)
+#define TOCK(x)
+#endif /* TEST */
+
+/*
+ * You should not need to change anything beyond this point.
+ * The first part of the file implements linear algebra in GF.
+ *
+ * gf is the type used to store an element of the Galois Field.
+ * Must constain at least GF_BITS bits.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium. We use int whenever have to deal with an
+ * index, since they are generally faster.
+ */
+#if (GF_BITS < 2 && GF_BITS >16)
+#error "GF_BITS must be 2 .. 16"
+#endif
+#if (GF_BITS <= 8)
+typedef unsigned char gf;
+#else
+typedef unsigned short gf;
+#endif
+
+#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */
+
+/*
+ * Primitive polynomials - see Lin & Costello, Appendix A,
+ * and Lee & Messerschmitt, p. 453.
+ */
+static char *allPp[] = { /* GF_BITS polynomial */
+ NULL, /* 0 no code */
+ NULL, /* 1 no code */
+ "111", /* 2 1+x+x^2 */
+ "1101", /* 3 1+x+x^3 */
+ "11001", /* 4 1+x+x^4 */
+ "101001", /* 5 1+x^2+x^5 */
+ "1100001", /* 6 1+x+x^6 */
+ "10010001", /* 7 1 + x^3 + x^7 */
+ "101110001", /* 8 1+x^2+x^3+x^4+x^8 */
+ "1000100001", /* 9 1+x^4+x^9 */
+ "10010000001", /* 10 1+x^3+x^10 */
+ "101000000001", /* 11 1+x^2+x^11 */
+ "1100101000001", /* 12 1+x+x^4+x^6+x^12 */
+ "11011000000001", /* 13 1+x+x^3+x^4+x^13 */
+ "110000100010001", /* 14 1+x+x^6+x^10+x^14 */
+ "1100000000000001", /* 15 1+x+x^15 */
+ "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */
+};
+
+
+/*
+ * To speed up computations, we have tables for logarithm, exponent
+ * and inverse of a number. If GF_BITS <= 8, we use a table for
+ * multiplication as well (it takes 64K, no big deal even on a PDA,
+ * especially because it can be pre-initialized an put into a ROM!),
+ * otherwhise we use a table of logarithms.
+ * In any case the macro gf_mul(x,y) takes care of multiplications.
+ */
+
+static gf gf_exp[2*GF_SIZE]; /* index->poly form conversion table */
+static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */
+static gf inverse[GF_SIZE+1]; /* inverse of field elem. */
+ /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */
+
+/*
+ * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1,
+ * without a slow divide.
+ */
+static inline gf
+modnn(int x)
+{
+ while (x >= GF_SIZE) {
+ x -= GF_SIZE;
+ x = (x >> GF_BITS) + (x & GF_SIZE);
+ }
+ return x;
+}
+
+#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;}
+
+/*
+ * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much
+ * faster to use a multiplication table.
+ *
+ * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying
+ * many numbers by the same constant. In this case the first
+ * call sets the constant, and others perform the multiplications.
+ * A value related to the multiplication is held in a local variable
+ * declared with USE_GF_MULC . See usage in addmul1().
+ */
+#if (GF_BITS <= 8)
+static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1];
+
+#define gf_mul(x,y) gf_mul_table[x][y]
+
+#define USE_GF_MULC register gf * __gf_mulc_
+#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c]
+#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x]
+
+static void
+init_mul_table()
+{
+ int i, j;
+ for (i=0; i< GF_SIZE+1; i++)
+ for (j=0; j< GF_SIZE+1; j++)
+ gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ;
+
+ for (j=0; j< GF_SIZE+1; j++)
+ gf_mul_table[0][j] = gf_mul_table[j][0] = 0;
+}
+#else /* GF_BITS > 8 */
+static inline gf
+gf_mul(x,y)
+{
+ if ( (x) == 0 || (y)==0 ) return 0;
+
+ return gf_exp[gf_log[x] + gf_log[y] ] ;
+}
+#define init_mul_table()
+
+#define USE_GF_MULC register gf * __gf_mulc_
+#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ]
+#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; }
+#endif
+
+/*
+ * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
+ * Lookup tables:
+ * index->polynomial form gf_exp[] contains j= \alpha^i;
+ * polynomial form -> index form gf_log[ j = \alpha^i ] = i
+ * \alpha=x is the primitive element of GF(2^m)
+ *
+ * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple
+ * multiplication of two numbers can be resolved without calling modnn
+ */
+
+/*
+ * i use malloc so many times, it is easier to put checks all in
+ * one place.
+ */
+static void *
+my_malloc(int sz, char *err_string)
+{
+ void *p = malloc( sz );
+ if (p == NULL) {
+ fprintf(stderr, "-- malloc failure allocating %s\n", err_string);
+ exit(1) ;
+ }
+ return p ;
+}
+
+#define NEW_GF_MATRIX(rows, cols) \
+ (gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " )
+
+/*
+ * initialize the data structures used for computations in GF.
+ */
+static void
+generate_gf(void)
+{
+ int i;
+ gf mask;
+ char *Pp = allPp[GF_BITS] ;
+
+ mask = 1; /* x ** 0 = 1 */
+ gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */
+ /*
+ * first, generate the (polynomial representation of) powers of \alpha,
+ * which are stored in gf_exp[i] = \alpha ** i .
+ * At the same time build gf_log[gf_exp[i]] = i .
+ * The first GF_BITS powers are simply bits shifted to the left.
+ */
+ for (i = 0; i < GF_BITS; i++, mask <<= 1 ) {
+ gf_exp[i] = mask;
+ gf_log[gf_exp[i]] = i;
+ /*
+ * If Pp[i] == 1 then \alpha ** i occurs in poly-repr
+ * gf_exp[GF_BITS] = \alpha ** GF_BITS
+ */
+ if ( Pp[i] == '1' )
+ gf_exp[GF_BITS] ^= mask;
+ }
+ /*
+ * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als
+ * compute its inverse.
+ */
+ gf_log[gf_exp[GF_BITS]] = GF_BITS;
+ /*
+ * Poly-repr of \alpha ** (i+1) is given by poly-repr of
+ * \alpha ** i shifted left one-bit and accounting for any
+ * \alpha ** GF_BITS term that may occur when poly-repr of
+ * \alpha ** i is shifted.
+ */
+ mask = 1 << (GF_BITS - 1 ) ;
+ for (i = GF_BITS + 1; i < GF_SIZE; i++) {
+ if (gf_exp[i - 1] >= mask)
+ gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1);
+ else
+ gf_exp[i] = gf_exp[i - 1] << 1;
+ gf_log[gf_exp[i]] = i;
+ }
+ /*
+ * log(0) is not defined, so use a special value
+ */
+ gf_log[0] = GF_SIZE ;
+ /* set the extended gf_exp values for fast multiply */
+ for (i = 0 ; i < GF_SIZE ; i++)
+ gf_exp[i + GF_SIZE] = gf_exp[i] ;
+
+ /*
+ * again special cases. 0 has no inverse. This used to
+ * be initialized to GF_SIZE, but it should make no difference
+ * since noone is supposed to read from here.
+ */
+ inverse[0] = 0 ;
+ inverse[1] = 1;
+ for (i=2; i<=GF_SIZE; i++)
+ inverse[i] = gf_exp[GF_SIZE-gf_log[i]];
+}
+
+/*
+ * Various linear algebra operations that i use often.
+ */
+
+/*
+ * addmul() computes dst[] = dst[] + c * src[]
+ * This is used often, so better optimize it! Currently the loop is
+ * unrolled 16 times, a good value for 486 and pentium-class machines.
+ * The case c=0 is also optimized, whereas c=1 is not. These
+ * calls are unfrequent in my typical apps so I did not bother.
+ *
+ * Note that gcc on
+ */
+#define addmul(dst, src, c, sz) \
+ if (c != 0) addmul1(dst, src, c, sz)
+
+#define UNROLL 16 /* 1, 4, 8, 16 */
+static void
+addmul1(gf *dst1, gf *src1, gf c, int sz)
+{
+ USE_GF_MULC ;
+ register gf *dst = dst1, *src = src1 ;
+ gf *lim = &dst[sz - UNROLL + 1] ;
+
+ GF_MULC0(c) ;
+
+#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */
+ for (; dst < lim ; dst += UNROLL, src += UNROLL ) {
+ GF_ADDMULC( dst[0] , src[0] );
+ GF_ADDMULC( dst[1] , src[1] );
+ GF_ADDMULC( dst[2] , src[2] );
+ GF_ADDMULC( dst[3] , src[3] );
+#if (UNROLL > 4)
+ GF_ADDMULC( dst[4] , src[4] );
+ GF_ADDMULC( dst[5] , src[5] );
+ GF_ADDMULC( dst[6] , src[6] );
+ GF_ADDMULC( dst[7] , src[7] );
+#endif
+#if (UNROLL > 8)
+ GF_ADDMULC( dst[8] , src[8] );
+ GF_ADDMULC( dst[9] , src[9] );
+ GF_ADDMULC( dst[10] , src[10] );
+ GF_ADDMULC( dst[11] , src[11] );
+ GF_ADDMULC( dst[12] , src[12] );
+ GF_ADDMULC( dst[13] , src[13] );
+ GF_ADDMULC( dst[14] , src[14] );
+ GF_ADDMULC( dst[15] , src[15] );
+#endif
+ }
+#endif
+ lim += UNROLL - 1 ;
+ for (; dst < lim; dst++, src++ ) /* final components */
+ GF_ADDMULC( *dst , *src );
+}
+
+/*
+ * computes C = AB where A is n*k, B is k*m, C is n*m
+ */
+static void
+matmul(gf *a, gf *b, gf *c, int n, int k, int m)
+{
+ int row, col, i ;
+
+ for (row = 0; row < n ; row++) {
+ for (col = 0; col < m ; col++) {
+ gf *pa = &a[ row * k ];
+ gf *pb = &b[ col ];
+ gf acc = 0 ;
+ for (i = 0; i < k ; i++, pa++, pb += m )
+ acc ^= gf_mul( *pa, *pb ) ;
+ c[ row * m + col ] = acc ;
+ }
+ }
+}
+
+#ifdef DEBUG
+/*
+ * returns 1 if the square matrix is identiy
+ * (only for test)
+ */
+static int
+is_identity(gf *m, int k)
+{
+ int row, col ;
+ for (row=0; row<k; row++)
+ for (col=0; col<k; col++)
+ if ( (row==col && *m != 1) ||
+ (row!=col && *m != 0) )
+ return 0 ;
+ else
+ m++ ;
+ return 1 ;
+}
+#endif /* debug */
+
+/*
+ * invert_mat() takes a matrix and produces its inverse
+ * k is the size of the matrix.
+ * (Gauss-Jordan, adapted from Numerical Recipes in C)
+ * Return non-zero if singular.
+ */
+DEB( int pivloops=0; int pivswaps=0 ; /* diagnostic */)
+static int
+invert_mat(gf *src, int k)
+{
+ gf c, *p ;
+ int irow, icol, row, col, i, ix ;
+
+ int error = 1 ;
+ int *indxc = my_malloc(k*sizeof(int), "indxc");
+ int *indxr = my_malloc(k*sizeof(int), "indxr");
+ int *ipiv = my_malloc(k*sizeof(int), "ipiv");
+ gf *id_row = NEW_GF_MATRIX(1, k);
+ gf *temp_row = NEW_GF_MATRIX(1, k);
+
+ memset(id_row, '\0', k*sizeof(gf));
+ DEB( pivloops=0; pivswaps=0 ; /* diagnostic */ )
+ /*
+ * ipiv marks elements already used as pivots.
+ */
+ for (i = 0; i < k ; i++)
+ ipiv[i] = 0 ;
+
+ for (col = 0; col < k ; col++) {
+ gf *pivot_row ;
+ /*
+ * Zeroing column 'col', look for a non-zero element.
+ * First try on the diagonal, if it fails, look elsewhere.
+ */
+ irow = icol = -1 ;
+ if (ipiv[col] != 1 && src[col*k + col] != 0) {
+ irow = col ;
+ icol = col ;
+ goto found_piv ;
+ }
+ for (row = 0 ; row < k ; row++) {
+ if (ipiv[row] != 1) {
+ for (ix = 0 ; ix < k ; ix++) {
+ DEB( pivloops++ ; )
+ if (ipiv[ix] == 0) {
+ if (src[row*k + ix] != 0) {
+ irow = row ;
+ icol = ix ;
+ goto found_piv ;
+ }
+ } else if (ipiv[ix] > 1) {
+ fprintf(stderr, "singular matrix\n");
+ goto fail ;
+ }
+ }
+ }
+ }
+ if (icol == -1) {
+ fprintf(stderr, "XXX pivot not found!\n");
+ goto fail ;
+ }
+found_piv:
+ ++(ipiv[icol]) ;
+ /*
+ * swap rows irow and icol, so afterwards the diagonal
+ * element will be correct. Rarely done, not worth
+ * optimizing.
+ */
+ if (irow != icol) {
+ for (ix = 0 ; ix < k ; ix++ ) {
+ SWAP( src[irow*k + ix], src[icol*k + ix], gf) ;
+ }
+ }
+ indxr[col] = irow ;
+ indxc[col] = icol ;
+ pivot_row = &src[icol*k] ;
+ c = pivot_row[icol] ;
+ if (c == 0) {
+ fprintf(stderr, "singular matrix 2\n");
+ goto fail ;
+ }
+ if (c != 1 ) { /* otherwhise this is a NOP */
+ /*
+ * this is done often , but optimizing is not so
+ * fruitful, at least in the obvious ways (unrolling)
+ */
+ DEB( pivswaps++ ; )
+ c = inverse[ c ] ;
+ pivot_row[icol] = 1 ;
+ for (ix = 0 ; ix < k ; ix++ )
+ pivot_row[ix] = gf_mul(c, pivot_row[ix] );
+ }
+ /*
+ * from all rows, remove multiples of the selected row
+ * to zero the relevant entry (in fact, the entry is not zero
+ * because we know it must be zero).
+ * (Here, if we know that the pivot_row is the identity,
+ * we can optimize the addmul).
+ */
+ id_row[icol] = 1;
+ if (memcmp(pivot_row, id_row, k*sizeof(gf)) != 0) {
+ for (p = src, ix = 0 ; ix < k ; ix++, p += k ) {
+ if (ix != icol) {
+ c = p[icol] ;
+ p[icol] = 0 ;
+ addmul(p, pivot_row, c, k );
+ }
+ }
+ }
+ id_row[icol] = 0;
+ } /* done all columns */
+ for (col = k-1 ; col >= 0 ; col-- ) {
+ if (indxr[col] <0 || indxr[col] >= k)
+ fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]);
+ else if (indxc[col] <0 || indxc[col] >= k)
+ fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]);
+ else
+ if (indxr[col] != indxc[col] ) {
+ for (row = 0 ; row < k ; row++ ) {
+ SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ;
+ }
+ }
+ }
+ error = 0 ;
+fail:
+ free(indxc);
+ free(indxr);
+ free(ipiv);
+ free(id_row);
+ free(temp_row);
+ return error ;
+}
+
+/*
+ * fast code for inverting a vandermonde matrix.
+ * XXX NOTE: It assumes that the matrix
+ * is not singular and _IS_ a vandermonde matrix. Only uses
+ * the second column of the matrix, containing the p_i's.
+ *
+ * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but
+ * largely revised for my purposes.
+ * p = coefficients of the matrix (p_i)
+ * q = values of the polynomial (known)
+ */
+
+int
+invert_vdm(gf *src, int k)
+{
+ int i, j, row, col ;
+ gf *b, *c, *p;
+ gf t, xx ;
+
+ if (k == 1) /* degenerate case, matrix must be p^0 = 1 */
+ return 0 ;
+ /*
+ * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
+ * b holds the coefficient for the matrix inversion
+ */
+ c = NEW_GF_MATRIX(1, k);
+ b = NEW_GF_MATRIX(1, k);
+
+ p = NEW_GF_MATRIX(1, k);
+
+ for ( j=1, i = 0 ; i < k ; i++, j+=k ) {
+ c[i] = 0 ;
+ p[i] = src[j] ; /* p[i] */
+ }
+ /*
+ * construct coeffs. recursively. We know c[k] = 1 (implicit)
+ * and start P_0 = x - p_0, then at each stage multiply by
+ * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1}
+ * After k steps we are done.
+ */
+ c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */
+ for (i = 1 ; i < k ; i++ ) {
+ gf p_i = p[i] ; /* see above comment */
+ for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ )
+ c[j] ^= gf_mul( p_i, c[j+1] ) ;
+ c[k-1] ^= p_i ;
+ }
+
+ for (row = 0 ; row < k ; row++ ) {
+ /*
+ * synthetic division etc.
+ */
+ xx = p[row] ;
+ t = 1 ;
+ b[k-1] = 1 ; /* this is in fact c[k] */
+ for (i = k-2 ; i >= 0 ; i-- ) {
+ b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ;
+ t = gf_mul(xx, t) ^ b[i] ;
+ }
+ for (col = 0 ; col < k ; col++ )
+ src[col*k + row] = gf_mul(inverse[t], b[col] );
+ }
+ free(c) ;
+ free(b) ;
+ free(p) ;
+ return 0 ;
+}
+
+static int fec_initialized = 0 ;
+static void
+init_fec()
+{
+ TICK(ticks[0]);
+ generate_gf();
+ TOCK(ticks[0]);
+ DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);)
+ TICK(ticks[0]);
+ init_mul_table();
+ TOCK(ticks[0]);
+ DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);)
+ fec_initialized = 1 ;
+}
+
+/*
+ * This section contains the proper FEC encoding/decoding routines.
+ * The encoding matrix is computed starting with a Vandermonde matrix,
+ * and then transforming it into a systematic matrix.
+ */
+
+#define FEC_MAGIC 0xFECC0DEC
+
+struct fec_parms {
+ u_long magic ;
+ int k, n ; /* parameters of the code */
+ gf *enc_matrix ;
+} ;
+
+void
+fec_free(struct fec_parms *p)
+{
+ if (p==NULL ||
+ p->magic != ( ( (FEC_MAGIC ^ p->k) ^ p->n) ^ (int)(p->enc_matrix)) ) {
+ fprintf(stderr, "bad parameters to fec_free\n");
+ return ;
+ }
+ free(p->enc_matrix);
+ free(p);
+}
+
+/*
+ * create a new encoder, returning a descriptor. This contains k,n and
+ * the encoding matrix.
+ */
+struct fec_parms *
+fec_new(int k, int n)
+{
+ int row, col ;
+ gf *p, *tmp_m ;
+
+ struct fec_parms *retval ;
+
+ if (fec_initialized == 0)
+ init_fec();
+
+ if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n ) {
+ fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n",
+ k, n, GF_SIZE );
+ return NULL ;
+ }
+ retval = my_malloc(sizeof(struct fec_parms), "new_code");
+ retval->k = k ;
+ retval->n = n ;
+ retval->enc_matrix = NEW_GF_MATRIX(n, k);
+ retval->magic = ( ( FEC_MAGIC ^ k) ^ n) ^ (int)(retval->enc_matrix) ;
+ tmp_m = NEW_GF_MATRIX(n, k);
+ /*
+ * fill the matrix with powers of field elements, starting from 0.
+ * The first row is special, cannot be computed with exp. table.
+ */
+ tmp_m[0] = 1 ;
+ for (col = 1; col < k ; col++)
+ tmp_m[col] = 0 ;
+ for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) {
+ for ( col = 0 ; col < k ; col ++ )
+ p[col] = gf_exp[modnn(row*col)];
+ }
+
+ /*
+ * quick code to build systematic matrix: invert the top
+ * k*k vandermonde matrix, multiply right the bottom n-k rows
+ * by the inverse, and construct the identity matrix at the top.
+ */
+ TICK(ticks[3]);
+ invert_vdm(tmp_m, k); /* much faster than invert_mat */
+ matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k);
+ /*
+ * the upper matrix is I so do not bother with a slow multiply
+ */
+ memset(retval->enc_matrix, '\0', k*k*sizeof(gf) );
+ for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 )
+ *p = 1 ;
+ free(tmp_m);
+ TOCK(ticks[3]);
+
+ DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n",
+ ticks[3]);)
+ DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");)
+ return retval ;
+}
+
+/*
+ * fec_encode accepts as input pointers to n data packets of size sz,
+ * and produces as output a packet pointed to by fec, computed
+ * with index "index".
+ */
+void
+fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz)
+{
+ int i, k = code->k ;
+ gf *p ;
+
+ if (GF_BITS > 8)
+ sz /= 2 ;
+
+ if (index < k)
+ memcpy(fec, src[index], sz*sizeof(gf) ) ;
+ else if (index < code->n) {
+ p = &(code->enc_matrix[index*k] );
+ memset(fec, '\0', sz*sizeof(gf));
+ for (i = 0; i < k ; i++)
+ addmul(fec, src[i], p[i], sz ) ;
+ } else
+ fprintf(stderr, "Invalid index %d (max %d)\n",
+ index, code->n - 1 );
+}
+
+void fec_encode_linear(struct fec_parms *code, gf *src, gf *fec, int index, int sz)
+{
+ int i, k = code->k ;
+ gf *p ;
+
+ if (GF_BITS > 8)
+ sz /= 2 ;
+
+ if (index < k)
+ memcpy(fec, src + (index * sz), sz*sizeof(gf) ) ;
+ else if (index < code->n) {
+ p = &(code->enc_matrix[index*k] );
+ memset(fec, '\0', sz*sizeof(gf));
+ for (i = 0; i < k ; i++)
+ addmul(fec, src + (i * sz), p[i], sz ) ;
+ } else
+ fprintf(stderr, "Invalid index %d (max %d)\n",
+ index, code->n - 1 );
+}
+/*
+ * shuffle move src packets in their position
+ */
+static int
+shuffle(gf *pkt[], int index[], int k)
+{
+ int i;
+
+ for ( i = 0 ; i < k ; ) {
+ if (index[i] >= k || index[i] == i)
+ i++ ;
+ else {
+ /*
+ * put pkt in the right position (first check for conflicts).
+ */
+ int c = index[i] ;
+
+ if (index[c] == c) {
+ DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);)
+ return 1 ;
+ }
+ SWAP(index[i], index[c], int) ;
+ SWAP(pkt[i], pkt[c], gf *) ;
+ }
+ }
+ DEB( /* just test that it works... */
+ for ( i = 0 ; i < k ; i++ ) {
+ if (index[i] < k && index[i] != i) {
+ fprintf(stderr, "shuffle: after\n");
+ for (i=0; i<k ; i++) fprintf(stderr, "%3d ", index[i]);
+ fprintf(stderr, "\n");
+ return 1 ;
+ }
+ }
+ )
+ return 0 ;
+}
+
+/*
+ * build_decode_matrix constructs the encoding matrix given the
+ * indexes. The matrix must be already allocated as
+ * a vector of k*k elements, in row-major order
+ */
+static gf *
+build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[])
+{
+ int i , k = code->k ;
+ gf *p, *matrix = NEW_GF_MATRIX(k, k);
+
+ TICK(ticks[9]);
+ for (i = 0, p = matrix ; i < k ; i++, p += k ) {
+#if 1 /* this is simply an optimization, not very useful indeed */
+ if (index[i] < k) {
+ memset(p, '\0', k*sizeof(gf) );
+ p[i] = 1 ;
+ } else
+#endif
+ if (index[i] < code->n )
+ memcpy(p, &(code->enc_matrix[index[i]*k]), k*sizeof(gf) );
+ else {
+ fprintf(stderr, "decode: invalid index %d (max %d)\n",
+ index[i], code->n - 1 );
+ free(matrix) ;
+ return NULL ;
+ }
+ }
+ TICK(ticks[9]);
+ if (invert_mat(matrix, k)) {
+ free(matrix);
+ matrix = NULL ;
+ }
+ TOCK(ticks[9]);
+ return matrix ;
+}
+
+/*
+ * fec_decode receives as input a vector of packets, the indexes of
+ * packets, and produces the correct vector as output.
+ *
+ * Input:
+ * code: pointer to code descriptor
+ * pkt: pointers to received packets. They are modified
+ * to store the output packets (in place)
+ * index: pointer to packet indexes (modified)
+ * sz: size of each packet
+ */
+int
+fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz)
+{
+ gf *m_dec ;
+ gf **new_pkt ;
+ int row, col , k = code->k ;
+
+ if (GF_BITS > 8)
+ sz /= 2 ;
+
+ if (shuffle(pkt, index, k)) /* error if true */
+ return 1 ;
+ m_dec = build_decode_matrix(code, pkt, index);
+
+ if (m_dec == NULL)
+ return 1 ; /* error */
+ /*
+ * do the actual decoding
+ */
+ new_pkt = my_malloc (k * sizeof (gf * ), "new pkt pointers" );
+ for (row = 0 ; row < k ; row++ ) {
+ if (index[row] >= k) {
+ new_pkt[row] = my_malloc (sz * sizeof (gf), "new pkt buffer" );
+ memset(new_pkt[row], '\0', sz * sizeof(gf) ) ;
+ for (col = 0 ; col < k ; col++ )
+ addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ;
+ }
+ }
+ /*
+ * move pkts to their final destination
+ */
+ for (row = 0 ; row < k ; row++ ) {
+ if (index[row] >= k) {
+ memcpy(pkt[row], new_pkt[row], sz*sizeof(gf));
+ free(new_pkt[row]);
+ }
+ }
+ free(new_pkt);
+ free(m_dec);
+
+ return 0;
+}
+
+/*********** end of FEC code -- beginning of test code ************/
+
+#if (TEST || DEBUG)
+void
+test_gf()
+{
+ int i ;
+ /*
+ * test gf tables. Sufficiently tested...
+ */
+ for (i=0; i<= GF_SIZE; i++) {
+ if (gf_exp[gf_log[i]] != i)
+ fprintf(stderr, "bad exp/log i %d log %d exp(log) %d\n",
+ i, gf_log[i], gf_exp[gf_log[i]]);
+
+ if (i != 0 && gf_mul(i, inverse[i]) != 1)
+ fprintf(stderr, "bad mul/inv i %d inv %d i*inv(i) %d\n",
+ i, inverse[i], gf_mul(i, inverse[i]) );
+ if (gf_mul(0,i) != 0)
+ fprintf(stderr, "bad mul table 0,%d\n",i);
+ if (gf_mul(i,0) != 0)
+ fprintf(stderr, "bad mul table %d,0\n",i);
+ }
+}
+#endif /* TEST */
diff --git a/mtd-utils-1.3.1/fectest.c b/mtd-utils-1.3.1/fectest.c
new file mode 100644
index 0000000..d5893b9
--- /dev/null
+++ b/mtd-utils-1.3.1/fectest.c
@@ -0,0 +1,92 @@
+#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 PKT_SIZE 1400
+#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/mtd-utils-1.3.1/flash_erase.c b/mtd-utils-1.3.1/flash_erase.c
new file mode 100644
index 0000000..fdf9918
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_erase.c
@@ -0,0 +1,189 @@
+/*
+ * flash_erase.c -- erase parts of a MTD device
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <mtd/mtd-user.h>
+
+int region_erase(int Fd, int start, int count, int unlock, int regcount)
+{
+ int i, j;
+ region_info_t * reginfo;
+
+ reginfo = calloc(regcount, sizeof(region_info_t));
+
+ for(i = 0; i < regcount; i++)
+ {
+ reginfo[i].regionindex = i;
+ if(ioctl(Fd,MEMGETREGIONINFO,&(reginfo[i])) != 0)
+ return 8;
+ else
+ printf("Region %d is at %d of %d sector and with sector "
+ "size %x\n", i, reginfo[i].offset, reginfo[i].numblocks,
+ reginfo[i].erasesize);
+ }
+
+ // We have all the information about the chip we need.
+
+ for(i = 0; i < regcount; i++)
+ { //Loop through the regions
+ region_info_t * r = &(reginfo[i]);
+
+ if((start >= reginfo[i].offset) &&
+ (start < (r->offset + r->numblocks*r->erasesize)))
+ break;
+ }
+
+ if(i >= regcount)
+ {
+ printf("Starting offset %x not within chip.\n", start);
+ return 8;
+ }
+
+ //We are now positioned within region i of the chip, so start erasing
+ //count sectors from there.
+
+ for(j = 0; (j < count)&&(i < regcount); j++)
+ {
+ erase_info_t erase;
+ region_info_t * r = &(reginfo[i]);
+
+ erase.start = start;
+ erase.length = r->erasesize;
+
+ if(unlock != 0)
+ { //Unlock the sector first.
+ if(ioctl(Fd, MEMUNLOCK, &erase) != 0)
+ {
+ perror("\nMTD Unlock failure");
+ close(Fd);
+ return 8;
+ }
+ }
+ printf("\rPerforming Flash Erase of length %u at offset 0x%x",
+ erase.length, erase.start);
+ fflush(stdout);
+ if(ioctl(Fd, MEMERASE, &erase) != 0)
+ {
+ perror("\nMTD Erase failure");
+ close(Fd);
+ return 8;
+ }
+
+
+ start += erase.length;
+ if(start >= (r->offset + r->numblocks*r->erasesize))
+ { //We finished region i so move to region i+1
+ printf("\nMoving to region %d\n", i+1);
+ i++;
+ }
+ }
+
+ printf(" done\n");
+
+ return 0;
+}
+
+int non_region_erase(int Fd, int start, int count, int unlock)
+{
+ mtd_info_t meminfo;
+
+ if (ioctl(Fd,MEMGETINFO,&meminfo) == 0)
+ {
+ erase_info_t erase;
+
+ erase.start = start;
+
+ erase.length = meminfo.erasesize;
+
+ for (; count > 0; count--) {
+ printf("\rPerforming Flash Erase of length %u at offset 0x%x",
+ erase.length, erase.start);
+ fflush(stdout);
+
+ if(unlock != 0)
+ {
+ //Unlock the sector first.
+ printf("\rPerforming Flash unlock at offset 0x%x",erase.start);
+ if(ioctl(Fd, MEMUNLOCK, &erase) != 0)
+ {
+ perror("\nMTD Unlock failure");
+ close(Fd);
+ return 8;
+ }
+ }
+
+ if (ioctl(Fd,MEMERASE,&erase) != 0)
+ {
+ perror("\nMTD Erase failure");
+ close(Fd);
+ return 8;
+ }
+ erase.start += meminfo.erasesize;
+ }
+ printf(" done\n");
+ }
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int regcount;
+ int Fd;
+ int start;
+ int count;
+ int unlock;
+ int res = 0;
+
+ if (1 >= argc || !strcmp(argv[1], "-h") || !strcmp (argv[1], "--help") ) {
+ printf("Usage: flash_erase MTD-device [start] [cnt (# erase blocks)] [lock]\n"
+ " flash_erase -h | --help\n") ;
+ return 16 ;
+ }
+
+ if (argc > 2)
+ start = strtol(argv[2], NULL, 0);
+ else
+ start = 0;
+
+ if (argc > 3)
+ count = strtol(argv[3], NULL, 0);
+ else
+ count = 1;
+
+ if(argc > 4)
+ unlock = strtol(argv[4], NULL, 0);
+ else
+ unlock = 0;
+
+
+ // Open and size the device
+ if ((Fd = open(argv[1],O_RDWR)) < 0)
+ {
+ fprintf(stderr,"File open error\n");
+ return 8;
+ }
+
+ printf("Erase Total %d Units\n", count);
+
+ if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0)
+ {
+ if(regcount == 0)
+ {
+ res = non_region_erase(Fd, start, count, unlock);
+ }
+ else
+ {
+ res = region_erase(Fd, start, count, unlock, regcount);
+ }
+ }
+
+ return res;
+}
diff --git a/mtd-utils-1.3.1/flash_eraseall.c b/mtd-utils-1.3.1/flash_eraseall.c
new file mode 100644
index 0000000..a22fc49
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_eraseall.c
@@ -0,0 +1,289 @@
+/* eraseall.c -- erase the whole of a MTD device
+
+ Copyright (C) 2000 Arcom Control System Ltd
+
+ Renamed to flash_eraseall.c
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include "crc32.h"
+
+#include <mtd/mtd-user.h>
+#include <mtd/jffs2-user.h>
+
+#define PROGRAM "flash_eraseall"
+#define VERSION "$Revision: 1.22 $"
+
+static const char *exe_name;
+static const char *mtd_device;
+static int quiet; /* true -- don't output progress */
+static int jffs2; // format for jffs2 usage
+
+static void process_options (int argc, char *argv[]);
+void show_progress (mtd_info_t *meminfo, erase_info_t *erase);
+static void display_help (void);
+static void display_version (void);
+static struct jffs2_unknown_node cleanmarker;
+int target_endian = __BYTE_ORDER;
+
+int main (int argc, char *argv[])
+{
+ mtd_info_t meminfo;
+ int fd, clmpos = 0, clmlen = 8;
+ erase_info_t erase;
+ int isNAND, bbtest = 1;
+
+ process_options(argc, argv);
+
+ if ((fd = open(mtd_device, O_RDWR)) < 0) {
+ fprintf(stderr, "%s: %s: %s\n", exe_name, mtd_device, strerror(errno));
+ return 1;
+ }
+
+
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ fprintf(stderr, "%s: %s: unable to get MTD device info\n", exe_name, mtd_device);
+ return 1;
+ }
+
+ erase.length = meminfo.erasesize;
+ isNAND = meminfo.type == MTD_NANDFLASH ? 1 : 0;
+
+ 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 (struct jffs2_unknown_node));
+ else {
+ struct nand_oobinfo oobinfo;
+
+ if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) {
+ fprintf(stderr, "%s: %s: unable to get NAND oobinfo\n", exe_name, mtd_device);
+ return 1;
+ }
+
+ /* Check for autoplacement */
+ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
+ /* Get the position of the free bytes */
+ if (!oobinfo.oobfree[0][1]) {
+ fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n");
+ return 1;
+ }
+ clmpos = oobinfo.oobfree[0][0];
+ clmlen = oobinfo.oobfree[0][1];
+ if (clmlen > 8)
+ clmlen = 8;
+ } else {
+ /* Legacy mode */
+ switch (meminfo.oobsize) {
+ 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 (crc32 (0, &cleanmarker, sizeof (struct jffs2_unknown_node) - 4));
+ }
+
+ for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) {
+ if (bbtest) {
+ loff_t offset = erase.start;
+ int ret = ioctl(fd, MEMGETBADBLOCK, &offset);
+ if (ret > 0) {
+ if (!quiet)
+ printf ("\nSkipping bad block at 0x%08x\n", erase.start);
+ continue;
+ } else if (ret < 0) {
+ if (errno == EOPNOTSUPP) {
+ bbtest = 0;
+ if (isNAND) {
+ fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device);
+ return 1;
+ }
+ } else {
+ fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno));
+ return 1;
+ }
+ }
+ }
+
+ if (!quiet)
+ show_progress(&meminfo, &erase);
+
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+
+ /* format for JFFS2 ? */
+ if (!jffs2)
+ continue;
+
+ /* write cleanmarker */
+ if (isNAND) {
+ struct mtd_oob_buf oob;
+ oob.ptr = (unsigned char *) &cleanmarker;
+ oob.start = erase.start + clmpos;
+ oob.length = clmlen;
+ if (ioctl (fd, MEMWRITEOOB, &oob) != 0) {
+ fprintf(stderr, "\n%s: %s: MTD writeoob failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+ } else {
+ if (lseek (fd, erase.start, SEEK_SET) < 0) {
+ fprintf(stderr, "\n%s: %s: MTD lseek failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+ if (write (fd , &cleanmarker, sizeof (cleanmarker)) != sizeof (cleanmarker)) {
+ fprintf(stderr, "\n%s: %s: MTD write failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+ }
+ if (!quiet)
+ printf (" Cleanmarker written at %x.", erase.start);
+ }
+ if (!quiet) {
+ show_progress(&meminfo, &erase);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+
+void process_options (int argc, char *argv[])
+{
+ int error = 0;
+
+ exe_name = argv[0];
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "jq";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"jffs2", no_argument, 0, 'j'},
+ {"quiet", no_argument, 0, 'q'},
+ {"silent", no_argument, 0, 'q'},
+
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ switch (option_index) {
+ case 0:
+ display_help();
+ break;
+ case 1:
+ display_version();
+ break;
+ }
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'j':
+ jffs2 = 1;
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+ if (optind == argc) {
+ fprintf(stderr, "%s: no MTD device specified\n", exe_name);
+ error = 1;
+ }
+ if (error) {
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ exe_name);
+ exit(1);
+ }
+
+ mtd_device = argv[optind];
+}
+
+void show_progress (mtd_info_t *meminfo, erase_info_t *erase)
+{
+ printf("\rErasing %d Kibyte @ %x -- %2llu %% complete.",
+ meminfo->erasesize / 1024, erase->start,
+ (unsigned long long) erase->start * 100 / meminfo->size);
+ fflush(stdout);
+}
+
+void display_help (void)
+{
+ printf("Usage: %s [OPTION] MTD_DEVICE\n"
+ "Erases all of the specified MTD device.\n"
+ "\n"
+ " -j, --jffs2 format the device for jffs2\n"
+ " -q, --quiet don't display progress messages\n"
+ " --silent same as --quiet\n"
+ " --help display this help and exit\n"
+ " --version output version information and exit\n",
+ exe_name);
+ exit(0);
+}
+
+
+void display_version (void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "Copyright (C) 2000 Arcom Control Systems Ltd\n"
+ "\n"
+ PROGRAM " comes with NO WARRANTY\n"
+ "to the extent permitted by law.\n"
+ "\n"
+ "You may redistribute copies of " PROGRAM "\n"
+ "under the terms of the GNU General Public Licence.\n"
+ "See the file `COPYING' for more information.\n");
+ exit(0);
+}
diff --git a/mtd-utils-1.3.1/flash_info.c b/mtd-utils-1.3.1/flash_info.c
new file mode 100644
index 0000000..f5ed1c6
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_info.c
@@ -0,0 +1,55 @@
+/*
+ * flash_info.c -- print info about a MTD device
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int regcount;
+ int Fd;
+
+ if (1 >= argc)
+ {
+ fprintf(stderr,"Usage: flash_info device\n");
+ return 16;
+ }
+
+ // Open and size the device
+ if ((Fd = open(argv[1],O_RDONLY)) < 0)
+ {
+ fprintf(stderr,"File open error\n");
+ return 8;
+ }
+
+ if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0)
+ {
+ int i;
+ region_info_t reginfo;
+ printf("Device %s has %d erase regions\n", argv[1], regcount);
+ for (i = 0; i < regcount; i++)
+ {
+ reginfo.regionindex = i;
+ if(ioctl(Fd, MEMGETREGIONINFO, ®info) == 0)
+ {
+ printf("Region %d is at 0x%x with size 0x%x and "
+ "has 0x%x blocks\n", i, reginfo.offset,
+ reginfo.erasesize, reginfo.numblocks);
+ }
+ else
+ {
+ printf("Strange can not read region %d from a %d region device\n",
+ i, regcount);
+ }
+ }
+ }
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/flash_lock.c b/mtd-utils-1.3.1/flash_lock.c
new file mode 100644
index 0000000..37f2ad3
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_lock.c
@@ -0,0 +1,83 @@
+/*
+ * FILE flash_lock.c
+ *
+ * This utility locks one or more sectors of flash device.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct mtd_info_user mtdInfo;
+ struct erase_info_user mtdLockInfo;
+ int num_sectors;
+ int ofs;
+
+ /*
+ * Parse command line options
+ */
+ if(argc != 4)
+ {
+ fprintf(stderr, "USAGE: %s <mtd device> <ofs in hex> <num of sectors in decimal or -1 for all sectors>\n", argv[0]);
+ exit(1);
+ }
+ else if(strncmp(argv[1], "/dev/mtd", 8) != 0)
+ {
+ fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]);
+ exit(1);
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if(fd < 0)
+ {
+ fprintf(stderr, "Could not open mtd device: %s\n", argv[1]);
+ exit(1);
+ }
+
+ if(ioctl(fd, MEMGETINFO, &mtdInfo))
+ {
+ fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+ sscanf(argv[2], "%x",&ofs);
+ sscanf(argv[3], "%d",&num_sectors);
+ if(ofs > mtdInfo.size - mtdInfo.erasesize)
+ {
+ fprintf(stderr, "%x is beyond device size %x\n",ofs,(unsigned int)(mtdInfo.size - mtdInfo.erasesize));
+ exit(1);
+ }
+
+ if (num_sectors == -1) {
+ num_sectors = mtdInfo.size/mtdInfo.erasesize;
+ }
+ else {
+ if(num_sectors > mtdInfo.size/mtdInfo.erasesize)
+ {
+ fprintf(stderr, "%d are too many sectors, device only has %d\n",num_sectors,(int)(mtdInfo.size/mtdInfo.erasesize));
+ exit(1);
+ }
+ }
+
+ mtdLockInfo.start = ofs;
+ mtdLockInfo.length = (num_sectors - 1) * mtdInfo.erasesize;
+ if(ioctl(fd, MEMLOCK, &mtdLockInfo))
+ {
+ fprintf(stderr, "Could not lock MTD device: %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/flash_otp_dump.c b/mtd-utils-1.3.1/flash_otp_dump.c
new file mode 100644
index 0000000..a18130d
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_otp_dump.c
@@ -0,0 +1,54 @@
+/*
+ * flash_otp_dump.c -- display One-Time-Programm data
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, i, offset, ret;
+ unsigned char buf[16];
+
+ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) {
+ fprintf(stderr,"Usage: %s [ -f | -u ] <device>\n", argv[0]);
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ printf("OTP %s data for %s\n",
+ argv[1][1] == 'f' ? "factory" : "user", argv[2]);
+ offset = 0;
+ while ((ret = read(fd, buf, sizeof(buf)))) {
+ if (ret < 0) {
+ perror("read()");
+ return errno;
+ }
+ printf("0x%04x:", offset);
+ for (i = 0; i < ret; i++)
+ printf(" %02x", buf[i]);
+ printf("\n");
+ offset += ret;
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/flash_otp_info.c b/mtd-utils-1.3.1/flash_otp_info.c
new file mode 100644
index 0000000..c9486ee
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_otp_info.c
@@ -0,0 +1,63 @@
+/*
+ * flash_otp_info.c -- print info about One-Time-Programm data
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, i, ret;
+
+ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) {
+ fprintf(stderr,"Usage: %s [ -f | -u ] <device>\n", argv[0]);
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ ret = ioctl(fd, OTPGETREGIONCOUNT, &val);
+ if (ret < 0) {
+ perror("OTPGETREGIONCOUNT");
+ return errno;
+ }
+
+ printf("Number of OTP %s blocks on %s: %d\n",
+ argv[1][1] == 'f' ? "factory" : "user", argv[2], val);
+
+ if (val > 0) {
+ struct otp_info info[val];
+
+ ret = ioctl(fd, OTPGETREGIONINFO, &info);
+ if (ret < 0) {
+ perror("OTPGETREGIONCOUNT");
+ return errno;
+ }
+
+ for (i = 0; i < val; i++)
+ printf("block %2d: offset = 0x%04x "
+ "size = %2d bytes %s\n",
+ i, info[i].start, info[i].length,
+ info[i].locked ? "[locked]" : "[unlocked]");
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/flash_otp_lock.c b/mtd-utils-1.3.1/flash_otp_lock.c
new file mode 100644
index 0000000..d0e06cd
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_otp_lock.c
@@ -0,0 +1,70 @@
+/*
+ * flash_otp_lock.c -- lock area of One-Time-Program data
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, ret, offset, size;
+ char *p, buf[8];
+
+ if (argc != 5 || strcmp(argv[1], "-u")) {
+ fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", argv[0]);
+ fprintf(stderr, "offset and size must match on OTP region boundaries\n");
+ fprintf(stderr, "CAUTION! ONCE LOCKED, OTP REGIONS CAN'T BE UNLOCKED!\n");
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_WRONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ offset = strtoul(argv[3], &p, 0);
+ if (argv[3][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad offset value\n", argv[0]);
+ return ERANGE;
+ }
+
+ size = strtoul(argv[4], &p, 0);
+ if (argv[4][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad size value\n", argv[0]);
+ return ERANGE;
+ }
+
+ printf("About to lock OTP user data on %s from 0x%x to 0x%x\n",
+ argv[2], offset, offset + size);
+ printf("Are you sure (yes|no)? ");
+ if (fgets(buf, sizeof(buf), stdin) && strcmp(buf, "yes\n") == 0) {
+ struct otp_info info;
+ info.start = offset;
+ info.length = size;
+ ret = ioctl(fd, OTPLOCK, &info);
+ if (ret < 0) {
+ perror("OTPLOCK");
+ return errno;
+ }
+ printf("Done.\n");
+ } else {
+ printf("Aborted\n");
+ }
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/flash_otp_write.c b/mtd-utils-1.3.1/flash_otp_write.c
new file mode 100644
index 0000000..f01df51
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_otp_write.c
@@ -0,0 +1,96 @@
+/*
+ * flash_otp_write.c -- write One-Time-Program data
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, ret, size, wrote, len;
+ mtd_info_t mtdInfo;
+ off_t offset;
+ char *p, buf[2048];
+
+ if (argc != 4 || strcmp(argv[1], "-u")) {
+ fprintf(stderr, "Usage: %s -u <device> <offset>\n", argv[0]);
+ fprintf(stderr, "the raw data to write should be provided on stdin\n");
+ fprintf(stderr, "CAUTION! ONCE SET TO 0, OTP DATA BITS CAN'T BE ERASED!\n");
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_WRONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &mtdInfo)) {
+ perror("MEMGETINFO");
+ return errno;
+ }
+
+ offset = strtoul(argv[3], &p, 0);
+ if (argv[3][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad offset value\n", argv[0]);
+ return ERANGE;
+ }
+
+ if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+ perror("lseek()");
+ return errno;
+ }
+
+ printf("Writing OTP user data on %s at offset 0x%lx\n", argv[2], offset);
+
+ if (mtdInfo.type == MTD_NANDFLASH)
+ len = mtdInfo.writesize;
+ else
+ len = 256;
+
+ wrote = 0;
+ while ((size = read(0, buf, len))) {
+ if (size < 0) {
+ perror("read()");
+ return errno;
+ }
+ p = buf;
+ while (size > 0) {
+ if (mtdInfo.type == MTD_NANDFLASH) {
+ /* Fill remain buffers with 0xff */
+ memset(buf + size, 0xff, mtdInfo.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/mtd-utils-1.3.1/flash_unlock.c b/mtd-utils-1.3.1/flash_unlock.c
new file mode 100644
index 0000000..3969453
--- /dev/null
+++ b/mtd-utils-1.3.1/flash_unlock.c
@@ -0,0 +1,74 @@
+/*
+ * FILE flash_unlock.c
+ *
+ * This utility unlock all sectors of flash device.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct mtd_info_user mtdInfo;
+ struct erase_info_user mtdLockInfo;
+ int count;
+
+ /*
+ * Parse command line options
+ */
+ if(argc < 2)
+ {
+ fprintf(stderr, "USAGE: %s <mtd device> <offset in hex> <block count in decimal number>\n", argv[0]);
+ exit(1);
+ }
+ else if(strncmp(argv[1], "/dev/mtd", 8) != 0)
+ {
+ fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]);
+ exit(1);
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if(fd < 0)
+ {
+ fprintf(stderr, "Could not open mtd device: %s\n", argv[1]);
+ exit(1);
+ }
+
+ if(ioctl(fd, MEMGETINFO, &mtdInfo))
+ {
+ fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+
+ if (argc > 2)
+ mtdLockInfo.start = strtol(argv[2], NULL, 0);
+ else
+ mtdLockInfo.start = 0;
+
+ if (argc > 3) {
+ count = strtol(argv[3], NULL, 0);
+ mtdLockInfo.length = mtdInfo.erasesize * count;
+ } else {
+ mtdLockInfo.length = mtdInfo.size - mtdInfo.erasesize;
+ }
+
+ if(ioctl(fd, MEMUNLOCK, &mtdLockInfo))
+ {
+ fprintf(stderr, "Could not unlock MTD device: %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/flashcp.c b/mtd-utils-1.3.1/flashcp.c
new file mode 100644
index 0000000..8775022
--- /dev/null
+++ b/mtd-utils-1.3.1/flashcp.c
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <mtd/mtd-user.h>
+#include <getopt.h>
+
+typedef int bool;
+#define true 1
+#define false 0
+
+#define EXIT_FAILURE 1
+#define EXIT_SUCCESS 0
+
+/* for debugging purposes only */
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); }
+#else
+#undef DEBUG
+#define DEBUG(fmt,args...)
+#endif
+
+#define KB(x) ((x) / 1024)
+#define PERCENTAGE(x,total) (((x) * 100) / (total))
+
+/* size of read/write buffer */
+#define BUFSIZE (10 * 1024)
+
+/* cmd-line flags */
+#define FLAG_NONE 0x00
+#define FLAG_VERBOSE 0x01
+#define FLAG_HELP 0x02
+#define FLAG_FILENAME 0x04
+#define FLAG_DEVICE 0x08
+
+/* error levels */
+#define LOG_NORMAL 1
+#define LOG_ERROR 2
+
+static void log_printf (int level,const char *fmt, ...)
+{
+ FILE *fp = level == LOG_NORMAL ? stdout : stderr;
+ va_list ap;
+ va_start (ap,fmt);
+ vfprintf (fp,fmt,ap);
+ va_end (ap);
+ fflush (fp);
+}
+
+static void showusage (const char *progname,bool error)
+{
+ int level = error ? LOG_ERROR : LOG_NORMAL;
+
+ log_printf (level,
+ "\n"
+ "Flash Copy - Written by Abraham van der Merwe <abraham@2d3d.co.za>\n"
+ "\n"
+ "usage: %s [ -v | --verbose ] <filename> <device>\n"
+ " %s -h | --help\n"
+ "\n"
+ " -h | --help Show this help message\n"
+ " -v | --verbose Show progress reports\n"
+ " <filename> File which you want to copy to flash\n"
+ " <device> Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
+ "\n",
+ progname,progname);
+
+ exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int safe_open (const char *pathname,int flags)
+{
+ int fd;
+
+ fd = open (pathname,flags);
+ if (fd < 0)
+ {
+ log_printf (LOG_ERROR,"While trying to open %s",pathname);
+ if (flags & O_RDWR)
+ log_printf (LOG_ERROR," for read/write access");
+ else if (flags & O_RDONLY)
+ log_printf (LOG_ERROR," for read access");
+ else if (flags & O_WRONLY)
+ log_printf (LOG_ERROR," for write access");
+ log_printf (LOG_ERROR,": %m\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return (fd);
+}
+
+static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose)
+{
+ ssize_t result;
+
+ result = read (fd,buf,count);
+ if (count != result)
+ {
+ if (verbose) log_printf (LOG_NORMAL,"\n");
+ if (result < 0)
+ {
+ log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+ log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename);
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void safe_rewind (int fd,const char *filename)
+{
+ if (lseek (fd,0L,SEEK_SET) < 0)
+ {
+ log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+}
+
+/******************************************************************************/
+
+static int dev_fd = -1,fil_fd = -1;
+
+static void cleanup (void)
+{
+ if (dev_fd > 0) close (dev_fd);
+ if (fil_fd > 0) close (fil_fd);
+}
+
+int main (int argc,char *argv[])
+{
+ const char *progname,*filename = NULL,*device = NULL;
+ int i,flags = FLAG_NONE;
+ ssize_t result;
+ size_t size,written;
+ struct mtd_info_user mtd;
+ struct erase_info_user erase;
+ struct stat filestat;
+ unsigned char src[BUFSIZE],dest[BUFSIZE];
+
+ (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);
+
+ /*********************
+ * parse cmd-line
+ *****************/
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hv";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"verbose", no_argument, 0, 'v'},
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ flags |= FLAG_HELP;
+ DEBUG("Got FLAG_HELP\n");
+ break;
+ case 'v':
+ flags |= FLAG_VERBOSE;
+ DEBUG("Got FLAG_VERBOSE\n");
+ break;
+ default:
+ DEBUG("Unknown parameter: %s\n",argv[option_index]);
+ showusage (progname,true);
+ }
+ }
+ if (optind+2 == argc) {
+ flags |= FLAG_FILENAME;
+ filename = argv[optind];
+ DEBUG("Got filename: %s\n",filename);
+
+ flags |= FLAG_DEVICE;
+ device = argv[optind+1];
+ DEBUG("Got device: %s\n",device);
+ }
+
+ if (flags & FLAG_HELP || progname == NULL || device == NULL)
+ showusage (progname,flags != FLAG_HELP);
+
+ atexit (cleanup);
+
+ /* get some info about the flash device */
+ dev_fd = safe_open (device,O_SYNC | O_RDWR);
+ if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
+ {
+ DEBUG("ioctl(): %m\n");
+ log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* get some info about the file we want to copy */
+ fil_fd = safe_open (filename,O_RDONLY);
+ if (fstat (fil_fd,&filestat) < 0)
+ {
+ log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+
+ /* does it fit into the device/partition? */
+ if (filestat.st_size > mtd.size)
+ {
+ log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device);
+ exit (EXIT_FAILURE);
+ }
+
+ /*****************************************************
+ * erase enough blocks so that we can write the file *
+ *****************************************************/
+
+#warning "Check for smaller erase regions"
+
+ erase.start = 0;
+ erase.length = (filestat.st_size + mtd.erasesize - 1) / 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/mtd-utils-1.3.1/ftl_check.c b/mtd-utils-1.3.1/ftl_check.c
new file mode 100644
index 0000000..f41e79a
--- /dev/null
+++ b/mtd-utils-1.3.1/ftl_check.c
@@ -0,0 +1,232 @@
+/* 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.
+
+ ======================================================================*/
+
+#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 <byteswap.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define TO_LE32(x) (x)
+# define TO_LE16(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define TO_LE32(x) (bswap_32(x))
+# define TO_LE16(x) (bswap_16(x))
+#else
+# error cannot detect endianess
+#endif
+
+#define FROM_LE32(x) TO_LE32(x)
+#define FROM_LE16(x) TO_LE16(x)
+
+/*====================================================================*/
+
+static void print_size(u_int s)
+{
+ if ((s > 0x100000) && ((s % 0x100000) == 0))
+ printf("%d mb", s / 0x100000);
+ else if ((s > 0x400) && ((s % 0x400) == 0))
+ printf("%d kb", s / 0x400);
+ else
+ printf("%d bytes", s);
+}
+
+/*====================================================================*/
+
+static void check_partition(int fd, int verbose)
+{
+ mtd_info_t mtd;
+ erase_unit_header_t hdr, hdr2;
+ u_int i, j, nbam, *bam;
+ int control, data, free, deleted;
+
+ /* Get partition size, block size */
+ if (ioctl(fd, MEMGETINFO, &mtd) != 0) {
+ perror("get info failed");
+ return;
+ }
+
+ printf("Memory region info:\n");
+ printf(" Region size = ");
+ print_size(mtd.size);
+ printf(" Erase block size = ");
+ print_size(mtd.erasesize);
+ printf("\n\n");
+
+ for (i = 0; i < mtd.size/mtd.erasesize; i++) {
+ if (lseek(fd, (i * mtd.erasesize), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ read(fd, &hdr, sizeof(hdr));
+ if ((FROM_LE32(hdr.FormattedSize) > 0) &&
+ (FROM_LE32(hdr.FormattedSize) <= mtd.size) &&
+ (FROM_LE16(hdr.NumEraseUnits) > 0) &&
+ (FROM_LE16(hdr.NumEraseUnits) <= mtd.size/mtd.erasesize))
+ break;
+ }
+ if (i == mtd.size/mtd.erasesize) {
+ fprintf(stderr, "No valid erase unit headers!\n");
+ return;
+ }
+
+ printf("Partition header:\n");
+ printf(" Formatted size = ");
+ print_size(FROM_LE32(hdr.FormattedSize));
+ printf(", erase units = %d, transfer units = %d\n",
+ FROM_LE16(hdr.NumEraseUnits), hdr.NumTransferUnits);
+ printf(" Erase unit size = ");
+ print_size(1 << hdr.EraseUnitSize);
+ printf(", virtual block size = ");
+ print_size(1 << hdr.BlockSize);
+ printf("\n");
+
+ /* Create basic block allocation table for control blocks */
+ nbam = (mtd.erasesize >> hdr.BlockSize);
+ bam = malloc(nbam * sizeof(u_int));
+
+ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) {
+ if (lseek(fd, (i << hdr.EraseUnitSize), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (read(fd, &hdr2, sizeof(hdr2)) == -1) {
+ perror("read failed");
+ break;
+ }
+ printf("\nErase unit %d:\n", i);
+ if ((hdr2.FormattedSize != hdr.FormattedSize) ||
+ (hdr2.NumEraseUnits != hdr.NumEraseUnits) ||
+ (hdr2.SerialNumber != hdr.SerialNumber))
+ printf(" Erase unit header is corrupt.\n");
+ else if (FROM_LE16(hdr2.LogicalEUN) == 0xffff)
+ printf(" Transfer unit, erase count = %d\n", FROM_LE32(hdr2.EraseCount));
+ else {
+ printf(" Logical unit %d, erase count = %d\n",
+ FROM_LE16(hdr2.LogicalEUN), FROM_LE32(hdr2.EraseCount));
+ if (lseek(fd, (i << hdr.EraseUnitSize)+FROM_LE32(hdr.BAMOffset),
+ SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (read(fd, bam, nbam * sizeof(u_int)) == -1) {
+ perror("read failed");
+ break;
+ }
+ free = deleted = control = data = 0;
+ for (j = 0; j < nbam; j++) {
+ if (BLOCK_FREE(FROM_LE32(bam[j])))
+ free++;
+ else if (BLOCK_DELETED(FROM_LE32(bam[j])))
+ deleted++;
+ else switch (BLOCK_TYPE(FROM_LE32(bam[j]))) {
+ case BLOCK_CONTROL: control++; break;
+ case BLOCK_DATA: data++; break;
+ default: break;
+ }
+ }
+ printf(" Block allocation: %d control, %d data, %d free,"
+ " %d deleted\n", control, data, free, deleted);
+ }
+ }
+} /* format_partition */
+
+/* Show usage information */
+void showusage(char *pname)
+{
+ fprintf(stderr, "usage: %s [-v] device\n", pname);
+ fprintf(stderr, "-v verbose messages\n");
+}
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int verbose;
+ int optch, errflg, fd;
+ struct stat buf;
+
+ errflg = 0;
+ verbose = 0;
+ while ((optch = getopt(argc, argv, "vh")) != -1) {
+ switch (optch) {
+ case 'h':
+ errflg = 1; break;
+ case 'v':
+ verbose = 1; break;
+ default:
+ errflg = -1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ showusage(argv[0]);
+ exit(errflg > 0 ? 0 : EXIT_FAILURE);
+ }
+
+ if (stat(argv[optind], &buf) != 0) {
+ perror("status check failed");
+ exit(EXIT_FAILURE);
+ }
+ if (!(buf.st_mode & S_IFCHR)) {
+ fprintf(stderr, "%s is not a character special device\n",
+ argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ fd = open(argv[optind], O_RDONLY);
+ if (fd == -1) {
+ perror("open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ check_partition(fd, verbose);
+ close(fd);
+
+ exit(EXIT_SUCCESS);
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/ftl_format.c b/mtd-utils-1.3.1/ftl_format.c
new file mode 100644
index 0000000..ae00c99
--- /dev/null
+++ b/mtd-utils-1.3.1/ftl_format.c
@@ -0,0 +1,342 @@
+/* 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.
+
+ ======================================================================*/
+
+#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 <byteswap.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define TO_LE32(x) (x)
+# define TO_LE16(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define TO_LE32(x) (bswap_32(x))
+# define TO_LE16(x) (bswap_16(x))
+#else
+# error cannot detect endianess
+#endif
+
+#define FROM_LE32(x) TO_LE32(x)
+#define FROM_LE16(x) TO_LE16(x)
+
+/*====================================================================*/
+
+static void print_size(u_int s)
+{
+ if ((s > 0x100000) && ((s % 0x100000) == 0))
+ printf("%d mb", s / 0x100000);
+ else if ((s > 0x400) && ((s % 0x400) == 0))
+ printf("%d kb", s / 0x400);
+ else
+ printf("%d bytes", s);
+}
+
+/*====================================================================*/
+
+static const char LinkTarget[] = {
+ 0x13, 0x03, 'C', 'I', 'S'
+};
+static const char DataOrg[] = {
+ 0x46, 0x39, 0x00, 'F', 'T', 'L', '1', '0', '0', 0x00
+};
+
+static void build_header(erase_unit_header_t *hdr, u_int RegionSize,
+ u_int BlockSize, u_int Spare, int Reserve,
+ u_int BootSize)
+{
+ u_int i, BootUnits, nbam, __FormattedSize;
+
+ /* Default everything to the erased state */
+ memset(hdr, 0xff, sizeof(*hdr));
+ memcpy(hdr->LinkTargetTuple, LinkTarget, 5);
+ memcpy(hdr->DataOrgTuple, DataOrg, 10);
+ hdr->EndTuple[0] = hdr->EndTuple[1] = 0xff;
+ BootSize = (BootSize + (BlockSize-1)) & ~(BlockSize-1);
+ BootUnits = BootSize / BlockSize;
+
+ /* We only support 512-byte blocks */
+ hdr->BlockSize = 9;
+ hdr->EraseUnitSize = 0;
+ for (i = BlockSize; i > 1; i >>= 1)
+ hdr->EraseUnitSize++;
+ hdr->EraseCount = TO_LE32(0);
+ hdr->FirstPhysicalEUN = TO_LE16(BootUnits);
+ hdr->NumEraseUnits = TO_LE16((RegionSize - BootSize) >> hdr->EraseUnitSize);
+ hdr->NumTransferUnits = Spare;
+ __FormattedSize = RegionSize - ((Spare + BootUnits) << hdr->EraseUnitSize);
+ /* Leave a little bit of space between the CIS and BAM */
+ hdr->BAMOffset = TO_LE32(0x80);
+ /* Adjust size to account for BAM space */
+ nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int)
+ + FROM_LE32(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize;
+
+ __FormattedSize -=
+ (FROM_LE16(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize);
+ __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff);
+
+ hdr->FormattedSize = TO_LE32(__FormattedSize);
+
+ /* hdr->FirstVMAddress defaults to erased state */
+ hdr->NumVMPages = TO_LE16(0);
+ hdr->Flags = 0;
+ /* hdr->Code defaults to erased state */
+ hdr->SerialNumber = TO_LE32(time(NULL));
+ /* hdr->AltEUHOffset defaults to erased state */
+
+} /* build_header */
+
+/*====================================================================*/
+
+static int format_partition(int fd, int quiet, int interrogate,
+ u_int spare, int reserve, u_int bootsize)
+{
+ mtd_info_t mtd;
+ erase_info_t erase;
+ erase_unit_header_t hdr;
+ u_int step, lun, i, nbam, *bam;
+
+ /* Get partition size, block size */
+ if (ioctl(fd, MEMGETINFO, &mtd) != 0) {
+ perror("get info failed");
+ return -1;
+ }
+
+#if 0
+ /* Intel Series 100 Flash: skip first block */
+ if ((region.JedecMfr == 0x89) && (region.JedecInfo == 0xaa) &&
+ (bootsize == 0)) {
+ if (!quiet)
+ printf("Skipping first block to protect CIS info...\n");
+ bootsize = 1;
+ }
+#endif
+
+ /* Create header */
+ build_header(&hdr, mtd.size, mtd.erasesize,
+ spare, reserve, bootsize);
+
+ if (!quiet) {
+ printf("Partition size = ");
+ print_size(mtd.size);
+ printf(", erase unit size = ");
+ print_size(mtd.erasesize);
+ printf(", %d transfer units\n", spare);
+ if (bootsize != 0) {
+ print_size(FROM_LE16(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize);
+ printf(" allocated for boot image\n");
+ }
+ printf("Reserved %d%%, formatted size = ", reserve);
+ print_size(FROM_LE32(hdr.FormattedSize));
+ printf("\n");
+ fflush(stdout);
+ }
+
+ if (interrogate) {
+ char str[3];
+ printf("This will destroy all data on the target device. "
+ "Confirm (y/n): ");
+ if (fgets(str, 3, stdin) == NULL)
+ return -1;
+ if ((strcmp(str, "y\n") != 0) && (strcmp(str, "Y\n") != 0))
+ return -1;
+ }
+
+ /* Create basic block allocation table for control blocks */
+ nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int)
+ + FROM_LE32(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize;
+ bam = malloc(nbam * sizeof(u_int));
+ for (i = 0; i < nbam; i++)
+ bam[i] = TO_LE32(BLOCK_CONTROL);
+
+ /* Erase partition */
+ if (!quiet) {
+ printf("Erasing all blocks...\n");
+ fflush(stdout);
+ }
+ erase.length = mtd.erasesize;
+ erase.start = mtd.erasesize * FROM_LE16(hdr.FirstPhysicalEUN);
+ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) {
+ if (ioctl(fd, MEMERASE, &erase) < 0) {
+ if (!quiet) {
+ putchar('\n');
+ fflush(stdout);
+ }
+ perror("block erase failed");
+ return -1;
+ }
+ erase.start += erase.length;
+ if (!quiet) {
+ if (mtd.size <= 0x800000) {
+ if (erase.start % 0x100000) {
+ if (!(erase.start % 0x20000)) putchar('-');
+ }
+ else putchar('+');
+ }
+ else {
+ if (erase.start % 0x800000) {
+ if (!(erase.start % 0x100000)) putchar('+');
+ }
+ else putchar('*');
+ }
+ fflush(stdout);
+ }
+ }
+ if (!quiet) putchar('\n');
+
+ /* Prepare erase units */
+ if (!quiet) {
+ printf("Writing erase unit headers...\n");
+ fflush(stdout);
+ }
+ lun = 0;
+ /* Distribute transfer units over the entire region */
+ step = (spare) ? (FROM_LE16(hdr.NumEraseUnits)/spare) : (FROM_LE16(hdr.NumEraseUnits)+1);
+ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) {
+ u_int ofs = (i + FROM_LE16(hdr.FirstPhysicalEUN)) << hdr.EraseUnitSize;
+ if (lseek(fd, ofs, SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ /* Is this a transfer unit? */
+ if (((i+1) % step) == 0)
+ hdr.LogicalEUN = TO_LE16(0xffff);
+ else {
+ hdr.LogicalEUN = TO_LE16(lun);
+ lun++;
+ }
+ if (write(fd, &hdr, sizeof(hdr)) == -1) {
+ perror("write failed");
+ break;
+ }
+ if (lseek(fd, ofs + FROM_LE32(hdr.BAMOffset), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (write(fd, bam, nbam * sizeof(u_int)) == -1) {
+ perror("write failed");
+ break;
+ }
+ }
+ if (i < FROM_LE16(hdr.NumEraseUnits))
+ return -1;
+ else
+ return 0;
+} /* format_partition */
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int quiet, interrogate, reserve;
+ int optch, errflg, fd, ret;
+ u_int spare, bootsize;
+ char *s;
+ extern char *optarg;
+ struct stat buf;
+
+ quiet = 0;
+ interrogate = 0;
+ spare = 1;
+ reserve = 5;
+ errflg = 0;
+ bootsize = 0;
+
+ while ((optch = getopt(argc, argv, "qir:s:b:")) != -1) {
+ switch (optch) {
+ case 'q':
+ quiet = 1; break;
+ case 'i':
+ interrogate = 1; break;
+ case 's':
+ spare = strtoul(optarg, NULL, 0); break;
+ case 'r':
+ reserve = strtoul(optarg, NULL, 0); break;
+ case 'b':
+ bootsize = strtoul(optarg, &s, 0);
+ if ((*s == 'k') || (*s == 'K'))
+ bootsize *= 1024;
+ break;
+ default:
+ errflg = 1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ fprintf(stderr, "usage: %s [-q] [-i] [-s spare-blocks]"
+ " [-r reserve-percent] [-b bootsize] device\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(argv[optind], &buf) != 0) {
+ perror("status check failed");
+ exit(EXIT_FAILURE);
+ }
+ if (!(buf.st_mode & S_IFCHR)) {
+ fprintf(stderr, "%s is not a character special device\n",
+ argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ fd = open(argv[optind], O_RDWR);
+ if (fd == -1) {
+ perror("open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = format_partition(fd, quiet, interrogate, spare, reserve,
+ bootsize);
+ if (!quiet) {
+ if (ret)
+ printf("format failed.\n");
+ else
+ printf("format successful.\n");
+ }
+ close(fd);
+
+ exit((ret) ? EXIT_FAILURE : EXIT_SUCCESS);
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/include/linux/jffs2.h b/mtd-utils-1.3.1/include/linux/jffs2.h
new file mode 100644
index 0000000..c2f684a
--- /dev/null
+++ b/mtd-utils-1.3.1/include/linux/jffs2.h
@@ -0,0 +1,218 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $
+ *
+ */
+
+#ifndef __LINUX_JFFS2_H__
+#define __LINUX_JFFS2_H__
+
+/* You must include something which defines the C99 uintXX_t types.
+ We don't do it from here because this file is used in too many
+ different environments. */
+
+#define JFFS2_SUPER_MAGIC 0x72b6
+
+/* Values we may expect to find in the 'magic' field */
+#define JFFS2_OLD_MAGIC_BITMASK 0x1984
+#define JFFS2_MAGIC_BITMASK 0x1985
+#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+#define JFFS2_EMPTY_BITMASK 0xffff
+#define JFFS2_DIRTY_BITMASK 0x0000
+
+/* Summary node MAGIC marker */
+#define JFFS2_SUM_MAGIC 0x02851885
+
+/* We only allow a single char for length, and 0xFF is empty flash so
+ we don't want it confused with a real length. Hence max 254.
+*/
+#define JFFS2_MAX_NAME_LEN 254
+
+/* How small can we sensibly write nodes? */
+#define JFFS2_MIN_DATA_LEN 128
+
+#define JFFS2_COMPR_NONE 0x00
+#define JFFS2_COMPR_ZERO 0x01
+#define JFFS2_COMPR_RTIME 0x02
+#define JFFS2_COMPR_RUBINMIPS 0x03
+#define JFFS2_COMPR_COPY 0x04
+#define JFFS2_COMPR_DYNRUBIN 0x05
+#define JFFS2_COMPR_ZLIB 0x06
+#define JFFS2_COMPR_LZO 0x07
+/* Compatibility flags. */
+#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
+#define JFFS2_NODE_ACCURATE 0x2000
+/* INCOMPAT: Fail to mount the filesystem */
+#define JFFS2_FEATURE_INCOMPAT 0xc000
+/* ROCOMPAT: Mount read-only */
+#define JFFS2_FEATURE_ROCOMPAT 0x8000
+/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
+/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
+
+#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
+
+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER 1 /* for "user." */
+#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION 0x0001
+
+// Maybe later...
+//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+
+#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at
+ mount time, don't wait for it to
+ happen later */
+#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific
+ compression type */
+
+
+/* These can go once we've made sure we've caught all uses without
+ byteswapping */
+
+typedef struct {
+ uint32_t v32;
+} __attribute__((packed)) jint32_t;
+
+typedef struct {
+ uint32_t m;
+} __attribute__((packed)) jmode_t;
+
+typedef struct {
+ uint16_t v16;
+} __attribute__((packed)) jint16_t;
+
+struct jffs2_unknown_node
+{
+ /* All start like this */
+ jint16_t magic;
+ jint16_t nodetype;
+ jint32_t totlen; /* So we can skip over nodes we don't grok */
+ jint32_t hdr_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_dirent
+{
+ jint16_t magic;
+ jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t pino;
+ jint32_t version;
+ jint32_t ino; /* == zero for unlink */
+ jint32_t mctime;
+ uint8_t nsize;
+ uint8_t type;
+ uint8_t unused[2];
+ jint32_t node_crc;
+ jint32_t name_crc;
+ uint8_t name[0];
+} __attribute__((packed));
+
+/* The JFFS2 raw inode structure: Used for storage on physical media. */
+/* The uid, gid, atime, mtime and ctime members could be longer, but
+ are left like this for space efficiency. If and when people decide
+ they really need them extended, it's simple enough to add support for
+ a new type of raw node.
+*/
+struct jffs2_raw_inode
+{
+ jint16_t magic; /* A constant magic number. */
+ jint16_t nodetype; /* == JFFS2_NODETYPE_INODE */
+ jint32_t totlen; /* Total length of this node (inc data, etc.) */
+ jint32_t hdr_crc;
+ jint32_t ino; /* Inode number. */
+ jint32_t version; /* Version number. */
+ jmode_t mode; /* The file's type or mode. */
+ jint16_t uid; /* The file's owner. */
+ jint16_t gid; /* The file's group. */
+ jint32_t isize; /* Total resultant size of this inode (used for truncations) */
+ jint32_t atime; /* Last access time. */
+ jint32_t mtime; /* Last modification time. */
+ jint32_t ctime; /* Change time. */
+ jint32_t offset; /* Where to begin to write. */
+ jint32_t csize; /* (Compressed) data size */
+ jint32_t dsize; /* Size of the node's data. (after decompression) */
+ uint8_t compr; /* Compression algorithm used */
+ uint8_t usercompr; /* Compression algorithm requested by the user */
+ jint16_t flags; /* See JFFS2_INO_FLAG_* */
+ jint32_t data_crc; /* CRC for the (compressed) data. */
+ jint32_t node_crc; /* CRC for the raw inode (excluding data) */
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xattr {
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t version;
+ uint8_t xprefix;
+ uint8_t name_len;
+ jint16_t value_len;
+ jint32_t data_crc;
+ jint32_t node_crc;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t ino; /* inode number */
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t xseqno; /* xref sequencial number */
+ jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_summary
+{
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_SUMMARY */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t sum_num; /* number of sum entries*/
+ jint32_t cln_mkr; /* clean marker size, 0 = no cleanmarker */
+ jint32_t padded; /* sum of the size of padding nodes */
+ jint32_t sum_crc; /* summary information crc */
+ jint32_t node_crc; /* node crc */
+ jint32_t sum[0]; /* inode summary info */
+} __attribute__((packed));
+
+union jffs2_node_union
+{
+ struct jffs2_raw_inode i;
+ struct jffs2_raw_dirent d;
+ struct jffs2_raw_xattr x;
+ struct jffs2_raw_xref r;
+ struct jffs2_raw_summary s;
+ struct jffs2_unknown_node u;
+};
+
+#endif /* __LINUX_JFFS2_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/ftl-user.h b/mtd-utils-1.3.1/include/mtd/ftl-user.h
new file mode 100644
index 0000000..53e94c2
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/ftl-user.h
@@ -0,0 +1,76 @@
+/*
+ * $Id: ftl.h,v 1.7 2005/11/07 11:14:54 gleixner Exp $
+ *
+ * Derived from (and probably identical to):
+ * ftl.h 1.7 1999/10/25 20:23:17
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 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 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.
+ */
+
+#ifndef __MTD_FTL_USER_H__
+#define __MTD_FTL_USER_H__
+
+typedef struct erase_unit_header_t {
+ u_int8_t LinkTargetTuple[5];
+ u_int8_t DataOrgTuple[10];
+ u_int8_t NumTransferUnits;
+ u_int32_t EraseCount;
+ u_int16_t LogicalEUN;
+ u_int8_t BlockSize;
+ u_int8_t EraseUnitSize;
+ u_int16_t FirstPhysicalEUN;
+ u_int16_t NumEraseUnits;
+ u_int32_t FormattedSize;
+ u_int32_t FirstVMAddress;
+ u_int16_t NumVMPages;
+ u_int8_t Flags;
+ u_int8_t Code;
+ u_int32_t SerialNumber;
+ u_int32_t AltEUHOffset;
+ u_int32_t BAMOffset;
+ u_int8_t Reserved[12];
+ u_int8_t EndTuple[2];
+} erase_unit_header_t;
+
+/* Flags in erase_unit_header_t */
+#define HIDDEN_AREA 0x01
+#define REVERSE_POLARITY 0x02
+#define DOUBLE_BAI 0x04
+
+/* Definitions for block allocation information */
+
+#define BLOCK_FREE(b) ((b) == 0xffffffff)
+#define BLOCK_DELETED(b) (((b) == 0) || ((b) == 0xfffffffe))
+
+#define BLOCK_TYPE(b) ((b) & 0x7f)
+#define BLOCK_ADDRESS(b) ((b) & ~0x7f)
+#define BLOCK_NUMBER(b) ((b) >> 9)
+#define BLOCK_CONTROL 0x30
+#define BLOCK_DATA 0x40
+#define BLOCK_REPLACEMENT 0x60
+#define BLOCK_BAD 0x70
+
+#endif /* __MTD_FTL_USER_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/inftl-user.h b/mtd-utils-1.3.1/include/mtd/inftl-user.h
new file mode 100644
index 0000000..9b1e252
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/inftl-user.h
@@ -0,0 +1,91 @@
+/*
+ * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $
+ *
+ * Parts of INFTL headers shared with userspace
+ *
+ */
+
+#ifndef __MTD_INFTL_USER_H__
+#define __MTD_INFTL_USER_H__
+
+#define OSAK_VERSION 0x5120
+#define PERCENTUSED 98
+
+#define SECTORSIZE 512
+
+/* Block Control Information */
+
+struct inftl_bci {
+ uint8_t ECCsig[6];
+ uint8_t Status;
+ uint8_t Status1;
+} __attribute__((packed));
+
+struct inftl_unithead1 {
+ uint16_t virtualUnitNo;
+ uint16_t prevUnitNo;
+ uint8_t ANAC;
+ uint8_t NACs;
+ uint8_t parityPerField;
+ uint8_t discarded;
+} __attribute__((packed));
+
+struct inftl_unithead2 {
+ uint8_t parityPerField;
+ uint8_t ANAC;
+ uint16_t prevUnitNo;
+ uint16_t virtualUnitNo;
+ uint8_t NACs;
+ uint8_t discarded;
+} __attribute__((packed));
+
+struct inftl_unittail {
+ uint8_t Reserved[4];
+ uint16_t EraseMark;
+ uint16_t EraseMark1;
+} __attribute__((packed));
+
+union inftl_uci {
+ struct inftl_unithead1 a;
+ struct inftl_unithead2 b;
+ struct inftl_unittail c;
+};
+
+struct inftl_oob {
+ struct inftl_bci b;
+ union inftl_uci u;
+};
+
+
+/* INFTL Media Header */
+
+struct INFTLPartition {
+ __u32 virtualUnits;
+ __u32 firstUnit;
+ __u32 lastUnit;
+ __u32 flags;
+ __u32 spareUnits;
+ __u32 Reserved0;
+ __u32 Reserved1;
+} __attribute__((packed));
+
+struct INFTLMediaHeader {
+ char bootRecordID[8];
+ __u32 NoOfBootImageBlocks;
+ __u32 NoOfBinaryPartitions;
+ __u32 NoOfBDTLPartitions;
+ __u32 BlockMultiplierBits;
+ __u32 FormatFlags;
+ __u32 OsakVersion;
+ __u32 PercentUsed;
+ struct INFTLPartition Partitions[4];
+} __attribute__((packed));
+
+/* Partition flag types */
+#define INFTL_BINARY 0x20000000
+#define INFTL_BDTL 0x40000000
+#define INFTL_LAST 0x80000000
+
+#endif /* __MTD_INFTL_USER_H__ */
+
+
diff --git a/mtd-utils-1.3.1/include/mtd/jffs2-user.h b/mtd-utils-1.3.1/include/mtd/jffs2-user.h
new file mode 100644
index 0000000..bc5d99a
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/jffs2-user.h
@@ -0,0 +1,82 @@
+/*
+ * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
+ *
+ * JFFS2 definitions for use in user space only
+ */
+
+#ifndef __JFFS2_USER_H__
+#define __JFFS2_USER_H__
+
+/* This file is blessed for inclusion by userspace */
+#include <linux/jffs2.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#undef cpu_to_je16
+#undef cpu_to_je32
+#undef cpu_to_jemode
+#undef je16_to_cpu
+#undef je32_to_cpu
+#undef jemode_to_cpu
+
+extern int target_endian;
+
+#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
+#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
+
+#define cpu_to_je16(x) ((jint16_t){t16(x)})
+#define cpu_to_je32(x) ((jint32_t){t32(x)})
+#define cpu_to_jemode(x) ((jmode_t){t32(x)})
+
+#define je16_to_cpu(x) (t16((x).v16))
+#define je32_to_cpu(x) (t32((x).v32))
+#define jemode_to_cpu(x) (t32((x).m))
+
+#define le16_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x))
+#define le32_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x))
+#define cpu_to_le16(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x))
+#define cpu_to_le32(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x))
+
+/* XATTR/POSIX-ACL related definition */
+/* Namespaces copied from xattr.h and posix_acl_xattr.h */
+#define XATTR_USER_PREFIX "user."
+#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
+#define XATTR_SECURITY_PREFIX "security."
+#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1)
+#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access"
+#define POSIX_ACL_XATTR_ACCESS_LEN (sizeof (POSIX_ACL_XATTR_ACCESS) - 1)
+#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default"
+#define POSIX_ACL_XATTR_DEFAULT_LEN (sizeof (POSIX_ACL_XATTR_DEFAULT) - 1)
+#define XATTR_TRUSTED_PREFIX "trusted."
+#define XATTR_TRUSTED_PREFIX_LEN (sizeof (XATTR_TRUSTED_PREFIX) - 1)
+
+struct jffs2_acl_entry {
+ jint16_t e_tag;
+ jint16_t e_perm;
+ jint32_t e_id;
+};
+
+struct jffs2_acl_entry_short {
+ jint16_t e_tag;
+ jint16_t e_perm;
+};
+
+struct jffs2_acl_header {
+ jint32_t a_version;
+};
+
+/* copied from include/linux/posix_acl_xattr.h */
+#define POSIX_ACL_XATTR_VERSION 0x0002
+
+struct posix_acl_xattr_entry {
+ uint16_t e_tag;
+ uint16_t e_perm;
+ uint32_t e_id;
+};
+
+struct posix_acl_xattr_header {
+ uint32_t a_version;
+ struct posix_acl_xattr_entry a_entries[0];
+};
+
+#endif /* __JFFS2_USER_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/mtd-abi.h b/mtd-utils-1.3.1/include/mtd/mtd-abi.h
new file mode 100644
index 0000000..86defe1
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/mtd-abi.h
@@ -0,0 +1,152 @@
+/*
+ * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $
+ *
+ * Portions of MTD ABI definition which are shared by kernel and user space
+ */
+
+#ifndef __MTD_ABI_H__
+#define __MTD_ABI_H__
+
+struct erase_info_user {
+ uint32_t start;
+ uint32_t length;
+};
+
+struct mtd_oob_buf {
+ uint32_t start;
+ uint32_t length;
+ unsigned char *ptr;
+};
+
+#define MTD_ABSENT 0
+#define MTD_RAM 1
+#define MTD_ROM 2
+#define MTD_NORFLASH 3
+#define MTD_NANDFLASH 4
+#define MTD_DATAFLASH 6
+#define MTD_UBIVOLUME 7
+
+#define MTD_WRITEABLE 0x400 /* Device is writeable */
+#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
+#define MTD_NO_ERASE 0x1000 /* No erase necessary */
+#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */
+
+// Some common devices / combinations of capabilities
+#define MTD_CAP_ROM 0
+#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
+#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
+#define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
+
+/* ECC byte placement */
+#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
+#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
+#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
+#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
+#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default
+
+/* OTP mode selection */
+#define MTD_OTP_OFF 0
+#define MTD_OTP_FACTORY 1
+#define MTD_OTP_USER 2
+
+struct mtd_info_user {
+ uint8_t type;
+ uint32_t flags;
+ uint32_t size; // Total size of the MTD
+ uint32_t erasesize;
+ uint32_t writesize;
+ uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
+ /* The below two fields are obsolete and broken, do not use them
+ * (TODO: remove at some point) */
+ uint32_t ecctype;
+ uint32_t eccsize;
+};
+
+struct region_info_user {
+ uint32_t offset; /* At which this region starts,
+ * from the beginning of the MTD */
+ uint32_t erasesize; /* For this region */
+ uint32_t numblocks; /* Number of blocks in this region */
+ uint32_t regionindex;
+};
+
+struct otp_info {
+ uint32_t start;
+ uint32_t length;
+ uint32_t locked;
+};
+
+#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
+#define MEMERASE _IOW('M', 2, struct erase_info_user)
+#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
+#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
+#define MEMLOCK _IOW('M', 5, struct erase_info_user)
+#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
+#define MEMGETREGIONCOUNT _IOR('M', 7, int)
+#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
+#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
+#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
+#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
+#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
+#define OTPSELECT _IOR('M', 13, int)
+#define OTPGETREGIONCOUNT _IOW('M', 14, int)
+#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
+#define OTPLOCK _IOR('M', 16, struct otp_info)
+#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
+#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
+#define MTDFILEMODE _IO('M', 19)
+
+/*
+ * Obsolete legacy interface. Keep it in order not to break userspace
+ * interfaces
+ */
+struct nand_oobinfo {
+ uint32_t useecc;
+ uint32_t eccbytes;
+ uint32_t oobfree[8][2];
+ uint32_t eccpos[32];
+};
+
+struct nand_oobfree {
+ uint32_t offset;
+ uint32_t length;
+};
+
+#define MTD_MAX_OOBFREE_ENTRIES 8
+/*
+ * ECC layout control structure. Exported to userspace for
+ * diagnosis and to allow creation of raw images
+ */
+struct nand_ecclayout {
+ uint32_t eccbytes;
+ uint32_t eccpos[64];
+ uint32_t oobavail;
+ struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
+};
+
+/**
+ * struct mtd_ecc_stats - error correction stats
+ *
+ * @corrected: number of corrected bits
+ * @failed: number of uncorrectable errors
+ * @badblocks: number of bad blocks in this partition
+ * @bbtblocks: number of blocks reserved for bad block tables
+ */
+struct mtd_ecc_stats {
+ uint32_t corrected;
+ uint32_t failed;
+ uint32_t badblocks;
+ uint32_t bbtblocks;
+};
+
+/*
+ * Read/write file modes for access to MTD
+ */
+enum mtd_file_modes {
+ MTD_MODE_NORMAL = MTD_OTP_OFF,
+ MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
+ MTD_MODE_OTP_USER = MTD_OTP_USER,
+ MTD_MODE_RAW,
+};
+
+#endif /* __MTD_ABI_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/mtd-user.h b/mtd-utils-1.3.1/include/mtd/mtd-user.h
new file mode 100644
index 0000000..713f34d
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/mtd-user.h
@@ -0,0 +1,21 @@
+/*
+ * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $
+ *
+ * MTD ABI header for use by user space only.
+ */
+
+#ifndef __MTD_USER_H__
+#define __MTD_USER_H__
+
+#include <stdint.h>
+
+/* This file is blessed for inclusion by userspace */
+#include <mtd/mtd-abi.h>
+
+typedef struct mtd_info_user mtd_info_t;
+typedef struct erase_info_user erase_info_t;
+typedef struct region_info_user region_info_t;
+typedef struct nand_oobinfo nand_oobinfo_t;
+typedef struct nand_ecclayout nand_ecclayout_t;
+
+#endif /* __MTD_USER_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/nftl-user.h b/mtd-utils-1.3.1/include/mtd/nftl-user.h
new file mode 100644
index 0000000..b2bca18
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/nftl-user.h
@@ -0,0 +1,76 @@
+/*
+ * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $
+ *
+ * Parts of NFTL headers shared with userspace
+ *
+ */
+
+#ifndef __MTD_NFTL_USER_H__
+#define __MTD_NFTL_USER_H__
+
+/* Block Control Information */
+
+struct nftl_bci {
+ unsigned char ECCSig[6];
+ uint8_t Status;
+ uint8_t Status1;
+}__attribute__((packed));
+
+/* Unit Control Information */
+
+struct nftl_uci0 {
+ uint16_t VirtUnitNum;
+ uint16_t ReplUnitNum;
+ uint16_t SpareVirtUnitNum;
+ uint16_t SpareReplUnitNum;
+} __attribute__((packed));
+
+struct nftl_uci1 {
+ uint32_t WearInfo;
+ uint16_t EraseMark;
+ uint16_t EraseMark1;
+} __attribute__((packed));
+
+struct nftl_uci2 {
+ uint16_t FoldMark;
+ uint16_t FoldMark1;
+ uint32_t unused;
+} __attribute__((packed));
+
+union nftl_uci {
+ struct nftl_uci0 a;
+ struct nftl_uci1 b;
+ struct nftl_uci2 c;
+};
+
+struct nftl_oob {
+ struct nftl_bci b;
+ union nftl_uci u;
+};
+
+/* NFTL Media Header */
+
+struct NFTLMediaHeader {
+ char DataOrgID[6];
+ uint16_t NumEraseUnits;
+ uint16_t FirstPhysicalEUN;
+ uint32_t FormattedSize;
+ unsigned char UnitSizeFactor;
+} __attribute__((packed));
+
+#define MAX_ERASE_ZONES (8192 - 512)
+
+#define ERASE_MARK 0x3c69
+#define SECTOR_FREE 0xff
+#define SECTOR_USED 0x55
+#define SECTOR_IGNORE 0x11
+#define SECTOR_DELETED 0x00
+
+#define FOLD_MARK_IN_PROGRESS 0x5555
+
+#define ZONE_GOOD 0xff
+#define ZONE_BAD_ORIGINAL 0
+#define ZONE_BAD_MARKED 7
+
+
+#endif /* __MTD_NFTL_USER_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/ubi-media.h b/mtd-utils-1.3.1/include/mtd/ubi-media.h
new file mode 100644
index 0000000..08bec3e
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/ubi-media.h
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ * Thomas Gleixner
+ * Frank Haverkamp
+ * Oliver Lohmann
+ * Andreas Arnez
+ */
+
+/*
+ * This file defines the layout of UBI headers and all the other UBI on-flash
+ * data structures.
+ */
+
+#ifndef __UBI_MEDIA_H__
+#define __UBI_MEDIA_H__
+
+#include <asm/byteorder.h>
+
+/* The version of UBI images supported by this implementation */
+#define UBI_VERSION 1
+
+/* The highest erase counter value supported by this implementation */
+#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF
+
+/* The initial CRC32 value used when calculating CRC checksums */
+#define UBI_CRC32_INIT 0xFFFFFFFFU
+
+/* Erase counter header magic number (ASCII "UBI#") */
+#define UBI_EC_HDR_MAGIC 0x55424923
+/* Volume identifier header magic number (ASCII "UBI!") */
+#define UBI_VID_HDR_MAGIC 0x55424921
+
+/*
+ * Volume type constants used in the volume identifier header.
+ *
+ * @UBI_VID_DYNAMIC: dynamic volume
+ * @UBI_VID_STATIC: static volume
+ */
+enum {
+ UBI_VID_DYNAMIC = 1,
+ UBI_VID_STATIC = 2
+};
+
+/*
+ * Volume flags used in the volume table record.
+ *
+ * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume
+ *
+ * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume
+ * table. UBI automatically re-sizes the volume which has this flag and makes
+ * the volume to be of largest possible size. This means that if after the
+ * initialization UBI finds out that there are available physical eraseblocks
+ * present on the device, it automatically appends all of them to the volume
+ * (the physical eraseblocks reserved for bad eraseblocks handling and other
+ * reserved physical eraseblocks are not taken). So, if there is a volume with
+ * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical
+ * eraseblocks will be zero after UBI is loaded, because all of them will be
+ * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared
+ * after the volume had been initialized.
+ *
+ * The auto-resize feature is useful for device production purposes. For
+ * example, different NAND flash chips may have different amount of initial bad
+ * eraseblocks, depending of particular chip instance. Manufacturers of NAND
+ * chips usually guarantee that the amount of initial bad eraseblocks does not
+ * exceed certain percent, e.g. 2%. When one creates an UBI image which will be
+ * flashed to the end devices in production, he does not know the exact amount
+ * of good physical eraseblocks the NAND chip on the device will have, but this
+ * number is required to calculate the volume sized and put them to the volume
+ * table of the UBI image. In this case, one of the volumes (e.g., the one
+ * which will store the root file system) is marked as "auto-resizable", and
+ * UBI will adjust its size on the first boot if needed.
+ *
+ * Note, first UBI reserves some amount of physical eraseblocks for bad
+ * eraseblock handling, and then re-sizes the volume, not vice-versa. This
+ * means that the pool of reserved physical eraseblocks will always be present.
+ */
+enum {
+ UBI_VTBL_AUTORESIZE_FLG = 0x01,
+};
+
+/*
+ * Compatibility constants used by internal volumes.
+ *
+ * @UBI_COMPAT_DELETE: delete this internal volume before anything is written
+ * to the flash
+ * @UBI_COMPAT_RO: attach this device in read-only mode
+ * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
+ * physical eraseblocks, don't allow the wear-leveling
+ * sub-system to move them
+ * @UBI_COMPAT_REJECT: reject this UBI image
+ */
+enum {
+ UBI_COMPAT_DELETE = 1,
+ UBI_COMPAT_RO = 2,
+ UBI_COMPAT_PRESERVE = 4,
+ UBI_COMPAT_REJECT = 5
+};
+
+/* Sizes of UBI headers */
+#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr)
+#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
+
+/* Sizes of UBI headers without the ending CRC */
+#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32))
+#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32))
+
+/**
+ * struct ubi_ec_hdr - UBI erase counter header.
+ * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
+ * @version: version of UBI implementation which is supposed to accept this
+ * UBI image
+ * @padding1: reserved for future, zeroes
+ * @ec: the erase counter
+ * @vid_hdr_offset: where the VID header starts
+ * @data_offset: where the user data start
+ * @image_seq: image sequence number
+ * @padding2: reserved for future, zeroes
+ * @hdr_crc: erase counter header CRC checksum
+ *
+ * The erase counter header takes 64 bytes and has a plenty of unused space for
+ * future usage. The unused fields are zeroed. The @version field is used to
+ * indicate the version of UBI implementation which is supposed to be able to
+ * work with this UBI image. If @version is greater than the current UBI
+ * version, the image is rejected. This may be useful in future if something
+ * is changed radically. This field is duplicated in the volume identifier
+ * header.
+ *
+ * The @vid_hdr_offset and @data_offset fields contain the offset of the the
+ * volume identifier header and user data, relative to the beginning of the
+ * physical eraseblock. These values have to be the same for all physical
+ * eraseblocks.
+ *
+ * The @image_seq field is used to validate a UBI image that has been prepared
+ * for a UBI device. The @image_seq value can be any value, but it must be the
+ * same on all eraseblocks. UBI will ensure that all new erase counter headers
+ * also contain this value, and will check the value when scanning at start-up.
+ * One way to make use of @image_seq is to increase its value by one every time
+ * an image is flashed over an existing image, then, if the flashing does not
+ * complete, UBI will detect the error when scanning.
+ */
+struct ubi_ec_hdr {
+ __be32 magic;
+ __u8 version;
+ __u8 padding1[3];
+ __be64 ec; /* Warning: the current limit is 31-bit anyway! */
+ __be32 vid_hdr_offset;
+ __be32 data_offset;
+ __be32 image_seq;
+ __u8 padding2[32];
+ __be32 hdr_crc;
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_vid_hdr - on-flash UBI volume identifier header.
+ * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
+ * @version: UBI implementation version which is supposed to accept this UBI
+ * image (%UBI_VERSION)
+ * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
+ * @copy_flag: if this logical eraseblock was copied from another physical
+ * eraseblock (for wear-leveling reasons)
+ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
+ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
+ * @vol_id: ID of this volume
+ * @lnum: logical eraseblock number
+ * @padding1: reserved for future, zeroes
+ * @data_size: how many bytes of data this logical eraseblock contains
+ * @used_ebs: total number of used logical eraseblocks in this volume
+ * @data_pad: how many bytes at the end of this physical eraseblock are not
+ * used
+ * @data_crc: CRC checksum of the data stored in this logical eraseblock
+ * @padding2: reserved for future, zeroes
+ * @sqnum: sequence number
+ * @padding3: reserved for future, zeroes
+ * @hdr_crc: volume identifier header CRC checksum
+ *
+ * The @sqnum is the value of the global sequence counter at the time when this
+ * VID header was created. The global sequence counter is incremented each time
+ * UBI writes a new VID header to the flash, i.e. when it maps a logical
+ * eraseblock to a new physical eraseblock. The global sequence counter is an
+ * unsigned 64-bit integer and we assume it never overflows. The @sqnum
+ * (sequence number) is used to distinguish between older and newer versions of
+ * logical eraseblocks.
+ *
+ * There are 2 situations when there may be more than one physical eraseblock
+ * corresponding to the same logical eraseblock, i.e., having the same @vol_id
+ * and @lnum values in the volume identifier header. Suppose we have a logical
+ * eraseblock L and it is mapped to the physical eraseblock P.
+ *
+ * 1. Because UBI may erase physical eraseblocks asynchronously, the following
+ * situation is possible: L is asynchronously erased, so P is scheduled for
+ * erasure, then L is written to,i.e. mapped to another physical eraseblock P1,
+ * so P1 is written to, then an unclean reboot happens. Result - there are 2
+ * physical eraseblocks P and P1 corresponding to the same logical eraseblock
+ * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the
+ * flash.
+ *
+ * 2. From time to time UBI moves logical eraseblocks to other physical
+ * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P
+ * to P1, and an unclean reboot happens before P is physically erased, there
+ * are two physical eraseblocks P and P1 corresponding to L and UBI has to
+ * select one of them when the flash is attached. The @sqnum field says which
+ * PEB is the original (obviously P will have lower @sqnum) and the copy. But
+ * it is not enough to select the physical eraseblock with the higher sequence
+ * number, because the unclean reboot could have happen in the middle of the
+ * copying process, so the data in P is corrupted. It is also not enough to
+ * just select the physical eraseblock with lower sequence number, because the
+ * data there may be old (consider a case if more data was added to P1 after
+ * the copying). Moreover, the unclean reboot may happen when the erasure of P
+ * was just started, so it result in unstable P, which is "mostly" OK, but
+ * still has unstable bits.
+ *
+ * UBI uses the @copy_flag field to indicate that this logical eraseblock is a
+ * copy. UBI also calculates data CRC when the data is moved and stores it at
+ * the @data_crc field of the copy (P1). So when UBI needs to pick one physical
+ * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
+ * examined. If it is cleared, the situation* is simple and the newer one is
+ * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
+ * checksum is correct, this physical eraseblock is selected (P1). Otherwise
+ * the older one (P) is selected.
+ *
+ * There are 2 sorts of volumes in UBI: user volumes and internal volumes.
+ * Internal volumes are not seen from outside and are used for various internal
+ * UBI purposes. In this implementation there is only one internal volume - the
+ * layout volume. Internal volumes are the main mechanism of UBI extensions.
+ * For example, in future one may introduce a journal internal volume. Internal
+ * volumes have their own reserved range of IDs.
+ *
+ * The @compat field is only used for internal volumes and contains the "degree
+ * of their compatibility". It is always zero for user volumes. This field
+ * provides a mechanism to introduce UBI extensions and to be still compatible
+ * with older UBI binaries. For example, if someone introduced a journal in
+ * future, he would probably use %UBI_COMPAT_DELETE compatibility for the
+ * journal volume. And in this case, older UBI binaries, which know nothing
+ * about the journal volume, would just delete this volume and work perfectly
+ * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image
+ * - it just ignores the Ext3fs journal.
+ *
+ * The @data_crc field contains the CRC checksum of the contents of the logical
+ * eraseblock if this is a static volume. In case of dynamic volumes, it does
+ * not contain the CRC checksum as a rule. The only exception is when the
+ * data of the physical eraseblock was moved by the wear-leveling sub-system,
+ * then the wear-leveling sub-system calculates the data CRC and stores it in
+ * the @data_crc field. And of course, the @copy_flag is %in this case.
+ *
+ * The @data_size field is used only for static volumes because UBI has to know
+ * how many bytes of data are stored in this eraseblock. For dynamic volumes,
+ * this field usually contains zero. The only exception is when the data of the
+ * physical eraseblock was moved to another physical eraseblock for
+ * wear-leveling reasons. In this case, UBI calculates CRC checksum of the
+ * contents and uses both @data_crc and @data_size fields. In this case, the
+ * @data_size field contains data size.
+ *
+ * The @used_ebs field is used only for static volumes and indicates how many
+ * eraseblocks the data of the volume takes. For dynamic volumes this field is
+ * not used and always contains zero.
+ *
+ * The @data_pad is calculated when volumes are created using the alignment
+ * parameter. So, effectively, the @data_pad field reduces the size of logical
+ * eraseblocks of this volume. This is very handy when one uses block-oriented
+ * software (say, cramfs) on top of the UBI volume.
+ */
+struct ubi_vid_hdr {
+ __be32 magic;
+ __u8 version;
+ __u8 vol_type;
+ __u8 copy_flag;
+ __u8 compat;
+ __be32 vol_id;
+ __be32 lnum;
+ __be32 leb_ver;
+ __be32 data_size;
+ __be32 used_ebs;
+ __be32 data_pad;
+ __be32 data_crc;
+ __u8 padding2[4];
+ __be64 sqnum;
+ __u8 padding3[12];
+ __be32 hdr_crc;
+} __attribute__ ((packed));
+
+/* Internal UBI volumes count */
+#define UBI_INT_VOL_COUNT 1
+
+/*
+ * Starting ID of internal volumes. There is reserved room for 4096 internal
+ * volumes.
+ */
+#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
+
+/* The layout volume contains the volume table */
+
+#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START
+#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC
+#define UBI_LAYOUT_VOLUME_ALIGN 1
+#define UBI_LAYOUT_VOLUME_EBS 2
+#define UBI_LAYOUT_VOLUME_NAME "layout volume"
+#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
+
+/* The maximum number of volumes per one UBI device */
+#define UBI_MAX_VOLUMES 128
+
+/* The maximum volume name length */
+#define UBI_VOL_NAME_MAX 127
+
+/* Size of the volume table record */
+#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record)
+
+/* Size of the volume table record without the ending CRC */
+#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32))
+
+/**
+ * struct ubi_vtbl_record - a record in the volume table.
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are unused at the end of the each physical
+ * eraseblock to satisfy the requested alignment
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @upd_marker: if volume update was started but not finished
+ * @name_len: volume name length
+ * @name: the volume name
+ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
+ * @padding: reserved, zeroes
+ * @crc: a CRC32 checksum of the record
+ *
+ * The volume table records are stored in the volume table, which is stored in
+ * the layout volume. The layout volume consists of 2 logical eraseblock, each
+ * of which contains a copy of the volume table (i.e., the volume table is
+ * duplicated). The volume table is an array of &struct ubi_vtbl_record
+ * objects indexed by the volume ID.
+ *
+ * If the size of the logical eraseblock is large enough to fit
+ * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES
+ * records. Otherwise, it contains as many records as it can fit (i.e., size of
+ * logical eraseblock divided by sizeof(struct ubi_vtbl_record)).
+ *
+ * The @upd_marker flag is used to implement volume update. It is set to %1
+ * before update and set to %0 after the update. So if the update operation was
+ * interrupted, UBI knows that the volume is corrupted.
+ *
+ * The @alignment field is specified when the volume is created and cannot be
+ * later changed. It may be useful, for example, when a block-oriented file
+ * system works on top of UBI. The @data_pad field is calculated using the
+ * logical eraseblock size and @alignment. The alignment must be multiple to the
+ * minimal flash I/O unit. If @alignment is 1, all the available space of
+ * the physical eraseblocks is used.
+ *
+ * Empty records contain all zeroes and the CRC checksum of those zeroes.
+ */
+struct ubi_vtbl_record {
+ __be32 reserved_pebs;
+ __be32 alignment;
+ __be32 data_pad;
+ __u8 vol_type;
+ __u8 upd_marker;
+ __be16 name_len;
+ __u8 name[UBI_VOL_NAME_MAX+1];
+ __u8 flags;
+ __u8 padding[23];
+ __be32 crc;
+} __attribute__ ((packed));
+
+#endif /* !__UBI_MEDIA_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd/ubi-user.h b/mtd-utils-1.3.1/include/mtd/ubi-user.h
new file mode 100644
index 0000000..296efae
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd/ubi-user.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_USER_H__
+#define __UBI_USER_H__
+
+/*
+ * UBI device creation (the same as MTD device attachment)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI
+ * control device. The caller has to properly fill and pass
+ * &struct ubi_attach_req object - UBI will attach the MTD device specified in
+ * the request and return the newly created UBI device number as the ioctl
+ * return value.
+ *
+ * UBI device deletion (the same as MTD device detachment)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI
+ * control device.
+ *
+ * UBI volume creation
+ * ~~~~~~~~~~~~~~~~~~~
+ *
+ * UBI volumes are created via the %UBI_IOCMKVOL ioctl command of UBI character
+ * device. A &struct ubi_mkvol_req object has to be properly filled and a
+ * pointer to it has to be passed to the ioctl.
+ *
+ * UBI volume deletion
+ * ~~~~~~~~~~~~~~~~~~~
+ *
+ * To delete a volume, the %UBI_IOCRMVOL ioctl command of the UBI character
+ * device should be used. A pointer to the 32-bit volume ID hast to be passed
+ * to the ioctl.
+ *
+ * UBI volume re-size
+ * ~~~~~~~~~~~~~~~~~~
+ *
+ * To re-size a volume, the %UBI_IOCRSVOL ioctl command of the UBI character
+ * device should be used. A &struct ubi_rsvol_req object has to be properly
+ * filled and a pointer to it has to be passed to the ioctl.
+ *
+ * UBI volumes re-name
+ * ~~~~~~~~~~~~~~~~~~~
+ *
+ * To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command
+ * of the UBI character device should be used. A &struct ubi_rnvol_req object
+ * has to be properly filled and a pointer to it has to be passed to the ioctl.
+ *
+ * UBI volume update
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * Volume update should be done via the %UBI_IOCVOLUP ioctl command of the
+ * corresponding UBI volume character device. A pointer to a 64-bit update
+ * size should be passed to the ioctl. After this, UBI expects user to write
+ * this number of bytes to the volume character device. The update is finished
+ * when the claimed number of bytes is passed. So, the volume update sequence
+ * is something like:
+ *
+ * fd = open("/dev/my_volume");
+ * ioctl(fd, UBI_IOCVOLUP, &image_size);
+ * write(fd, buf, image_size);
+ * close(fd);
+ *
+ * Logical eraseblock erase
+ * ~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the
+ * corresponding UBI volume character device should be used. This command
+ * unmaps the requested logical eraseblock, makes sure the corresponding
+ * physical eraseblock is successfully erased, and returns.
+ *
+ * Atomic logical eraseblock change
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH
+ * ioctl command of the corresponding UBI volume character device. A pointer to
+ * a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the
+ * user is expected to write the requested amount of bytes (similarly to what
+ * should be done in case of the "volume update" ioctl).
+ *
+ * Logical eraseblock map
+ * ~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP
+ * ioctl command should be used. A pointer to a &struct ubi_map_req object is
+ * expected to be passed. The ioctl maps the requested logical eraseblock to
+ * a physical eraseblock and returns. Only non-mapped logical eraseblocks can
+ * be mapped. If the logical eraseblock specified in the request is already
+ * mapped to a physical eraseblock, the ioctl fails and returns error.
+ *
+ * Logical eraseblock unmap
+ * ~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP
+ * ioctl command should be used. The ioctl unmaps the logical eraseblocks,
+ * schedules corresponding physical eraseblock for erasure, and returns. Unlike
+ * the "LEB erase" command, it does not wait for the physical eraseblock being
+ * erased. Note, the side effect of this is that if an unclean reboot happens
+ * after the unmap ioctl returns, you may find the LEB mapped again to the same
+ * physical eraseblock after the UBI is run again.
+ *
+ * Check if logical eraseblock is mapped
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To check if a logical eraseblock is mapped to a physical eraseblock, the
+ * %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is
+ * not mapped, and %1 if it is mapped.
+ *
+ * Set an UBI volume property
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be
+ * used. A pointer to a &struct ubi_set_prop_req object is expected to be
+ * passed. The object describes which property should be set, and to which value
+ * it should be set.
+ */
+
+/*
+ * When a new UBI volume or UBI device is created, users may either specify the
+ * volume/device number they want to create or to let UBI automatically assign
+ * the number using these constants.
+ */
+#define UBI_VOL_NUM_AUTO (-1)
+#define UBI_DEV_NUM_AUTO (-1)
+
+/* Maximum volume name length */
+#define UBI_MAX_VOLUME_NAME 127
+
+/* ioctl commands of UBI character devices */
+
+#define UBI_IOC_MAGIC 'o'
+
+/* Create an UBI volume */
+#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req)
+/* Remove an UBI volume */
+#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t)
+/* Re-size an UBI volume */
+#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
+/* Re-name volumes */
+#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
+
+/* ioctl commands of the UBI control character device */
+
+#define UBI_CTRL_IOC_MAGIC 'o'
+
+/* Attach an MTD device */
+#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req)
+/* Detach an MTD device */
+#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t)
+
+/* ioctl commands of UBI volume character devices */
+
+#define UBI_VOL_IOC_MAGIC 'O'
+
+/* Start UBI volume update */
+#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t)
+/* LEB erasure command, used for debugging, disabled by default */
+#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
+/* Atomic LEB change command */
+#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t)
+/* Map LEB command */
+#define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req)
+/* Unmap LEB command */
+#define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, int32_t)
+/* Check if LEB is mapped command */
+#define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, int32_t)
+/* Set an UBI volume property */
+#define UBI_IOCSETPROP _IOW(UBI_VOL_IOC_MAGIC, 6, struct ubi_set_prop_req)
+
+/* Maximum MTD device name length supported by UBI */
+#define MAX_UBI_MTD_NAME_LEN 127
+
+/* Maximum amount of UBI volumes that can be re-named at one go */
+#define UBI_MAX_RNVOL 32
+
+/*
+ * UBI data type hint constants.
+ *
+ * UBI_LONGTERM: long-term data
+ * UBI_SHORTTERM: short-term data
+ * UBI_UNKNOWN: data persistence is unknown
+ *
+ * These constants are used when data is written to UBI volumes in order to
+ * help the UBI wear-leveling unit to find more appropriate physical
+ * eraseblocks.
+ */
+enum {
+ UBI_LONGTERM = 1,
+ UBI_SHORTTERM = 2,
+ UBI_UNKNOWN = 3,
+};
+
+/*
+ * UBI volume type constants.
+ *
+ * @UBI_DYNAMIC_VOLUME: dynamic volume
+ * @UBI_STATIC_VOLUME: static volume
+ */
+enum {
+ UBI_DYNAMIC_VOLUME = 3,
+ UBI_STATIC_VOLUME = 4,
+};
+
+/*
+ * UBI set property ioctl constants
+ *
+ * @UBI_PROP_DIRECT_WRITE: allow / disallow user to directly write and
+ * erase individual eraseblocks on dynamic volumes
+ */
+enum {
+ UBI_PROP_DIRECT_WRITE = 1,
+};
+
+/**
+ * struct ubi_attach_req - attach MTD device request.
+ * @ubi_num: UBI device number to create
+ * @mtd_num: MTD device number to attach
+ * @vid_hdr_offset: VID header offset (use defaults if %0)
+ * @padding: reserved for future, not used, has to be zeroed
+ *
+ * This data structure is used to specify MTD device UBI has to attach and the
+ * parameters it has to use. The number which should be assigned to the new UBI
+ * device is passed in @ubi_num. UBI may automatically assign the number if
+ * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in
+ * @ubi_num.
+ *
+ * Most applications should pass %0 in @vid_hdr_offset to make UBI use default
+ * offset of the VID header within physical eraseblocks. The default offset is
+ * the next min. I/O unit after the EC header. For example, it will be offset
+ * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or
+ * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages.
+ *
+ * But in rare cases, if this optimizes things, the VID header may be placed to
+ * a different offset. For example, the boot-loader might do things faster if
+ * the VID header sits at the end of the first 2KiB NAND page with 4 sub-pages.
+ * As the boot-loader would not normally need to read EC headers (unless it
+ * needs UBI in RW mode), it might be faster to calculate ECC. This is weird
+ * example, but it real-life example. So, in this example, @vid_hdr_offer would
+ * be 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes
+ * aligned, which is OK, as UBI is clever enough to realize this is 4th
+ * sub-page of the first page and add needed padding.
+ */
+struct ubi_attach_req {
+ int32_t ubi_num;
+ int32_t mtd_num;
+ int32_t vid_hdr_offset;
+ int8_t padding[12];
+};
+
+/**
+ * struct ubi_mkvol_req - volume description data structure used in
+ * volume creation requests.
+ * @vol_id: volume number
+ * @alignment: volume alignment
+ * @bytes: volume size in bytes
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @padding1: reserved for future, not used, has to be zeroed
+ * @name_len: volume name length
+ * @padding2: reserved for future, not used, has to be zeroed
+ * @name: volume name
+ *
+ * This structure is used by user-space programs when creating new volumes. The
+ * @used_bytes field is only necessary when creating static volumes.
+ *
+ * The @alignment field specifies the required alignment of the volume logical
+ * eraseblock. This means, that the size of logical eraseblocks will be aligned
+ * to this number, i.e.,
+ * (UBI device logical eraseblock size) mod (@alignment) = 0.
+ *
+ * To put it differently, the logical eraseblock of this volume may be slightly
+ * shortened in order to make it properly aligned. The alignment has to be
+ * multiple of the flash minimal input/output unit, or %1 to utilize the entire
+ * available space of logical eraseblocks.
+ *
+ * The @alignment field may be useful, for example, when one wants to maintain
+ * a block device on top of an UBI volume. In this case, it is desirable to fit
+ * an integer number of blocks in logical eraseblocks of this UBI volume. With
+ * alignment it is possible to update this volume using plane UBI volume image
+ * BLOBs, without caring about how to properly align them.
+ */
+struct ubi_mkvol_req {
+ int32_t vol_id;
+ int32_t alignment;
+ int64_t bytes;
+ int8_t vol_type;
+ int8_t padding1;
+ int16_t name_len;
+ int8_t padding2[4];
+ char name[UBI_MAX_VOLUME_NAME + 1];
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_rsvol_req - a data structure used in volume re-size requests.
+ * @vol_id: ID of the volume to re-size
+ * @bytes: new size of the volume in bytes
+ *
+ * Re-sizing is possible for both dynamic and static volumes. But while dynamic
+ * volumes may be re-sized arbitrarily, static volumes cannot be made to be
+ * smaller than the number of bytes they bear. To arbitrarily shrink a static
+ * volume, it must be wiped out first (by means of volume update operation with
+ * zero number of bytes).
+ */
+struct ubi_rsvol_req {
+ int64_t bytes;
+ int32_t vol_id;
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_rnvol_req - volumes re-name request.
+ * @count: count of volumes to re-name
+ * @padding1: reserved for future, not used, has to be zeroed
+ * @vol_id: ID of the volume to re-name
+ * @name_len: name length
+ * @padding2: reserved for future, not used, has to be zeroed
+ * @name: new volume name
+ *
+ * UBI allows to re-name up to %32 volumes at one go. The count of volumes to
+ * re-name is specified in the @count field. The ID of the volumes to re-name
+ * and the new names are specified in the @vol_id and @name fields.
+ *
+ * The UBI volume re-name operation is atomic, which means that should power cut
+ * happen, the volumes will have either old name or new name. So the possible
+ * use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes
+ * A and B one may create temporary volumes %A1 and %B1 with the new contents,
+ * then atomically re-name A1->A and B1->B, in which case old %A and %B will
+ * be removed.
+ *
+ * If it is not desirable to remove old A and B, the re-name request has to
+ * contain 4 entries: A1->A, A->A1, B1->B, B->B1, in which case old A1 and B1
+ * become A and B, and old A and B will become A1 and B1.
+ *
+ * It is also OK to request: A1->A, A1->X, B1->B, B->Y, in which case old A1
+ * and B1 become A and B, and old A and B become X and Y.
+ *
+ * In other words, in case of re-naming into an existing volume name, the
+ * existing volume is removed, unless it is re-named as well at the same
+ * re-name request.
+ */
+struct ubi_rnvol_req {
+ int32_t count;
+ int8_t padding1[12];
+ struct {
+ int32_t vol_id;
+ int16_t name_len;
+ int8_t padding2[2];
+ char name[UBI_MAX_VOLUME_NAME + 1];
+ } ents[UBI_MAX_RNVOL];
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_leb_change_req - a data structure used in atomic LEB change
+ * requests.
+ * @lnum: logical eraseblock number to change
+ * @bytes: how many bytes will be written to the logical eraseblock
+ * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
+ * @padding: reserved for future, not used, has to be zeroed
+ */
+struct ubi_leb_change_req {
+ int32_t lnum;
+ int32_t bytes;
+ int8_t dtype;
+ int8_t padding[7];
+} __attribute__ ((packed));
+
+/**
+ * struct ubi_map_req - a data structure used in map LEB requests.
+ * @lnum: logical eraseblock number to unmap
+ * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
+ * @padding: reserved for future, not used, has to be zeroed
+ */
+struct ubi_map_req {
+ int32_t lnum;
+ int8_t dtype;
+ int8_t padding[3];
+} __attribute__ ((packed));
+
+
+/**
+ * struct ubi_set_prop_req - a data structure used to set an ubi volume
+ * property.
+ * @property: property to set (%UBI_PROP_DIRECT_WRITE)
+ * @padding: reserved for future, not used, has to be zeroed
+ * @value: value to set
+ */
+struct ubi_set_prop_req {
+ uint8_t property;
+ uint8_t padding[7];
+ uint64_t value;
+} __attribute__ ((packed));
+
+#endif /* __UBI_USER_H__ */
diff --git a/mtd-utils-1.3.1/include/mtd_swab.h b/mtd-utils-1.3.1/include/mtd_swab.h
new file mode 100644
index 0000000..c3340a6
--- /dev/null
+++ b/mtd-utils-1.3.1/include/mtd_swab.h
@@ -0,0 +1,51 @@
+#ifndef MTD_SWAB_H
+#define MTD_SWAB_H
+
+#include <endian.h>
+
+#define swab16(x) \
+ ((uint16_t)( \
+ (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \
+ (((uint16_t)(x) & (uint16_t)0xff00U) >> 8) ))
+#define swab32(x) \
+ ((uint32_t)( \
+ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#define swab64(x) \
+ ((uint64_t)( \
+ (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \
+ (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
+ (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
+ (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
+ (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
+ (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
+ (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
+ (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56) ))
+
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(x) ({ uint16_t _x = x; swab16(_x); })
+#define cpu_to_le32(x) ({ uint32_t _x = x; swab32(_x); })
+#define cpu_to_le64(x) ({ uint64_t _x = x; swab64(_x); })
+#define cpu_to_be16(x) (x)
+#define cpu_to_be32(x) (x)
+#define cpu_to_be64(x) (x)
+#else
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_le64(x) (x)
+#define cpu_to_be16(x) ({ uint16_t _x = x; swab16(_x); })
+#define cpu_to_be32(x) ({ uint32_t _x = x; swab32(_x); })
+#define cpu_to_be64(x) ({ uint64_t _x = x; swab64(_x); })
+#endif
+#define le16_to_cpu(x) cpu_to_le16(x)
+#define be16_to_cpu(x) cpu_to_be16(x)
+#define le32_to_cpu(x) cpu_to_le32(x)
+#define be32_to_cpu(x) cpu_to_be32(x)
+#define le64_to_cpu(x) cpu_to_le64(x)
+#define be64_to_cpu(x) cpu_to_be64(x)
+
+#endif
diff --git a/mtd-utils-1.3.1/jffs-dump.c b/mtd-utils-1.3.1/jffs-dump.c
new file mode 100644
index 0000000..31cdad2
--- /dev/null
+++ b/mtd-utils-1.3.1/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>
+
+#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
+
+#define min(x,y) (x) > (y) ? (y) : (x)
+
+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/mtd-utils-1.3.1/jffs2dump.c b/mtd-utils-1.3.1/jffs2dump.c
new file mode 100644
index 0000000..2802682
--- /dev/null
+++ b/mtd-utils-1.3.1/jffs2dump.c
@@ -0,0 +1,689 @@
+/*
+ * dumpjffs2.c
+ *
+ * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This utility dumps the contents of a binary JFFS2 image
+ *
+ *
+ * Bug/ToDo:
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <asm/types.h>
+#include <dirent.h>
+#include <mtd/jffs2-user.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <getopt.h>
+#include "crc32.h"
+#include "summary.h"
+
+#define PROGRAM "jffs2dump"
+#define VERSION "$Revision: 1.10 $"
+
+#define PAD(x) (((x)+3)&~3)
+
+/* For outputting a byte-swapped version of the input image. */
+#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)})
+#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)})
+
+#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; })
+#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)})
+
+// Global variables
+long imglen; // length of image
+char *data; // image data
+
+void display_help (void)
+{
+ printf("Usage: " PROGRAM " [OPTION]... INPUTFILE\n"
+ "Dump the contents of a binary JFFS2 image.\n\n"
+ " --help display this help and exit\n"
+ " --version display version information and exit\n"
+ " -b, --bigendian image is big endian\n"
+ " -l, --littleendian image is little endian\n"
+ " -c, --content dump image contents\n"
+ " -e, --endianconvert=FNAME convert image endianness, output to file fname\n"
+ " -r, --recalccrc recalc name and data crc on endian conversion\n"
+ " -d, --datsize=LEN size of data chunks, when oob data in binary image (NAND only)\n"
+ " -o, --oobsize=LEN size of oob data chunk in binary image (NAND only)\n"
+ " -v, --verbose verbose output\n");
+ exit(0);
+}
+
+void display_version (void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "Copyright (C) 2003 Thomas Gleixner \n"
+ "\n"
+ PROGRAM " comes with NO WARRANTY\n"
+ "to the extent permitted by law.\n"
+ "\n"
+ "You may redistribute copies of " PROGRAM "\n"
+ "under the terms of the GNU General Public Licence.\n"
+ "See the file `COPYING' for more information.\n");
+ exit(0);
+}
+
+// Option variables
+
+int verbose; // verbose output
+char *img; // filename of image
+int dumpcontent; // dump image content
+int target_endian = __BYTE_ORDER; // image endianess
+int convertendian; // convert endianness
+int recalccrc; // recalc name and data crc's on endian conversion
+char cnvfile[256]; // filename for conversion output
+int datsize; // Size of data chunks, when oob data is inside the binary image
+int oobsize; // Size of oob chunks, when oob data is inside the binary image
+
+void process_options (int argc, char *argv[])
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "blce:rd:o:v";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"bigendian", no_argument, 0, 'b'},
+ {"littleendian", no_argument, 0, 'l'},
+ {"content", no_argument, 0, 'c'},
+ {"endianconvert", required_argument, 0, 'e'},
+ {"datsize", required_argument, 0, 'd'},
+ {"oobsize", required_argument, 0, 'o'},
+ {"recalccrc", required_argument, 0, 'r'},
+ {"verbose", no_argument, 0, 'v'},
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ switch (option_index) {
+ case 0:
+ display_help();
+ break;
+ case 1:
+ display_version();
+ break;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'b':
+ target_endian = __BIG_ENDIAN;
+ break;
+ case 'l':
+ target_endian = __LITTLE_ENDIAN;
+ break;
+ case 'c':
+ dumpcontent = 1;
+ break;
+ case 'd':
+ datsize = atoi(optarg);
+ break;
+ case 'o':
+ oobsize = atoi(optarg);
+ break;
+ case 'e':
+ convertendian = 1;
+ strcpy (cnvfile, optarg);
+ break;
+ case 'r':
+ recalccrc = 1;
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1 || error)
+ display_help ();
+
+ img = argv[optind];
+}
+
+
+/*
+ * Dump image contents
+ */
+void do_dumpcontent (void)
+{
+ char *p = data, *p_free_begin;
+ union jffs2_node_union *node;
+ int empty = 0, dirty = 0;
+ char name[256];
+ uint32_t crc;
+ uint16_t type;
+ int bitchbitmask = 0;
+ int obsolete;
+
+ p_free_begin = NULL;
+ while ( p < (data + imglen)) {
+ node = (union jffs2_node_union*) p;
+
+ /* Skip empty space */
+ if (!p_free_begin)
+ p_free_begin = p;
+ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
+ p += 4;
+ empty += 4;
+ continue;
+ }
+
+ if (p != p_free_begin)
+ printf("Empty space found from 0x%08x to 0x%08x\n", p_free_begin-data, p-data);
+ p_free_begin = NULL;
+
+ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
+ if (!bitchbitmask++)
+ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic));
+ p += 4;
+ dirty += 4;
+ continue;
+ }
+ bitchbitmask = 0;
+
+ type = je16_to_cpu(node->u.nodetype);
+ if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
+ obsolete = 1;
+ type |= JFFS2_NODE_ACCURATE;
+ } else
+ obsolete = 0;
+ /* Set accurate for CRC check */
+ node->u.nodetype = cpu_to_je16(type);
+
+ crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
+ if (crc != je32_to_cpu (node->u.hdr_crc)) {
+ printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc);
+ p += 4;
+ dirty += 4;
+ continue;
+ }
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+
+ case JFFS2_NODETYPE_INODE:
+ printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
+ obsolete ? "Obsolete" : "",
+ p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
+ je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
+ je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));
+
+ crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8);
+ if (crc != je32_to_cpu (node->i.node_crc)) {
+ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc);
+ p += PAD(je32_to_cpu (node->i.totlen));
+ dirty += PAD(je32_to_cpu (node->i.totlen));;
+ continue;
+ }
+
+ crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize));
+ if (crc != je32_to_cpu(node->i.data_crc)) {
+ printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc);
+ p += PAD(je32_to_cpu (node->i.totlen));
+ dirty += PAD(je32_to_cpu (node->i.totlen));;
+ continue;
+ }
+
+ p += PAD(je32_to_cpu (node->i.totlen));
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ memcpy (name, node->d.name, node->d.nsize);
+ name [node->d.nsize] = 0x0;
+ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n",
+ obsolete ? "Obsolete" : "",
+ p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
+ je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
+ node->d.nsize, name);
+
+ crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8);
+ if (crc != je32_to_cpu (node->d.node_crc)) {
+ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc);
+ p += PAD(je32_to_cpu (node->d.totlen));
+ dirty += PAD(je32_to_cpu (node->d.totlen));;
+ continue;
+ }
+
+ crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize);
+ if (crc != je32_to_cpu(node->d.name_crc)) {
+ printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc);
+ p += PAD(je32_to_cpu (node->d.totlen));
+ dirty += PAD(je32_to_cpu (node->d.totlen));;
+ continue;
+ }
+
+ p += PAD(je32_to_cpu (node->d.totlen));
+ break;
+
+ case JFFS2_NODETYPE_SUMMARY: {
+
+ int i;
+ struct jffs2_sum_marker * sm;
+
+ printf("%8s Inode Sum node at 0x%08x, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n",
+ obsolete ? "Obsolete" : "",
+ p - data,
+ je32_to_cpu (node->s.totlen),
+ je32_to_cpu (node->s.sum_num),
+ je32_to_cpu (node->s.cln_mkr));
+
+ crc = crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8);
+ if (crc != je32_to_cpu (node->s.node_crc)) {
+ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc);
+ p += PAD(je32_to_cpu (node->s.totlen));
+ dirty += PAD(je32_to_cpu (node->s.totlen));;
+ continue;
+ }
+
+ crc = crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary));
+ if (crc != je32_to_cpu(node->s.sum_crc)) {
+ printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc);
+ p += PAD(je32_to_cpu (node->s.totlen));
+ dirty += PAD(je32_to_cpu (node->s.totlen));;
+ continue;
+ }
+
+ if (verbose) {
+ void *sp;
+ sp = (p + sizeof(struct jffs2_raw_summary));
+
+ for(i=0; i<je32_to_cpu(node->s.sum_num); i++) {
+
+ switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
+ case JFFS2_NODETYPE_INODE : {
+
+ struct jffs2_sum_inode_flash *spi;
+ spi = sp;
+
+ printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n",
+ "",
+ je32_to_cpu (spi->inode),
+ je32_to_cpu (spi->version),
+ je32_to_cpu (spi->offset),
+ je32_to_cpu (spi->totlen));
+
+ sp += JFFS2_SUMMARY_INODE_SIZE;
+ break;
+ }
+
+ case JFFS2_NODETYPE_DIRENT : {
+
+ char name[255];
+ struct jffs2_sum_dirent_flash *spd;
+ spd = sp;
+
+ memcpy(name,spd->name,spd->nsize);
+ name [spd->nsize] = 0x0;
+
+ printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n",
+ "",
+ je32_to_cpu (spd->offset),
+ je32_to_cpu (spd->totlen),
+ je32_to_cpu (spd->pino),
+ je32_to_cpu (spd->version),
+ je32_to_cpu (spd->ino),
+ spd->nsize,
+ name);
+
+ sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize);
+ break;
+ }
+
+ default :
+ printf("Unknown summary node!\n");
+ break;
+ }
+ }
+
+ sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker));
+
+ printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n",
+ "",
+ je32_to_cpu(sm->offset),
+ je32_to_cpu(sm->magic),
+ je32_to_cpu(node->s.padded));
+ }
+
+ p += PAD(je32_to_cpu (node->s.totlen));
+ break;
+ }
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ if (verbose) {
+ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - data, je32_to_cpu (node->u.totlen));
+ }
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case JFFS2_NODETYPE_PADDING:
+ if (verbose) {
+ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - data, je32_to_cpu (node->u.totlen));
+ }
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case 0xffff:
+ p += 4;
+ empty += 4;
+ break;
+
+ default:
+ if (verbose) {
+ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - data, je32_to_cpu (node->u.totlen));
+ }
+ p += PAD(je32_to_cpu (node->u.totlen));
+ dirty += PAD(je32_to_cpu (node->u.totlen));
+
+ }
+ }
+
+ if (verbose)
+ printf ("Empty space: %d, dirty space: %d\n", empty, dirty);
+}
+
+/*
+ * Convert endianess
+ */
+void do_endianconvert (void)
+{
+ char *p = data;
+ union jffs2_node_union *node, newnode;
+ int fd, len;
+ jint32_t mode;
+ uint32_t crc;
+
+ fd = open (cnvfile, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ fprintf (stderr, "Cannot open / create file: %s\n", cnvfile);
+ return;
+ }
+
+ while ( p < (data + imglen)) {
+ node = (union jffs2_node_union*) p;
+
+ /* Skip empty space */
+ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
+ write (fd, p, 4);
+ p += 4;
+ continue;
+ }
+
+ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
+ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic));
+ newnode.u.magic = cnv_e16 (node->u.magic);
+ newnode.u.nodetype = cnv_e16 (node->u.nodetype);
+ write (fd, &newnode, 4);
+ p += 4;
+ continue;
+ }
+
+ crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
+ if (crc != je32_to_cpu (node->u.hdr_crc)) {
+ printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc);
+ }
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+
+ case JFFS2_NODETYPE_INODE:
+
+ newnode.i.magic = cnv_e16 (node->i.magic);
+ newnode.i.nodetype = cnv_e16 (node->i.nodetype);
+ newnode.i.totlen = cnv_e32 (node->i.totlen);
+ newnode.i.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
+ newnode.i.ino = cnv_e32 (node->i.ino);
+ newnode.i.version = cnv_e32 (node->i.version);
+ mode.v32 = node->i.mode.m;
+ mode = cnv_e32 (mode);
+ newnode.i.mode.m = mode.v32;
+ newnode.i.uid = cnv_e16 (node->i.uid);
+ newnode.i.gid = cnv_e16 (node->i.gid);
+ newnode.i.isize = cnv_e32 (node->i.isize);
+ newnode.i.atime = cnv_e32 (node->i.atime);
+ newnode.i.mtime = cnv_e32 (node->i.mtime);
+ newnode.i.ctime = cnv_e32 (node->i.ctime);
+ newnode.i.offset = cnv_e32 (node->i.offset);
+ newnode.i.csize = cnv_e32 (node->i.csize);
+ newnode.i.dsize = cnv_e32 (node->i.dsize);
+ newnode.i.compr = node->i.compr;
+ newnode.i.usercompr = node->i.usercompr;
+ newnode.i.flags = cnv_e16 (node->i.flags);
+ if (recalccrc) {
+ len = je32_to_cpu(node->i.csize);
+ newnode.i.data_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_inode), len));
+ } else
+ newnode.i.data_crc = cnv_e32 (node->i.data_crc);
+
+ newnode.i.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8));
+
+ write (fd, &newnode, sizeof (struct jffs2_raw_inode));
+ write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode)));
+
+ p += PAD(je32_to_cpu (node->i.totlen));
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ newnode.d.magic = cnv_e16 (node->d.magic);
+ newnode.d.nodetype = cnv_e16 (node->d.nodetype);
+ newnode.d.totlen = cnv_e32 (node->d.totlen);
+ newnode.d.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
+ newnode.d.pino = cnv_e32 (node->d.pino);
+ newnode.d.version = cnv_e32 (node->d.version);
+ newnode.d.ino = cnv_e32 (node->d.ino);
+ newnode.d.mctime = cnv_e32 (node->d.mctime);
+ newnode.d.nsize = node->d.nsize;
+ newnode.d.type = node->d.type;
+ newnode.d.unused[0] = node->d.unused[0];
+ newnode.d.unused[1] = node->d.unused[1];
+ newnode.d.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8));
+ if (recalccrc)
+ newnode.d.name_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize));
+ else
+ newnode.d.name_crc = cnv_e32 (node->d.name_crc);
+
+ write (fd, &newnode, sizeof (struct jffs2_raw_dirent));
+ write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent)));
+ p += PAD(je32_to_cpu (node->d.totlen));
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ case JFFS2_NODETYPE_PADDING:
+ newnode.u.magic = cnv_e16 (node->u.magic);
+ newnode.u.nodetype = cnv_e16 (node->u.nodetype);
+ newnode.u.totlen = cnv_e32 (node->u.totlen);
+ newnode.u.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
+
+ write (fd, &newnode, sizeof (struct jffs2_unknown_node));
+ len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node));
+ if (len > 0)
+ write (fd, p + sizeof (struct jffs2_unknown_node), len);
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case JFFS2_NODETYPE_SUMMARY : {
+ struct jffs2_sum_marker *sm_ptr;
+ int i,sum_len;
+ int counter = 0;
+
+ newnode.s.magic = cnv_e16 (node->s.magic);
+ newnode.s.nodetype = cnv_e16 (node->s.nodetype);
+ newnode.s.totlen = cnv_e32 (node->s.totlen);
+ newnode.s.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
+ newnode.s.sum_num = cnv_e32 (node->s.sum_num);
+ newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr);
+ newnode.s.padded = cnv_e32 (node->s.padded);
+
+ newnode.s.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8));
+
+ // summary header
+ p += sizeof (struct jffs2_raw_summary);
+
+ // summary data
+ sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker);
+
+ for (i=0; i<je32_to_cpu (node->s.sum_num); i++) {
+ union jffs2_sum_flash *fl_ptr;
+
+ fl_ptr = (union jffs2_sum_flash *) p;
+
+ switch (je16_to_cpu (fl_ptr->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+
+ fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype);
+ fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode);
+ fl_ptr->i.version = cnv_e32 (fl_ptr->i.version);
+ fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset);
+ fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen);
+ p += sizeof (struct jffs2_sum_inode_flash);
+ counter += sizeof (struct jffs2_sum_inode_flash);
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype);
+ fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen);
+ fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset);
+ fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino);
+ fl_ptr->d.version = cnv_e32 (fl_ptr->d.version);
+ fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino);
+ p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize;
+ counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize;
+ break;
+
+ default :
+ printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype));
+ exit(EXIT_FAILURE);
+ break;
+ }
+
+ }
+
+ //pad
+ p += sum_len - counter;
+
+ // summary marker
+ sm_ptr = (struct jffs2_sum_marker *) p;
+ sm_ptr->offset = cnv_e32 (sm_ptr->offset);
+ sm_ptr->magic = cnv_e32 (sm_ptr->magic);
+ p += sizeof (struct jffs2_sum_marker);
+
+ // generate new crc on sum data
+ newnode.s.sum_crc = cpu_to_e32 ( crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary),
+ je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary)));
+
+ // write out new node header
+ write(fd, &newnode, sizeof (struct jffs2_raw_summary));
+ // write out new summary data
+ write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker));
+
+ break;
+ }
+
+ case 0xffff:
+ write (fd, p, 4);
+ p += 4;
+ break;
+
+ default:
+ printf ("Unknown node type: 0x%04x at 0x%08x, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen));
+ p += PAD(je32_to_cpu (node->u.totlen));
+
+ }
+ }
+
+ close (fd);
+
+}
+
+/*
+ * Main program
+ */
+int main(int argc, char **argv)
+{
+ int fd;
+
+ process_options(argc, argv);
+
+ /* Open the input file */
+ if ((fd = open(img, O_RDONLY)) == -1) {
+ perror("open input file");
+ exit(1);
+ }
+
+ // get image length
+ imglen = lseek(fd, 0, SEEK_END);
+ lseek (fd, 0, SEEK_SET);
+
+ data = malloc (imglen);
+ if (!data) {
+ perror("out of memory");
+ close (fd);
+ exit(1);
+ }
+
+ if (datsize && oobsize) {
+ int idx = 0;
+ long len = imglen;
+ uint8_t oob[oobsize];
+ printf ("Peeling data out of combined data/oob image\n");
+ while (len) {
+ // read image data
+ read (fd, &data[idx], datsize);
+ read (fd, oob, oobsize);
+ idx += datsize;
+ imglen -= oobsize;
+ len -= datsize + oobsize;
+ }
+
+ } else {
+ // read image data
+ read (fd, data, imglen);
+ }
+ // Close the input file
+ close(fd);
+
+ if (dumpcontent)
+ do_dumpcontent ();
+
+ if (convertendian)
+ do_endianconvert ();
+
+ // free memory
+ free (data);
+
+ // Return happy
+ exit (0);
+}
diff --git a/mtd-utils-1.3.1/jffs2reader.c b/mtd-utils-1.3.1/jffs2reader.c
new file mode 100644
index 0000000..cde1d06
--- /dev/null
+++ b/mtd-utils-1.3.1/jffs2reader.c
@@ -0,0 +1,939 @@
+/* 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.
+ */
+
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <zlib.h>
+#include <linux/jffs2.h>
+
+#define SCRATCH_SIZE (5*1024*1024)
+
+#ifndef MAJOR
+/* FIXME: I am using illicit insider knowledge of
+ * kernel major/minor representation... */
+#define MAJOR(dev) (((dev)>>8)&0xff)
+#define MINOR(dev) ((dev)&0xff)
+#endif
+
+
+#define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0)
+#define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0)
+
+struct dir {
+ struct dir *next;
+ uint8_t type;
+ uint8_t nsize;
+ uint32_t ino;
+ char name[256];
+};
+
+void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *);
+struct dir *putdir(struct dir *, struct jffs2_raw_dirent *);
+void printdir(char *o, size_t size, struct dir *d, char *path,
+ int recurse);
+void freedir(struct dir *);
+
+struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino);
+struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t,
+ char *, uint8_t);
+struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t);
+struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t);
+
+struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *,
+ uint32_t *, int);
+struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *,
+ uint32_t *);
+
+void lsdir(char *, size_t, char *, int);
+void catfile(char *, size_t, char *, char *, size_t, size_t *);
+
+int main(int, char **);
+
+/* writes file node into buffer, to the proper position. */
+/* reading all valid nodes in version order reconstructs the file. */
+
+/*
+ b - buffer
+ bsize - buffer size
+ rsize - result size
+ n - node
+ */
+
+void putblock(char *b, size_t bsize, size_t * rsize,
+ struct jffs2_raw_inode *n)
+{
+ uLongf dlen = n->dsize;
+
+ if (n->isize > bsize || (n->offset + dlen) > bsize) {
+ fprintf(stderr, "File does not fit into buffer!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (*rsize < n->isize)
+ bzero(b + *rsize, n->isize - *rsize);
+
+ switch (n->compr) {
+ case JFFS2_COMPR_ZLIB:
+ uncompress((Bytef *) b + n->offset, &dlen,
+ (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode),
+ (uLongf) n->csize);
+ break;
+
+ case JFFS2_COMPR_NONE:
+ memcpy(b + n->offset,
+ ((char *) n) + sizeof(struct jffs2_raw_inode), dlen);
+ break;
+
+ case JFFS2_COMPR_ZERO:
+ bzero(b + n->offset, dlen);
+ break;
+
+ /* [DYN]RUBIN support required! */
+
+ default:
+ fprintf(stderr, "Unsupported compression method!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ *rsize = n->isize;
+}
+
+/* adds/removes directory node into dir struct. */
+/* reading all valid nodes in version order reconstructs the directory. */
+
+/*
+ dd - directory struct being processed
+ n - node
+
+ return value: directory struct value replacing dd
+ */
+
+struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n)
+{
+ struct dir *o, *d, *p;
+
+ o = dd;
+
+ if (n->ino) {
+ if (dd == NULL) {
+ d = malloc(sizeof(struct dir));
+ d->type = n->type;
+ memcpy(d->name, n->name, n->nsize);
+ d->nsize = n->nsize;
+ d->ino = n->ino;
+ d->next = NULL;
+
+ return d;
+ }
+
+ while (1) {
+ if (n->nsize == dd->nsize &&
+ !memcmp(n->name, dd->name, n->nsize)) {
+ dd->type = n->type;
+ dd->ino = n->ino;
+
+ return o;
+ }
+
+ if (dd->next == NULL) {
+ dd->next = malloc(sizeof(struct dir));
+ dd->next->type = n->type;
+ memcpy(dd->next->name, n->name, n->nsize);
+ dd->next->nsize = n->nsize;
+ dd->next->ino = n->ino;
+ dd->next->next = NULL;
+
+ return o;
+ }
+
+ dd = dd->next;
+ }
+ } else {
+ if (dd == NULL)
+ return NULL;
+
+ if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) {
+ d = dd->next;
+ free(dd);
+ return d;
+ }
+
+ while (1) {
+ p = dd;
+ dd = dd->next;
+
+ if (dd == NULL)
+ return o;
+
+ if (n->nsize == dd->nsize &&
+ !memcmp(n->name, dd->name, n->nsize)) {
+ p->next = dd->next;
+ free(dd);
+
+ return o;
+ }
+ }
+ }
+}
+
+
+#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
+#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
+
+/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */
+static const mode_t SBIT[] = {
+ 0, 0, S_ISUID,
+ 0, 0, S_ISGID,
+ 0, 0, S_ISVTX
+};
+
+/* The 9 mode bits to test */
+static const mode_t MBIT[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP,
+ S_IROTH, S_IWOTH, S_IXOTH
+};
+
+static const char MODE1[] = "rwxrwxrwx";
+static const char MODE0[] = "---------";
+static const char SMODE1[] = "..s..s..t";
+static const char SMODE0[] = "..S..S..T";
+
+/*
+ * Return the standard ls-like mode string from a file mode.
+ * This is static and so is overwritten on each call.
+ */
+const char *mode_string(int mode)
+{
+ static char buf[12];
+
+ int i;
+
+ buf[0] = TYPECHAR(mode);
+ for (i = 0; i < 9; i++) {
+ if (mode & SBIT[i])
+ buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i];
+ else
+ buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i];
+ }
+ return buf;
+}
+
+/* prints contents of directory structure */
+
+/*
+ d - dir struct
+ */
+
+void printdir(char *o, size_t size, struct dir *d, char *path, int recurse)
+{
+ char m;
+ char *filetime;
+ time_t age;
+ struct jffs2_raw_inode *ri;
+
+ if (!path)
+ return;
+ if (strlen(path) == 1 && *path == '/')
+ path++;
+
+ while (d != NULL) {
+ switch (d->type) {
+ case DT_REG:
+ m = ' ';
+ break;
+
+ case DT_FIFO:
+ m = '|';
+ break;
+
+ case DT_CHR:
+ m = ' ';
+ break;
+
+ case DT_BLK:
+ m = ' ';
+ break;
+
+ case DT_DIR:
+ m = '/';
+ break;
+
+ case DT_LNK:
+ m = ' ';
+ break;
+
+ case DT_SOCK:
+ m = '=';
+ break;
+
+ default:
+ m = '?';
+ }
+ ri = find_raw_inode(o, size, d->ino);
+ if (!ri) {
+ fprintf(stderr, "bug: raw_inode missing!\n");
+ d = d->next;
+ continue;
+ }
+
+ filetime = ctime((const time_t *) &(ri->ctime));
+ age = time(NULL) - ri->ctime;
+ printf("%s %-4d %-8d %-8d ", mode_string(ri->mode),
+ 1, ri->uid, ri->gid);
+ if ( d->type==DT_BLK || d->type==DT_CHR ) {
+ dev_t rdev;
+ size_t devsize;
+ putblock((char*)&rdev, sizeof(rdev), &devsize, ri);
+ printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev));
+ } else {
+ printf("%9ld ", (long)ri->dsize);
+ }
+ d->name[d->nsize]='\0';
+ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
+ /* hh:mm if less than 6 months old */
+ printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m);
+ } else {
+ printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m);
+ }
+ if (d->type == DT_LNK) {
+ char symbuf[1024];
+ size_t symsize;
+ putblock(symbuf, sizeof(symbuf), &symsize, ri);
+ symbuf[symsize] = 0;
+ printf(" -> %s", symbuf);
+ }
+ printf("\n");
+
+ if (d->type == DT_DIR && recurse) {
+ char *tmp;
+ tmp = malloc(BUFSIZ);
+ if (!tmp) {
+ fprintf(stderr, "memory exhausted\n");
+ exit(EXIT_FAILURE);
+ }
+ sprintf(tmp, "%s/%s", path, d->name);
+ lsdir(o, size, tmp, recurse); /* Go recursive */
+ free(tmp);
+ }
+
+ d = d->next;
+ }
+}
+
+/* frees memory used by directory structure */
+
+/*
+ d - dir struct
+ */
+
+void freedir(struct dir *d)
+{
+ struct dir *t;
+
+ while (d != NULL) {
+ t = d->next;
+ free(d);
+ d = t;
+ }
+}
+
+/* collects directory/file nodes in version order. */
+
+/*
+ f - file flag.
+ if zero, collect file, compare ino to inode
+ otherwise, collect directory, compare ino to parent inode
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - inode to compare against. see f.
+
+ return value: a jffs2_raw_inode that corresponds the the specified
+ inode, or NULL
+ */
+
+struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+ union jffs2_node_union *lr; /* last block position */
+ union jffs2_node_union *mp = NULL; /* minimum position */
+
+ uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
+
+ vmin = 0; /* next to read */
+ vmax = ~((uint32_t) 0); /* last to read */
+ vmint = ~((uint32_t) 0);
+ vmaxt = 0; /* found maximum */
+ vcur = 0; /* XXX what is smallest version number used? */
+ /* too low version number can easily result excess log rereading */
+
+ n = (union jffs2_node_union *) o;
+ lr = n;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_INODE &&
+ n->i.ino == ino && (v = n->i.version) > vcur) {
+ /* XXX crc check */
+
+ if (vmaxt < v)
+ vmaxt = v;
+ if (vmint > v) {
+ vmint = v;
+ mp = n;
+ }
+
+ if (v == (vcur + 1))
+ return (&(n->i));
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
+
+ if (lr == n) { /* whole loop since last read */
+ vmax = vmaxt;
+ vmin = vmint;
+ vmint = ~((uint32_t) 0);
+
+ if (vcur < vmax && vcur < vmin)
+ return (&(mp->i));
+ }
+ } while (vcur < vmax);
+
+ return NULL;
+}
+
+/* collects dir struct for selected inode */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ pino - inode of the specified directory
+ d - input directory structure
+
+ return value: result directory structure, replaces d.
+ */
+
+struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+ union jffs2_node_union *lr; /* last block position */
+ union jffs2_node_union *mp = NULL; /* minimum position */
+
+ uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
+
+ vmin = 0; /* next to read */
+ vmax = ~((uint32_t) 0); /* last to read */
+ vmint = ~((uint32_t) 0);
+ vmaxt = 0; /* found maximum */
+ vcur = 0; /* XXX what is smallest version number used? */
+ /* too low version number can easily result excess log rereading */
+
+ n = (union jffs2_node_union *) o;
+ lr = n;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
+ n->d.pino == ino && (v = n->d.version) > vcur) {
+ /* XXX crc check */
+
+ if (vmaxt < v)
+ vmaxt = v;
+ if (vmint > v) {
+ vmint = v;
+ mp = n;
+ }
+
+ if (v == (vcur + 1)) {
+ d = putdir(d, &(n->d));
+
+ lr = n;
+ vcur++;
+ vmint = ~((uint32_t) 0);
+ }
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
+
+ if (lr == n) { /* whole loop since last read */
+ vmax = vmaxt;
+ vmin = vmint;
+ vmint = ~((uint32_t) 0);
+
+ if (vcur < vmax && vcur < vmin) {
+ d = putdir(d, &(mp->d));
+
+ lr = n =
+ (union jffs2_node_union *) (((char *) mp) +
+ ((mp->u.totlen + 3) & ~3));
+
+ vcur = vmin;
+ }
+ }
+ } while (vcur < vmax);
+
+ return d;
+}
+
+
+
+/* resolve dirent based on criteria */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - if zero, ignore,
+ otherwise compare against dirent inode
+ pino - if zero, ingore,
+ otherwise compare against parent inode
+ and use name and nsize as extra criteria
+ name - name of wanted dirent, used if pino!=0
+ nsize - length of name of wanted dirent, used if pino!=0
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+ */
+
+struct jffs2_raw_dirent *resolvedirent(char *o, size_t size,
+ uint32_t ino, uint32_t pino,
+ char *name, uint8_t nsize)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+
+ struct jffs2_raw_dirent *dd = NULL;
+
+ uint32_t vmax, v;
+
+ if (!pino && ino <= 1)
+ return dd;
+
+ vmax = 0;
+
+ n = (union jffs2_node_union *) o;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
+ (!ino || n->d.ino == ino) &&
+ (v = n->d.version) > vmax &&
+ (!pino || (n->d.pino == pino &&
+ nsize == n->d.nsize &&
+ !memcmp(name, n->d.name, nsize)))) {
+ /* XXX crc check */
+
+ if (vmax < v) {
+ vmax = v;
+ dd = &(n->d);
+ }
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ return dd;
+ } while (1);
+}
+
+/* resolve name under certain parent inode to dirent */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ pino - requested parent inode
+ name - name of wanted dirent
+ nsize - length of name of wanted dirent
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+ */
+
+struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino,
+ char *name, uint8_t nsize)
+{
+ return resolvedirent(o, size, 0, pino, name, nsize);
+}
+
+/* resolve inode to dirent */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - compare against dirent inode
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+ */
+
+struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino)
+{
+ return resolvedirent(o, size, ino, 0, NULL, 0);
+}
+
+/* resolve slash-style path into dirent and inode.
+ slash as first byte marks absolute path (root=inode 1).
+ . and .. are resolved properly, and symlinks are followed.
+ */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - root inode, used if path is relative
+ p - path to be resolved
+ inos - result inode, zero if failure
+ recc - recursion count, to detect symlink loops
+
+ return value: pointer to dirent struct in file system image.
+ note that root directory doesn't have dirent struct
+ (return value is NULL), but it has inode (*inos=1)
+ */
+
+struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino,
+ char *p, uint32_t * inos, int recc)
+{
+ struct jffs2_raw_dirent *dir = NULL;
+
+ int d = 1;
+ uint32_t tino;
+
+ char *next;
+
+ char *path, *pp;
+
+ char symbuf[1024];
+ size_t symsize;
+
+ if (recc > 16) {
+ /* probably symlink loop */
+ *inos = 0;
+ return NULL;
+ }
+
+ pp = path = strdup(p);
+
+ if (*path == '/') {
+ path++;
+ ino = 1;
+ }
+
+ if (ino > 1) {
+ dir = resolveinode(o, size, ino);
+
+ ino = DIRENT_INO(dir);
+ }
+
+ next = path - 1;
+
+ while (ino && next != NULL && next[1] != 0 && d) {
+ path = next + 1;
+ next = strchr(path, '/');
+
+ if (next != NULL)
+ *next = 0;
+
+ if (*path == '.' && path[1] == 0)
+ continue;
+ if (*path == '.' && path[1] == '.' && path[2] == 0) {
+ if (DIRENT_PINO(dir) == 1) {
+ ino = 1;
+ dir = NULL;
+ } else {
+ dir = resolveinode(o, size, DIRENT_PINO(dir));
+ ino = DIRENT_INO(dir);
+ }
+
+ continue;
+ }
+
+ dir = resolvename(o, size, ino, path, (uint8_t) strlen(path));
+
+ if (DIRENT_INO(dir) == 0 ||
+ (next != NULL &&
+ !(dir->type == DT_DIR || dir->type == DT_LNK))) {
+ free(pp);
+
+ *inos = 0;
+
+ return NULL;
+ }
+
+ if (dir->type == DT_LNK) {
+ struct jffs2_raw_inode *ri;
+ ri = find_raw_inode(o, size, DIRENT_INO(dir));
+ putblock(symbuf, sizeof(symbuf), &symsize, ri);
+ symbuf[symsize] = 0;
+
+ tino = ino;
+ ino = 0;
+
+ dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc);
+
+ if (dir != NULL && next != NULL &&
+ !(dir->type == DT_DIR || dir->type == DT_LNK)) {
+ free(pp);
+
+ *inos = 0;
+ return NULL;
+ }
+ }
+ if (dir != NULL)
+ ino = DIRENT_INO(dir);
+ }
+
+ free(pp);
+
+ *inos = ino;
+
+ return dir;
+}
+
+/* resolve slash-style path into dirent and inode.
+ slash as first byte marks absolute path (root=inode 1).
+ . and .. are resolved properly, and symlinks are followed.
+ */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - root inode, used if path is relative
+ p - path to be resolved
+ inos - result inode, zero if failure
+
+ return value: pointer to dirent struct in file system image.
+ note that root directory doesn't have dirent struct
+ (return value is NULL), but it has inode (*inos=1)
+ */
+
+struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino,
+ char *p, uint32_t * inos)
+{
+ return resolvepath0(o, size, ino, p, inos, 0);
+}
+
+/* lists files on directory specified by path */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ p - path to be resolved
+ */
+
+void lsdir(char *o, size_t size, char *path, int recurse)
+{
+ struct jffs2_raw_dirent *dd;
+ struct dir *d = NULL;
+
+ uint32_t ino;
+
+ dd = resolvepath(o, size, 1, path, &ino);
+
+ if (ino == 0 ||
+ (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) {
+ fprintf(stderr, "jffs2reader: %s: No such file or directory\n",
+ path);
+ exit(EXIT_FAILURE);
+ }
+
+ d = collectdir(o, size, ino, d);
+ printdir(o, size, d, path, recurse);
+ freedir(d);
+}
+
+/* writes file specified by path to the buffer */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ p - path to be resolved
+ b - file buffer
+ bsize - file buffer size
+ rsize - file result size
+ */
+
+void catfile(char *o, size_t size, char *path, char *b, size_t bsize,
+ size_t * rsize)
+{
+ struct jffs2_raw_dirent *dd;
+ struct jffs2_raw_inode *ri;
+ uint32_t ino;
+
+ dd = resolvepath(o, size, 1, path, &ino);
+
+ if (ino == 0) {
+ fprintf(stderr, "%s: No such file or directory\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (dd == NULL || dd->type != DT_REG) {
+ fprintf(stderr, "%s: Not a regular file\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ ri = find_raw_inode(o, size, ino);
+ putblock(b, bsize, rsize, ri);
+
+ write(1, b, *rsize);
+}
+
+/* usage example */
+
+int main(int argc, char **argv)
+{
+ int fd, opt, recurse = 0;
+ struct stat st;
+
+ char *scratch, *dir = NULL, *file = NULL;
+ size_t ssize = 0;
+
+ char *buf;
+
+ while ((opt = getopt(argc, argv, "rd:f:")) > 0) {
+ switch (opt) {
+ case 'd':
+ dir = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'r':
+ recurse++;
+ break;
+ default:
+ fprintf(stderr,
+ "Usage: jffs2reader <image> [-d|-f] < path > \n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fd = open(argv[optind], O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(2);
+ }
+
+ if (fstat(fd, &st)) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(3);
+ }
+
+ buf = malloc((size_t) st.st_size);
+ if (buf == NULL) {
+ fprintf(stderr, "%s: memory exhausted\n", argv[optind]);
+ exit(4);
+ }
+
+ if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(5);
+ }
+
+ if (dir)
+ lsdir(buf, st.st_size, dir, recurse);
+
+ if (file) {
+ scratch = malloc(SCRATCH_SIZE);
+ if (scratch == NULL) {
+ fprintf(stderr, "%s: memory exhausted\n", argv[optind]);
+ exit(6);
+ }
+
+ catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize);
+ free(scratch);
+ }
+
+ if (!dir && !file)
+ lsdir(buf, st.st_size, "/", 1);
+
+
+ free(buf);
+ exit(EXIT_SUCCESS);
+}
diff --git a/mtd-utils-1.3.1/load_nandsim.sh b/mtd-utils-1.3.1/load_nandsim.sh
new file mode 100755
index 0000000..bda3c79
--- /dev/null
+++ b/mtd-utils-1.3.1/load_nandsim.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+
+#
+# This script inserts NAND simulator module to emulate NAND flash of specified
+# size.
+#
+# Author: Artem Bityutskiy
+#
+
+# Check if nandsim module is loaded
+function nandsim_loaded()
+{
+ local NANDSIM=`lsmod | grep nandsim`
+ if [ -n "$NANDSIM" ]; then
+ return 1
+ fi
+ return 0
+}
+
+nandsim_loaded
+if (( $? != 0 )); then
+ echo "Error: nandsim is already loaded"
+ exit 1
+fi
+
+if (( $# < 1 )); then
+ echo "Load NAND simulator to simulate flash of a specified size."
+ echo ""
+ echo "Usage: ./load_nandsim.sh <size in MiB> <eraseblock size in KiB>"
+ echo " <page size (512 or 2048)>"
+ echo ""
+ echo "Only the first parameter is mandatory. Default eraseblock size"
+ echo "is 16KiB, default NAND page size is 512 bytes."
+ echo ""
+ echo "Only the following combinations are supported:"
+ echo "--------------------------------------------------"
+ echo "| size (MiB) | EB size (KiB) | Page size (bytes) |"
+ echo "--------------------------------------------------"
+ echo "| 16 | 16 | 512 |"
+ echo "| 32 | 16 | 512 |"
+ echo "| 64 | 16 | 512 |"
+ echo "| 128 | 16 | 512 |"
+ echo "| 256 | 16 | 512 |"
+ echo "| 64 | 64 | 2048 |"
+ echo "| 64 | 128 | 2048 |"
+ echo "| 64 | 256 | 2048 |"
+ echo "| 64 | 512 | 2048 |"
+ echo "| 128 | 64 | 2048 |"
+ echo "| 128 | 128 | 2048 |"
+ echo "| 128 | 256 | 2048 |"
+ echo "| 128 | 512 | 2048 |"
+ echo "| 256 | 64 | 2048 |"
+ echo "| 256 | 128 | 2048 |"
+ echo "| 256 | 256 | 2048 |"
+ echo "| 256 | 512 | 2048 |"
+ echo "| 512 | 64 | 2048 |"
+ echo "| 512 | 128 | 2048 |"
+ echo "| 512 | 256 | 2048 |"
+ echo "| 512 | 512 | 2048 |"
+ echo "| 1024 | 64 | 2048 |"
+ echo "| 1024 | 128 | 2048 |"
+ echo "| 1024 | 256 | 2048 |"
+ echo "| 1024 | 512 | 2048 |"
+ echo "--------------------------------------------------"
+ exit 1
+fi
+
+SZ=$1
+EBSZ=$2
+PGSZ=$3
+if [[ $# == '1' ]]; then
+ EBSZ=16
+ PGSZ=512
+elif [[ $# == '2' ]]; then
+ PGSZ=512
+fi
+
+if (( $PGSZ == 512 && $EBSZ != 16 )); then
+ echo "Error: only 16KiB eraseblocks are possible in case of 512 bytes page"
+ exit 1
+fi
+
+if (( $PGSZ == 512 )); then
+ case $SZ in
+ 16) modprobe nandsim first_id_byte=0x20 second_id_byte=0x33 ;;
+ 32) modprobe nandsim first_id_byte=0x20 second_id_byte=0x35 ;;
+ 64) modprobe nandsim first_id_byte=0x20 second_id_byte=0x36 ;;
+ 128) modprobe nandsim first_id_byte=0x20 second_id_byte=0x78 ;;
+ 256) modprobe nandsim first_id_byte=0x20 second_id_byte=0x71 ;;
+ *) echo "Flash size ${SZ}MiB is not supported, try 16, 32, 64 or 256"
+ exit 1 ;;
+ esac
+elif (( $PGSZ == 2048 )); then
+ case $EBSZ in
+ 64) FOURTH=0x05 ;;
+ 128) FOURTH=0x15 ;;
+ 256) FOURTH=0x25 ;;
+ 512) FOURTH=0x35 ;;
+ *) echo "Eraseblock ${EBSZ}KiB is not supported"
+ exit 1
+ esac
+
+ case $SZ in
+ 64) modprobe nandsim first_id_byte=0x20 second_id_byte=0xa2 third_id_byte=0x00 fourth_id_byte=$FOURTH ;;
+ 128) modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=$FOURTH ;;
+ 256) modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=$FOURTH ;;
+ 512) modprobe nandsim first_id_byte=0x20 second_id_byte=0xac third_id_byte=0x00 fourth_id_byte=$FOURTH ;;
+ 1024) modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=$FOURTH ;;
+ *) echo "Unable to emulate ${SZ}MiB flash with ${EBSZ}KiB eraseblock"
+ exit 1
+ esac
+else
+ echo "Error: bad NAND page size ${PGSZ}KiB, it has to be either 512 or 2048"
+ exit 1
+fi
+
+if (( $? != 0 )); then
+ echo "Error: cannot load nandsim"
+ exit 1
+fi
+
+echo "Loaded NAND simulator (${SZ}MiB, ${EBSZ}KiB eraseblock, $PGSZ bytes NAND page)"
+exit 0
diff --git a/mtd-utils-1.3.1/mcast_image.h b/mtd-utils-1.3.1/mcast_image.h
new file mode 100644
index 0000000..07b6e31
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.jffs2.1 b/mtd-utils-1.3.1/mkfs.jffs2.1
new file mode 100644
index 0000000..1eefeda
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.jffs2.1
@@ -0,0 +1,267 @@
+.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 avaiable compressors depending
+on the selected compression mode.
+
+File systems are created with the same endianness as the host,
+unless the
+.B -b
+or
+.B -l
+options are specified. JFFS2 driver in the 2.4 Linux kernel only
+supported images having the same endianness as the CPU. As of 2.5.48,
+the kernel can be changed with a #define to accept images of the
+non-native endianness. Full bi-endian support in the kernel is not
+planned.
+
+It is unlikely that JFFS2 images are useful except in conjuction
+with the MTD (Memory Technology Device) drivers in the Linux
+kernel, since the JFFS2 file system driver in the kernel requires
+MTD devices.
+.SH OPTIONS
+Options that take SIZE arguments can be specified as either
+decimal (e.g., 65536), octal (0200000), or hexidecimal (0x1000).
+.TP
+.B -p, --pad[=SIZE]
+Pad output to SIZE bytes with 0xFF. If SIZE is not specified,
+the output is padded to the end of the final erase block.
+.TP
+.B -r, -d, --root=DIR
+Build file system from directory DIR. The default is the current
+directory.
+.TP
+.B -s, --pagesize=SIZE
+Use page size SIZE. The default is 4 KiB. This size is the
+maximum size of a data node.
+.TP
+.B -e, --eraseblock=SIZE
+Use erase block size SIZE. The default is 64 KiB. If you use a erase
+block size different than the erase block size of the target MTD
+device, JFFS2 may not perform optimally. If the SIZE specified is
+below 4096, the units are assumed to be KiB.
+.TP
+.B -c, --cleanmarker=SIZE
+Write \'CLEANMARKER\' nodes with the size specified. It is not
+normally appropriate to specify a size other than the default 12
+bytes.
+.TP
+.B -n, --no-cleanmarkers
+Do not write \'CLEANMARKER\' nodes to the beginning of each erase
+block. This option can be useful for creating JFFS2 images for
+use on NAND flash, and for creating images which are to be used
+on a variety of hardware with differing eraseblock sizes.
+.TP
+.B -o, --output=FILE
+Write JFFS2 image to file FILE. Default is the standard output.
+.TP
+.B -l, --little-endian
+Create a little-endian JFFS2 image. Default is to make an image
+with the same endianness as the host.
+.TP
+.B -b, --big-endian
+Create a big-endian JFFS2 image. Default is to make an image
+with the same endianness as the host.
+.TP
+.B -D, --devtable=FILE
+Use the named FILE as a device table file, for including devices and
+changing permissions in the created image when the user does not have
+appropriate permissions to create them on the file system used as
+source.
+.TP
+.B -f, --faketime
+Change all file timestamps to \'0\' for regression testing.
+.TP
+.B -q, --squash
+Squash permissions and owners, making all files be owned by root and
+removing write permission for \'group\' and \'other\'.
+.TP
+.B -U, --squash-uids
+Squash owners making all files be owned by root.
+.TP
+.B -P, --squash-perms
+Squash permissions, removing write permission for \'group\' and \'other\'.
+.TP
+.B --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 avaiable compressors and their default states.
+.TP
+.B -X, --enable-compressor=NAME
+Enable a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default states.
+.TP
+.B -y, --compressor-priority=PRIORITY:NAME
+Set the priority of a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default priority.
+Priorities are used by priority compression mode.
+.TP
+.B -L, --list-compressors
+Show the list of the avaiable compressors and their states.
+.TP
+.B -t, --test-compression
+Call decompress after every compress - and compare the result with the original data -, and
+some other check.
+.TP
+.B -h, --help
+Display help text.
+.TP
+.B -v, --verbose
+Verbose operation.
+.TP
+.B -V, --version
+Display version information.
+.TP
+.B -i, --incremental=FILE
+Generate an appendage image for FILE. If FILE is written to flash and flash
+is appended with the output, then it seems as if it was one thing.
+
+.SH 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/mtd-utils-1.3.1/mkfs.jffs2.c b/mtd-utils-1.3.1/mkfs.jffs2.c
new file mode 100644
index 0000000..23a8cf8
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.jffs2.c
@@ -0,0 +1,1907 @@
+/* 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 _GNU_SOURCE
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#ifndef WITHOUT_XATTR
+#include <sys/xattr.h>
+#include <sys/acl.h>
+#endif
+#include <byteswap.h>
+#define crc32 __complete_crap
+#include <zlib.h>
+#undef crc32
+#include "crc32.h"
+#include "rbtree.h"
+
+/* Do not use the weird XPG version of basename */
+#undef basename
+
+//#define DMALLOC
+//#define mkfs_debug_msg error_msg
+#define mkfs_debug_msg(a...) { }
+#define min(x,y) ({ typeof((x)) _x = (x); typeof((y)) _y = (y); (_x>_y)?_y:_x; })
+
+#define PAD(x) (((x)+3)&~3)
+
+struct filesystem_entry {
+ char *name; /* Name of this directory (think basename) */
+ char *path; /* Path of this directory (think dirname) */
+ char *fullname; /* Full name of this directory (i.e. path+name) */
+ char *hostname; /* Full path to this file on the host filesystem */
+ 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;
+static const char *const app_name = "mkfs.jffs2";
+static const char *const memory_exhausted = "memory exhausted";
+
+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;
+}
+
+static void verror_msg(const char *s, va_list p)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s: ", app_name);
+ vfprintf(stderr, s, p);
+}
+static void error_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+}
+
+static void error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+static void vperror_msg(const char *s, va_list p)
+{
+ int err = errno;
+
+ if (s == 0)
+ s = "";
+ verror_msg(s, p);
+ if (*s)
+ s = ": ";
+ fprintf(stderr, "%s%s\n", s, strerror(err));
+}
+
+static void perror_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+}
+
+static void perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+ exit(EXIT_FAILURE);
+}
+
+#ifndef DMALLOC
+extern void *xmalloc(size_t size)
+{
+ void *ptr = malloc(size);
+
+ if (ptr == NULL && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+extern void *xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr = calloc(nmemb, size);
+
+ if (ptr == NULL && nmemb != 0 && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+extern void *xrealloc(void *ptr, size_t size)
+{
+ ptr = realloc(ptr, size);
+ if (ptr == NULL && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+extern char *xstrdup(const char *s)
+{
+ char *t;
+
+ if (s == NULL)
+ return NULL;
+ t = strdup(s);
+ if (t == NULL)
+ error_msg_and_die(memory_exhausted);
+ return t;
+}
+#endif
+
+extern char *xreadlink(const char *path)
+{
+ static const int GROWBY = 80; /* how large we will grow strings by */
+
+ char *buf = NULL;
+ int bufsize = 0, readsize = 0;
+
+ do {
+ buf = xrealloc(buf, bufsize += GROWBY);
+ readsize = readlink(path, buf, bufsize); /* 1st try */
+ if (readsize == -1) {
+ perror_msg("%s:%s", app_name, path);
+ return NULL;
+ }
+ }
+ while (bufsize < readsize + 1);
+
+ buf[readsize] = '\0';
+
+ return buf;
+}
+static FILE *xfopen(const char *path, const char *mode)
+{
+ FILE *fp;
+ if ((fp = fopen(path, mode)) == NULL)
+ perror_msg_and_die("%s", path);
+ return fp;
+}
+
+static struct filesystem_entry *find_filesystem_entry(
+ struct filesystem_entry *dir, char *fullname, uint32_t type)
+{
+ struct filesystem_entry *e = dir;
+
+ if (S_ISDIR(dir->sb.st_mode)) {
+ /* 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(
+ char *name, char *path, unsigned long uid, unsigned long gid,
+ unsigned long mode, dev_t rdev, struct filesystem_entry *parent)
+{
+ int status;
+ char *tmp;
+ struct stat sb;
+ time_t timestamp = time(NULL);
+ struct filesystem_entry *entry;
+
+ memset(&sb, 0, sizeof(struct stat));
+ status = lstat(path, &sb);
+
+ if (status >= 0) {
+ /* It is ok for some types of files to not exit on disk (such as
+ * device nodes), but if they _do_ exist the specified mode had
+ * better match the actual file or strange things will happen.... */
+ if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) {
+ error_msg_and_die ("%s: file type does not match specified type!", path);
+ }
+ timestamp = sb.st_mtime;
+ } else {
+ /* If this is a regular file, it _must_ exist on disk */
+ if ((mode & S_IFMT) == S_IFREG) {
+ error_msg_and_die("%s: does not exist!", path);
+ }
+ }
+
+ /* Squash all permissions so files are owned by root, all
+ * timestamps are _right now_, and file permissions
+ * have group and other write removed */
+ if (squash_uids) {
+ uid = gid = 0;
+ }
+ if (squash_perms) {
+ if (!S_ISLNK(mode)) {
+ mode &= ~(S_IWGRP | S_IWOTH);
+ mode &= ~(S_ISUID | S_ISGID);
+ }
+ }
+ if (fake_times) {
+ timestamp = 0;
+ }
+
+ entry = xcalloc(1, sizeof(struct filesystem_entry));
+
+ entry->hostname = xstrdup(path);
+ entry->fullname = xstrdup(name);
+ tmp = xstrdup(name);
+ entry->name = xstrdup(basename(tmp));
+ free(tmp);
+ tmp = xstrdup(name);
+ entry->path = xstrdup(dirname(tmp));
+ free(tmp);
+
+ entry->sb.st_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, char *targetpath, char *hostpath)
+{
+ int i, n;
+ struct stat sb;
+ char *hpath, *tpath;
+ struct dirent *dp, **namelist;
+ struct filesystem_entry *entry;
+
+
+ if (lstat(hostpath, &sb)) {
+ perror_msg_and_die("%s", hostpath);
+ }
+
+ entry = add_host_filesystem_entry(targetpath, hostpath,
+ sb.st_uid, sb.st_gid, sb.st_mode, 0, parent);
+
+ n = scandir(hostpath, &namelist, 0, alphasort);
+ if (n < 0) {
+ perror_msg_and_die("opening directory %s", hostpath);
+ }
+
+ for (i=0; i<n; i++)
+ {
+ dp = namelist[i];
+ if (dp->d_name[0] == '.' && (dp->d_name[1] == 0 ||
+ (dp->d_name[1] == '.' && dp->d_name[2] == 0)))
+ {
+ free(dp);
+ continue;
+ }
+
+ asprintf(&hpath, "%s/%s", hostpath, dp->d_name);
+ if (lstat(hpath, &sb)) {
+ perror_msg_and_die("%s", hpath);
+ }
+ if (strcmp(targetpath, "/") == 0) {
+ asprintf(&tpath, "%s%s", targetpath, dp->d_name);
+ } else {
+ asprintf(&tpath, "%s/%s", targetpath, dp->d_name);
+ }
+
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ recursive_add_host_directory(entry, tpath, hpath);
+ break;
+
+ case S_IFREG:
+ case S_IFSOCK:
+ case S_IFIFO:
+ case S_IFLNK:
+ case S_IFCHR:
+ case S_IFBLK:
+ add_host_filesystem_entry(tpath, hpath, sb.st_uid,
+ sb.st_gid, sb.st_mode, sb.st_rdev, entry);
+ break;
+
+ default:
+ error_msg("Unknown file type %o for %s", sb.st_mode, hpath);
+ break;
+ }
+ free(dp);
+ free(hpath);
+ free(tpath);
+ }
+ free(namelist);
+ return (entry);
+}
+
+/* the GNU C library has a wonderful scanf("%as", string) which will
+ allocate the string with the right size, good to avoid buffer overruns.
+ the following macros use it if available or use a hacky workaround...
+ */
+
+#ifdef __GNUC__
+#define SCANF_PREFIX "a"
+#define SCANF_STRING(s) (&s)
+#define GETCWD_SIZE 0
+#else
+#define SCANF_PREFIX "511"
+#define SCANF_STRING(s) (s = malloc(512))
+#define GETCWD_SIZE -1
+inline int snprintf(char *str, size_t n, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsprintf(str, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#endif
+
+/* device table entries take the form of:
+ <path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+ /dev/mem c 640 0 0 1 1 0 0 -
+
+ type can be one of:
+ f A regular file
+ d Directory
+ c Character special device file
+ b Block special device file
+ p Fifo (named pipe)
+
+ I don't bother with symlinks (permissions are irrelevant), hard
+ links (special cases of regular files), or sockets (why bother).
+
+ Regular files must exist in the target root directory. If a char,
+ block, fifo, or directory does not exist, it will be created.
+ */
+static int interpret_table_entry(struct filesystem_entry *root, char *line)
+{
+ char *hostpath;
+ char type, *name = NULL, *tmp, *dir;
+ unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
+ unsigned long start = 0, increment = 1, count = 0;
+ struct filesystem_entry *parent, *entry;
+
+ if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
+ SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor,
+ &start, &increment, &count) < 0)
+ {
+ return 1;
+ }
+
+ if (!strcmp(name, "/")) {
+ error_msg_and_die("Device table entries require absolute paths");
+ }
+
+ asprintf(&hostpath, "%s%s", rootdir, name);
+
+ /* Check if this file already exists... */
+ switch (type) {
+ case 'd':
+ mode |= S_IFDIR;
+ break;
+ case 'f':
+ mode |= S_IFREG;
+ break;
+ case 'p':
+ mode |= S_IFIFO;
+ break;
+ case 'c':
+ mode |= S_IFCHR;
+ break;
+ case 'b':
+ mode |= S_IFBLK;
+ break;
+ case 'l':
+ mode |= S_IFLNK;
+ break;
+ default:
+ error_msg_and_die("Unsupported file type '%c'", type);
+ }
+ entry = find_filesystem_entry(root, name, mode);
+ if (entry) {
+ /* Ok, we just need to fixup the existing entry
+ * and we will be all done... */
+ entry->sb.st_uid = uid;
+ entry->sb.st_gid = gid;
+ entry->sb.st_mode = mode;
+ if (major && minor) {
+ entry->sb.st_rdev = makedev(major, minor);
+ }
+ } else {
+ /* If parent is NULL (happens with device table entries),
+ * try and find our parent now) */
+ tmp = strdup(name);
+ dir = dirname(tmp);
+ parent = find_filesystem_entry(root, dir, S_IFDIR);
+ free(tmp);
+ if (parent == NULL) {
+ error_msg ("skipping device_table entry '%s': no parent directory!", name);
+ free(name);
+ free(hostpath);
+ return 1;
+ }
+
+ switch (type) {
+ case 'd':
+ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+ break;
+ case 'f':
+ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+ break;
+ case 'p':
+ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+ break;
+ case 'c':
+ case 'b':
+ if (count > 0) {
+ dev_t rdev;
+ unsigned long i;
+ char *dname, *hpath;
+
+ for (i = start; i < count; i++) {
+ asprintf(&dname, "%s%lu", name, i);
+ asprintf(&hpath, "%s/%s%lu", rootdir, name, i);
+ rdev = makedev(major, minor + (i * increment - start));
+ add_host_filesystem_entry(dname, hpath, uid, gid,
+ mode, rdev, parent);
+ free(dname);
+ free(hpath);
+ }
+ } else {
+ dev_t rdev = makedev(major, minor);
+ add_host_filesystem_entry(name, hostpath, uid, gid,
+ mode, rdev, parent);
+ }
+ break;
+ default:
+ error_msg_and_die("Unsupported file type '%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)
+ perror_msg_and_die("write");
+
+ if (ret == 0)
+ perror_msg_and_die("write returned zero");
+
+ len -= ret;
+ buf += ret;
+ out_ofs += ret;
+ }
+}
+
+static void padblock(void)
+{
+ while (out_ofs % erase_block_size) {
+ full_write(out_fd, ffbuf, min(sizeof(ffbuf),
+ erase_block_size - (out_ofs % erase_block_size)));
+ }
+}
+
+static void pad(int req)
+{
+ while (req) {
+ if (req > sizeof(ffbuf)) {
+ full_write(out_fd, ffbuf, sizeof(ffbuf));
+ req -= sizeof(ffbuf);
+ } else {
+ full_write(out_fd, ffbuf, req);
+ req = 0;
+ }
+ }
+}
+
+static inline void padword(void)
+{
+ if (out_ofs % 4) {
+ full_write(out_fd, ffbuf, 4 - (out_ofs % 4));
+ }
+}
+
+static inline void pad_block_if_less_than(int req)
+{
+ if (add_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(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(crc32(0, &rd, sizeof(rd) - 8));
+ rd.name_crc = cpu_to_je32(crc32(0, name, strlen(name)));
+
+ pad_block_if_less_than(sizeof(rd) + rd.nsize);
+ full_write(out_fd, &rd, sizeof(rd));
+ full_write(out_fd, name, rd.nsize);
+ padword();
+}
+
+static 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) {
+ error_msg("Skipping file \"%s\" too large.", e->path);
+ return -1;
+ }
+ fd = open(e->hostname, O_RDONLY);
+ if (fd == -1) {
+ perror_msg_and_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) {
+ perror_msg_and_die("read");
+ }
+
+ while (len) {
+ uint32_t dsize, space;
+ uint16_t compression;
+
+ pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN);
+
+ dsize = len;
+ space =
+ erase_block_size - (out_ofs % erase_block_size) -
+ sizeof(ri);
+ if (space > dsize)
+ space = dsize;
+
+ compression = jffs2_compress(tbuf, &cbuf, &dsize, &space);
+
+ ri.compr = compression & 0xff;
+ ri.usercompr = (compression >> 8) & 0xff;
+
+ if (ri.compr) {
+ wbuf = cbuf;
+ } else {
+ wbuf = tbuf;
+ dsize = space;
+ }
+
+ ri.totlen = cpu_to_je32(sizeof(ri) + space);
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+
+ ri.version = cpu_to_je32(++ver);
+ ri.offset = cpu_to_je32(offset);
+ ri.csize = cpu_to_je32(space);
+ ri.dsize = cpu_to_je32(dsize);
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(crc32(0, wbuf, space));
+
+ full_write(out_fd, &ri, sizeof(ri));
+ 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(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+ ri.csize = cpu_to_je32(0);
+ ri.dsize = cpu_to_je32(0);
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+
+ full_write(out_fd, &ri, sizeof(ri));
+ padword();
+ }
+ free(buf);
+ close(fd);
+ 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) {
+ error_msg("symlink too large. Truncated to %d chars.",
+ JFFS2_MAX_SYMLINK_LEN);
+ len = JFFS2_MAX_SYMLINK_LEN;
+ }
+
+ memset(&ri, 0, sizeof(ri));
+
+ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+ ri.totlen = cpu_to_je32(sizeof(ri) + len);
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+
+ ri.ino = cpu_to_je32(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(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(crc32(0, e->link, len));
+
+ pad_block_if_less_than(sizeof(ri) + len);
+ full_write(out_fd, &ri, sizeof(ri));
+ full_write(out_fd, e->link, len);
+ padword();
+}
+
+static void write_pipe(struct filesystem_entry *e)
+{
+ struct stat *statbuf;
+ struct jffs2_raw_inode ri;
+
+ statbuf = &(e->sb);
+ 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(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(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(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(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(crc32(0, &kdev, sizeof(kdev)));
+
+ pad_block_if_less_than(sizeof(ri) + sizeof(kdev));
+ full_write(out_fd, &ri, sizeof(ri));
+ full_write(out_fd, &kdev, sizeof(kdev));
+ padword();
+}
+
+#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;
+ 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(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(crc32(0, xe->xname, xe->name_len + 1 + xe->value_len));
+ rx.node_crc = cpu_to_je32(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 = (crc32(0, xname, name_len) ^ 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, *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(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(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 %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) (e->sb.st_uid), (int) (e->sb.st_gid),
+ e->name);
+ }
+ write_pipe(e);
+ write_xattr_entry(e);
+ break;
+ case S_IFSOCK:
+ if (verbose) {
+ printf("\ts %04o %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+ }
+ write_pipe(e);
+ write_xattr_entry(e);
+ break;
+ case S_IFIFO:
+ if (verbose) {
+ printf("\tp %04o %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+ }
+ write_pipe(e);
+ 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 %9lu %5d:%-3d %s -> %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name,
+ e->link);
+ }
+ write_symlink(e);
+ write_xattr_entry(e);
+ break;
+ case S_IFREG:
+ wrote = write_regular_file(e);
+ write_xattr_entry(e);
+ if (verbose) {
+ printf("\tf %04o %9lu (%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:
+ error_msg("Unknown mode %o for %s", e->sb.st_mode,
+ e->fullname);
+ break;
+ }
+ e = e->next;
+ }
+
+ e = dir->files;
+ while (e) {
+ if (S_ISDIR(e->sb.st_mode)) {
+ if (e->files) {
+ recursive_populate_directory(e);
+ } else if (verbose) {
+ printf("%s\n", e->fullname);
+ }
+ }
+ e = e->next;
+ }
+}
+
+static void create_target_filesystem(struct filesystem_entry *root)
+{
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.totlen = cpu_to_je32(cleanmarker_size);
+ cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4));
+
+ 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'},
+ {"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 char *helptext =
+"Usage: mkfs.jffs2 [OPTIONS]\n"
+"Make a JFFS2 file system image from an existing directory tree\n\n"
+"Options:\n"
+" -p, --pad[=SIZE] Pad output to SIZE bytes with 0xFF. If SIZE is\n"
+" not specified, the output is padded to the end of\n"
+" the final erase block\n"
+" -r, -d, --root=DIR Build file system from directory DIR (default: cwd)\n"
+" -s, --pagesize=SIZE Use page size (max data node size) SIZE (default: 4KiB)\n"
+" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n"
+" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n"
+" -m, --compr-mode=MODE Select compression mode (default: priortiry)\n"
+" -x, --disable-compressor=COMPRESSOR_NAME\n"
+" Disable a compressor\n"
+" -X, --enable-compressor=COMPRESSOR_NAME\n"
+" Enable a compressor\n"
+" -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n"
+" Set the priority of a compressor\n"
+" -L, --list-compressors Show the list of the avaiable compressors\n"
+" -t, --test-compression Call decompress and compare with the original (for test)\n"
+" -n, --no-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 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%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic));
+ p += 4;
+ continue;
+ }
+
+ bitchbitmask = 0;
+
+ type = je16_to_cpu(node->u.nodetype);
+ if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
+ obsolete = 1;
+ type |= JFFS2_NODE_ACCURATE;
+ } else
+ obsolete = 0;
+
+ node->u.nodetype = cpu_to_je16(type);
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+
+ case JFFS2_NODETYPE_INODE:
+ if(verbose)
+ printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
+ je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
+ je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));
+
+ if ( je32_to_cpu (node->i.ino) > ino )
+ ino = je32_to_cpu (node->i.ino);
+
+ p += PAD(je32_to_cpu (node->i.totlen));
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ memcpy (name, node->d.name, node->d.nsize);
+ name [node->d.nsize] = 0x0;
+
+ if(verbose)
+ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
+ je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
+ node->d.nsize, name);
+
+ p += PAD(je32_to_cpu (node->d.totlen));
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ if (verbose) {
+ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case JFFS2_NODETYPE_PADDING:
+ if (verbose) {
+ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case 0xffff:
+ p += 4;
+ break;
+
+ default:
+ if (verbose) {
+ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ }
+ }
+}
+
+void parse_image(){
+ int ret;
+
+ file_buffer = malloc(erase_block_size);
+
+ if (!file_buffer) {
+ perror("out of memory");
+ close (in_fd);
+ close (out_fd);
+ exit(1);
+ }
+
+ while ((ret = load_next_block())) {
+ process_buffer(ret);
+ }
+
+ if (file_buffer)
+ free(file_buffer);
+
+ close(in_fd);
+}
+
+int main(int argc, char **argv)
+{
+ int c, opt;
+ char *cwd;
+ struct stat sb;
+ FILE *devtable = NULL;
+ struct filesystem_entry *root;
+ char *compr_name = NULL;
+ int compr_prior = -1;
+ 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)
+ perror_msg_and_die(optarg);
+ if (sb.st_size < 10)
+ error_msg_and_die("%s: not a proper device table file", optarg);
+ break;
+
+ case 'r':
+ case 'd': /* for compatibility with mkfs.jffs, genext2fs, etc... */
+ if (rootdir != default_rootdir) {
+ error_msg_and_die("root directory specified more than once");
+ }
+ rootdir = xstrdup(optarg);
+ break;
+
+ case 's':
+ page_size = strtol(optarg, NULL, 0);
+ warn_page_size = 0; /* set by user, so don't need to warn */
+ break;
+
+ case 'o':
+ if (out_fd != -1) {
+ error_msg_and_die("output filename specified more than once");
+ }
+ out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (out_fd == -1) {
+ perror_msg_and_die("open output file");
+ }
+ break;
+
+ case 'q':
+ squash_uids = 1;
+ squash_perms = 1;
+ break;
+
+ case 'U':
+ squash_uids = 1;
+ break;
+
+ case 'P':
+ squash_perms = 1;
+ break;
+
+ case 'f':
+ fake_times = 1;
+ break;
+
+ case 'h':
+ case '?':
+ error_msg_and_die(helptext);
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'V':
+ error_msg_and_die("revision %s\n", revtext);
+
+ case 'e': {
+ char *next;
+ unsigned units = 0;
+ erase_block_size = strtol(optarg, &next, 0);
+ if (!erase_block_size)
+ error_msg_and_die("Unrecognisable erase size\n");
+
+ if (*next) {
+ if (!strcmp(next, "KiB")) {
+ units = 1024;
+ } else if (!strcmp(next, "MiB")) {
+ units = 1024 * 1024;
+ } else {
+ error_msg_and_die("Unknown units in erasesize\n");
+ }
+ } else {
+ if (erase_block_size < 0x1000)
+ units = 1024;
+ else
+ units = 1;
+ }
+ erase_block_size *= units;
+
+ /* If it's less than 8KiB, they're not allowed */
+ if (erase_block_size < 0x2000) {
+ fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n",
+ erase_block_size);
+ erase_block_size = 0x2000;
+ }
+ break;
+ }
+
+ case 'l':
+ target_endian = __LITTLE_ENDIAN;
+ break;
+
+ case 'b':
+ target_endian = __BIG_ENDIAN;
+ break;
+
+ case 'p':
+ if (optarg)
+ pad_fs_size = strtol(optarg, NULL, 0);
+ else
+ pad_fs_size = -1;
+ break;
+ case 'n':
+ add_cleanmarkers = 0;
+ break;
+ case 'c':
+ cleanmarker_size = strtol(optarg, NULL, 0);
+ if (cleanmarker_size < sizeof(cleanmarker)) {
+ error_msg_and_die("cleanmarker size must be >= 12");
+ }
+ if (cleanmarker_size >= erase_block_size) {
+ error_msg_and_die("cleanmarker size must be < eraseblock size");
+ }
+ break;
+ case 'm':
+ if (jffs2_set_compression_mode_name(optarg)) {
+ error_msg_and_die("Unknown compression mode %s", optarg);
+ }
+ break;
+ case 'x':
+ if (jffs2_disable_compressor_name(optarg)) {
+ error_msg_and_die("Unknown compressor name %s",optarg);
+ }
+ break;
+ case 'X':
+ if (jffs2_enable_compressor_name(optarg)) {
+ error_msg_and_die("Unknown compressor name %s",optarg);
+ }
+ break;
+ case 'L':
+ error_msg_and_die("\n%s",jffs2_list_compressors());
+ break;
+ case 't':
+ jffs2_compression_check_set(1);
+ break;
+ case 'y':
+ compr_name = malloc(strlen(optarg));
+ sscanf(optarg,"%d:%s",&compr_prior,compr_name);
+ if ((compr_prior>=0)&&(compr_name)) {
+ if (jffs2_set_compressor_priority(compr_name, compr_prior))
+ exit(EXIT_FAILURE);
+ }
+ else {
+ error_msg_and_die("Cannot parse %s",optarg);
+ }
+ free(compr_name);
+ break;
+ case 'i':
+ if (in_fd != -1) {
+ error_msg_and_die("(incremental) filename specified more than once");
+ }
+ in_fd = open(optarg, O_RDONLY);
+ if (in_fd == -1) {
+ perror_msg_and_die("cannot open (incremental) file");
+ }
+ break;
+#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) {
+ error_msg("Page size for this system is by default %d", page_size);
+ error_msg("Use the --pagesize=SIZE option if this is not what you want");
+ }
+ if (out_fd == -1) {
+ if (isatty(1)) {
+ error_msg_and_die(helptext);
+ }
+ out_fd = 1;
+ }
+ if (lstat(rootdir, &sb)) {
+ perror_msg_and_die("%s", rootdir);
+ }
+ if (chdir(rootdir))
+ perror_msg_and_die("%s", rootdir);
+
+ if (!(cwd = getcwd(0, GETCWD_SIZE)))
+ perror_msg_and_die("getcwd failed");
+
+ if(in_fd != -1)
+ parse_image();
+
+ root = recursive_add_host_directory(NULL, "/", cwd);
+
+ if (devtable)
+ parse_device_table(root, devtable);
+
+ create_target_filesystem(root);
+
+ cleanup(root);
+
+ if (rootdir != default_rootdir)
+ free(rootdir);
+
+ close(out_fd);
+
+ if (verbose) {
+ char *s = jffs2_stats();
+ fprintf(stderr,"\n\n%s",s);
+ free(s);
+ }
+ if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) {
+ fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get());
+ }
+
+ jffs2_compressors_exit();
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/mkfs.ubifs/COPYING b/mtd-utils-1.3.1/mkfs.ubifs/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/Makefile b/mtd-utils-1.3.1/mkfs.ubifs/Makefile
new file mode 100644
index 0000000..2b069ce
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/Makefile
@@ -0,0 +1,28 @@
+
+CPPFLAGS += -I../include -I../ubi-utils/include
+CPPFLAGS += $(ZLIBCPPFLAGS) $(LZOCPPFLAGS) $(UUIDCPPFLAGS)
+
+ALL_SOURCES=*.[ch] hashtable/*.[ch]
+
+TARGETS = mkfs.ubifs
+
+LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid -L$(BUILDDIR)/../ubi-utils/ -lubi
+LDLIBS_mkfs.ubifs += $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS)
+
+include ../common.mk
+
+$(BUILDDIR)/mkfs.ubifs: $(addprefix $(BUILDDIR)/,\
+ crc16.o crc32.o lpt.o compr.o devtable.o \
+ hashtable/hashtable.o hashtable/hashtable_itr.o)
+
+clean::
+ rm -f $(BUILDDIR)/hashtable/*.o cscope.*
+
+cscope:
+ @echo $(ALL_SOURCES) > cscope.files
+ @cscope -bR
+ @rm cscope.files
+
+install:: ${TARGETS}
+ mkdir -p ${DESTDIR}/${SBINDIR}
+ install -m 0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
diff --git a/mtd-utils-1.3.1/mkfs.ubifs/README b/mtd-utils-1.3.1/mkfs.ubifs/README
new file mode 100644
index 0000000..4fbaa7d
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/README
@@ -0,0 +1,10 @@
+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-media.h is fs/ubifs/ubifs-media.h from the linux kernel
+* 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/mtd-utils-1.3.1/mkfs.ubifs/compr.c b/mtd-utils-1.3.1/mkfs.ubifs/compr.c
new file mode 100644
index 0000000..e378c5d
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/compr.c
@@ -0,0 +1,217 @@
+/*
+ * 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 <zlib.h>
+#include <lzo/lzo1x.h>
+#include <linux/types.h>
+
+#include "compr.h"
+#include "ubifs-media.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/mtd-utils-1.3.1/mkfs.ubifs/compr.h b/mtd-utils-1.3.1/mkfs.ubifs/compr.h
new file mode 100644
index 0000000..e3dd95c
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/crc16.c b/mtd-utils-1.3.1/mkfs.ubifs/crc16.c
new file mode 100644
index 0000000..a19512e
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/crc16.h b/mtd-utils-1.3.1/mkfs.ubifs/crc16.h
new file mode 100644
index 0000000..539d21a
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/crc32.c b/mtd-utils-1.3.1/mkfs.ubifs/crc32.c
new file mode 100644
index 0000000..6b1e50c
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/crc32.c
@@ -0,0 +1,95 @@
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
diff --git a/mtd-utils-1.3.1/mkfs.ubifs/crc32.h b/mtd-utils-1.3.1/mkfs.ubifs/crc32.h
new file mode 100644
index 0000000..86fc841
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/crc32.h
@@ -0,0 +1,22 @@
+/*
+ * This code was taken from the linux kernel. The license is GPL Version 2.
+ */
+
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+static inline uint32_t ubifs_crc32(uint32_t val, const void *ss, int len)
+{
+ const unsigned char *s = ss;
+
+ while (--len >= 0)
+ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+ return val;
+}
+
+#endif /* __CRC32_H__ */
diff --git a/mtd-utils-1.3.1/mkfs.ubifs/defs.h b/mtd-utils-1.3.1/mkfs.ubifs/defs.h
new file mode 100644
index 0000000..06cf9e5
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/defs.h
@@ -0,0 +1,107 @@
+/*
+ * 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 ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
+#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
+
+#define min_t(t,x,y) ({ \
+ typeof((x)) _x = (x); \
+ typeof((y)) _y = (y); \
+ (_x < _y) ? _x : _y; \
+})
+
+#define max_t(t,x,y) ({ \
+ typeof((x)) _x = (x); \
+ typeof((y)) _y = (y); \
+ (_x > _y) ? _x : _y; \
+})
+
+#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/mtd-utils-1.3.1/mkfs.ubifs/devtable.c b/mtd-utils-1.3.1/mkfs.ubifs/devtable.c
new file mode 100644
index 0000000..dee035d
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable.c b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable.c
new file mode 100644
index 0000000..763357e
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#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 = sizeof(primes)/sizeof(primes[0]);
+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/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable.h b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable.h
new file mode 100644
index 0000000..b90781a
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_itr.c b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_itr.c
new file mode 100644
index 0000000..5dced84
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_itr.c
@@ -0,0 +1,188 @@
+/* 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;
+}
+
+/*****************************************************************************/
+/* key - return the key of the (key,value) pair at the current position */
+/* value - return the value of the (key,value) pair at the current position */
+
+void *
+hashtable_iterator_key(struct hashtable_itr *i)
+{ return i->e->k; }
+
+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)
+{
+ 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/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_itr.h b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_itr.h
new file mode 100644
index 0000000..eea699a
--- /dev/null
+++ b/mtd-utils-1.3.1/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 */
+
+extern 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 */
+
+extern 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/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_private.h b/mtd-utils-1.3.1/mkfs.ubifs/hashtable/hashtable_private.h
new file mode 100644
index 0000000..3e95f60
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/key.h b/mtd-utils-1.3.1/mkfs.ubifs/key.h
new file mode 100644
index 0000000..d3a02d4
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/lpt.c b/mtd-utils-1.3.1/mkfs.ubifs/lpt.c
new file mode 100644
index 0000000..60002ff
--- /dev/null
+++ b/mtd-utils-1.3.1/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_pnode));
+
+ 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, UBI_SHORTTERM);
+ 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, UBI_SHORTTERM);
+ 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, UBI_SHORTTERM);
+ 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, UBI_SHORTTERM);
+ 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, UBI_SHORTTERM);
+ 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/mtd-utils-1.3.1/mkfs.ubifs/lpt.h b/mtd-utils-1.3.1/mkfs.ubifs/lpt.h
new file mode 100644
index 0000000..4cde59d
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/mkfs.ubifs/mkfs.ubifs.c b/mtd-utils-1.3.1/mkfs.ubifs/mkfs.ubifs.c
new file mode 100644
index 0000000..e4b4e3c
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/mkfs.ubifs.c
@@ -0,0 +1,2343 @@
+/*
+ * 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
+ */
+
+#include "mkfs.ubifs.h"
+
+#define PROGRAM_VERSION "1.3"
+
+/* 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;
+
+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:h?vVe:c:g:f:P:k:x:X:j:R:l:j:U";
+
+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'},
+ {"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'},
+ {"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"
+"-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"
+"-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 -R parameter specifies amount of bytes reserved for the super-user.\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;
+}
+
+/**
+ * same_dir - determine if two file descriptors refer to the same directory.
+ * @fd1: file descriptor 1
+ * @fd2: file descriptor 2
+ */
+static int same_dir(int fd1, int fd2)
+{
+ struct stat stat1, stat2;
+
+ if (fstat(fd1, &stat1) == -1)
+ return -1;
+ if (fstat(fd2, &stat2) == -1)
+ return -1;
+ return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino;
+}
+
+/**
+ * do_openat - open a file in a directory.
+ * @fd: file descriptor of open directory
+ * @path: path relative to directory
+ * @flags: open flags
+ *
+ * This function is provided because the library function openat is sometimes
+ * not available.
+ */
+static int do_openat(int fd, const char *path, int flags)
+{
+ int ret;
+ char *cwd;
+
+ cwd = getcwd(NULL, 0);
+ if (!cwd)
+ return -1;
+ ret = fchdir(fd);
+ if (ret != -1)
+ ret = open(path, flags);
+ if (chdir(cwd) && !ret)
+ ret = -1;
+ free(cwd);
+ return ret;
+}
+
+/**
+ * in_path - determine if a file is beneath a directory.
+ * @dir_name: directory path name
+ * @file_name: file path name
+ */
+static int in_path(const char *dir_name, const char *file_name)
+{
+ char *fn = strdup(file_name);
+ char *dn;
+ int fd1, fd2, fd3, ret = -1, top_fd;
+
+ if (!fn)
+ return -1;
+ top_fd = open("/", O_RDONLY);
+ if (top_fd != -1) {
+ dn = dirname(fn);
+ fd1 = open(dir_name, O_RDONLY);
+ if (fd1 != -1) {
+ fd2 = open(dn, O_RDONLY);
+ if (fd2 != -1) {
+ while (1) {
+ int same;
+
+ same = same_dir(fd1, fd2);
+ if (same) {
+ ret = same;
+ break;
+ }
+ if (same_dir(fd2, top_fd)) {
+ ret = 0;
+ break;
+ }
+ fd3 = do_openat(fd2, "..", O_RDONLY);
+ if (fd3 == -1)
+ break;
+ close(fd2);
+ fd2 = fd3;
+ }
+ close(fd2);
+ }
+ close(fd1);
+ }
+ close(top_fd);
+ }
+ free(fn);
+ 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 inline int is_power_of_2(unsigned long long n)
+{
+ return (n != 0 && ((n & (n - 1)) == 0));
+}
+
+static int validate_options(void)
+{
+ int tmp;
+
+ if (!output)
+ return err_msg("no output file or UBI volume specified");
+ if (root && in_path(root, output))
+ 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->min_io_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 > 1024*1024)
+ return err_msg("too large LEB size %d", c->leb_size);
+ 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 = strdup(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 'h':
+ case '?':
+ printf("%s", helptext);
+ exit(0);
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf("Version " PROGRAM_VERSION "\n");
+ 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 '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 = strdup(argv[optind]);
+ if (output)
+ out_ubi = !open_ubi(output);
+
+ if (out_ubi) {
+ c->min_io_size = c->di.min_io_size;
+ c->leb_size = c->vi.leb_size;
+ c->max_leb_cnt = c->vi.rsvd_lebs;
+ }
+
+ if (!output)
+ return err_msg("not output device or file specified");
+
+ 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);
+ }
+
+ 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 = ubifs_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)
+ * @dtype: expected data type
+ */
+int write_leb(int lnum, int len, void *buf, int dtype)
+{
+ off64_t pos = (off64_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, dtype))
+ return sys_err_msg("ubi_leb_change_start failed");
+
+ if (lseek64(out_fd, pos, SEEK_SET) != pos)
+ return sys_err_msg("lseek64 failed seeking %lld",
+ (long long)pos);
+
+ if (write(out_fd, buf, c->leb_size) != c->leb_size)
+ return sys_err_msg("write failed writing %d bytes at pos %lld",
+ c->leb_size, (long long)pos);
+
+ return 0;
+}
+
+/**
+ * write_empty_leb - copy the image of an empty LEB to the output target.
+ * @lnum: LEB number
+ * @dtype: expected data type
+ */
+static int write_empty_leb(int lnum, int dtype)
+{
+ return write_leb(lnum, 0, leb_buf, dtype);
+}
+
+/**
+ * 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 = ubifs_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
+ * @dtype: expected data type
+ */
+static int write_node(void *node, int len, int lnum, int dtype)
+{
+ prepare_node(node, len);
+
+ memcpy(leb_buf, node, len);
+
+ len = do_pad(leb_buf, len);
+
+ return write_leb(lnum, len, leb_buf, dtype);
+}
+
+/**
+ * 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);
+ 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, UBI_UNKNOWN);
+ 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, inum,
+ (unsigned)type, 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;
+
+ 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_uid = root_st.st_gid = 0;
+ root_st.st_mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+
+ 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, 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, UBI_LONGTERM);
+ 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)
+ /* TODO: in this case it segfaults because buffer overruns - we
+ * somewhere allocate smaller buffers - fix */
+ 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);
+
+ return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM, UBI_LONGTERM);
+}
+
+/**
+ * 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,
+ UBI_SHORTTERM);
+ if (err)
+ return err;
+
+ err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
+ UBI_SHORTTERM);
+ 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, UBI_UNKNOWN);
+ if (err)
+ return err;
+
+ lnum += 1;
+
+ for (i = 1; i < c->log_lebs; i++, lnum++) {
+ err = write_empty_leb(lnum, UBI_UNKNOWN);
+ 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++, UBI_SHORTTERM);
+ 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, UBI_SHORTTERM);
+ 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_PROP_DIRECT_WRITE, 1))
+ return sys_err_msg("ubi_set_property failed");
+
+ if (check_volume_empty())
+ 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/mtd-utils-1.3.1/mkfs.ubifs/mkfs.ubifs.h b/mtd-utils-1.3.1/mkfs.ubifs/mkfs.ubifs.h
new file mode 100644
index 0000000..16b34c7
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/mkfs.ubifs.h
@@ -0,0 +1,148 @@
+/*
+ * 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__
+
+#define _GNU_SOURCE
+#define _LARGEFILE64_SOURCE
+#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 "libubi.h"
+#include "crc32.h"
+#include "defs.h"
+#include "crc16.h"
+#include "ubifs-media.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 dtype);
+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/mtd-utils-1.3.1/mkfs.ubifs/ubifs-media.h b/mtd-utils-1.3.1/mkfs.ubifs/ubifs-media.h
new file mode 100644
index 0000000..a9ecbd9
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/ubifs-media.h
@@ -0,0 +1,745 @@
+/*
+ * 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 file describes UBIFS on-flash format and contains definitions of all the
+ * relevant data structures and constants.
+ *
+ * All UBIFS on-flash objects are stored in the form of nodes. All nodes start
+ * with the UBIFS node magic number and have the same common header. Nodes
+ * always sit at 8-byte aligned positions on the media and node header sizes are
+ * also 8-byte aligned (except for the indexing node and the padding node).
+ */
+
+#ifndef __UBIFS_MEDIA_H__
+#define __UBIFS_MEDIA_H__
+
+/* UBIFS node magic number (must not have the padding byte first or last) */
+#define UBIFS_NODE_MAGIC 0x06101831
+
+/* UBIFS on-flash format version */
+#define UBIFS_FORMAT_VERSION 4
+
+/* Minimum logical eraseblock size in bytes */
+#define UBIFS_MIN_LEB_SZ (15*1024)
+
+/* Initial CRC32 value used when calculating CRC checksums */
+#define UBIFS_CRC32_INIT 0xFFFFFFFFU
+
+/*
+ * UBIFS does not try to compress data if its length is less than the below
+ * constant.
+ */
+#define UBIFS_MIN_COMPR_LEN 128
+
+/* Root inode number */
+#define UBIFS_ROOT_INO 1
+
+/* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */
+#define UBIFS_FIRST_INO 64
+
+/*
+ * Maximum file name and extended attribute length (must be a multiple of 8,
+ * minus 1).
+ */
+#define UBIFS_MAX_NLEN 255
+
+/* Maximum number of data journal heads */
+#define UBIFS_MAX_JHEADS 1
+
+/*
+ * Size of UBIFS data block. Note, UBIFS is not a block oriented file-system,
+ * which means that it does not treat the underlying media as consisting of
+ * blocks like in case of hard drives. Do not be confused. UBIFS block is just
+ * the maximum amount of data which one data node can have or which can be
+ * attached to an inode node.
+ */
+#define UBIFS_BLOCK_SIZE 4096
+#define UBIFS_BLOCK_SHIFT 12
+#define UBIFS_BLOCK_MASK 0x00000FFF
+
+/* UBIFS padding byte pattern (must not be first or last byte of node magic) */
+#define UBIFS_PADDING_BYTE 0xCE
+
+/* Maximum possible key length */
+#define UBIFS_MAX_KEY_LEN 16
+
+/* Key length ("simple" format) */
+#define UBIFS_SK_LEN 8
+
+/* Minimum index tree fanout */
+#define UBIFS_MIN_FANOUT 3
+
+/* Maximum number of levels in UBIFS indexing B-tree */
+#define UBIFS_MAX_LEVELS 512
+
+/* Maximum amount of data attached to an inode in bytes */
+#define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE
+
+/* LEB Properties Tree fanout (must be power of 2) and fanout shift */
+#define UBIFS_LPT_FANOUT 4
+#define UBIFS_LPT_FANOUT_SHIFT 2
+
+/* LEB Properties Tree bit field sizes */
+#define UBIFS_LPT_CRC_BITS 16
+#define UBIFS_LPT_CRC_BYTES 2
+#define UBIFS_LPT_TYPE_BITS 4
+
+/* The key is always at the same position in all keyed nodes */
+#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key)
+
+/*
+ * LEB Properties Tree node types.
+ *
+ * UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties)
+ * UBIFS_LPT_NNODE: LPT internal node
+ * UBIFS_LPT_LTAB: LPT's own lprops table
+ * UBIFS_LPT_LSAVE: LPT's save table (big model only)
+ * UBIFS_LPT_NODE_CNT: count of LPT node types
+ * UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type
+ */
+enum {
+ UBIFS_LPT_PNODE,
+ UBIFS_LPT_NNODE,
+ UBIFS_LPT_LTAB,
+ UBIFS_LPT_LSAVE,
+ UBIFS_LPT_NODE_CNT,
+ UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1,
+};
+
+/*
+ * UBIFS inode types.
+ *
+ * UBIFS_ITYPE_REG: regular file
+ * UBIFS_ITYPE_DIR: directory
+ * UBIFS_ITYPE_LNK: soft link
+ * UBIFS_ITYPE_BLK: block device node
+ * UBIFS_ITYPE_CHR: character device node
+ * UBIFS_ITYPE_FIFO: fifo
+ * UBIFS_ITYPE_SOCK: socket
+ * UBIFS_ITYPES_CNT: count of supported file types
+ */
+enum {
+ UBIFS_ITYPE_REG,
+ UBIFS_ITYPE_DIR,
+ UBIFS_ITYPE_LNK,
+ UBIFS_ITYPE_BLK,
+ UBIFS_ITYPE_CHR,
+ UBIFS_ITYPE_FIFO,
+ UBIFS_ITYPE_SOCK,
+ UBIFS_ITYPES_CNT,
+};
+
+/*
+ * Supported key hash functions.
+ *
+ * UBIFS_KEY_HASH_R5: R5 hash
+ * UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name
+ */
+enum {
+ UBIFS_KEY_HASH_R5,
+ UBIFS_KEY_HASH_TEST,
+};
+
+/*
+ * Supported key formats.
+ *
+ * UBIFS_SIMPLE_KEY_FMT: simple key format
+ */
+enum {
+ UBIFS_SIMPLE_KEY_FMT,
+};
+
+/*
+ * The simple key format uses 29 bits for storing UBIFS block number and hash
+ * value.
+ */
+#define UBIFS_S_KEY_BLOCK_BITS 29
+#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF
+#define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS
+#define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK
+
+/*
+ * Key types.
+ *
+ * UBIFS_INO_KEY: inode node key
+ * UBIFS_DATA_KEY: data node key
+ * UBIFS_DENT_KEY: directory entry node key
+ * UBIFS_XENT_KEY: extended attribute entry key
+ * UBIFS_KEY_TYPES_CNT: number of supported key types
+ */
+enum {
+ UBIFS_INO_KEY,
+ UBIFS_DATA_KEY,
+ UBIFS_DENT_KEY,
+ UBIFS_XENT_KEY,
+ UBIFS_KEY_TYPES_CNT,
+};
+
+/* Count of LEBs reserved for the superblock area */
+#define UBIFS_SB_LEBS 1
+/* Count of LEBs reserved for the master area */
+#define UBIFS_MST_LEBS 2
+
+/* First LEB of the superblock area */
+#define UBIFS_SB_LNUM 0
+/* First LEB of the master area */
+#define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS)
+/* First LEB of the log area */
+#define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS)
+
+/*
+ * The below constants define the absolute minimum values for various UBIFS
+ * media areas. Many of them actually depend of flash geometry and the FS
+ * configuration (number of journal heads, orphan LEBs, etc). This means that
+ * the smallest volume size which can be used for UBIFS cannot be pre-defined
+ * by these constants. The file-system that meets the below limitation will not
+ * necessarily mount. UBIFS does run-time calculations and validates the FS
+ * size.
+ */
+
+/* Minimum number of logical eraseblocks in the log */
+#define UBIFS_MIN_LOG_LEBS 2
+/* Minimum number of bud logical eraseblocks (one for each head) */
+#define UBIFS_MIN_BUD_LEBS 3
+/* Minimum number of journal logical eraseblocks */
+#define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS)
+/* Minimum number of LPT area logical eraseblocks */
+#define UBIFS_MIN_LPT_LEBS 2
+/* Minimum number of orphan area logical eraseblocks */
+#define UBIFS_MIN_ORPH_LEBS 1
+/*
+ * Minimum number of main area logical eraseblocks (buds, 3 for the index, 1
+ * for GC, 1 for deletions, and at least 1 for committed data).
+ */
+#define UBIFS_MIN_MAIN_LEBS (UBIFS_MIN_BUD_LEBS + 6)
+
+/* Minimum number of logical eraseblocks */
+#define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \
+ UBIFS_MIN_LOG_LEBS + UBIFS_MIN_LPT_LEBS + \
+ UBIFS_MIN_ORPH_LEBS + UBIFS_MIN_MAIN_LEBS)
+
+/* Node sizes (N.B. these are guaranteed to be multiples of 8) */
+#define UBIFS_CH_SZ sizeof(struct ubifs_ch)
+#define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node)
+#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node)
+#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node)
+#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node)
+#define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node)
+#define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node)
+#define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node)
+#define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node)
+#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node)
+#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node)
+#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
+/* Extended attribute entry nodes are identical to directory entry nodes */
+#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
+/* Only this does not have to be multiple of 8 bytes */
+#define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch)
+
+/* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */
+#define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE)
+#define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA)
+#define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1)
+#define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ
+
+/* The largest UBIFS node */
+#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ
+
+/*
+ * On-flash inode flags.
+ *
+ * UBIFS_COMPR_FL: use compression for this inode
+ * UBIFS_SYNC_FL: I/O on this inode has to be synchronous
+ * UBIFS_IMMUTABLE_FL: inode is immutable
+ * UBIFS_APPEND_FL: writes to the inode may only append data
+ * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous
+ * UBIFS_XATTR_FL: this inode is the inode for an extended attribute value
+ *
+ * Note, these are on-flash flags which correspond to ioctl flags
+ * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not
+ * have to be the same.
+ */
+enum {
+ UBIFS_COMPR_FL = 0x01,
+ UBIFS_SYNC_FL = 0x02,
+ UBIFS_IMMUTABLE_FL = 0x04,
+ UBIFS_APPEND_FL = 0x08,
+ UBIFS_DIRSYNC_FL = 0x10,
+ UBIFS_XATTR_FL = 0x20,
+};
+
+/* Inode flag bits used by UBIFS */
+#define UBIFS_FL_MASK 0x0000001F
+
+/*
+ * UBIFS compression algorithms.
+ *
+ * UBIFS_COMPR_NONE: no compression
+ * UBIFS_COMPR_LZO: LZO compression
+ * UBIFS_COMPR_ZLIB: ZLIB compression
+ * UBIFS_COMPR_TYPES_CNT: count of supported compression types
+ */
+enum {
+ UBIFS_COMPR_NONE,
+ UBIFS_COMPR_LZO,
+ UBIFS_COMPR_ZLIB,
+ UBIFS_COMPR_TYPES_CNT,
+};
+
+/*
+ * UBIFS node types.
+ *
+ * UBIFS_INO_NODE: inode node
+ * UBIFS_DATA_NODE: data node
+ * UBIFS_DENT_NODE: directory entry node
+ * UBIFS_XENT_NODE: extended attribute node
+ * UBIFS_TRUN_NODE: truncation node
+ * UBIFS_PAD_NODE: padding node
+ * UBIFS_SB_NODE: superblock node
+ * UBIFS_MST_NODE: master node
+ * UBIFS_REF_NODE: LEB reference node
+ * UBIFS_IDX_NODE: index node
+ * UBIFS_CS_NODE: commit start node
+ * UBIFS_ORPH_NODE: orphan node
+ * UBIFS_NODE_TYPES_CNT: count of supported node types
+ *
+ * Note, we index arrays by these numbers, so keep them low and contiguous.
+ * Node type constants for inodes, direntries and so on have to be the same as
+ * corresponding key type constants.
+ */
+enum {
+ UBIFS_INO_NODE,
+ UBIFS_DATA_NODE,
+ UBIFS_DENT_NODE,
+ UBIFS_XENT_NODE,
+ UBIFS_TRUN_NODE,
+ UBIFS_PAD_NODE,
+ UBIFS_SB_NODE,
+ UBIFS_MST_NODE,
+ UBIFS_REF_NODE,
+ UBIFS_IDX_NODE,
+ UBIFS_CS_NODE,
+ UBIFS_ORPH_NODE,
+ UBIFS_NODE_TYPES_CNT,
+};
+
+/*
+ * Master node flags.
+ *
+ * UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty
+ * UBIFS_MST_NO_ORPHS: no orphan inodes present
+ * UBIFS_MST_RCVRY: written by recovery
+ */
+enum {
+ UBIFS_MST_DIRTY = 1,
+ UBIFS_MST_NO_ORPHS = 2,
+ UBIFS_MST_RCVRY = 4,
+};
+
+/*
+ * Node group type (used by recovery to recover whole group or none).
+ *
+ * UBIFS_NO_NODE_GROUP: this node is not part of a group
+ * UBIFS_IN_NODE_GROUP: this node is a part of a group
+ * UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group
+ */
+enum {
+ UBIFS_NO_NODE_GROUP = 0,
+ UBIFS_IN_NODE_GROUP,
+ UBIFS_LAST_OF_NODE_GROUP,
+};
+
+/*
+ * Superblock flags.
+ *
+ * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
+ */
+enum {
+ UBIFS_FLG_BIGLPT = 0x02,
+};
+
+/**
+ * struct ubifs_ch - common header node.
+ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
+ * @crc: CRC-32 checksum of the node header
+ * @sqnum: sequence number
+ * @len: full node length
+ * @node_type: node type
+ * @group_type: node group type
+ * @padding: reserved for future, zeroes
+ *
+ * Every UBIFS node starts with this common part. If the node has a key, the
+ * key always goes next.
+ */
+struct ubifs_ch {
+ __le32 magic;
+ __le32 crc;
+ __le64 sqnum;
+ __le32 len;
+ __u8 node_type;
+ __u8 group_type;
+ __u8 padding[2];
+} __attribute__ ((packed));
+
+/**
+ * union ubifs_dev_desc - device node descriptor.
+ * @new: new type device descriptor
+ * @huge: huge type device descriptor
+ *
+ * This data structure describes major/minor numbers of a device node. In an
+ * inode is a device node then its data contains an object of this type. UBIFS
+ * uses standard Linux "new" and "huge" device node encodings.
+ */
+union ubifs_dev_desc {
+ __le32 new;
+ __le64 huge;
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_ino_node - inode node.
+ * @ch: common header
+ * @key: node key
+ * @creat_sqnum: sequence number at time of creation
+ * @size: inode size in bytes (amount of uncompressed data)
+ * @atime_sec: access time seconds
+ * @ctime_sec: creation time seconds
+ * @mtime_sec: modification time seconds
+ * @atime_nsec: access time nanoseconds
+ * @ctime_nsec: creation time nanoseconds
+ * @mtime_nsec: modification time nanoseconds
+ * @nlink: number of hard links
+ * @uid: owner ID
+ * @gid: group ID
+ * @mode: access flags
+ * @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc)
+ * @data_len: inode data length
+ * @xattr_cnt: count of extended attributes this inode has
+ * @xattr_size: summarized size of all extended attributes in bytes
+ * @padding1: reserved for future, zeroes
+ * @xattr_names: sum of lengths of all extended attribute names belonging to
+ * this inode
+ * @compr_type: compression type used for this inode
+ * @padding2: reserved for future, zeroes
+ * @data: data attached to the inode
+ *
+ * Note, even though inode compression type is defined by @compr_type, some
+ * nodes of this inode may be compressed with different compressor - this
+ * happens if compression type is changed while the inode already has data
+ * nodes. But @compr_type will be use for further writes to the inode.
+ *
+ * Note, do not forget to amend 'zero_ino_node_unused()' function when changing
+ * the padding fields.
+ */
+struct ubifs_ino_node {
+ struct ubifs_ch ch;
+ __u8 key[UBIFS_MAX_KEY_LEN];
+ __le64 creat_sqnum;
+ __le64 size;
+ __le64 atime_sec;
+ __le64 ctime_sec;
+ __le64 mtime_sec;
+ __le32 atime_nsec;
+ __le32 ctime_nsec;
+ __le32 mtime_nsec;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le32 flags;
+ __le32 data_len;
+ __le32 xattr_cnt;
+ __le32 xattr_size;
+ __u8 padding1[4]; /* Watch 'zero_ino_node_unused()' if changing! */
+ __le32 xattr_names;
+ __le16 compr_type;
+ __u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */
+ __u8 data[];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_dent_node - directory entry node.
+ * @ch: common header
+ * @key: node key
+ * @inum: target inode number
+ * @padding1: reserved for future, zeroes
+ * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc)
+ * @nlen: name length
+ * @padding2: reserved for future, zeroes
+ * @name: zero-terminated name
+ *
+ * Note, do not forget to amend 'zero_dent_node_unused()' function when
+ * changing the padding fields.
+ */
+struct ubifs_dent_node {
+ struct ubifs_ch ch;
+ __u8 key[UBIFS_MAX_KEY_LEN];
+ __le64 inum;
+ __u8 padding1;
+ __u8 type;
+ __le16 nlen;
+ __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
+ __u8 name[];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_data_node - data node.
+ * @ch: common header
+ * @key: node key
+ * @size: uncompressed data size in bytes
+ * @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc)
+ * @padding: reserved for future, zeroes
+ * @data: data
+ *
+ * Note, do not forget to amend 'zero_data_node_unused()' function when
+ * changing the padding fields.
+ */
+struct ubifs_data_node {
+ struct ubifs_ch ch;
+ __u8 key[UBIFS_MAX_KEY_LEN];
+ __le32 size;
+ __le16 compr_type;
+ __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
+ __u8 data[];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_trun_node - truncation node.
+ * @ch: common header
+ * @inum: truncated inode number
+ * @padding: reserved for future, zeroes
+ * @old_size: size before truncation
+ * @new_size: size after truncation
+ *
+ * This node exists only in the journal and never goes to the main area. Note,
+ * do not forget to amend 'zero_trun_node_unused()' function when changing the
+ * padding fields.
+ */
+struct ubifs_trun_node {
+ struct ubifs_ch ch;
+ __le32 inum;
+ __u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */
+ __le64 old_size;
+ __le64 new_size;
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_pad_node - padding node.
+ * @ch: common header
+ * @pad_len: how many bytes after this node are unused (because padded)
+ * @padding: reserved for future, zeroes
+ */
+struct ubifs_pad_node {
+ struct ubifs_ch ch;
+ __le32 pad_len;
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_sb_node - superblock node.
+ * @ch: common header
+ * @padding: reserved for future, zeroes
+ * @key_hash: type of hash function used in keys
+ * @key_fmt: format of the key
+ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
+ * @min_io_size: minimal input/output unit size
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_cnt: count of LEBs used by file-system
+ * @max_leb_cnt: maximum count of LEBs used by file-system
+ * @max_bud_bytes: maximum amount of data stored in buds
+ * @log_lebs: log size in logical eraseblocks
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @orph_lebs: number of LEBs used for recording orphans
+ * @jhead_cnt: count of journal heads
+ * @fanout: tree fanout (max. number of links per indexing node)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @fmt_version: UBIFS on-flash format version
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @padding1: reserved for future, zeroes
+ * @rp_uid: reserve pool UID
+ * @rp_gid: reserve pool GID
+ * @rp_size: size of the reserved pool in bytes
+ * @padding2: reserved for future, zeroes
+ * @time_gran: time granularity in nanoseconds
+ * @uuid: UUID generated when the file system image was created
+ */
+struct ubifs_sb_node {
+ struct ubifs_ch ch;
+ __u8 padding[2];
+ __u8 key_hash;
+ __u8 key_fmt;
+ __le32 flags;
+ __le32 min_io_size;
+ __le32 leb_size;
+ __le32 leb_cnt;
+ __le32 max_leb_cnt;
+ __le64 max_bud_bytes;
+ __le32 log_lebs;
+ __le32 lpt_lebs;
+ __le32 orph_lebs;
+ __le32 jhead_cnt;
+ __le32 fanout;
+ __le32 lsave_cnt;
+ __le32 fmt_version;
+ __le16 default_compr;
+ __u8 padding1[2];
+ __le32 rp_uid;
+ __le32 rp_gid;
+ __le64 rp_size;
+ __le32 time_gran;
+ __u8 uuid[16];
+ __u8 padding2[3972];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_mst_node - master node.
+ * @ch: common header
+ * @highest_inum: highest inode number in the committed index
+ * @cmt_no: commit number
+ * @flags: various flags (%UBIFS_MST_DIRTY, etc)
+ * @log_lnum: start of the log
+ * @root_lnum: LEB number of the root indexing node
+ * @root_offs: offset within @root_lnum
+ * @root_len: root indexing node length
+ * @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was
+ * not reserved and should be reserved on mount)
+ * @ihead_lnum: LEB number of index head
+ * @ihead_offs: offset of index head
+ * @index_size: size of index on flash
+ * @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)
+ * @lpt_lnum: LEB number of LPT root nnode
+ * @lpt_offs: offset of LPT root nnode
+ * @nhead_lnum: LEB number of LPT head
+ * @nhead_offs: offset of LPT head
+ * @ltab_lnum: LEB number of LPT's own lprops table
+ * @ltab_offs: offset of LPT's own lprops table
+ * @lsave_lnum: LEB number of LPT's save table (big model only)
+ * @lsave_offs: offset of LPT's save table (big model only)
+ * @lscan_lnum: LEB number of last LPT scan
+ * @empty_lebs: number of empty logical eraseblocks
+ * @idx_lebs: number of indexing logical eraseblocks
+ * @leb_cnt: count of LEBs used by file-system
+ * @padding: reserved for future, zeroes
+ */
+struct ubifs_mst_node {
+ struct ubifs_ch ch;
+ __le64 highest_inum;
+ __le64 cmt_no;
+ __le32 flags;
+ __le32 log_lnum;
+ __le32 root_lnum;
+ __le32 root_offs;
+ __le32 root_len;
+ __le32 gc_lnum;
+ __le32 ihead_lnum;
+ __le32 ihead_offs;
+ __le64 index_size;
+ __le64 total_free;
+ __le64 total_dirty;
+ __le64 total_used;
+ __le64 total_dead;
+ __le64 total_dark;
+ __le32 lpt_lnum;
+ __le32 lpt_offs;
+ __le32 nhead_lnum;
+ __le32 nhead_offs;
+ __le32 ltab_lnum;
+ __le32 ltab_offs;
+ __le32 lsave_lnum;
+ __le32 lsave_offs;
+ __le32 lscan_lnum;
+ __le32 empty_lebs;
+ __le32 idx_lebs;
+ __le32 leb_cnt;
+ __u8 padding[344];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_ref_node - logical eraseblock reference node.
+ * @ch: common header
+ * @lnum: the referred logical eraseblock number
+ * @offs: start offset in the referred LEB
+ * @jhead: journal head number
+ * @padding: reserved for future, zeroes
+ */
+struct ubifs_ref_node {
+ struct ubifs_ch ch;
+ __le32 lnum;
+ __le32 offs;
+ __le32 jhead;
+ __u8 padding[28];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_branch - key/reference/length branch
+ * @lnum: LEB number of the target node
+ * @offs: offset within @lnum
+ * @len: target node length
+ * @key: key
+ */
+struct ubifs_branch {
+ __le32 lnum;
+ __le32 offs;
+ __le32 len;
+ __u8 key[];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_idx_node - indexing node.
+ * @ch: common header
+ * @child_cnt: number of child index nodes
+ * @level: tree level
+ * @branches: LEB number / offset / length / key branches
+ */
+struct ubifs_idx_node {
+ struct ubifs_ch ch;
+ __le16 child_cnt;
+ __le16 level;
+ __u8 branches[];
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_cs_node - commit start node.
+ * @ch: common header
+ * @cmt_no: commit number
+ */
+struct ubifs_cs_node {
+ struct ubifs_ch ch;
+ __le64 cmt_no;
+} __attribute__ ((packed));
+
+/**
+ * struct ubifs_orph_node - orphan node.
+ * @ch: common header
+ * @cmt_no: commit number (also top bit is set on the last node of the commit)
+ * @inos: inode numbers of orphans
+ */
+struct ubifs_orph_node {
+ struct ubifs_ch ch;
+ __le64 cmt_no;
+ __le64 inos[];
+} __attribute__ ((packed));
+
+#endif /* __UBIFS_MEDIA_H__ */
diff --git a/mtd-utils-1.3.1/mkfs.ubifs/ubifs.h b/mtd-utils-1.3.1/mkfs.ubifs/ubifs.h
new file mode 100644
index 0000000..5c29046
--- /dev/null
+++ b/mtd-utils-1.3.1/mkfs.ubifs/ubifs.h
@@ -0,0 +1,436 @@
+/*
+ * 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__
+
+/* 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
+ * @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;
+ 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-utils-1.3.1/mtd-utils.spec b/mtd-utils-1.3.1/mtd-utils.spec
new file mode 100644
index 0000000..606a6a2
--- /dev/null
+++ b/mtd-utils-1.3.1/mtd-utils.spec
@@ -0,0 +1,40 @@
+Summary: Tools for maintaining Memory Technology Devices
+Name: mtd-utils
+Version: 1.0
+Release: 1
+License: GPL
+Group: Applications/System
+URL: http://www.linux-mtd.infradead.org/
+Source0: %{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+%description
+This package contains tools for erasing and formatting flash devices,
+including JFFS2, M-Systems DiskOnChip devices, etc.
+
+%prep
+%setup -q
+
+%build
+make -C util
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR=$RPM_BUILD_ROOT -C util install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-,root,root,-)
+/usr/sbin
+/usr/man/man1/mkfs.jffs2.1.gz
+/usr/include/mtd
+%doc
+
+
+%changelog
+* Wed May 5 2004 <dwmw2@infradead.org> - 1.0
+- Initial build.
+
diff --git a/mtd-utils-1.3.1/mtd_debug.c b/mtd-utils-1.3.1/mtd_debug.c
new file mode 100644
index 0000000..85d48e9
--- /dev/null
+++ b/mtd-utils-1.3.1/mtd_debug.c
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <mtd/mtd-user.h>
+
+/*
+ * MEMGETINFO
+ */
+static int getmeminfo (int fd,struct mtd_info_user *mtd)
+{
+ return (ioctl (fd,MEMGETINFO,mtd));
+}
+
+/*
+ * MEMERASE
+ */
+static int memerase (int fd,struct erase_info_user *erase)
+{
+ return (ioctl (fd,MEMERASE,erase));
+}
+
+/*
+ * MEMGETREGIONCOUNT
+ * MEMGETREGIONINFO
+ */
+static int getregions (int fd,struct region_info_user *regions,int *n)
+{
+ int i,err;
+ err = ioctl (fd,MEMGETREGIONCOUNT,n);
+ if (err) return (err);
+ for (i = 0; i < *n; i++)
+ {
+ regions[i].regionindex = i;
+ err = ioctl (fd,MEMGETREGIONINFO,®ions[i]);
+ if (err) return (err);
+ }
+ return (0);
+}
+
+int erase_flash (int fd,u_int32_t offset,u_int32_t bytes)
+{
+ int err;
+ struct erase_info_user erase;
+ erase.start = offset;
+ erase.length = bytes;
+ err = memerase (fd,&erase);
+ if (err < 0)
+ {
+ perror ("MEMERASE");
+ return (1);
+ }
+ fprintf (stderr,"Erased %d bytes from address 0x%.8x in flash\n",bytes,offset);
+ return (0);
+}
+
+void printsize (u_int32_t x)
+{
+ int i;
+ static const char *flags = "KMGT";
+ printf ("%u ",x);
+ for (i = 0; x >= 1024 && flags[i] != '\0'; i++) x /= 1024;
+ i--;
+ if (i >= 0) printf ("(%u%c)",x,flags[i]);
+}
+
+int flash_to_file (int fd,u_int32_t offset,size_t len,const char *filename)
+{
+ u_int8_t *buf = NULL;
+ int outfd,err;
+ int size = len * sizeof (u_int8_t);
+ int n = len;
+
+ if (offset != lseek (fd,offset,SEEK_SET))
+ {
+ perror ("lseek()");
+ goto err0;
+ }
+ outfd = creat (filename,O_WRONLY);
+ if (outfd < 0)
+ {
+ perror ("creat()");
+ goto err1;
+ }
+
+retry:
+ if ((buf = (u_int8_t *) malloc (size)) == NULL)
+ {
+#define BUF_SIZE (64 * 1024 * sizeof (u_int8_t))
+ fprintf (stderr, "%s: malloc(%#x)\n", __FUNCTION__, size);
+ if (size != BUF_SIZE) {
+ size = BUF_SIZE;
+ fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size);
+ goto retry;
+ }
+ perror ("malloc()");
+ goto err0;
+ }
+ do {
+ if (n <= size)
+ size = n;
+ err = read (fd,buf,size);
+ if (err < 0)
+ {
+ fprintf (stderr, "%s: read, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("read()");
+ goto err2;
+ }
+ err = write (outfd,buf,size);
+ if (err < 0)
+ {
+ fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("write()");
+ goto err2;
+ }
+ if (err != size)
+ {
+ fprintf (stderr,"Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n",filename,err,size);
+ goto err2;
+ }
+ n -= size;
+ } while (n > 0);
+
+ if (buf != NULL)
+ free (buf);
+ close (outfd);
+ printf ("Copied %d bytes from address 0x%.8x in flash to %s\n",len,offset,filename);
+ return (0);
+
+err2:
+ close (outfd);
+err1:
+ if (buf != NULL)
+ free (buf);
+err0:
+ return (1);
+}
+
+int file_to_flash (int fd,u_int32_t offset,u_int32_t len,const char *filename)
+{
+ u_int8_t *buf = NULL;
+ FILE *fp;
+ int err;
+ int size = len * sizeof (u_int8_t);
+ int n = len;
+
+ if (offset != lseek (fd,offset,SEEK_SET))
+ {
+ perror ("lseek()");
+ return (1);
+ }
+ if ((fp = fopen (filename,"r")) == NULL)
+ {
+ perror ("fopen()");
+ return (1);
+ }
+retry:
+ if ((buf = (u_int8_t *) malloc (size)) == NULL)
+ {
+ fprintf (stderr, "%s: malloc(%#x) failed\n", __FUNCTION__, size);
+ if (size != BUF_SIZE) {
+ size = BUF_SIZE;
+ fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size);
+ goto retry;
+ }
+ perror ("malloc()");
+ fclose (fp);
+ return (1);
+ }
+ do {
+ if (n <= size)
+ size = n;
+ if (fread (buf,size,1,fp) != 1 || ferror (fp))
+ {
+ fprintf (stderr, "%s: fread, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("fread()");
+ free (buf);
+ fclose (fp);
+ return (1);
+ }
+ err = write (fd,buf,size);
+ if (err < 0)
+ {
+ fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("write()");
+ free (buf);
+ fclose (fp);
+ return (1);
+ }
+ n -= size;
+ } while (n > 0);
+
+ if (buf != NULL)
+ free (buf);
+ fclose (fp);
+ printf ("Copied %d bytes from %s to address 0x%.8x in flash\n",len,filename,offset);
+ return (0);
+}
+
+int showinfo (int fd)
+{
+ int i,err,n;
+ struct mtd_info_user mtd;
+ static struct region_info_user region[1024];
+
+ err = getmeminfo (fd,&mtd);
+ if (err < 0)
+ {
+ perror ("MEMGETINFO");
+ return (1);
+ }
+
+ err = getregions (fd,region,&n);
+ if (err < 0)
+ {
+ perror ("MEMGETREGIONCOUNT");
+ return (1);
+ }
+
+ printf ("mtd.type = ");
+ switch (mtd.type)
+ {
+ case MTD_ABSENT:
+ printf ("MTD_ABSENT");
+ break;
+ case MTD_RAM:
+ printf ("MTD_RAM");
+ break;
+ case MTD_ROM:
+ printf ("MTD_ROM");
+ break;
+ case MTD_NORFLASH:
+ printf ("MTD_NORFLASH");
+ break;
+ case MTD_NANDFLASH:
+ printf ("MTD_NANDFLASH");
+ break;
+ case MTD_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_STUPID_LOCK", MTD_STUPID_LOCK },
+ { NULL, -1 }
+ };
+ for (i = 0; flags[i].name != NULL; i++)
+ if (mtd.flags & flags[i].value)
+ {
+ if (first)
+ {
+ printf (flags[i].name);
+ first = 0;
+ }
+ else printf (" | %s",flags[i].name);
+ }
+ }
+
+ printf ("\nmtd.size = ");
+ printsize (mtd.size);
+
+ printf ("\nmtd.erasesize = ");
+ printsize (mtd.erasesize);
+
+ printf ("\nmtd.writesize = ");
+ printsize (mtd.writesize);
+
+ printf ("\nmtd.oobsize = ");
+ printsize (mtd.oobsize);
+
+ printf ("\n"
+ "regions = %d\n"
+ "\n",
+ n);
+
+ for (i = 0; i < n; i++)
+ {
+ printf ("region[%d].offset = 0x%.8x\n"
+ "region[%d].erasesize = ",
+ i,region[i].offset,i);
+ printsize (region[i].erasesize);
+ printf ("\nregion[%d].numblocks = %d\n"
+ "region[%d].regionindex = %d\n",
+ i,region[i].numblocks,
+ i,region[i].regionindex);
+ }
+ return (0);
+}
+
+void showusage (const char *progname)
+{
+ fprintf (stderr,
+ "usage: %s info <device>\n"
+ " %s read <device> <offset> <len> <dest-filename>\n"
+ " %s write <device> <offset> <len> <source-filename>\n"
+ " %s erase <device> <offset> <len>\n",
+ progname,
+ progname,
+ progname,
+ progname);
+ exit (1);
+}
+
+#define OPT_INFO 1
+#define OPT_READ 2
+#define OPT_WRITE 3
+#define OPT_ERASE 4
+
+int main (int argc,char *argv[])
+{
+ const char *progname;
+ int err = 0,fd,option = OPT_INFO;
+ int open_flag;
+ (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);
+
+ /* parse command-line options */
+ if (argc == 3 && !strcmp (argv[1],"info"))
+ option = OPT_INFO;
+ else if (argc == 6 && !strcmp (argv[1],"read"))
+ option = OPT_READ;
+ else if (argc == 6 && !strcmp (argv[1],"write"))
+ option = OPT_WRITE;
+ else if (argc == 5 && !strcmp (argv[1],"erase"))
+ option = OPT_ERASE;
+ else
+ showusage (progname);
+
+ /* open device */
+ open_flag = (option==OPT_INFO || option==OPT_READ) ? O_RDONLY : O_RDWR;
+ if ((fd = open (argv[2],O_SYNC | open_flag)) < 0)
+ {
+ perror ("open()");
+ exit (1);
+ }
+
+ switch (option)
+ {
+ case OPT_INFO:
+ showinfo (fd);
+ break;
+ case OPT_READ:
+ err = flash_to_file (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]);
+ break;
+ case OPT_WRITE:
+ err = file_to_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]);
+ break;
+ case OPT_ERASE:
+ err = erase_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0));
+ break;
+ }
+
+ /* close device */
+ if (close (fd) < 0)
+ perror ("close()");
+
+ exit (err);
+}
+
diff --git a/mtd-utils-1.3.1/nanddump.c b/mtd-utils-1.3.1/nanddump.c
new file mode 100644
index 0000000..398fcdc
--- /dev/null
+++ b/mtd-utils-1.3.1/nanddump.c
@@ -0,0 +1,437 @@
+/*
+ * 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 _GNU_SOURCE
+#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>
+
+#define PROGRAM "nanddump"
+#define VERSION "$Revision: 1.29 $"
+
+static struct nand_oobinfo none_oobinfo = {
+ .useecc = MTD_NANDECC_OFF,
+};
+
+static void display_help (void)
+{
+ printf(
+"Usage: nanddump [OPTIONS] MTD-device\n"
+"Dumps the contents of a nand mtd partition.\n"
+"\n"
+" --help Display this help and exit\n"
+" --version Output version information and exit\n"
+"-f file --file=file Dump to file\n"
+"-i --ignoreerrors Ignore errors\n"
+"-l length --length=length Length\n"
+"-n --noecc Read without error correction\n"
+"-o --omitoob Omit oob data\n"
+"-b --omitbad Omit bad blocks from the dump\n"
+"-p --prettyprint Print nice (hexdump)\n"
+"-q --quiet Don't display progress and status messages\n"
+"-s addr --startaddress=addr Start address\n"
+"-e --omitempty Omit empty pages\n"
+ );
+ exit(EXIT_SUCCESS);
+}
+
+static void display_version (void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ PROGRAM " comes with NO WARRANTY\n"
+ "to the extent permitted by law.\n"
+ "\n"
+ "You may redistribute copies of " PROGRAM "\n"
+ "under the terms of the GNU General Public Licence.\n"
+ "See the file `COPYING' for more information.\n");
+ exit(EXIT_SUCCESS);
+}
+
+// Option variables
+
+static bool ignoreerrors = false; // ignore errors
+static bool pretty_print = false; // print nice in ascii
+static bool noecc = false; // don't error correct
+static bool omitoob = false; // omit oob data
+static unsigned long start_addr; // start address
+static unsigned long length; // dump length
+static const char *mtddev; // mtd device name
+static const char *dumpfile; // dump file name
+static bool omitbad = false;
+static bool quiet = false; // suppress diagnostic output
+static bool omitempty = false; // don't output empty pages
+
+static void process_options (int argc, char * const argv[])
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "bs:f:il:opqne";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"file", required_argument, 0, 'f'},
+ {"ignoreerrors", no_argument, 0, 'i'},
+ {"prettyprint", no_argument, 0, 'p'},
+ {"omitoob", no_argument, 0, 'o'},
+ {"omitbad", no_argument, 0, 'b'},
+ {"startaddress", required_argument, 0, 's'},
+ {"length", required_argument, 0, 'l'},
+ {"noecc", no_argument, 0, 'n'},
+ {"quiet", no_argument, 0, 'q'},
+ {"omitempty", no_argument, 0, 'e'},
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ switch (option_index) {
+ case 0:
+ display_help();
+ break;
+ case 1:
+ display_version();
+ break;
+ }
+ break;
+ case 'b':
+ omitbad = true;
+ break;
+ case 's':
+ start_addr = strtol(optarg, NULL, 0);
+ break;
+ case 'f':
+ if (!(dumpfile = strdup(optarg))) {
+ perror("stddup");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'i':
+ ignoreerrors = true;
+ break;
+ case 'l':
+ length = strtol(optarg, NULL, 0);
+ break;
+ case 'o':
+ omitoob = true;
+ break;
+ case 'p':
+ pretty_print = true;
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 'n':
+ noecc = true;
+ break;
+ case 'e':
+ omitempty = true;
+ break;
+ case '?':
+ error++;
+ break;
+ }
+ }
+
+ 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 ((argc - optind) != 1 || error)
+ display_help ();
+
+ mtddev = argv[optind];
+}
+
+/*
+ * Main program
+ */
+int main(int argc, char * const argv[])
+{
+ unsigned long ofs, end_addr = 0;
+ unsigned long long blockstart = 1;
+ int ret, i, fd, ofd = 0, bs, badblock = 0;
+ struct mtd_oob_buf oob;
+ mtd_info_t meminfo;
+ char pretty_buf[80];
+ int oobinfochanged = 0 ;
+ struct nand_oobinfo old_oobinfo;
+ struct mtd_ecc_stats stat1, stat2;
+ bool eccstats = false;
+ bool isempty;
+ unsigned char *readbuf = NULL, *oobbuf = NULL;
+
+ process_options(argc, argv);
+
+ /* Open MTD device */
+ if ((fd = open(mtddev, O_RDONLY)) == -1) {
+ perror(mtddev);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Fill in MTD device capability structure */
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("MEMGETINFO");
+ close(fd);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Allocate buffers */
+ oobbuf = malloc(sizeof(oobbuf) * meminfo.oobsize);
+ readbuf = malloc(sizeof(readbuf) * meminfo.writesize);
+
+ if (oobbuf == NULL || readbuf == NULL)
+ goto closeall;
+
+ /* Fill in oob info */
+ oob.start = 0;
+ oob.length = meminfo.oobsize;
+ oob.ptr = oobbuf;
+
+ if (noecc) {
+ ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW);
+ if (ret == 0) {
+ oobinfochanged = 2;
+ } else {
+ switch (errno) {
+ case ENOTTY:
+ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMGETOOBSEL");
+ goto closeall;
+ }
+ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ goto closeall;
+ }
+ oobinfochanged = 1;
+ break;
+ default:
+ 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;
+ }
+
+ /* Initialize start/end addresses and block size */
+ if (length)
+ end_addr = start_addr + length;
+ if (!length || end_addr > meminfo.size)
+ end_addr = meminfo.size;
+
+ bs = meminfo.writesize;
+
+ /* Print informative message */
+
+ if (!quiet) {
+ fprintf(stderr, "Block size %u, page size %u, OOB size %u\n",
+ meminfo.erasesize, meminfo.writesize, meminfo.oobsize);
+ fprintf(stderr,
+ "Dumping data starting at 0x%08x and ending at 0x%08x...\n",
+ (unsigned int) start_addr, (unsigned int) end_addr);
+ }
+ /* Dump the flash contents */
+ for (ofs = start_addr; ofs < end_addr ; ofs+=bs) {
+
+ // new eraseblock , check for bad block
+ if (blockstart != (ofs & (~meminfo.erasesize + 1))) {
+ blockstart = ofs & (~meminfo.erasesize + 1);
+ if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) {
+ perror("ioctl(MEMGETBADBLOCK)");
+ goto closeall;
+ }
+ }
+
+ if (badblock) {
+ if (omitbad)
+ continue;
+ memset (readbuf, 0xff, bs);
+ } else {
+ /* Read page data and exit on failure */
+ if (pread(fd, readbuf, bs, ofs) != bs) {
+ perror("pread");
+ goto closeall;
+ }
+ }
+
+ /* 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%08lx\n",
+ stat2.failed - stat1.failed, ofs);
+ if (stat1.corrected != stat2.corrected)
+ fprintf(stderr, "ECC: %d corrected bitflip(s) at"
+ " offset 0x%08lx\n",
+ stat2.corrected - stat1.corrected, ofs);
+ stat1 = stat2;
+ }
+
+ if (omitempty) {
+ isempty = true;
+ for (i = 0; i < bs; i++) {
+ if (readbuf[i] != 0xff) {
+ isempty = false;
+ break;
+ }
+ }
+
+ if (isempty) {
+ if (!quiet)
+ fprintf(stderr, "Skipping empty page at offset 0x%08lx\n", ofs);
+
+ continue;
+ }
+ }
+
+ /* Write out page data */
+ if (pretty_print) {
+ for (i = 0; i < bs; i += 16) {
+ sprintf(pretty_buf,
+ "0x%08x: %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ (unsigned int) (ofs + i), readbuf[i],
+ readbuf[i+1], readbuf[i+2],
+ readbuf[i+3], readbuf[i+4],
+ readbuf[i+5], readbuf[i+6],
+ readbuf[i+7], readbuf[i+8],
+ readbuf[i+9], readbuf[i+10],
+ readbuf[i+11], readbuf[i+12],
+ readbuf[i+13], readbuf[i+14],
+ readbuf[i+15]);
+ write(ofd, pretty_buf, 60);
+ }
+ } else
+ write(ofd, readbuf, bs);
+
+
+
+ if (omitoob)
+ continue;
+
+ if (badblock) {
+ memset (readbuf, 0xff, meminfo.oobsize);
+ } else {
+ /* Read OOB data and exit on failure */
+ oob.start = ofs;
+ if (ioctl(fd, MEMREADOOB, &oob) != 0) {
+ perror("ioctl(MEMREADOOB)");
+ goto closeall;
+ }
+ }
+
+ /* Write out OOB data */
+ if (pretty_print) {
+ if (meminfo.oobsize < 16) {
+ sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x "
+ "%02x %02x\n",
+ oobbuf[0], oobbuf[1], oobbuf[2],
+ oobbuf[3], oobbuf[4], oobbuf[5],
+ oobbuf[6], oobbuf[7]);
+ write(ofd, pretty_buf, 48);
+ continue;
+ }
+
+ for (i = 0; i < meminfo.oobsize; i += 16) {
+ sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ oobbuf[i], oobbuf[i+1], oobbuf[i+2],
+ oobbuf[i+3], oobbuf[i+4], oobbuf[i+5],
+ oobbuf[i+6], oobbuf[i+7], oobbuf[i+8],
+ oobbuf[i+9], oobbuf[i+10], oobbuf[i+11],
+ oobbuf[i+12], oobbuf[i+13], oobbuf[i+14],
+ oobbuf[i+15]);
+ write(ofd, pretty_buf, 60);
+ }
+ } else
+ write(ofd, oobbuf, meminfo.oobsize);
+ }
+
+ /* reset oobinfo */
+ if (oobinfochanged == 1) {
+ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ goto closeall;
+ }
+ }
+ /* Close the output file and MTD device, free memory */
+ close(fd);
+ close(ofd);
+ free(oobbuf);
+ free(readbuf);
+
+ /* Exit happy */
+ return EXIT_SUCCESS;
+
+closeall:
+ /* The new mode change is per file descriptor ! */
+ if (oobinfochanged == 1) {
+ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ }
+ }
+ close(fd);
+ close(ofd);
+ free(oobbuf);
+ free(readbuf);
+ exit(EXIT_FAILURE);
+}
diff --git a/mtd-utils-1.3.1/nandtest.c b/mtd-utils-1.3.1/nandtest.c
new file mode 100644
index 0000000..e65381b
--- /dev/null
+++ b/mtd-utils-1.3.1/nandtest.c
@@ -0,0 +1,355 @@
+#define PROGRAM_NAME "nandtest"
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.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(void)
+{
+ fprintf(stderr, "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"
+ " -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(1);
+}
+
+struct mtd_info_user meminfo;
+struct mtd_ecc_stats oldstats, newstats;
+int fd;
+int markbad=0;
+int seed;
+
+/*
+ * Erase and write block by block, checking for errors
+ */
+int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf)
+{
+ struct erase_info_user er;
+ ssize_t len;
+ int i;
+
+ 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);
+ }
+
+ printf("\r%08x: reading...", (unsigned)ofs);
+ fflush(stdout);
+
+ 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.corrected = newstats.corrected;
+ }
+ if (len < meminfo.erasesize)
+ exit(1);
+
+ 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]);
+ }
+ exit(1);
+ }
+ return 0;
+}
+
+/*
+ * Restore page by page, skipping empty writes (writing all 0xff can result in non-0xff
+ * OOB bytes in empty blocks, which will be a problem for the fs after the test is complete).
+ */
+int erase_and_restore(loff_t ofs, unsigned char *data)
+{
+ struct erase_info_user er;
+ ssize_t len;
+ int page;
+ int page_count = (meminfo.erasesize/meminfo.writesize);
+ int i;
+ bool isempty;
+ unsigned char *data_page = data;
+
+ printf("\r%08x: erasing... ", (unsigned)ofs);
+ fflush(stdout);
+
+ er.start = ofs;
+ er.length = meminfo.erasesize;
+
+ /* Erase the whole block */
+ 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);
+
+ /* Write back page by page */
+ for (page = 0; page < page_count; page++, data_page += meminfo.writesize, ofs += meminfo.writesize) {
+ isempty = true;
+ for (i = 0; i < meminfo.writesize; i++) {
+ if (data_page[i] != 0xff) {
+ isempty = false;
+ break;
+ }
+ }
+
+ if (isempty == false) {
+ len = pwrite(fd, data_page, meminfo.writesize, 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.writesize) {
+ printf("\n");
+ fprintf(stderr, "Short write (%zd bytes)\n", len);
+ exit(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 keep_contents = 0;
+ uint32_t offset = 0;
+ uint32_t length = -1;
+
+ for (;;) {
+ static const char *short_options="hkl:mo:p: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' },
+ { "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':
+ case '?':
+ usage();
+ 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 'o':
+ offset = atol(optarg);
+ break;
+
+ case 'l':
+ length = strtol(optarg, NULL, 0);
+ break;
+
+ }
+ }
+ if (argc - optind != 1)
+ usage();
+
+ 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 * 3);
+ 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);
+
+ 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))
+ continue;
+ if (keep_contents)
+ erase_and_restore(test_ofs, kbuf);
+ }
+ printf("\nFinished pass %d successfully\n", pass+1);
+ }
+ /* Return happy */
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/nandwrite.c b/mtd-utils-1.3.1/nandwrite.c
new file mode 100644
index 0000000..e6df678
--- /dev/null
+++ b/mtd-utils-1.3.1/nandwrite.c
@@ -0,0 +1,692 @@
+/*
+ * 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 _GNU_SOURCE
+#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"
+
+#define PROGRAM "nandwrite"
+#define VERSION "$Revision: 1.32 $"
+
+#define MAX_PAGE_SIZE 4096
+#define MAX_OOB_SIZE 128
+
+// oob layouts to pass into the kernel as default
+static struct nand_oobinfo none_oobinfo = {
+ .useecc = MTD_NANDECC_OFF,
+};
+
+static struct nand_oobinfo jffs2_oobinfo = {
+ .useecc = MTD_NANDECC_PLACE,
+ .eccbytes = 6,
+ .eccpos = { 0, 1, 2, 3, 6, 7 }
+};
+
+static struct nand_oobinfo yaffs_oobinfo = {
+ .useecc = MTD_NANDECC_PLACE,
+ .eccbytes = 6,
+ .eccpos = { 8, 9, 10, 13, 14, 15}
+};
+
+static struct nand_oobinfo autoplace_oobinfo = {
+ .useecc = MTD_NANDECC_AUTOPLACE
+};
+
+static void display_help (void)
+{
+ printf(
+"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n"
+"Writes to the specified MTD device.\n"
+"\n"
+" -a, --autoplace Use auto oob layout\n"
+" -j, --jffs2 Force jffs2 oob layout (legacy support)\n"
+" -y, --yaffs Force yaffs oob layout (legacy support)\n"
+" -f, --forcelegacy Force legacy support on autoplacement-enabled mtd\n"
+" device\n"
+" -m, --markbad Mark blocks bad if write fails\n"
+" -n, --noecc Write without ecc\n"
+" -o, --oob Image contains oob data\n"
+" -s addr, --start=addr Set start address (default is 0)\n"
+" -p, --pad Pad to page size\n"
+" -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n"
+" -q, --quiet Don't display progress messages\n"
+" --help Display this help and exit\n"
+" --version Output version information and exit\n"
+ );
+ exit (EXIT_SUCCESS);
+}
+
+static void display_version (void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "Copyright (C) 2003 Thomas Gleixner \n"
+ "\n"
+ PROGRAM " comes with NO WARRANTY\n"
+ "to the extent permitted by law.\n"
+ "\n"
+ "You may redistribute copies of " PROGRAM "\n"
+ "under the terms of the GNU General Public Licence.\n"
+ "See the file `COPYING' for more information.\n");
+ exit (EXIT_SUCCESS);
+}
+
+static const char *standard_input = "-";
+static const char *mtd_device, *img;
+static int mtdoffset = 0;
+static bool quiet = false;
+static bool writeoob = false;
+static bool autoplace = false;
+static bool markbad = false;
+static bool forcejffs2 = false;
+static bool forceyaffs = false;
+static bool forcelegacy = false;
+static bool noecc = false;
+static bool pad = false;
+static int blockalign = 1; /*default to using 16K block size */
+
+static void process_options (int argc, char * const argv[])
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "ab:fjmnopqs:y";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"autoplace", no_argument, 0, 'a'},
+ {"blockalign", required_argument, 0, 'b'},
+ {"forcelegacy", no_argument, 0, 'f'},
+ {"jffs2", no_argument, 0, 'j'},
+ {"markbad", no_argument, 0, 'm'},
+ {"noecc", no_argument, 0, 'n'},
+ {"oob", no_argument, 0, 'o'},
+ {"pad", no_argument, 0, 'p'},
+ {"quiet", no_argument, 0, 'q'},
+ {"start", required_argument, 0, 's'},
+ {"yaffs", no_argument, 0, 'y'},
+ {0, 0, 0, 0},
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ switch (option_index) {
+ case 0:
+ display_help();
+ break;
+ case 1:
+ display_version();
+ break;
+ }
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 'a':
+ autoplace = true;
+ break;
+ case 'j':
+ forcejffs2 = true;
+ break;
+ case 'y':
+ forceyaffs = true;
+ break;
+ case 'f':
+ forcelegacy = true;
+ break;
+ case 'n':
+ noecc = true;
+ break;
+ case 'm':
+ markbad = true;
+ break;
+ case 'o':
+ writeoob = true;
+ break;
+ case 'p':
+ pad = true;
+ break;
+ case 's':
+ mtdoffset = strtol (optarg, NULL, 0);
+ break;
+ case 'b':
+ blockalign = atoi (optarg);
+ break;
+ case '?':
+ error++;
+ break;
+ }
+ }
+
+ if (mtdoffset < 0) {
+ fprintf(stderr, "Can't specify a negative device offset `%d'\n",
+ mtdoffset);
+ exit (EXIT_FAILURE);
+ }
+
+ 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 ();
+
+ 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 cnt = 0;
+ int fd = -1;
+ int ifd = -1;
+ int imglen = 0, pagelen;
+ bool baderaseblock = false;
+ int blockstart = -1;
+ struct mtd_info_user meminfo;
+ struct mtd_oob_buf oob;
+ loff_t offs;
+ int ret;
+ int oobinfochanged = 0;
+ struct nand_oobinfo old_oobinfo;
+ 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 *oobreadbuf = NULL;
+ unsigned char oobbuf[MAX_OOB_SIZE];
+
+ process_options(argc, argv);
+
+ erase_buffer(oobbuf, sizeof(oobbuf));
+
+ if (pad && writeoob) {
+ fprintf(stderr, "Can't pad when oob data is present.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Open the device */
+ if ((fd = open(mtd_device, O_RDWR)) == -1) {
+ perror(mtd_device);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Fill in MTD device capability structure */
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("MEMGETINFO");
+ close(fd);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Set erasesize to specified number of blocks - to match jffs2
+ * (virtual) block size */
+ meminfo.erasesize *= blockalign;
+
+ /* Make sure device page sizes are valid */
+ if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) &&
+ !(meminfo.oobsize == 8 && meminfo.writesize == 256) &&
+ !(meminfo.oobsize == 64 && meminfo.writesize == 2048) &&
+ !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) {
+ fprintf(stderr, "Unknown flash (not normal NAND)\n");
+ close(fd);
+ exit (EXIT_FAILURE);
+ }
+
+ if (autoplace) {
+ /* Read the current oob info */
+ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMGETOOBSEL");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+
+ // autoplace ECC ?
+ if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
+
+ if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+ oobinfochanged = 1;
+ }
+ }
+
+ if (noecc) {
+ ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW);
+ if (ret == 0) {
+ oobinfochanged = 2;
+ } else {
+ switch (errno) {
+ case ENOTTY:
+ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMGETOOBSEL");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+ oobinfochanged = 1;
+ break;
+ default:
+ perror ("MTDFILEMODE");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+ }
+ }
+
+ /*
+ * force oob layout for jffs2 or yaffs ?
+ * Legacy support
+ */
+ if (forcejffs2 || forceyaffs) {
+ struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
+
+ if (autoplace) {
+ fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n");
+ goto restoreoob;
+ }
+ if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) {
+ fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n");
+ goto restoreoob;
+ }
+ if (meminfo.oobsize == 8) {
+ if (forceyaffs) {
+ fprintf (stderr, "YAFSS cannot operate on 256 Byte page size");
+ goto restoreoob;
+ }
+ /* Adjust number of ecc bytes */
+ jffs2_oobinfo.eccbytes = 3;
+ }
+
+ if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) {
+ perror ("MEMSETOOBSEL");
+ goto restoreoob;
+ }
+ }
+
+ oob.length = meminfo.oobsize;
+ oob.ptr = noecc ? oobreadbuf : oobbuf;
+
+ /* 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 restoreoob;
+ }
+
+ pagelen = meminfo.writesize + ((writeoob) ? meminfo.oobsize : 0);
+
+ /*
+ * For the standard input case, the input size is merely an
+ * invariant placeholder and is set to the write page
+ * size. Otherwise, just use the input file size.
+ *
+ * TODO: Add support for the -l,--length=length option (see
+ * previous discussion by Tommi Airikka <tommi.airikka@ericsson.com> at
+ * <http://lists.infradead.org/pipermail/linux-mtd/2008-September/
+ * 022913.html>
+ */
+
+ if (ifd == STDIN_FILENO) {
+ imglen = pagelen;
+ } else {
+ imglen = lseek(ifd, 0, SEEK_END);
+ lseek (ifd, 0, SEEK_SET);
+ }
+
+ // 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) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
+ fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
+ imglen, pagelen, meminfo.writesize, meminfo.size);
+ perror ("Input file does not fit into device");
+ goto closeall;
+ }
+
+ // Allocate a buffer big enough to contain all the data (OOB included) for one eraseblock
+ filebuf_max = pagelen * meminfo.erasesize / meminfo.writesize;
+ filebuf = (unsigned char*)malloc(filebuf_max);
+ if (!filebuf) {
+ fprintf(stderr, "Failed to allocate memory for file buffer (%d bytes)\n",
+ pagelen * meminfo.erasesize / meminfo.writesize);
+ goto closeall;
+ }
+ 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 < meminfo.size))
+ {
+ // new eraseblock , check for bad block(s)
+ // Stay in the loop to be sure if the mtdoffset changes because
+ // of a bad block, that the next block that will be written to
+ // is also checked. Thus avoiding errors if the block(s) after the
+ // skipped block(s) is also bad (number of blocks depending on
+ // the blockalign
+ while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
+ blockstart = mtdoffset & (~meminfo.erasesize + 1);
+ offs = blockstart;
+
+ // 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 %d at offset 0x%x\n",
+ blockstart / meminfo.erasesize, blockstart);
+
+ /* Check all the blocks in an erase block for bad blocks */
+ do {
+ if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
+ perror("ioctl(MEMGETBADBLOCK)");
+ goto closeall;
+ }
+ if (ret == 1) {
+ baderaseblock = true;
+ if (!quiet)
+ fprintf (stderr, "Bad block at %x, %u block(s) "
+ "from %x will be skipped\n",
+ (int) offs, blockalign, blockstart);
+ }
+
+ if (baderaseblock) {
+ mtdoffset = blockstart + meminfo.erasesize;
+ if (mtdoffset >= meminfo.size) {
+ goto closeall;
+ }
+ }
+ offs += meminfo.erasesize / blockalign ;
+ } while ( offs < blockstart + meminfo.erasesize );
+
+ }
+
+ // Read more data from the input if there isn't enough in the buffer
+ if ((writebuf + meminfo.writesize) > (filebuf + filebuf_len)) {
+ int readlen = meminfo.writesize;
+
+ int alreadyread = (filebuf + filebuf_len) - writebuf;
+ int tinycnt = alreadyread;
+
+ 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 the imglen to 0 to signal
+ // the end of the "file". For non standard 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 "
+ "%d 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) {
+ oobreadbuf = writebuf + meminfo.writesize;
+
+ // Read more data for the OOB from the input if there isn't enough in the buffer
+ if ((oobreadbuf + meminfo.oobsize) > (filebuf + filebuf_len)) {
+ int readlen = meminfo.oobsize;
+
+ int alreadyread = (filebuf + filebuf_len) - oobreadbuf;
+ int tinycnt = alreadyread;
+
+ while (tinycnt < readlen) {
+ cnt = read(ifd, oobreadbuf + 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 "
+ "%d 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;
+ }
+ }
+
+ if (noecc) {
+ oob.ptr = oobreadbuf;
+ } else {
+ int i, start, len;
+ /*
+ * We use autoplacement and have the oobinfo with the autoplacement
+ * information from the kernel available
+ *
+ * Modified to support out of order oobfree segments,
+ * such as the layout used by diskonchip.c
+ */
+ if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) {
+ for (i = 0;old_oobinfo.oobfree[i][1]; i++) {
+ /* Set the reserved bytes to 0xff */
+ start = old_oobinfo.oobfree[i][0];
+ len = old_oobinfo.oobfree[i][1];
+ memcpy(oobbuf + start,
+ oobreadbuf + start,
+ len);
+ }
+ } else {
+ /* Set at least the ecc byte positions to 0xff */
+ start = old_oobinfo.eccbytes;
+ len = meminfo.oobsize - start;
+ memcpy(oobbuf + start,
+ oobreadbuf + start,
+ len);
+ }
+ }
+ /* Write OOB data first, as ecc will be placed in there*/
+ oob.start = mtdoffset;
+ if (ioctl(fd, MEMWRITEOOB, &oob) != 0) {
+ perror ("ioctl(MEMWRITEOOB)");
+ goto closeall;
+ }
+ }
+
+ /* Write out the Page data */
+ if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) {
+ erase_info_t erase;
+
+ if (errno != EIO) {
+ perror("pwrite");
+ goto closeall;
+ }
+
+ /* Must rewind to blockstart if we can */
+ writebuf = filebuf;
+
+ erase.start = blockstart;
+ erase.length = meminfo.erasesize;
+ fprintf(stderr, "Erasing failed write from %08lx-%08lx\n",
+ (long)erase.start, (long)erase.start+erase.length-1);
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ int errno_tmp = errno;
+ perror("MEMERASE");
+ if (errno_tmp != EIO) {
+ goto closeall;
+ }
+ }
+
+ if (markbad) {
+ loff_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1);
+ fprintf(stderr, "Marking block at %08lx bad\n", (long)bad_addr);
+ if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
+ perror("MEMSETBADBLOCK");
+ goto closeall;
+ }
+ }
+ mtdoffset = blockstart + meminfo.erasesize;
+
+ continue;
+ }
+ mtdoffset += meminfo.writesize;
+ writebuf += pagelen;
+ }
+
+ failed = false;
+
+closeall:
+ if (filebuf) {
+ free(filebuf);
+ }
+
+ close(ifd);
+
+restoreoob:
+ if (oobinfochanged == 1) {
+ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ close(fd);
+
+ if (failed
+ || ((ifd != STDIN_FILENO) && (imglen > 0))
+ || (writebuf < (filebuf + filebuf_len)))
+ {
+ perror ("Data was only partially written due to error\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Return happy */
+ return EXIT_SUCCESS;
+}
diff --git a/mtd-utils-1.3.1/nftl_format.c b/mtd-utils-1.3.1/nftl_format.c
new file mode 100644
index 0000000..42949a0
--- /dev/null
+++ b/mtd-utils-1.3.1/nftl_format.c
@@ -0,0 +1,419 @@
+/*
+ * 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 _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("nftl_format: failed to read BBT, errno=%d\n", errno);
+ return (-1);
+ }
+
+
+ for (i = 0; (i < 512); i++) {
+ addr = i / 4;
+ bits = 0x3 << ((i % 4) * 2);
+ if ((bbt[addr] & bits) == 0) {
+ BadUnitTable[i] = ZONE_BAD_ORIGINAL;
+ }
+ }
+
+ return (0);
+}
+
+void usage(int rc)
+{
+ fprintf(stderr, "Usage: nftl_format [-ib] <mtddevice> [<start offset> [<size>]]\n");
+ exit(rc);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned long startofs = 0, part_size = 0;
+ unsigned long ezones = 0, ezone = 0, bad_zones = 0;
+ unsigned char unit_factor = 0xFF;
+ long MediaUnit1 = -1, MediaUnit2 = -1;
+ long MediaUnitOff1 = 0, MediaUnitOff2 = 0;
+ unsigned char oobbuf[16];
+ struct mtd_oob_buf oob = {0, 16, oobbuf};
+ char *mtddevice, *nftl;
+ int c, do_inftl = 0, do_bbt = 0;
+
+
+ printf("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/mtd-utils-1.3.1/nftldump.c b/mtd-utils-1.3.1/nftldump.c
new file mode 100644
index 0000000..6d72acd
--- /dev/null
+++ b/mtd-utils-1.3.1/nftldump.c
@@ -0,0 +1,281 @@
+/*
+ * 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 _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];
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SWAP16(x) do { ; } while(0)
+#define SWAP32(x) do { ; } while(0)
+#else
+#define SWAP16(x) do { x = swab16(x); } while(0)
+#define SWAP32(x) do { x = swab32(x); } while(0)
+#endif
+
+/* VUCtable, store the Erase Unit Number of the first Erase Unit in the chain */
+static unsigned short *VUCtable;
+
+/* FixMe: make this dynamic allocated */
+#define ERASESIZE 0x2000
+#define NUMVUNITS ((40*1024*1024) / ERASESIZE)
+static union nftl_uci UCItable[NUMVUNITS][3];
+
+static unsigned short nextEUN(unsigned short curEUN)
+{
+ return UCItable[curEUN][0].a.ReplUnitNum;
+}
+
+static unsigned int find_media_headers(void)
+{
+ int i;
+ static unsigned long ofs = 0;
+
+ NumMedHeads = 0;
+ while (ofs < meminfo.size) {
+ pread(fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs);
+ if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) {
+ SWAP16(MedHead[NumMedHeads].NumEraseUnits);
+ SWAP16(MedHead[NumMedHeads].FirstPhysicalEUN);
+ SWAP32(MedHead[NumMedHeads].FormattedSize);
+
+ if (NumMedHeads == 0) {
+ printf("NFTL Media Header found at offset 0x%08lx:\n", ofs);
+ printf("NumEraseUnits: %d\n",
+ MedHead[NumMedHeads].NumEraseUnits);
+ printf("FirstPhysicalEUN: %d\n",
+ MedHead[NumMedHeads].FirstPhysicalEUN);
+ printf("Formatted Size: %d\n",
+ MedHead[NumMedHeads].FormattedSize);
+ printf("UnitSizeFactor: 0x%x\n",
+ MedHead[NumMedHeads].UnitSizeFactor);
+
+ /* read BadUnitTable, I don't know why pread() does not work for
+ larger (7680 bytes) chunks */
+ for (i = 0; i < MAX_ERASE_ZONES; i += 512)
+ pread(fd, &BadUnitTable[i], 512, ofs + 512 + i);
+ } else
+ printf("Second NFTL Media Header found at offset 0x%08lx\n",ofs);
+ NumMedHeads++;
+ }
+
+ ofs += meminfo.erasesize;
+ if (NumMedHeads == 2) {
+ if (strncmp((char *)&MedHead[0], (char *)&MedHead[1], sizeof(struct NFTLMediaHeader)) != 0) {
+ printf("warning: NFTL Media Header is not consistent with "
+ "Spare NFTL Media Header\n");
+ }
+ break;
+ }
+ }
+
+ /* allocate Virtual Unit Chain table for this NFTL partition */
+ VUCtable = calloc(MedHead[0].NumEraseUnits, sizeof(unsigned short));
+ return NumMedHeads;
+}
+
+static void dump_erase_units(void)
+{
+ int i, j;
+ unsigned long ofs;
+
+ for (i = MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN +
+ MedHead[0].NumEraseUnits; i++) {
+ /* For each Erase Unit */
+ ofs = i * meminfo.erasesize;
+
+ /* read the Unit Control Information */
+ for (j = 0; j < 3; j++) {
+ oob.start = ofs + (j * 512);
+ if (ioctl(fd, MEMREADOOB, &oob))
+ printf("MEMREADOOB at %lx: %s\n",
+ (unsigned long) oob.start, strerror(errno));
+ memcpy(&UCItable[i][j], &oobbuf.u, 8);
+ }
+ if (UCItable[i][1].b.EraseMark != cpu_to_le16(0x3c69)) {
+ printf("EraseMark not present in unit %d: %x\n",
+ i, UCItable[i][1].b.EraseMark);
+ } else {
+ /* a properly formatted unit */
+ SWAP16(UCItable[i][0].a.VirtUnitNum);
+ SWAP16(UCItable[i][0].a.ReplUnitNum);
+ SWAP16(UCItable[i][0].a.SpareVirtUnitNum);
+ SWAP16(UCItable[i][0].a.SpareReplUnitNum);
+ SWAP32(UCItable[i][1].b.WearInfo);
+ SWAP16(UCItable[i][1].b.EraseMark);
+ SWAP16(UCItable[i][1].b.EraseMark1);
+ SWAP16(UCItable[i][2].c.FoldMark);
+ SWAP16(UCItable[i][2].c.FoldMark1);
+
+ if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) {
+ /* If this is the first in a chain, store the EUN in the VUC table */
+ if (VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) {
+ printf("Duplicate start of chain for VUC %d: "
+ "Unit %d replaces Unit %d\n",
+ UCItable[i][0].a.VirtUnitNum & 0x7fff,
+ i, VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]);
+ }
+ VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i;
+ }
+ }
+
+ switch (BadUnitTable[i]) {
+ case ZONE_BAD_ORIGINAL:
+ printf("Unit %d is marked as ZONE_BAD_ORIGINAL\n", i);
+ continue;
+ case ZONE_BAD_MARKED:
+ printf("Unit %d is marked as ZONE_BAD_MARKED\n", i);
+ continue;
+ }
+
+ /* ZONE_GOOD */
+ if (UCItable[i][0].a.VirtUnitNum == 0xffff)
+ printf("Unit %d is free\n", i);
+ else
+ printf("Unit %d is in chain %d and %s a replacement\n", i,
+ UCItable[i][0].a.VirtUnitNum & 0x7fff,
+ UCItable[i][0].a.VirtUnitNum & 0x8000 ? "is" : "is not");
+ }
+}
+
+static void dump_virtual_units(void)
+{
+ int i, j;
+ char readbuf[512];
+
+ for (i = 0; i < (MedHead[0].FormattedSize / meminfo.erasesize); i++) {
+ unsigned short curEUN = VUCtable[i];
+
+ printf("Virtual Unit #%d: ", i);
+ if (!curEUN) {
+ printf("Not present\n");
+ continue;
+ }
+ printf("%d", curEUN);
+
+ /* walk through the Virtual Unit Chain */
+ while ((curEUN = nextEUN(curEUN)) != 0xffff) {
+ printf(", %d", curEUN & 0x7fff);
+ }
+ printf("\n");
+
+ if (ofd != -1) {
+ /* Actually write out the data */
+ for (j = 0; j < meminfo.erasesize / 512; j++) {
+ /* For each sector in the block */
+ unsigned short lastgoodEUN = 0xffff, thisEUN = VUCtable[i];
+ unsigned int status;
+
+ if (thisEUN == 0xffff) thisEUN = 0;
+
+ while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) {
+ oob.start = (thisEUN * ERASESIZE) + (j * 512);
+ ioctl(fd, MEMREADOOB, &oob);
+ status = oobbuf.b.Status | oobbuf.b.Status1;
+
+ switch (status) {
+ case SECTOR_FREE:
+ /* This is still free. Don't look any more */
+ thisEUN = 0;
+ break;
+
+ case SECTOR_USED:
+ /* SECTOR_USED. This is a good one. */
+ lastgoodEUN = thisEUN;
+ break;
+ }
+
+ /* Find the next erase unit in this chain, if any */
+ if (thisEUN)
+ thisEUN = nextEUN(thisEUN) & 0x7fff;
+ }
+
+ if (lastgoodEUN == 0xffff)
+ memset(readbuf, 0, 512);
+ else
+ pread(fd, readbuf, 512,
+ (lastgoodEUN * ERASESIZE) + (j * 512));
+
+ write(ofd, readbuf, 512);
+ }
+
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ printf("Usage: %s <device> [<outfile>]\n", argv[0]);
+ exit(1);
+ }
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ perror("open flash");
+ exit (1);
+ }
+
+ if (argc > 2) {
+ ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ if (ofd == -1)
+ perror ("open outfile");
+ }
+
+ /* get size information of the MTD device */
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ close(fd);
+ return 1;
+ }
+
+ while (find_media_headers() != 0) {
+ dump_erase_units();
+ dump_virtual_units();
+ free(VUCtable);
+ }
+
+ exit(0);
+}
diff --git a/mtd-utils-1.3.1/rbtree.c b/mtd-utils-1.3.1/rbtree.c
new file mode 100644
index 0000000..dd50134
--- /dev/null
+++ b/mtd-utils-1.3.1/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/mtd-utils-1.3.1/rbtree.h b/mtd-utils-1.3.1/rbtree.h
new file mode 100644
index 0000000..9597b10
--- /dev/null
+++ b/mtd-utils-1.3.1/rbtree.h
@@ -0,0 +1,168 @@
+/*
+ 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, }
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#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/mtd-utils-1.3.1/recv_image.c b/mtd-utils-1.3.1/recv_image.c
new file mode 100644
index 0000000..d65aa2a
--- /dev/null
+++ b/mtd-utils-1.3.1/recv_image.c
@@ -0,0 +1,484 @@
+
+#define _XOPEN_SOURCE 500
+
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#define __USE_GNU
+#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"
+
+#define min(x,y) ( (x)>(y)?(y):(x) )
+
+#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;
+ size_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;
+ 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;
+ int i;
+ struct eraseblock *eraseblocks = NULL;
+ uint32_t start_seq;
+ 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",
+ (strrchr(argv[0], '/')?:argv[0]-1)+1);
+ 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 %d bytes (expected %d)\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 (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),
+ 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 (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,
+ 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/mtd-utils-1.3.1/rfddump.c b/mtd-utils-1.3.1/rfddump.c
new file mode 100644
index 0000000..73b0eca
--- /dev/null
+++ b/mtd-utils-1.3.1/rfddump.c
@@ -0,0 +1,336 @@
+/*
+ * 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 _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;
+};
+
+#define PROGRAM "rfddump"
+#define VERSION "$Revision 1.0 $"
+
+void display_help(void)
+{
+ printf("Usage: " PROGRAM " [OPTIONS] MTD-device filename\n"
+ "Dumps the contents of a resident flash disk\n"
+ "\n"
+ "-h --help display this help and exit\n"
+ "-V --version output version information and exit\n"
+ "-v --verbose Be verbose\n"
+ "-b size --blocksize Block size (defaults to erase unit)\n");
+ exit(0);
+}
+
+void display_version(void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
+
+ exit(0);
+}
+
+void process_options(int argc, char *argv[], struct rfd *rfd)
+{
+ int error = 0;
+
+ rfd->block_size = 0;
+ rfd->verbose = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hvVb:";
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V', },
+ { "blocksize", required_argument, 0, 'b' },
+ { "verbose", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 'h':
+ display_help();
+ break;
+ case 'V':
+ display_version();
+ break;
+ case 'v':
+ rfd->verbose = 1;
+ break;
+ case 'b':
+ rfd->block_size = atoi(optarg);
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 2 || error)
+ display_help();
+
+ rfd->mtd_filename = argv[optind];
+ rfd->out_filename = argv[optind + 1];
+}
+
+int build_block_map(struct rfd *rfd, int fd, int block)
+{
+ int i;
+ int sectors;
+
+ if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size)
+ != rfd->header_size) {
+ return -1;
+ }
+
+ if (le16_to_cpu(rfd->header[0]) != RFD_MAGIC) {
+ if (rfd->verbose)
+ printf("Block #%02d: Magic missing\n", block);
+
+ return 0;
+ }
+
+ sectors = 0;
+ for (i=0; i<rfd->data_sectors; i++) {
+ 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);
+ close(fd);
+ return 2;
+ }
+ rfd.sector_map = malloc(rfd.sector_count * sizeof(int));
+ if (!rfd.sector_map) {
+ perror(PROGRAM);
+ close(fd);
+ free(rfd.sector_map);
+ return 2;
+ }
+
+ rfd.mtd_filename = rfd.mtd_filename;
+
+ for (i=0; i<rfd.sector_count; i++)
+ rfd.sector_map[i] = -1;
+
+ for (blocks_found=i=0; i<rfd.block_count; i++) {
+ rc = build_block_map(&rfd, fd, i);
+ if (rc > 0)
+ blocks_found++;
+ if (rc < 0)
+ goto err;
+ }
+
+ if (!blocks_found) {
+ fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename);
+ goto err;
+ }
+
+ for (i=0; i<rfd.sector_count; i++) {
+ if (rfd.sector_map[i] != -1)
+ break;
+ }
+
+ if (i == rfd.sector_count) {
+ fprintf(stderr, "%s: no sectors found\n", rfd.mtd_filename);
+ goto err;
+ }
+
+ out_fd = open(rfd.out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+ if (out_fd == -1) {
+ perror(rfd.out_filename);
+ goto err;
+ }
+
+ blank = 0;
+ for (i=0; i<rfd.sector_count; i++) {
+ if (rfd.sector_map[i] == -1) {
+ memset(sector, 0, SECTOR_SIZE);
+ blank++;
+ } else {
+ if (pread(fd, sector, SECTOR_SIZE, rfd.sector_map[i])
+ != SECTOR_SIZE) {
+ perror(rfd.mtd_filename);
+ goto err;
+ }
+ }
+
+ if (write(out_fd, sector, SECTOR_SIZE) != SECTOR_SIZE) {
+ perror(rfd.out_filename);
+ goto err;
+ }
+ }
+
+ if (rfd.verbose)
+ printf("Copied %d sectors (%d blank)\n", rfd.sector_count, blank);
+
+ close(out_fd);
+ close(fd);
+ free(rfd.header);
+ free(rfd.sector_map);
+
+ return 0;
+
+err:
+ if (out_fd)
+ close(out_fd);
+
+ close(fd);
+ free(rfd.header);
+ free(rfd.sector_map);
+
+ return 2;
+}
+
diff --git a/mtd-utils-1.3.1/rfdformat.c b/mtd-utils-1.3.1/rfdformat.c
new file mode 100644
index 0000000..d2637a1
--- /dev/null
+++ b/mtd-utils-1.3.1/rfdformat.c
@@ -0,0 +1,158 @@
+/*
+ * 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 _XOPEN_SOURCE 500 /* For pread/pwrite */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <mtd/mtd-user.h>
+#include <linux/types.h>
+
+#define PROGRAM "rfdformat"
+#define VERSION "$Revision 1.0 $"
+
+void display_help(void)
+{
+ printf("Usage: " PROGRAM " [OPTIONS] MTD-device\n"
+ "Formats NOR flash for resident flash disk\n"
+ "\n"
+ "-h --help display this help and exit\n"
+ "-V --version output version information and exit\n");
+ exit(0);
+}
+
+void display_version(void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
+
+ exit(0);
+}
+
+void process_options(int argc, char *argv[], const char **mtd_filename)
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hV";
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V', },
+ { NULL, 0, 0, 0 }
+ };
+
+ int c = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 'h':
+ display_help();
+ break;
+ case 'V':
+ display_version();
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1 || error)
+ display_help();
+
+ *mtd_filename = argv[optind];
+}
+
+int main(int argc, char *argv[])
+{
+ static const 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/mtd-utils-1.3.1/serve_image.c b/mtd-utils-1.3.1/serve_image.c
new file mode 100644
index 0000000..adb4869
--- /dev/null
+++ b/mtd-utils-1.3.1/serve_image.c
@@ -0,0 +1,299 @@
+#define _POSIX_C_SOURCE 199309
+
+#include <time.h>
+
+#include <errno.h>
+#include <error.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 "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",
+ (strrchr(argv[0], '/')?:argv[0]-1)+1);
+ 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 %ld 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(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] = 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(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/mtd-utils-1.3.1/summary.h b/mtd-utils-1.3.1/summary.h
new file mode 100644
index 0000000..95f25c6
--- /dev/null
+++ b/mtd-utils-1.3.1/summary.h
@@ -0,0 +1,178 @@
+/*
+ * 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/uio.h>
+#include <linux/jffs2.h>
+
+#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->dirty_size += _x; \
+ jeb->free_size -= _x ; jeb->dirty_size += _x; \
+}while(0)
+#define USED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->used_size += _x; \
+ jeb->free_size -= _x ; jeb->used_size += _x; \
+}while(0)
+#define WASTED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->wasted_size += _x; \
+ jeb->free_size -= _x ; jeb->wasted_size += _x; \
+}while(0)
+#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->unchecked_size += _x; \
+ jeb->free_size -= _x ; jeb->unchecked_size += _x; \
+}while(0)
+
+#define BLK_STATE_ALLFF 0
+#define BLK_STATE_CLEAN 1
+#define BLK_STATE_PARTDIRTY 2
+#define BLK_STATE_CLEANMARKER 3
+#define BLK_STATE_ALLDIRTY 4
+#define BLK_STATE_BADBLOCK 5
+
+#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
+#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
+#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+#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/mtd-utils-1.3.1/sumtool.c b/mtd-utils-1.3.1/sumtool.c
new file mode 100644
index 0000000..6bb7168
--- /dev/null
+++ b/mtd-utils-1.3.1/sumtool.c
@@ -0,0 +1,951 @@
+/*
+ * 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
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <asm/types.h>
+#include <dirent.h>
+#include <mtd/jffs2-user.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <getopt.h>
+#include "crc32.h"
+#include "summary.h"
+
+#define PAD(x) (((x)+3)&~3)
+
+static const char *const app_name = "sumtool";
+
+static struct jffs2_summary *sum_collected = NULL;
+
+static int verbose = 0;
+static int padto = 0; /* pad the output with 0xFF to the end of the final eraseblock */
+static int add_cleanmarkers = 1; /* add cleanmarker to output */
+static int use_input_cleanmarker_size = 1; /* use input file's cleanmarker size (default) */
+static int found_cleanmarkers = 0; /* cleanmarker found in input file */
+static struct jffs2_unknown_node cleanmarker;
+static int cleanmarker_size = sizeof(cleanmarker);
+static const char *short_options = "o:i:e:hvVblnc:p";
+static int erase_block_size = 65536;
+static int out_fd = -1;
+static int in_fd = -1;
+
+static uint8_t *data_buffer = NULL; /* buffer for inodes */
+static unsigned int data_ofs = 0; /* inode buffer offset */
+
+static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/
+static unsigned int file_ofs = 0; /* position in the buffer */
+
+int target_endian = __BYTE_ORDER;
+
+static struct option long_options[] = {
+ {"output", 1, NULL, 'o'},
+ {"input", 1, NULL, 'i'},
+ {"eraseblock", 1, NULL, 'e'},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"version", 0, NULL, 'V'},
+ {"bigendian", 0, NULL, 'b'},
+ {"littleendian", 0, NULL, 'l'},
+ {"no-cleanmarkers", 0, NULL, 'n'},
+ {"cleanmarker", 1, NULL, 'c'},
+ {"pad", 0, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+};
+
+static char *helptext =
+"Usage: sumtool [OPTIONS] -i inputfile -o outputfile\n\n"
+"Convert the input JFFS2 image to a summarized JFFS2 image\n"
+"Summary makes mounting faster - if summary support enabled in your kernel\n\n"
+"Options:\n"
+" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n"
+" (usually 16KiB on NAND)\n"
+" -c, --cleanmarker=SIZE Size of cleanmarker (default 12).\n"
+" (usually 16 bytes on NAND, and will be set to\n"
+" this value if left at the default 12). Will be\n"
+" stored in OOB after each physical page composing\n"
+" a physical eraseblock.\n"
+" -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n"
+" -o, --output=FILE Output to FILE \n"
+" -i, --input=FILE Input from FILE \n"
+" -b, --bigendian Image is big endian\n"
+" -l --littleendian Image is little endian\n"
+" -h, --help Display this help text\n"
+" -v, --verbose Verbose operation\n"
+" -V, --version Display version information\n"
+" -p, --pad Pad the OUTPUT with 0xFF to the end of the final\n"
+" eraseblock\n\n";
+
+
+static char *revtext = "$Revision: 1.9 $";
+
+static unsigned char ffbuf[16] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static void verror_msg(const char *s, va_list p)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s: ", app_name);
+ vfprintf(stderr, s, p);
+}
+
+static void error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+static void vperror_msg(const char *s, va_list p)
+{
+ int err = errno;
+
+ if (s == 0)
+ s = "";
+ verror_msg(s, p);
+ if (*s)
+ s = ": ";
+ fprintf(stderr, "%s%s\n", s, strerror(err));
+}
+
+static void perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+ exit(EXIT_FAILURE);
+}
+
+
+
+static void full_write(void *target_buff, const void *buf, int len);
+
+void setup_cleanmarker()
+{
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.totlen = cpu_to_je32(cleanmarker_size);
+ cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4));
+}
+
+void process_options (int argc, char **argv)
+{
+ int opt,c;
+
+ while ((opt = getopt_long(argc, argv, short_options, long_options, &c)) >= 0) {
+ switch (opt) {
+ case 'o':
+ if (out_fd != -1)
+ error_msg_and_die("output filename specified more than once");
+ out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (out_fd == -1)
+ perror_msg_and_die("open output file");
+ break;
+
+ case 'i':
+ if (in_fd != -1)
+ error_msg_and_die("input filename specified more than once");
+ in_fd = open(optarg, O_RDONLY);
+ if (in_fd == -1)
+ perror_msg_and_die("open input file");
+ break;
+ case 'b':
+ target_endian = __BIG_ENDIAN;
+ break;
+ case 'l':
+ target_endian = __LITTLE_ENDIAN;
+ break;
+ case 'h':
+ case '?':
+ error_msg_and_die(helptext);
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'V':
+ error_msg_and_die("revision %.*s\n",
+ (int) strlen(revtext) - 13, revtext + 11);
+
+ case 'e': {
+ char *next;
+ unsigned units = 0;
+ erase_block_size = strtol(optarg, &next, 0);
+ if (!erase_block_size)
+ error_msg_and_die("Unrecognisable erase size\n");
+
+ if (*next) {
+ if (!strcmp(next, "KiB")) {
+ units = 1024;
+ } else if (!strcmp(next, "MiB")) {
+ units = 1024 * 1024;
+ } else {
+ error_msg_and_die("Unknown units in erasesize\n");
+ }
+ } else {
+ if (erase_block_size < 0x1000)
+ units = 1024;
+ else
+ units = 1;
+ }
+ erase_block_size *= units;
+
+ /* If it's less than 8KiB, they're not allowed */
+ if (erase_block_size < 0x2000) {
+ fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n",
+ erase_block_size);
+ erase_block_size = 0x2000;
+ }
+ break;
+ }
+
+ case 'n':
+ add_cleanmarkers = 0;
+ break;
+ case 'c':
+ cleanmarker_size = strtol(optarg, NULL, 0);
+
+ if (cleanmarker_size < sizeof(cleanmarker)) {
+ error_msg_and_die("cleanmarker size must be >= 12");
+ }
+ if (cleanmarker_size >= erase_block_size) {
+ error_msg_and_die("cleanmarker size must be < eraseblock size");
+ }
+
+ use_input_cleanmarker_size = 0;
+ found_cleanmarkers = 1;
+ setup_cleanmarker();
+
+ break;
+ case 'p':
+ padto = 1;
+ break;
+ }
+ }
+}
+
+
+void init_buffers()
+{
+ data_buffer = malloc(erase_block_size);
+
+ if (!data_buffer) {
+ perror("out of memory");
+ close (in_fd);
+ close (out_fd);
+ exit(1);
+ }
+
+ file_buffer = malloc(erase_block_size);
+
+ if (!file_buffer) {
+ perror("out of memory");
+ close (in_fd);
+ close (out_fd);
+ exit(1);
+ }
+}
+
+void init_sumlist()
+{
+ sum_collected = (struct jffs2_summary *) malloc (sizeof(struct jffs2_summary));
+
+ if (!sum_collected)
+ error_msg_and_die("Can't allocate memory for jffs2_summary!\n");
+
+ memset(sum_collected, 0, sizeof(struct jffs2_summary));
+}
+
+void clean_buffers()
+{
+ if (data_buffer)
+ free(data_buffer);
+ if (file_buffer)
+ free(file_buffer);
+}
+
+void clean_sumlist()
+{
+ union jffs2_sum_mem *temp;
+
+ if (sum_collected) {
+
+ while (sum_collected->sum_list_head) {
+ temp = sum_collected->sum_list_head;
+ sum_collected->sum_list_head = sum_collected->sum_list_head->u.next;
+ free(temp);
+ sum_collected->sum_num--;
+ }
+
+ if (sum_collected->sum_num != 0)
+ printf("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???");
+
+ free(sum_collected);
+ }
+}
+
+int load_next_block()
+{
+ int ret;
+ ret = read(in_fd, file_buffer, erase_block_size);
+ file_ofs = 0;
+
+ if (verbose)
+ printf("Load next block : %d bytes read\n",ret);
+
+ return ret;
+}
+
+void write_buff_to_file()
+{
+ int ret;
+ int len = data_ofs;
+
+ uint8_t *buf = NULL;
+
+ buf = data_buffer;
+ while (len > 0) {
+ ret = write(out_fd, buf, len);
+
+ if (ret < 0)
+ perror_msg_and_die("write");
+
+ if (ret == 0)
+ perror_msg_and_die("write returned zero");
+
+ len -= ret;
+ buf += ret;
+ }
+
+ data_ofs = 0;
+}
+
+void dump_sum_records()
+{
+
+ struct jffs2_raw_summary isum;
+ struct jffs2_sum_marker *sm;
+ union jffs2_sum_mem *temp;
+ jint32_t offset;
+ jint32_t *tpage;
+ void *wpage;
+ int datasize, infosize, padsize;
+ jint32_t magic = cpu_to_je32(JFFS2_SUM_MAGIC);
+
+ if (!sum_collected->sum_num || !sum_collected->sum_list_head)
+ return;
+
+ datasize = sum_collected->sum_size + sizeof(struct jffs2_sum_marker);
+ infosize = sizeof(struct jffs2_raw_summary) + datasize;
+ padsize = erase_block_size - data_ofs - infosize;
+ infosize += padsize; datasize += padsize;
+ offset = cpu_to_je32(data_ofs);
+
+ tpage = (jint32_t *) malloc(datasize);
+
+ if(!tpage)
+ error_msg_and_die("Can't allocate memory to dump summary information!\n");
+
+ memset(tpage, 0xff, datasize);
+ memset(&isum, 0, sizeof(isum));
+
+ isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
+ isum.totlen = cpu_to_je32(infosize);
+ isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4));
+ isum.padded = cpu_to_je32(0);
+
+ if (add_cleanmarkers && found_cleanmarkers) {
+ isum.cln_mkr = cpu_to_je32(cleanmarker_size);
+ } else {
+ isum.cln_mkr = cpu_to_je32(0);
+ }
+
+ isum.sum_num = cpu_to_je32(sum_collected->sum_num);
+ wpage = tpage;
+
+ while (sum_collected->sum_num) {
+ switch(je16_to_cpu(sum_collected->sum_list_head->u.nodetype)) {
+
+ case JFFS2_NODETYPE_INODE : {
+ struct jffs2_sum_inode_flash *sino_ptr = wpage;
+
+ sino_ptr->nodetype = sum_collected->sum_list_head->i.nodetype;
+ sino_ptr->inode = sum_collected->sum_list_head->i.inode;
+ sino_ptr->version = sum_collected->sum_list_head->i.version;
+ sino_ptr->offset = sum_collected->sum_list_head->i.offset;
+ sino_ptr->totlen = sum_collected->sum_list_head->i.totlen;
+
+ wpage += JFFS2_SUMMARY_INODE_SIZE;
+ break;
+ }
+
+ case JFFS2_NODETYPE_DIRENT : {
+ struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
+
+ sdrnt_ptr->nodetype = sum_collected->sum_list_head->d.nodetype;
+ sdrnt_ptr->totlen = sum_collected->sum_list_head->d.totlen;
+ sdrnt_ptr->offset = sum_collected->sum_list_head->d.offset;
+ sdrnt_ptr->pino = sum_collected->sum_list_head->d.pino;
+ sdrnt_ptr->version = sum_collected->sum_list_head->d.version;
+ sdrnt_ptr->ino = sum_collected->sum_list_head->d.ino;
+ sdrnt_ptr->nsize = sum_collected->sum_list_head->d.nsize;
+ sdrnt_ptr->type = sum_collected->sum_list_head->d.type;
+
+ memcpy(sdrnt_ptr->name, sum_collected->sum_list_head->d.name,
+ sum_collected->sum_list_head->d.nsize);
+
+ wpage += JFFS2_SUMMARY_DIRENT_SIZE(sum_collected->sum_list_head->d.nsize);
+ break;
+ }
+
+ 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 : {
+ printf("Unknown node type!\n");
+ }
+ }
+
+ temp = sum_collected->sum_list_head;
+ sum_collected->sum_list_head = sum_collected->sum_list_head->u.next;
+ free(temp);
+
+ sum_collected->sum_num--;
+ }
+
+ sum_collected->sum_size = 0;
+ sum_collected->sum_num = 0;
+ sum_collected->sum_list_tail = NULL;
+
+ wpage += padsize;
+
+ sm = wpage;
+ sm->offset = offset;
+ sm->magic = magic;
+
+ isum.sum_crc = cpu_to_je32(crc32(0, tpage, datasize));
+ isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8));
+
+ full_write(data_buffer + data_ofs, &isum, sizeof(isum));
+ full_write(data_buffer + data_ofs, tpage, datasize);
+
+ free(tpage);
+}
+
+static void full_write(void *target_buff, const void *buf, int len)
+{
+ memcpy(target_buff, buf, len);
+ data_ofs += len;
+}
+
+static void pad(int req)
+{
+ while (req) {
+ if (req > sizeof(ffbuf)) {
+ full_write(data_buffer + data_ofs, ffbuf, sizeof(ffbuf));
+ req -= sizeof(ffbuf);
+ } else {
+ full_write(data_buffer + data_ofs, ffbuf, req);
+ req = 0;
+ }
+ }
+}
+
+static inline void padword()
+{
+ if (data_ofs % 4)
+ full_write(data_buffer + data_ofs, ffbuf, 4 - (data_ofs % 4));
+}
+
+
+static inline void pad_block_if_less_than(int req,int plus)
+{
+
+ int datasize = req + plus + sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8;
+ datasize += (4 - (datasize % 4)) % 4;
+
+ if (data_ofs + req > erase_block_size - datasize) {
+ dump_sum_records();
+ write_buff_to_file();
+ }
+
+ if (add_cleanmarkers && found_cleanmarkers) {
+ if (!data_ofs) {
+ full_write(data_buffer, &cleanmarker, sizeof(cleanmarker));
+ pad(cleanmarker_size - sizeof(cleanmarker));
+ padword();
+ }
+ }
+}
+
+void flush_buffers()
+{
+
+ if ((add_cleanmarkers == 1) && (found_cleanmarkers == 1)) { /* CLEANMARKER */
+ if (data_ofs != cleanmarker_size) { /* INODE BUFFER */
+
+ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8;
+ datasize += (4 - (datasize % 4)) % 4;
+
+ /* If we have a full inode buffer, then write out inode and summary data */
+ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) {
+ dump_sum_records();
+ write_buff_to_file();
+ } else { /* else just write out inode data */
+ if (padto)
+ pad(erase_block_size - data_ofs);
+ write_buff_to_file();
+ }
+ }
+ } else { /* NO CLEANMARKER */
+ if (data_ofs != 0) { /* INODE BUFFER */
+
+ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8;
+ datasize += (4 - (datasize % 4)) % 4;
+
+ /* If we have a full inode buffer, then write out inode and summary data */
+ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) {
+ dump_sum_records();
+ write_buff_to_file();
+ } else { /* Else just write out inode data */
+ if(padto)
+ pad(erase_block_size - data_ofs);
+ write_buff_to_file();
+ }
+ }
+ }
+}
+
+int add_sum_mem(union jffs2_sum_mem *item)
+{
+
+ if (!sum_collected->sum_list_head)
+ sum_collected->sum_list_head = (union jffs2_sum_mem *) item;
+ if (sum_collected->sum_list_tail)
+ sum_collected->sum_list_tail->u.next = (union jffs2_sum_mem *) item;
+ sum_collected->sum_list_tail = (union jffs2_sum_mem *) item;
+
+ switch (je16_to_cpu(item->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+ sum_collected->sum_size += JFFS2_SUMMARY_INODE_SIZE;
+ sum_collected->sum_num++;
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ sum_collected->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize);
+ sum_collected->sum_num++;
+ break;
+
+ 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:
+ error_msg_and_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype));
+ }
+ return 0;
+}
+
+void add_sum_inode_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_inode_mem *temp = (struct jffs2_sum_inode_mem *) malloc(sizeof(struct jffs2_sum_inode_mem));
+
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ temp->nodetype = node->i.nodetype;
+ temp->inode = node->i.ino;
+ temp->version = node->i.version;
+ temp->offset = cpu_to_je32(data_ofs);
+ temp->totlen = node->i.totlen;
+ temp->next = NULL;
+
+ add_sum_mem((union jffs2_sum_mem *) temp);
+}
+
+void add_sum_dirent_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_dirent_mem *temp = (struct jffs2_sum_dirent_mem *)
+ malloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize);
+
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ temp->nodetype = node->d.nodetype;
+ temp->totlen = node->d.totlen;
+ temp->offset = cpu_to_je32(data_ofs);
+ temp->pino = node->d.pino;
+ temp->version = node->d.version;
+ temp->ino = node->d.ino;
+ temp->nsize = node->d.nsize;
+ temp->type = node->d.type;
+ temp->next = NULL;
+
+ memcpy(temp->name,node->d.name,node->d.nsize);
+ add_sum_mem((union jffs2_sum_mem *) temp);
+}
+
+void add_sum_xattr_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_xattr_mem *temp = (struct jffs2_sum_xattr_mem *)
+ malloc(sizeof(struct jffs2_sum_xattr_mem));
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ 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 = (struct jffs2_sum_xref_mem *)
+ malloc(sizeof(struct jffs2_sum_xref_mem));
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ 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++)
+ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic));
+ p += 4;
+ continue;
+ }
+
+ bitchbitmask = 0;
+
+ type = je16_to_cpu(node->u.nodetype);
+ if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
+ obsolete = 1;
+ type |= JFFS2_NODE_ACCURATE;
+ } else {
+ obsolete = 0;
+ }
+
+ node->u.nodetype = cpu_to_je16(type);
+
+ crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
+ if (crc != je32_to_cpu (node->u.hdr_crc)) {
+ printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->u.hdr_crc), crc);
+ p += 4;
+ continue;
+ }
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+ if (verbose)
+ printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
+ je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
+ je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));
+
+ crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8);
+ if (crc != je32_to_cpu (node->i.node_crc)) {
+ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.node_crc), crc);
+ p += PAD(je32_to_cpu (node->i.totlen));
+ continue;
+ }
+
+ crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize));
+ if (crc != je32_to_cpu(node->i.data_crc)) {
+ printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.data_crc), crc);
+ p += PAD(je32_to_cpu (node->i.totlen));
+ continue;
+ }
+
+ write_inode_to_buff(node);
+
+ p += PAD(je32_to_cpu (node->i.totlen));
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ memcpy (name, node->d.name, node->d.nsize);
+ name [node->d.nsize] = 0x0;
+
+ if (verbose)
+ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
+ je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
+ node->d.nsize, name);
+
+ crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8);
+ if (crc != je32_to_cpu (node->d.node_crc)) {
+ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.node_crc), crc);
+ p += PAD(je32_to_cpu (node->d.totlen));
+ continue;
+ }
+
+ crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize);
+ if (crc != je32_to_cpu(node->d.name_crc)) {
+ printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.name_crc), crc);
+ p += PAD(je32_to_cpu (node->d.totlen));
+ continue;
+ }
+
+ write_dirent_to_buff(node);
+
+ p += PAD(je32_to_cpu (node->d.totlen));
+ break;
+
+ case JFFS2_NODETYPE_XATTR:
+ if (je32_to_cpu(node->x.node_crc) == 0xffffffff)
+ obsolete = 1;
+ if (verbose)
+ printf("%8s Xdatum node at 0x%08x, 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 = crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4);
+ if (crc != je32_to_cpu(node->x.node_crc)) {
+ printf("Wrong node_crc at 0x%08x, 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 = crc32(0, node->x.data, length);
+ if (crc != je32_to_cpu(node->x.data_crc)) {
+ printf("Wrong data_crc at 0x%08x, 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;
+ if (verbose)
+ printf("%8s Xref node at 0x%08x, 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 = crc32(0, node, sizeof (struct jffs2_raw_xref) - 4);
+ if (crc != je32_to_cpu(node->r.node_crc)) {
+ printf("Wrong node_crc at 0x%08x, 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:
+ if (verbose) {
+ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ if (!found_cleanmarkers) {
+ found_cleanmarkers = 1;
+
+ if (add_cleanmarkers == 1 && use_input_cleanmarker_size == 1){
+ cleanmarker_size = je32_to_cpu (node->u.totlen);
+ setup_cleanmarker();
+ }
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case JFFS2_NODETYPE_PADDING:
+ if (verbose) {
+ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case 0xffff:
+ p += 4;
+ break;
+
+ default:
+ if (verbose) {
+ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ process_options(argc,argv);
+
+ if ((in_fd == -1) || (out_fd == -1)) {
+ if(in_fd != -1)
+ close(in_fd);
+ if(out_fd != -1)
+ close(out_fd);
+ fprintf(stderr,helptext);
+ error_msg_and_die("You must specify input and output files!\n");
+ }
+
+ init_buffers();
+ init_sumlist();
+
+ while ((ret = load_next_block())) {
+ create_summed_image(ret);
+ }
+
+ flush_buffers();
+ clean_buffers();
+ clean_sumlist();
+
+ if (in_fd != -1)
+ close(in_fd);
+ if (out_fd != -1)
+ close(out_fd);
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/checkfs/Makefile b/mtd-utils-1.3.1/tests/checkfs/Makefile
new file mode 100644
index 0000000..ac94dde
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/checkfs/Makefile
@@ -0,0 +1,14 @@
+
+all: checkfs makefiles
+
+checkfs: checkfs.c Makefile common.h comm.o
+ gcc -g -Wall checkfs.c comm.o -o checkfs
+
+comm.o: comm.c Makefile
+ gcc -g -Wall -c comm.c -o comm.o
+
+makefiles: makefiles.c Makefile common.h
+ gcc -g -Wall makefiles.c -o makefiles
+
+clean:
+ rm -f makefiles checkfs *~ *.o
diff --git a/mtd-utils-1.3.1/tests/checkfs/README b/mtd-utils-1.3.1/tests/checkfs/README
new file mode 100644
index 0000000..d9966a5
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/checkfs/README
@@ -0,0 +1,173 @@
+$Id: README,v 1.2 2001/06/21 23:07:06 dwmw2 Exp $
+$Log: README,v $
+Revision 1.2 2001/06/21 23:07:06 dwmw2
+Initial import to MTD CVS
+
+Revision 1.1 2001/06/11 19:34:40 vipin
+Added README file to dir.
+
+
+This is the README file for the "checkfs" power fail test program.
+By: Vipin Malik
+
+NOTE: This program requires an external "power cycling box"
+connected to one of the com ports of the system under test.
+This power cycling box should wait for a random amount of time
+after it receives a "ok to power me down" message over the
+serial port, and then yank power to the system under test.
+(The box that I rigged up tested with waits anywhere from
+0 to ~40 seconds).
+
+
+It should then restore power after a few seconds and wait for the
+message again.
+
+
+ABOUT:
+
+This program's primary purpose it to test the reliiability
+of various file systems under Linux.
+
+SETUP:
+
+You need to setup the file system you want to test and run the
+"makefiles" program ONCE. This creates a set of files that are
+required by the "checkfs" program.
+
+Also copy the "checkfs" executable program to the same dir.
+
+Then you need to make sure that the program "checkfs" is called
+automatically on startup. You can customise the operation of
+the "checkfs" program by passing it various cmd line arguments.
+run "checkfs -?" for more details.
+
+****NOTE*******
+Make sure that you call the checkfs program only after you have
+mounted the file system you want to test (this is obvious), but
+also after you have run any "scan" utilities to check for and
+fix any file systems errors. The e2fsck is one utility for the
+ext2 file system. For an automated setup you of course need to
+provide these scan programs to run in standalone mode (-f -y
+flags for e2fsck for example).
+
+File systems like JFFS and JFFS2 do not have any such external
+utilities and you may call "checkfs" right after you have mounted
+the respective file system under test.
+
+There are two ways you can mount the file system under test:
+
+1. Mount your root fs on a "standard" fs like ext2 and then
+mount the file system under test (which may be ext2 on another
+partition or device) and then run "checkfs" on this mounted
+partition OR
+
+2. Make your fs AND device that you have put this fs as your
+root fs and run "checkfs" on the root device (i.e. "/").
+You can of course still run checkfs under a separate dir
+under your "/" root dir.
+
+I have found the second method to be a particularly stringent
+arrangement (and thus preferred when you are trying to break
+something).
+
+Using this arrangement I was able to find that JFFS clobbered
+some "sister" files on the root fs even though "checkfs" would
+run fine through all its own check files.
+
+(I found this out when one of the clobbered sister file happened
+to be /bin/bash. The system refused to run rc.local thus
+preventing my "checkfs" program from being launched :)
+
+"checkfs":
+
+The "formatting" reliability of the fs as well as the file data integrity
+of files on the fs can be checked using this program.
+
+"formatiing" reliability can only be checked via an indirect method.
+If there is severe formatting reliability issues with the file system,
+it will most likely cause other system failures that will prevent this
+program from running successfully on a power up. This will prevent
+a "ok to power me down" message from going out to the power cycling
+black box and prevent power being turned off again.
+
+File data reliability is checked more directly. A fixed number of
+files are created in the current dir (using the program "makefiles").
+
+Each file has a random number of bytes in it (set by using the
+-s cmd line flag). The number of "ints" in the file is stored as the
+first "int" in it (note: 0 length files are not allowed). Each file
+is then filled with random data and a 16 bit CRC appended at the end.
+
+When "checkfs" is run, it runs through all files (with predetermined
+file names)- one at a time- and checks for the number of "int's"
+in it as well as the ending CRC.
+
+The program exits if the numbers of files that are corrupt are greater
+that a user specified parameter (set by using the -e cmd line flag).
+
+If the number of corrupt files is less than this parameter, the corrupt
+files are repaired and operation resumes as explained below.
+
+The idea behind allowing a user specified amount of corrupt files is as
+follows:
+
+If you are testing for "formatting" reliability of a fs, and for
+the data reliability of "other" files present of the fs, use -e 1.
+"other" files are defined as sister files on the fs, not being written to
+by the "checkfs" test program.
+
+As mentioned, in this case you would set -e 1, or allow at most 1 file
+to be corrupt each time after a power fail. This would be the file
+that was probably being written to when power failed (and CRC was not
+updated to reflect the new data being written). You would check file
+systems like ext2 etc. with such a configuration.
+(As you have no hope that these file systems provide for either your
+new data or old data to be present in the file if power failed during
+the write. This is called "roll back and recover".)
+
+With JFFS2 I tested for such "roll back and recover" file data reliability
+by setting -e 0 and making sure that all writes to the file being
+updated are done in a *single* write().
+
+This is how I found that JFFS2 (yet) does NOT support this functionality.
+(There was a great debate if this was a bug or a feature that was lacking
+or even an issue at all. See the mtd archives for more details).
+
+In other words, JFFS2 will partially update a file on FLASH even before
+the write() command has completed, thus leaving part old data part new
+data in your file if power failed in the middle of a write().
+
+This is bad functionality if you are updating a binary structure or a
+CRC protected file (as in our case).
+
+
+If All Files Check Out OK:
+
+On the startup scan, if there are less errors than specified by the "-e flag"
+a "ok to power me down message" is sent via the specified com port.
+
+The actual format of this message will depend on the format expected
+by the power cycling box that will receive this message. One may customise
+the actual message that goes out in the "do_pwr_dn)" routine in "comm.c".
+
+This file is called with an open file descriptor to the comm port that
+this message needs to go out over and the count of the current power
+cycle (in case your power cycling box can display/log this count).
+
+After this message has been sent out, the checkfs program goes into
+a while(1) loop of writing new data (with CRC), one at a time, into
+all the "check files" in the dir.
+
+Its life comes to a sudden end when power is asynchronously pulled from
+under its feet (by your external power cycling box).
+
+It comes back to life when power is restored and the system boots and
+checkfs is called from the rc.local script file.
+
+The cycle then repeats till a problem is detected, at which point
+the "ok to power me down" message is not sent and the cycle stops
+waiting for the user to examine the system.
+
+
+
+
diff --git a/mtd-utils-1.3.1/tests/checkfs/checkfs.c b/mtd-utils-1.3.1/tests/checkfs/checkfs.c
new file mode 100644
index 0000000..da2d0c4
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/checkfs/checkfs.c
@@ -0,0 +1,695 @@
+/*
+
+ * Copyright Daniel Industries.
+ *
+ * Created by: Vipin Malik (vipin.malik@daniel.com)
+ *
+ * This code is released under the GPL version 2. See the file COPYING
+ * for more details.
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+
+ This program opens files in progression (file00001, file00002 etc),
+ upto MAX_NUM_FILES and checks their CRC. If a file is not found or the
+ CRC does not match it stops it's operation.
+
+ Everything is logged in a logfile called './logfile'.
+
+ If everything is ok this program sends a signal, via com1, to the remote
+ power control box to power cycle this computer.
+
+ This program then proceeds to create new files file0....file<MAX_NUM_FILES>
+ in a endless loop and checksum each before closing them.
+
+ STRUCTURE OF THE FILES:
+ The fist int is the size of the file in bytes.
+ The last 2 bytes are the CRC for the entire file.
+ There is random data in between.
+
+ The files are opened in the current dir.
+
+ $Id: checkfs.c,v 1.8 2005/11/07 11:15:17 gleixner Exp $
+ $Log: checkfs.c,v $
+ Revision 1.8 2005/11/07 11:15:17 gleixner
+ [MTD / JFFS2] Clean up trailing white spaces
+
+ Revision 1.7 2001/06/21 23:04:17 dwmw2
+ Initial import to MTD CVS
+
+ Revision 1.6 2001/06/08 22:26:05 vipin
+ Split the modbus comm part of the program (that sends the ok to pwr me down
+ message) into another file "comm.c"
+
+ Revision 1.5 2001/06/08 21:29:56 vipin
+ fixed small issue with write() checking for < 0 instead of < (bytes to be written).
+ Now it does the latter (as it should).
+
+ Revision 1.4 2001/05/11 22:29:40 vipin
+ Added a test to check and err out if the first int in file (which tells us
+ how many bytes there are in the file) is zero. This will prevent a corrupt
+ file with zero's in it from passing the crc test.
+
+ Revision 1.3 2001/05/11 21:33:54 vipin
+ Changed to use write() rather than fwrite() when creating new file. Additionally,
+ and more important, it now does a single write() for the entire data. This will
+ enable us to use this program to test for power fail *data* reliability when
+ writing over an existing file, specially on powr fail "safe" file systems as
+ jffs/jffs2. Also added a new cmdline parameter "-e" that specifies the max # of
+ errors that can be tolerated. This should be set to ZERO to test for the above,
+ as old data should be reliabily maintained if the newer write never "took" before
+ power failed. If the write did succeed, then the newer data will have its own
+ CRC in place when it gets checked => hence no error. In theory at least!
+
+
+ Revision 1.2 2001/05/11 19:27:33 vipin
+ Added cmd line args to change serial port, and specify max size of
+ random files created. Some cleanup. Added -Wall to Makefile.
+
+ Revision 1.1 2001/05/11 16:06:28 vipin
+ Importing checkfs (the power fail test program) into CVS. This was
+ originally done for NEWS. NEWS had a lot of version, this is
+ based off the last one done for NEWS. The "makefiles" program
+ is run once initially to create the files in the current dir.
+ "checkfs" is then run on every powerup to check consistancy
+ of the files. See checkfs.c for more details.
+
+
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include "common.h"
+
+
+
+extern int do_pwr_dn(int fd, int cycleCnt);
+
+#define CMDLINE_PORT "-p"
+#define CMDLINE_MAXFILEBYTES "-s"
+#define CMDLINE_MAXERROR "-e"
+#define CMDLINE_HELPSHORT "-?"
+#define CMDLINE_HELPLONG "--help"
+
+
+int CycleCount;
+
+char SerialDevice[255] = "/dev/ttyS0"; /* default, can be changed
+ through cmd line. */
+
+#define MAX_INTS_ALLOW 100000 /* max # of int's in the file written.
+ Statis limit to size struct. */
+float FileSizeMax = 1024.0; /*= (file size in bytes), MUST be float*/
+
+int MaxErrAllowed = 1; /* default, can ge changed thru cmd line*/
+
+
+/* Needed for CRC generation/checking */
+static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+
+/*
+ Set's up the Linux serial port. Must be passed string to device to
+ open. Parameters are fixed to 9600,e,1
+
+ [A possible enhancement to this program would be to pass these
+ parameters via the command line.]
+
+ Returns file descriptor to open port. Use this fd to write to port
+ and close it later, when done.
+*/
+int setupSerial (const char *dev) {
+ int i, fd;
+ struct termios tios;
+
+ fd = open(dev,O_RDWR | O_NDELAY );
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s\n", dev, sys_errlist[errno]);
+ exit(1);
+ }
+ if (tcgetattr(fd, &tios) < 0) {
+ fprintf(stderr,"Could not get terminal attributes: %s",sys_errlist[errno]);
+ exit(1);
+ }
+
+ tios.c_cflag =
+ CS7 |
+ CREAD | // Enable Receiver
+ HUPCL | // Hangup after close
+ CLOCAL | // Ignore modem control lines
+ PARENB; // Enable parity (even by default)
+
+
+
+ tios.c_iflag = IGNBRK; // Ignore break
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ for(i = 0; i < NCCS; i++) {
+ tios.c_cc[i] = '\0'; // no special characters
+ }
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ cfsetospeed (&tios, B9600);
+ cfsetispeed (&tios, B9600);
+
+ if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+ fprintf(stderr,"Could not set attributes: ,%s",sys_errlist[errno]);
+ exit(1);
+ }
+ return fd;
+}
+
+
+
+
+
+//A portion of this code was taken from the AX.25 HDLC packet driver
+//in LINUX. Once I test and have a better understanding of what
+//it is doing, it will be better commented.
+
+//For now one can speculate that the CRC routine always expects the
+//CRC to calculate out to 0xf0b8 (the hardcoded value at the end)
+//and returns TRUE if it is and FALSE if it doesn't.
+//Why don't people document better!!!!
+int check_crc_ccitt(char *filename)
+{
+ FILE *fp;
+ FILE *logfp;
+ unsigned short crc = 0xffff;
+ int len;
+ char dataByte;
+ int retry;
+ char done;
+
+ fp = fopen(filename,"rb");
+ if(!fp){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename);
+ fclose(logfp);
+ return FALSE;
+ }
+
+
+ /*the first int contains an int that is the length of the file in long.*/
+ if(fread(&len, sizeof(int), 1, fp) != 1){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "verify checksum:Error reading from file: %s\n", filename);
+ fclose(fp);
+ fclose(logfp);
+ return FALSE;
+ }
+
+ /* printf("Checking %i bytes for CRC in \"%s\".\n", len, filename); */
+
+ /* Make sure that we did not read 0 as the number of bytes in file. This
+ check prevents a corrupt file with zero's in it from passing the
+ CRC test. A good file will always have some data in it. */
+ if(len == 0)
+ {
+
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "verify checksum: first int claims there are 0 data in file. Error!: %s\n", filename);
+ fclose(fp);
+ fclose(logfp);
+ return FALSE;
+ }
+
+
+ rewind(fp);
+ len+=2; /*the file has two extra bytes at the end, it's checksum. Those
+ two MUST also be included in the checksum calculation.
+ */
+
+ for (;len>0;len--){
+ retry=5; /*retry 5 times*/
+ done = FALSE;
+ while(!done){
+ if(fread(&dataByte, sizeof(char), 1, fp) != 1){
+ retry--;
+ }else{
+ done = TRUE;
+ }
+ if(retry == 0){
+ done = TRUE;
+ }
+ }
+ if(!retry){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "Unexpected end of file: %s\n", filename);
+ fprintf(logfp, "...bytes left to be read %i.\n",len);
+ fclose(logfp);
+ fclose(fp);
+ return FALSE;
+ }
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff];
+ }
+ fclose(fp);
+ if( (crc & 0xffff) != 0xf0b8){
+ /*printf("The CRC of read file:%x\n", crc); */
+ return FALSE;
+ }
+ return TRUE;
+}/*end check_crc_ccitt() */
+
+
+
+/*
+ Sends "OK to power me down" message to the remote
+ power cycling box, via the serial port.
+ Also updates the num power cycle count in a local
+ file.
+ This file "./cycleCnt" must be present. This is
+ initially (and once) created by the separate "makefiles.c"
+ program.
+*/
+void send_pwrdn_ok(void){
+
+ int fd;
+ FILE *cyclefp;
+ int cycle_fd;
+
+ cyclefp = fopen("cycleCnt","rb");
+ if(!cyclefp){
+ printf("expecting file \"cycleCnt\". Cannot continue.\n");
+ exit(1);
+ }
+ if(fread(&CycleCount, sizeof(CycleCount),1,cyclefp) != 1){
+ fprintf(stderr, "Error! Unexpected end of file cycleCnt.\n");
+ exit(1);
+ }
+ fclose(cyclefp);
+
+ CycleCount++;
+
+ /*now write this puppy back*/
+ cyclefp = fopen("cycleCnt","wb");
+ cycle_fd = fileno(cyclefp);
+ if(!cyclefp){
+ fprintf(stderr, "Error! cannot open file for write:\"cycleCnt\". Cannot continue.\n");
+ exit(1);
+ }
+ if(fwrite(&CycleCount, sizeof(CycleCount), 1,cyclefp) !=1){
+ fprintf(stderr, "Error writing to file cycleCnt. Cannot continue.\n");
+ exit(1);
+ }
+ if(fdatasync(cycle_fd)){
+ fprintf(stderr, "Error! cannot sync file buffer with disk.\n");
+ exit(1);
+ }
+
+ fclose(cyclefp);
+ (void)sync();
+
+ printf("\n\n Sending Power down command to the remote box.\n");
+ fd = setupSerial(SerialDevice);
+
+ if(do_pwr_dn(fd, CycleCount) < 0)
+ {
+ fprintf(stderr, "Error sending power down command.\n");
+ exit(1);
+ }
+
+ close(fd);
+}//end send_pwrnd_ok()
+
+
+
+
+/*
+ Appends 16bit CRC at the end of numBytes long buffer.
+ Make sure buf, extends at least 2 bytes beyond.
+ */
+void appendChecksum(char *buf, int numBytes){
+
+ unsigned short crc = 0xffff;
+ int index = 0;
+
+ /* printf("Added CRC (2 bytes) to %i bytes.\n", numBytes); */
+
+ for (; numBytes > 0; numBytes--){
+
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ buf[index++]) & 0xff];
+ }
+ crc ^= 0xffff;
+ /*printf("The CRC: %x\n\n", crc);*/
+
+ buf[index++] = crc;
+ buf[index++] = crc >> 8;
+
+
+
+}/*end checksum()*/
+
+
+
+
+
+/*
+ This guy make a new "random data file" with the filename
+ passed to it. This file is checksummed with the checksum
+ stored at the end. The first "int" in the file is the
+ number of int's in it (this is needed to know how much
+ data to read and checksum later).
+*/
+void make_new_file(char *filename){
+
+
+ int dfd; /* data file descriptor */
+ int rand_data;
+ int data_size;
+ int temp_size;
+ int dataIndex = 0;
+ int err;
+
+
+ struct {
+ int sizeInBytes; /* must be int */
+ int dataInt[MAX_INTS_ALLOW+1]; /* how many int's can we write? */
+ }__attribute((packed)) dataBuf;
+
+
+ fprintf(stderr, "Creating File:%s. ", filename);
+
+ if((dfd = open(filename, O_RDWR | O_CREAT | O_SYNC)) <= 0)
+ {
+ printf("Error! Cannot open file: %s\n",filename);
+ perror("Error");
+ exit(1);
+ }
+
+ /*now write a bunch of random binary data to the file*/
+ /*first figure out how much data to write. That is random also.*/
+
+ /*file should not be less than 5 ints long. (so that we have decent length files,
+ that's all)*/
+ while(
+ ((data_size = (int)(1+(int)((FileSizeMax/sizeof(int))*rand()/(RAND_MAX+1.0)))) < 5)
+ );
+
+ /* printf("Writing %i ints to the file.\n", data_size); */
+
+ temp_size = data_size * sizeof(int);
+
+ /* Make sure that all data is written in one go! This is important to
+ check for reliability of file systems like JFFS/JFFS that purport to
+ have "reliable" writes during powre fail.
+ */
+
+ dataBuf.sizeInBytes = temp_size;
+
+ data_size--; /*one alrady written*/
+ dataIndex = 0;
+
+ while(data_size--){
+ rand_data = (int)(1 + (int)(10000.0*rand()/(RAND_MAX+1.0)));
+
+ dataBuf.dataInt[dataIndex++] = rand_data;
+
+ }
+
+ /*now calculate the file checksum and append it to the end*/
+ appendChecksum((char *)&dataBuf, dataBuf.sizeInBytes);
+
+ /* Don't forget to increase the size of data written by the 2 chars of CRC at end.
+ These 2 bytes are NOT included in the sizeInBytes field. */
+ if((err = write(dfd, (void *)&dataBuf, dataBuf.sizeInBytes + sizeof(short))) <
+ (dataBuf.sizeInBytes + sizeof(short))
+ )
+ {
+ printf("Error writing data buffer to file. Written %i bytes rather than %i bytes.",
+ err, dataBuf.sizeInBytes);
+ perror("Error");
+ exit(1);
+ }
+
+ /* Now that the data is (hopefully) safely written. I can truncate the file to the new
+ length so that I can reclaim any unused space, if the older file was larger.
+ */
+ if(ftruncate(dfd, dataBuf.sizeInBytes + sizeof(short)) < 0)
+ {
+ perror("Error: Unable to truncate file.");
+ exit(1);
+ }
+
+
+ close(dfd);
+
+
+}//end make_new_file()
+
+
+
+/*
+ Show's help on stdout
+ */
+void printHelp(char **argv)
+{
+ printf("Usage:%s <options, defined below>\n", argv[0]);
+ printf("%s </dev/ttyS0,1,2...>: Set com port to send ok to pwr dn msg on\n",
+ CMDLINE_PORT);
+ printf("%s <n>: Set Max size in bytes of each file to be created.\n",
+ CMDLINE_MAXFILEBYTES);
+ printf("%s <n>: Set Max errors allowed when checking all files for CRC on start.\n",
+ CMDLINE_MAXERROR);
+ printf("%s or %s: This Help screen.\n", CMDLINE_HELPSHORT,
+ CMDLINE_HELPLONG);
+
+}/* end printHelp()*/
+
+
+
+void processCmdLine(int argc, char **argv)
+{
+
+ int cnt;
+
+ /* skip past name of this program, process rest */
+ for(cnt = 1; cnt < argc; cnt++)
+ {
+ if(strcmp(argv[cnt], CMDLINE_PORT) == 0)
+ {
+ strncpy(SerialDevice, argv[++cnt], sizeof(SerialDevice));
+ continue;
+ }else
+ if(strcmp(argv[cnt], CMDLINE_MAXFILEBYTES) == 0)
+ {
+ FileSizeMax = (float)atoi(argv[++cnt]);
+ if(FileSizeMax > (MAX_INTS_ALLOW*sizeof(int)))
+ {
+ printf("Max file size allowd is %i.\n",
+ MAX_INTS_ALLOW*sizeof(int));
+ exit(0);
+ }
+
+ continue;
+ }else
+ if(strcmp(argv[cnt], CMDLINE_HELPSHORT) == 0)
+ {
+ printHelp(argv);
+ exit(0);
+
+ }else
+ if(strcmp(argv[cnt], CMDLINE_HELPLONG) == 0)
+ {
+ printHelp(argv);
+ exit(0);
+ }else
+
+ if(strcmp(argv[cnt], CMDLINE_MAXERROR) == 0)
+ {
+ MaxErrAllowed = atoi(argv[++cnt]);
+ }
+ else
+ {
+ printf("Unknown cmd line option:%s\n", argv[cnt]);
+ printHelp(argv);
+ exit(0);
+
+ }
+ }
+
+
+}/* end processCmdLine() */
+
+
+
+
+
+int main(int argc, char **argv){
+
+ FILE *logfp;
+ int log_fd;
+ char filename[30];
+ short filenameCounter = 0;
+ unsigned short counter;
+ unsigned short numberFiles;
+ char error = FALSE;
+ short errorCnt = 0;
+ time_t timep;
+ char * time_string;
+ unsigned int seed;
+
+
+ numberFiles = MAX_NUM_FILES;
+
+ if(argc >= 1)
+ {
+ processCmdLine(argc, argv);
+ }
+
+
+ /*
+ First open MAX_NUM_FILES and make sure that the checksum is ok.
+ Also make an intry into the logfile.
+ */
+ /* timestamp! */
+ time(&timep);
+ time_string = (char *)ctime((time_t *)&timep);
+
+ /*start a new check, make a log entry and continue*/
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"%s", time_string);
+ fprintf(logfp,"Starting new check.\n");
+ if(fdatasync(log_fd) == -1){
+ fprintf(stderr,"Error! Cannot sync file data with disk.\n");
+ exit(1);
+ }
+
+ fclose(logfp);
+ (void)sync();
+
+ /*
+ Now check all random data files in this dir.
+ */
+ for(counter=0;counter<MAX_NUM_FILES;counter++){
+
+ fprintf(stderr, "%i.", counter);
+
+ /*create the filename in sequence. The number of files
+ to check and the algorithm to create the filename is
+ fixed and known in advance.*/
+ sprintf(filename,"file%i",filenameCounter++);
+
+ if(!check_crc_ccitt(filename)){
+ /*oops, checksum does not match. Make an entry into the log file
+ and decide if we can continue or not.*/
+ fprintf(stderr, "crcError:%s ", filename);
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"CRC error in file: %s\n", filename);
+ if(fdatasync(log_fd) == -1){
+ fprintf(stderr,"Error! Cannot sync file data with disk.\n");
+ exit(1);
+ }
+ fclose(logfp);
+ (void)sync();
+
+ error = TRUE;
+ errorCnt++;
+
+ if(errorCnt > MaxErrAllowed){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"\nMax Error count exceed. Stopping!\n");
+ if(fdatasync(log_fd) == -1){
+ fprintf(stderr,"Error! Cannot sync file data with disk.\n");
+ exit(1);
+ }
+ fclose(logfp);
+ (void)sync();
+
+ fprintf(stderr, "Too many errors. See \"logfile\".\n");
+ exit(1);
+ }/* if too many errors */
+
+ /*we have decided to continue, however first repair this file
+ so that we do not cumulate errors across power cycles.*/
+ make_new_file(filename);
+ }
+ }//for
+
+ /*all files checked, make a log entry and continue*/
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"All files checked. Total errors found: %i\n\n", errorCnt);
+ if(fdatasync(log_fd)){
+ fprintf(stderr, "Error! cannot sync file buffer with disk.\n");
+ exit(1);
+ }
+
+ fclose(logfp);
+ (void)sync();
+
+ /*now send a message to the remote power box and have it start a random
+ pwer down timer after which power will be killed to this unit.
+ */
+ send_pwrdn_ok();
+
+ /*now go into a forever loop of writing to files and CRC'ing them on
+ a continious basis.*/
+
+ /*start from a random file #*/
+ /*seed rand based on the current time*/
+ seed = (unsigned int)time(NULL);
+ srand(seed);
+
+ filenameCounter=(int)(1+(int)((float)(MAX_NUM_FILES-1)*rand()/(RAND_MAX+1.0)));
+
+ while(1){
+
+ for(;filenameCounter<MAX_NUM_FILES;filenameCounter++){
+
+ /*create the filename in sequence*/
+ sprintf(filename,"file%i",filenameCounter);
+ make_new_file(filename);
+ }
+ filenameCounter = 0;
+ }
+
+ exit(0); /* though we will never reach here, but keeps the compiler happy*/
+}/*end main()*/
diff --git a/mtd-utils-1.3.1/tests/checkfs/comm.c b/mtd-utils-1.3.1/tests/checkfs/comm.c
new file mode 100644
index 0000000..57fc7ed
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/checkfs/comm.c
@@ -0,0 +1,67 @@
+/*
+ File: comm.c
+ Desc: This file implements the actual transmission portion
+ of the "ok to power me down" message to the remote
+ power cycling black box.
+
+ It's been sepatated into a separate file so that
+ it may be replaced by any other comm mechanism desired.
+
+ (including none or non serial mode at all)
+
+ $Id: comm.c,v 1.3 2005/11/07 11:15:17 gleixner Exp $
+ $Log: comm.c,v $
+ Revision 1.3 2005/11/07 11:15:17 gleixner
+ [MTD / JFFS2] Clean up trailing white spaces
+
+ Revision 1.2 2001/06/21 23:07:18 dwmw2
+ Initial import to MTD CVS
+
+ Revision 1.1 2001/06/08 22:26:05 vipin
+ Split the modbus comm part of the program (that sends the ok to pwr me down
+ message) into another file "comm.c"
+
+
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+
+/*
+ This is the routine that forms and
+ sends the "ok to pwr me down" message
+ to the remote power cycling "black box".
+
+ */
+int do_pwr_dn(int fd, int cycleCnt)
+{
+
+ char buf[200];
+
+ sprintf(buf, "ok to power me down!\nCount = %i\n", cycleCnt);
+
+ if(write(fd, buf, strlen(buf)) < strlen(buf))
+ {
+ perror("write error");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mtd-utils-1.3.1/tests/checkfs/common.h b/mtd-utils-1.3.1/tests/checkfs/common.h
new file mode 100644
index 0000000..1d33f8b
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/checkfs/common.h
@@ -0,0 +1,7 @@
+/* $Id: common.h,v 1.1 2001/06/21 23:07:56 dwmw2 Exp $ */
+//this .h file is common to both the file creation utility and
+//the file checking utility.
+#define TRUE 1
+#define FALSE 0
+
+#define MAX_NUM_FILES 100
diff --git a/mtd-utils-1.3.1/tests/checkfs/makefiles.c b/mtd-utils-1.3.1/tests/checkfs/makefiles.c
new file mode 100644
index 0000000..662fe86
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/checkfs/makefiles.c
@@ -0,0 +1,264 @@
+/*
+
+ * Copyright Daniel Industries.
+
+ * Created by: Vipin Malik (vipin.malik@daniel.com)
+ *
+ * This is GPL code. See the file COPYING for more details
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+
+ * $Id: makefiles.c,v 1.2 2005/11/07 11:15:17 gleixner Exp $
+
+This program creates MAX_NUM_FILES files (file00001, file00002 etc) and
+fills them with random numbers till they are a random length. Then it checksums
+the files (with the checksum as the last two bytes) and closes the file.
+
+The fist int is the size of the file in bytes.
+
+It then opens another file and the process continues.
+
+The files are opened in the current dir.
+
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+
+#define FILESIZE_MAX 20000.0 /* for each file in sizeof(int). Must be a float #
+ Hence, 20000.0 = 20000*4 = 80KB max file size
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+//This code was taken from the AX.25 HDLC packet driver
+//in LINUX. Once I test and have a better understanding of what
+//it is doing, it will be better commented.
+
+//For now one can speculate that the CRC routine always expects the
+//CRC to calculate out to 0xf0b8 (the hardcoded value at the end)
+//and returns TRUE if it is and FALSE if it doesn't.
+//Why don't people document better!!!!
+void check_crc_ccitt(char *filename)
+{
+ FILE *fp;
+ unsigned short crc = 0xffff;
+ int len;
+ char dataByte;
+ int retry;
+
+ fp = fopen(filename,"rb");
+ if(!fp){
+ printf("Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename);
+ exit(1);
+ }
+ /*the first int contains an int that is the length of the file in long.*/
+ if(fread(&len, sizeof(int), 1, fp) != 1){
+ printf("verify checksum:Error reading from file: %s", filename);
+ fclose(fp);
+ exit(1);
+ }
+ rewind(fp);
+ len+=2; /*the file has two extra bytes at the end, it's checksum. Those
+ two MUST also be included in the checksum calculation.
+ */
+
+ for (;len>0;len--){
+ retry=5; /*retry 5 times*/
+ while(!fread(&dataByte, sizeof(char), 1, fp) && retry--);
+ if(!retry){
+ printf("Unexpected error reading from file: %s\n", filename);
+ printf("...bytes left to be read %i.\n\n",len);
+ fclose(fp);
+ exit(1);
+ }
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff];
+ }
+ fclose(fp);
+ if( (crc & 0xffff) != 0xf0b8){
+ printf("Verify checksum: Error in file %s.\n\n",filename);
+ exit(1);
+ }
+}//end check_crc_ccitt()
+
+
+
+/*this routine opens a file 'filename' and checksumn's the entire
+ contents, and then appends the checksum at the end of the file,
+ closes the file and returns.
+*/
+void checksum(char *filename){
+
+ FILE *fp;
+ unsigned short crc = 0xffff;
+ int len;
+ char dataByte;
+ int retry;
+
+ fp = fopen(filename,"rb");
+ if(!fp){
+ printf("Error! Cannot open filename passed for checksum: %s\n",filename);
+ exit(1);
+ }
+ /*the first int contains an int that is the length of the file in longs.*/
+ if(fread(&len, sizeof(int), 1, fp) != 1){
+ printf("Error reading from file: %s", filename);
+ fclose(fp);
+ exit(1);
+ }
+ printf("Calculating checksum on %i bytes.\n",len);
+ rewind(fp); /*the # of bytes int is also included in the checksum.*/
+
+ for (;len>0;len--){
+ retry=5; /*retry 5 times*/
+ while(!fread(&dataByte, sizeof(char), 1, fp) && retry--);
+ if(!retry){
+ printf("Unexpected error reading from file: %s\n", filename);
+ printf("...bytes left to be read %i.\n\n",len);
+ fclose(fp);
+ exit(1);
+ }
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff];
+ }
+ crc ^= 0xffff;
+ printf("The CRC: %x\n\n", crc);
+
+ /*the CRC has been calculated. now close the file and open it in append mode*/
+ fclose(fp);
+
+ fp = fopen(filename,"ab"); /*open in append mode. CRC goes at the end.*/
+ if(!fp){
+ printf("Error! Cannot open filename to update checksum: %s\n",filename);
+ exit(1);
+ }
+ if(fwrite(&crc, sizeof(crc), 1, fp) != 1){
+ printf("error! unable to update the file for checksum.\n");
+ fclose(fp);
+ exit(1);
+ }
+ fflush(fp);
+ fclose(fp);
+
+
+}/*end checksum()*/
+
+
+
+int main(void){
+
+ FILE *fp, *cyclefp;
+ int cycleCount;
+ int rand_data;
+ int data_size;
+ int temp_size;
+ char filename[30];
+ short filenameCounter = 0;
+ unsigned short counter;
+ unsigned short numberFiles;
+
+ numberFiles = MAX_NUM_FILES;
+
+ for(counter=0;counter<numberFiles;counter++){
+ /*create the filename in sequence*/
+ sprintf(filename,"file%i",filenameCounter++);
+ fp = fopen(filename,"wb");
+ if(!fp){
+ printf("Error! Cannot open file: %s\n",filename);
+ exit(1);
+ }
+ /*now write a bunch of random binary data to the file*/
+ /*first figure out how much data to write. That is random also.*/
+
+ while(
+ ((data_size = (int)(1 + (int)(FILESIZE_MAX*rand()/(RAND_MAX+1.0)))) < 100)
+ )/*file should not be less than 100 ints long. (so that we have decent length files, that's all)*/
+
+ printf("Writing %i ints to the file.\n", data_size);
+
+ temp_size = data_size * sizeof(int);
+
+ if(!fwrite(&temp_size, sizeof(int), 1, fp)){
+ printf("File write error!!.\n");
+ fclose(fp);
+ exit(1);
+ }
+ data_size--; /*one alrady written*/
+
+ while(data_size--){
+ rand_data = (int)(1 + (int)(10000.0*rand()/(RAND_MAX+1.0)));
+ if(!fwrite(&rand_data, sizeof(int), 1, fp)){
+ printf("File write error!!.\n");
+ fclose(fp);
+ exit(1);
+ }
+ }
+ fflush(fp);
+ fclose(fp);
+ /*now calculate the file checksum and append it to the end*/
+ checksum(filename);
+ /*this is just a test. Check the CRC to amek sure that it is OK.*/
+ check_crc_ccitt(filename);
+ }
+
+ /*now make a file called "cycleCnt" and put a binary (int)0 in it.
+ This file keeps count as to how many cycles have taken place!*/
+ cyclefp = fopen("cycleCnt","wb");
+ if(!cyclefp){
+ printf("cannot open file \"cycleCnt\". Cannot continue.\n");
+ exit(1);
+ }
+ cycleCount = 0;
+ if(fwrite(&cycleCount, sizeof(cycleCount), 1,cyclefp) !=1){
+ printf("Error writing to file cycleCnt. Cannot continue.\n");
+ exit(1);
+ }
+ fclose(cyclefp);
+ exit(0);
+
+}/*end main()*/
+
+
+
+
+
+
+
diff --git a/mtd-utils-1.3.1/tests/fs-tests/Makefile b/mtd-utils-1.3.1/tests/fs-tests/Makefile
new file mode 100644
index 0000000..d188796
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/Makefile
@@ -0,0 +1,8 @@
+
+SUBDIRS = lib simple stress integrity utils
+
+all clean tests: $(SUBDIRS)
+
+.PHONY: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
diff --git a/mtd-utils-1.3.1/tests/fs-tests/help_all.sh b/mtd-utils-1.3.1/tests/fs-tests/help_all.sh
new file mode 100755
index 0000000..34b890b
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/help_all.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+echo -------------------------------------------------------------------------------
+./simple/test_1 -h
+echo -------------------------------------------------------------------------------
+./simple/test_2 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/stress_1 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/stress_2 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/stress_3 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/fwrite00 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/gcd_hupper -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/pdfrun -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/rmdir00 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/rndrm00 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/rndwrite00 -h
+echo -------------------------------------------------------------------------------
+./integrity/integck -h
+echo -------------------------------------------------------------------------------
diff --git a/mtd-utils-1.3.1/tests/fs-tests/integrity/Makefile b/mtd-utils-1.3.1/tests/fs-tests/integrity/Makefile
new file mode 100644
index 0000000..a35f4d0
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/integrity/Makefile
@@ -0,0 +1,22 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = integck
+
+all: $(TARGETS)
+
+$(TARGETS): ../lib/tests.o
+
+../lib/tests.o: ../lib/tests.h
+
+clean:
+ rm -f *.o $(TARGETS)
+
+tests: all
+ ./integck
diff --git a/mtd-utils-1.3.1/tests/fs-tests/integrity/integck.c b/mtd-utils-1.3.1/tests/fs-tests/integrity/integck.c
new file mode 100644
index 0000000..145557f
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/integrity/integck.c
@@ -0,0 +1,2064 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/mman.h>
+
+#include "tests.h"
+
+/* Structures to store data written to the test file system,
+ so that we can check whether the file system is correct. */
+
+struct write_info /* Record of random data written into a file */
+{
+ struct write_info *next;
+ off_t offset; /* Where in the file the data was written */
+ size_t size; /* Number of bytes written */
+ unsigned random_seed; /* Seed for rand() to create random data */
+ off_t random_offset; /* Call rand() this number of times first */
+ int trunc; /* Records a truncation (raw_writes only) */
+};
+
+struct dir_entry_info;
+
+struct file_info /* Each file has one of these */
+{
+ char *name; /* Original name */
+ struct write_info *writes; /* Record accumulated writes to the file */
+ struct write_info *raw_writes;
+ /* Record in order all writes to the file */
+ struct fd_info *fds; /* All open file descriptors for this file */
+ struct dir_entry_info *links;
+ int link_count;
+ off_t length;
+ int deleted; /* File has been deleted but is still open */
+ int no_space_error; /* File has incurred a ENOSPC error */
+ uint64_t check_run_no; /* Run number used when checking */
+};
+
+struct symlink_info /* Each symlink has one of these */
+{
+ char *target_pathname;
+ struct dir_entry_info *entry; /* dir entry of this symlink */
+};
+
+struct dir_info /* Each directory has one of these */
+{
+ char *name;
+ struct dir_info *parent; /* Parent directory or null
+ for our top directory */
+ unsigned number_of_entries;
+ struct dir_entry_info *first;
+ struct dir_entry_info *entry; /* Dir entry of this dir */
+};
+
+struct dir_entry_info /* Each entry in a directory has one of these */
+{
+ struct dir_entry_info *next; /* List of entries in directory */
+ struct dir_entry_info *prev; /* List of entries in directory */
+ struct dir_entry_info *next_link; /* List of hard links for same file */
+ struct dir_entry_info *prev_link; /* List of hard links for same file */
+ char *name;
+ struct dir_info *parent; /* Parent directory */
+ char type; /* f => file, d => dir, s => symlink */
+ int checked; /* Temporary flag used when checking */
+ union entry_
+ {
+ struct file_info *file;
+ struct dir_info *dir;
+ struct symlink_info *symlink;
+ void *target;
+ } entry;
+};
+
+struct fd_info /* We keep a number of files open */
+{
+ struct fd_info *next;
+ struct file_info *file;
+ int fd;
+};
+
+struct open_file_info /* We keep a list of open files */
+{
+ struct open_file_info *next;
+ struct fd_info *fdi;
+};
+
+static struct dir_info *top_dir = NULL; /* Our top directory */
+
+static struct open_file_info *open_files = NULL; /* We keep a list of
+ open files */
+static size_t open_files_count = 0;
+
+static int grow = 1; /* Should we try to grow files and directories */
+static int shrink = 0; /* Should we try to shrink files and directories */
+static int full = 0; /* Flag that the file system is full */
+static uint64_t operation_count = 0; /* Number of operations used to fill
+ up the file system */
+static uint64_t initial_free_space = 0; /* Free space on file system when
+ test starts */
+static unsigned log10_initial_free_space = 0; /* log10 of initial_free_space */
+
+static int check_nospc_files = 0; /* Also check data in files that incurred a
+ "no space" error */
+
+static int can_mmap = 0; /* Can write via mmap */
+
+static long mem_page_size; /* Page size for mmap */
+
+static uint64_t check_run_no;
+
+static char *copy_string(const char *s)
+{
+ char *str;
+
+ if (!s)
+ return NULL;
+ str = (char *) malloc(strlen(s) + 1);
+ CHECK(str != NULL);
+ strcpy(str, s);
+ return str;
+}
+
+static char *cat_strings(const char *a, const char *b)
+{
+ char *str;
+ size_t sz;
+
+ if (a && !b)
+ return copy_string(a);
+ if (b && !a)
+ return copy_string(b);
+ if (!a && !b)
+ return NULL;
+ sz = strlen(a) + strlen(b) + 1;
+ str = (char *) malloc(sz);
+ CHECK(str != NULL);
+ strcpy(str, a);
+ strcat(str, b);
+ return str;
+}
+
+static char *cat_paths(const char *a, const char *b)
+{
+ char *str;
+ size_t sz;
+ int as, bs;
+ size_t na, nb;
+
+ if (a && !b)
+ return copy_string(a);
+ if (b && !a)
+ return copy_string(b);
+ if (!a && !b)
+ return NULL;
+
+ as = 0;
+ bs = 0;
+ na = strlen(a);
+ nb = strlen(b);
+ if (na && a[na - 1] == '/')
+ as = 1;
+ if (nb && b[0] == '/')
+ bs = 1;
+ if ((as && !bs) || (!as && bs))
+ return cat_strings(a, b);
+ if (as && bs)
+ return cat_strings(a, b + 1);
+
+ sz = na + nb + 2;
+ str = (char *) malloc(sz);
+ CHECK(str != NULL);
+ strcpy(str, a);
+ strcat(str, "/");
+ strcat(str, b);
+ return str;
+}
+
+static char *dir_path(struct dir_info *parent, const char *name)
+{
+ char *parent_path;
+ char *path;
+
+ if (!parent)
+ return cat_paths(tests_file_system_mount_dir, name);
+ parent_path = dir_path(parent->parent, parent->name);
+ path = cat_paths(parent_path, name);
+ free(parent_path);
+ return path;
+}
+
+static void open_file_add(struct fd_info *fdi)
+{
+ struct open_file_info *ofi;
+ size_t sz;
+
+ sz = sizeof(struct open_file_info);
+ ofi = (struct open_file_info *) malloc(sz);
+ CHECK(ofi != NULL);
+ memset(ofi, 0, sz);
+ ofi->next = open_files;
+ ofi->fdi = fdi;
+ open_files = ofi;
+ open_files_count += 1;
+}
+
+static void open_file_remove(struct fd_info *fdi)
+{
+ struct open_file_info *ofi;
+ struct open_file_info **prev;
+
+ prev = &open_files;
+ for (ofi = open_files; ofi; ofi = ofi->next) {
+ if (ofi->fdi == fdi) {
+ *prev = ofi->next;
+ free(ofi);
+ open_files_count -= 1;
+ return;
+ }
+ prev = &ofi->next;
+ }
+ CHECK(0); /* We are trying to remove something that is not there */
+}
+
+static struct fd_info *add_fd(struct file_info *file, int fd)
+{
+ struct fd_info *fdi;
+ size_t sz;
+
+ sz = sizeof(struct fd_info);
+ fdi = (struct fd_info *) malloc(sz);
+ CHECK(fdi != NULL);
+ memset(fdi, 0, sz);
+ fdi->next = file->fds;
+ fdi->file = file;
+ fdi->fd = fd;
+ file->fds = fdi;
+ open_file_add(fdi);
+ return fdi;
+}
+
+static void add_dir_entry(struct dir_info *parent, char type, const char *name,
+ void *target)
+{
+ struct dir_entry_info *entry;
+ size_t sz;
+
+ sz = sizeof(struct dir_entry_info);
+ entry = (struct dir_entry_info *) malloc(sz);
+ CHECK(entry != NULL);
+ memset(entry, 0, sz);
+
+ entry->type = type;
+ entry->name = copy_string(name);
+ entry->parent = parent;
+
+ entry->next = parent->first;
+ if (parent->first)
+ parent->first->prev = entry;
+ parent->first = entry;
+ parent->number_of_entries += 1;
+
+ if (entry->type == 'f') {
+ struct file_info *file = target;
+
+ entry->entry.file = file;
+ entry->next_link = file->links;
+ if (file->links)
+ file->links->prev_link = entry;
+ file->links = entry;
+ file->link_count += 1;
+ } else if (entry->type == 'd') {
+ struct dir_info *dir = target;
+
+ entry->entry.dir = dir;
+ dir->entry = entry;
+ dir->name = copy_string(name);
+ dir->parent = parent;
+ } else if (entry->type == 's') {
+ struct symlink_info *symlink = target;
+
+ entry->entry.symlink = symlink;
+ symlink->entry = entry;
+ }
+}
+
+static void remove_dir_entry(struct dir_entry_info *entry)
+{
+ entry->parent->number_of_entries -= 1;
+ if (entry->parent->first == entry)
+ entry->parent->first = entry->next;
+ if (entry->prev)
+ entry->prev->next = entry->next;
+ if (entry->next)
+ entry->next->prev = entry->prev;
+
+ if (entry->type == 'f') {
+ struct file_info *file = entry->entry.file;
+
+ if (entry->prev_link)
+ entry->prev_link->next_link = entry->next_link;
+ if (entry->next_link)
+ entry->next_link->prev_link = entry->prev_link;
+ if (file->links == entry)
+ file->links = entry->next_link;
+ file->link_count -= 1;
+ if (file->link_count == 0)
+ file->deleted = 1;
+ }
+
+ free(entry->name);
+ free(entry);
+}
+
+static struct dir_info *dir_new(struct dir_info *parent, const char *name)
+{
+ struct dir_info *dir;
+ size_t sz;
+ char *path;
+
+ path = dir_path(parent, name);
+ if (mkdir(path, 0777) == -1) {
+ CHECK(errno == ENOSPC);
+ full = 1;
+ free(path);
+ return NULL;
+ }
+ free(path);
+
+ sz = sizeof(struct dir_info);
+ dir = (struct dir_info *) malloc(sz);
+ CHECK(dir != NULL);
+ memset(dir, 0, sz);
+ dir->name = copy_string(name);
+ dir->parent = parent;
+ if (parent)
+ add_dir_entry(parent, 'd', name, dir);
+ return dir;
+}
+
+static void file_delete(struct file_info *file);
+static void file_unlink(struct dir_entry_info *entry);
+static void symlink_remove(struct symlink_info *symlink);
+
+static void dir_remove(struct dir_info *dir)
+{
+ char *path;
+
+ /* Remove directory contents */
+ while (dir->first) {
+ struct dir_entry_info *entry;
+
+ entry = dir->first;
+ if (entry->type == 'd')
+ dir_remove(entry->entry.dir);
+ else if (entry->type == 'f')
+ file_unlink(entry);
+ else if (entry->type == 's')
+ symlink_remove(entry->entry.symlink);
+ else
+ CHECK(0); /* Invalid struct dir_entry_info */
+ }
+ /* Remove entry from parent directory */
+ remove_dir_entry(dir->entry);
+ /* Remove directory itself */
+ path = dir_path(dir->parent, dir->name);
+ CHECK(rmdir(path) != -1);
+ free(dir);
+}
+
+static struct file_info *file_new(struct dir_info *parent, const char *name)
+{
+ struct file_info *file = NULL;
+ char *path;
+ mode_t mode;
+ int fd;
+ size_t sz;
+
+ CHECK(parent != NULL);
+
+ path = dir_path(parent, name);
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(path, O_CREAT | O_EXCL | O_RDWR, mode);
+ if (fd == -1) {
+ CHECK(errno == ENOSPC);
+ free(path);
+ full = 1;
+ return NULL;
+ }
+ free(path);
+
+ sz = sizeof(struct file_info);
+ file = (struct file_info *) malloc(sz);
+ CHECK(file != NULL);
+ memset(file, 0, sz);
+ file->name = copy_string(name);
+
+ add_dir_entry(parent, 'f', name, file);
+
+ add_fd(file, fd);
+
+ return file;
+}
+
+static void link_new(struct dir_info *parent, const char *name,
+ struct file_info *file)
+{
+ struct dir_entry_info *entry;
+ char *path, *target;
+ int ret;
+
+ if (!file)
+ return;
+ entry = file->links;
+ if (!entry)
+ return;
+ path = dir_path(parent, name);
+ target = dir_path(entry->parent, entry->name);
+ ret = link(target, path);
+ if (ret == -1) {
+ CHECK(errno == ENOSPC);
+ free(target);
+ free(path);
+ full = 1;
+ return;
+ }
+ free(target);
+ free(path);
+
+ add_dir_entry(parent, 'f', name, file);
+}
+
+static void file_close(struct fd_info *fdi);
+
+static void file_close_all(struct file_info *file)
+{
+ struct fd_info *fdi = file->fds;
+
+ while (fdi) {
+ struct fd_info *next = fdi->next;
+
+ file_close(fdi);
+ fdi = next;
+ }
+}
+
+static void file_unlink(struct dir_entry_info *entry)
+{
+ struct file_info *file = entry->entry.file;
+ char *path;
+
+ path = dir_path(entry->parent, entry->name);
+
+ /* Remove file entry from parent directory */
+ remove_dir_entry(entry);
+
+ /* Unlink the file */
+ CHECK(unlink(path) != -1);
+ free(path);
+
+ /* Free struct file_info if file is not open and not linked */
+ if (!file->fds && !file->links) {
+ struct write_info *w, *next;
+
+ free(file->name);
+ w = file->writes;
+ while (w) {
+ next = w->next;
+ free(w);
+ w = next;
+ }
+ free(file);
+ } else if (!file->links)
+ file->deleted = 1;
+}
+
+static struct dir_entry_info *pick_entry(struct file_info *file)
+{
+ struct dir_entry_info *entry;
+ size_t r;
+
+ if (!file->link_count)
+ return NULL;
+ r = tests_random_no(file->link_count);
+ entry = file->links;
+ while (entry && r--)
+ entry = entry->next_link;
+ return entry;
+}
+
+static void file_unlink_file(struct file_info *file)
+{
+ struct dir_entry_info *entry;
+
+ entry = pick_entry(file);
+ if (!entry)
+ return;
+ file_unlink(entry);
+}
+
+static void file_delete(struct file_info *file)
+{
+ struct dir_entry_info *entry = file->links;
+
+ file_close_all(file);
+ while (entry) {
+ struct dir_entry_info *next = entry->next_link;
+
+ file_unlink(entry);
+ entry = next;
+ }
+}
+
+static void file_info_display(struct file_info *file)
+{
+ struct dir_entry_info *entry;
+ struct write_info *w;
+ unsigned wcnt;
+
+ fprintf(stderr, "File Info:\n");
+ fprintf(stderr, " Original name: %s\n", file->name);
+ fprintf(stderr, " Link count: %d\n", file->link_count);
+ fprintf(stderr, " Links:\n");
+ entry = file->links;
+ while (entry) {
+ fprintf(stderr, " Name: %s\n", entry->name);
+ fprintf(stderr, " Directory: %s\n", entry->parent->name);
+ entry = entry->next_link;
+ }
+ fprintf(stderr, " Length: %u\n", (unsigned) file->length);
+ fprintf(stderr, " File was open: %s\n",
+ (file->fds == NULL) ? "false" : "true");
+ fprintf(stderr, " File was deleted: %s\n",
+ (file->deleted == 0) ? "false" : "true");
+ fprintf(stderr, " File was out of space: %s\n",
+ (file->no_space_error == 0) ? "false" : "true");
+ fprintf(stderr, " File Data:\n");
+ wcnt = 0;
+ w = file->writes;
+ while (w) {
+ fprintf(stderr, " Offset: %u Size: %u Seed: %u"
+ " R.Off: %u\n",
+ (unsigned) w->offset,
+ (unsigned) w->size,
+ (unsigned) w->random_seed,
+ (unsigned) w->random_offset);
+ wcnt += 1;
+ w = w->next;
+ }
+ fprintf(stderr, " %u writes\n", wcnt);
+ fprintf(stderr, " ============================================\n");
+ fprintf(stderr, " Write Info:\n");
+ wcnt = 0;
+ w = file->raw_writes;
+ while (w) {
+ if (w->trunc)
+ fprintf(stderr, " Trunc from %u to %u\n",
+ (unsigned) w->offset,
+ (unsigned) w->random_offset);
+ else
+ fprintf(stderr, " Offset: %u Size: %u Seed: %u"
+ " R.Off: %u\n",
+ (unsigned) w->offset,
+ (unsigned) w->size,
+ (unsigned) w->random_seed,
+ (unsigned) w->random_offset);
+ wcnt += 1;
+ w = w->next;
+ }
+ fprintf(stderr, " %u writes or truncations\n", wcnt);
+ fprintf(stderr, " ============================================\n");
+}
+
+static struct fd_info *file_open(struct file_info *file)
+{
+ int fd, flags = O_RDWR;
+ char *path;
+
+ path = dir_path(file->links->parent, file->links->name);
+ if (tests_random_no(100) == 1)
+ flags |= O_SYNC;
+ fd = open(path, flags);
+ CHECK(fd != -1);
+ free(path);
+ return add_fd(file, fd);
+}
+
+#define BUFFER_SIZE 32768
+
+static size_t file_write_data( struct file_info *file,
+ int fd,
+ off_t offset,
+ size_t size,
+ unsigned seed)
+{
+ size_t remains, actual, block;
+ ssize_t written;
+ char buf[BUFFER_SIZE];
+
+ srand(seed);
+ CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1);
+ remains = size;
+ actual = 0;
+ written = BUFFER_SIZE;
+ while (remains) {
+ /* Fill up buffer with random data */
+ if (written < BUFFER_SIZE) {
+ memmove(buf, buf + written, BUFFER_SIZE - written);
+ written = BUFFER_SIZE - written;
+ } else
+ written = 0;
+ for (; written < BUFFER_SIZE; ++written)
+ buf[written] = rand();
+ /* Write a block of data */
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written < 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ full = 1;
+ file->no_space_error = 1;
+ break;
+ }
+ remains -= written;
+ actual += written;
+ }
+ return actual;
+}
+
+static void file_write_info(struct file_info *file,
+ off_t offset,
+ size_t size,
+ unsigned seed)
+{
+ struct write_info *new_write, *w, **prev, *tmp;
+ int inserted;
+ size_t sz;
+ off_t end, chg;
+
+ /* Create struct write_info */
+ sz = sizeof(struct write_info);
+ new_write = (struct write_info *) malloc(sz);
+ CHECK(new_write != NULL);
+ memset(new_write, 0, sz);
+ new_write->offset = offset;
+ new_write->size = size;
+ new_write->random_seed = seed;
+
+ w = (struct write_info *) malloc(sz);
+ CHECK(w != NULL);
+ memset(w, 0, sz);
+ w->next = file->raw_writes;
+ w->offset = offset;
+ w->size = size;
+ w->random_seed = seed;
+ file->raw_writes = w;
+
+ /* Insert it into file->writes */
+ inserted = 0;
+ end = offset + size;
+ w = file->writes;
+ prev = &file->writes;
+ while (w) {
+ if (w->offset >= end) {
+ /* w comes after new_write, so insert before it */
+ new_write->next = w;
+ *prev = new_write;
+ inserted = 1;
+ break;
+ }
+ /* w does not come after new_write */
+ if (w->offset + w->size > offset) {
+ /* w overlaps new_write */
+ if (w->offset < offset) {
+ /* w begins before new_write begins */
+ if (w->offset + w->size <= end)
+ /* w ends before new_write ends */
+ w->size = offset - w->offset;
+ else {
+ /* w ends after new_write ends */
+ /* Split w */
+ tmp = (struct write_info *) malloc(sz);
+ CHECK(tmp != NULL);
+ *tmp = *w;
+ chg = end - tmp->offset;
+ tmp->offset += chg;
+ tmp->random_offset += chg;
+ tmp->size -= chg;
+ w->size = offset - w->offset;
+ /* Insert new struct write_info */
+ w->next = new_write;
+ new_write->next = tmp;
+ inserted = 1;
+ break;
+ }
+ } else {
+ /* w begins after new_write begins */
+ if (w->offset + w->size <= end) {
+ /* w is completely overlapped,
+ so remove it */
+ *prev = w->next;
+ tmp = w;
+ w = w->next;
+ free(tmp);
+ continue;
+ }
+ /* w ends after new_write ends */
+ chg = end - w->offset;
+ w->offset += chg;
+ w->random_offset += chg;
+ w->size -= chg;
+ continue;
+ }
+ }
+ prev = &w->next;
+ w = w->next;
+ }
+ if (!inserted)
+ *prev = new_write;
+ /* Update file length */
+ if (end > file->length)
+ file->length = end;
+}
+
+/* Randomly select offset and and size to write in a file */
+static void get_offset_and_size(struct file_info *file,
+ off_t *offset,
+ size_t *size)
+{
+ size_t r, n;
+
+ r = tests_random_no(100);
+ if (r == 0 && grow)
+ /* 1 time in 100, when growing, write off the end of the file */
+ *offset = file->length + tests_random_no(10000000);
+ else if (r < 4)
+ /* 3 (or 4) times in 100, write at the beginning of file */
+ *offset = 0;
+ else if (r < 52 || !grow)
+ /* 48 times in 100, write into the file */
+ *offset = tests_random_no(file->length);
+ else
+ /* 48 times in 100, write at the end of the file */
+ *offset = file->length;
+ /* Distribute the size logarithmically */
+ if (tests_random_no(1000) == 0)
+ r = tests_random_no(log10_initial_free_space + 2);
+ else
+ r = tests_random_no(log10_initial_free_space);
+ n = 1;
+ while (r--)
+ n *= 10;
+ *size = tests_random_no(n);
+ if (!grow && *offset + *size > file->length)
+ *size = file->length - *offset;
+ if (*size == 0)
+ *size = 1;
+}
+
+static void file_truncate_info(struct file_info *file, size_t new_length);
+
+static int file_ftruncate(struct file_info *file, int fd, off_t new_length)
+{
+ if (ftruncate(fd, new_length) == -1) {
+ CHECK(errno = ENOSPC);
+ file->no_space_error = 1;
+ /* Delete errored files */
+ if (!check_nospc_files)
+ file_delete(file);
+ return 0;
+ }
+ return 1;
+}
+
+static void file_mmap_write(struct file_info *file)
+{
+ size_t write_cnt = 0, r, i, len, size;
+ struct write_info *w = file->writes;
+ void *addr;
+ char *waddr;
+ off_t offs, offset;
+ unsigned seed;
+ uint64_t free_space;
+ int fd;
+ char *path;
+
+ if (!file->links)
+ return;
+ free_space = tests_get_free_space();
+ if (!free_space)
+ return;
+ /* Randomly pick a written area of the file */
+ if (!w)
+ return;
+ while (w) {
+ write_cnt += 1;
+ w = w->next;
+ }
+ r = tests_random_no(write_cnt);
+ w = file->writes;
+ for (i = 0; w && w->next && i < r; i++)
+ w = w->next;
+
+ offs = (w->offset / mem_page_size) * mem_page_size;
+ len = w->size + (w->offset - offs);
+ if (len > 1 << 24)
+ len = 1 << 24;
+
+ /* Open it */
+ path = dir_path(file->links->parent, file->links->name);
+ fd = open(path, O_RDWR);
+ CHECK(fd != -1);
+ free(path);
+
+ /* mmap it */
+ addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offs);
+ CHECK(close(fd) != -1);
+ CHECK(addr != MAP_FAILED);
+
+ /* Randomly select a part of the mmapped area to write */
+ size = tests_random_no(w->size);
+ if (size > free_space)
+ size = free_space;
+ if (size == 0)
+ size = 1;
+ offset = w->offset + tests_random_no(w->size - size);
+
+ /* Write it */
+ seed = tests_random_no(10000000);
+ srand(seed);
+ waddr = addr + (offset - offs);
+ for (i = 0; i < size; i++)
+ waddr[i] = rand();
+
+ /* Unmap it */
+ CHECK(munmap(addr, len) != -1);
+
+ /* Record what was written */
+ file_write_info(file, offset, size, seed);
+}
+
+static void file_write(struct file_info *file, int fd)
+{
+ off_t offset;
+ size_t size, actual;
+ unsigned seed;
+ int truncate = 0;
+
+ if (can_mmap && !full && !file->deleted && tests_random_no(100) == 1) {
+ file_mmap_write(file);
+ return;
+ }
+
+ get_offset_and_size(file, &offset, &size);
+ seed = tests_random_no(10000000);
+ actual = file_write_data(file, fd, offset, size, seed);
+
+ if (offset + actual <= file->length && shrink)
+ /* 1 time in 100, when shrinking
+ truncate after the write */
+ if (tests_random_no(100) == 0)
+ truncate = 1;
+
+ if (actual != 0)
+ file_write_info(file, offset, actual, seed);
+
+ /* Delete errored files */
+ if (!check_nospc_files && file->no_space_error) {
+ file_delete(file);
+ return;
+ }
+
+ if (truncate) {
+ size_t new_length = offset + actual;
+ if (file_ftruncate(file, fd, new_length))
+ file_truncate_info(file, new_length);
+ }
+}
+
+static void file_write_file(struct file_info *file)
+{
+ int fd;
+ char *path;
+
+ path = dir_path(file->links->parent, file->links->name);
+ fd = open(path, O_WRONLY);
+ CHECK(fd != -1);
+ file_write(file, fd);
+ CHECK(close(fd) != -1);
+ free(path);
+}
+
+static void file_truncate_info(struct file_info *file, size_t new_length)
+{
+ struct write_info *w, **prev, *tmp;
+ size_t sz;
+
+ /* Remove / truncate file->writes */
+ w = file->writes;
+ prev = &file->writes;
+ while (w) {
+ if (w->offset >= new_length) {
+ /* w comes after eof, so remove it */
+ *prev = w->next;
+ tmp = w;
+ w = w->next;
+ free(tmp);
+ continue;
+ }
+ if (w->offset + w->size > new_length)
+ w->size = new_length - w->offset;
+ prev = &w->next;
+ w = w->next;
+ }
+ /* Add an entry in raw_writes for the truncation */
+ sz = sizeof(struct write_info);
+ w = (struct write_info *) malloc(sz);
+ CHECK(w != NULL);
+ memset(w, 0, sz);
+ w->next = file->raw_writes;
+ w->offset = file->length;
+ w->random_offset = new_length; /* Abuse random_offset */
+ w->trunc = 1;
+ file->raw_writes = w;
+ /* Update file length */
+ file->length = new_length;
+}
+
+static void file_truncate(struct file_info *file, int fd)
+{
+ size_t new_length;
+
+ new_length = tests_random_no(file->length);
+
+ if (file_ftruncate(file, fd, new_length))
+ file_truncate_info(file, new_length);
+}
+
+static void file_truncate_file(struct file_info *file)
+{
+ int fd;
+ char *path;
+
+ path = dir_path(file->links->parent, file->links->name);
+ fd = open(path, O_WRONLY);
+ CHECK(fd != -1);
+ file_truncate(file, fd);
+ CHECK(close(fd) != -1);
+ free(path);
+}
+
+static void file_close(struct fd_info *fdi)
+{
+ struct file_info *file;
+ struct fd_info *fdp;
+ struct fd_info **prev;
+
+ /* Close file */
+ CHECK(close(fdi->fd) != -1);
+ /* Remove struct fd_info */
+ open_file_remove(fdi);
+ file = fdi->file;
+ prev = &file->fds;
+ for (fdp = file->fds; fdp; fdp = fdp->next) {
+ if (fdp == fdi) {
+ *prev = fdi->next;
+ free(fdi);
+ if (file->deleted && !file->fds) {
+ /* Closing deleted file */
+ struct write_info *w, *next;
+
+ w = file->writes;
+ while (w) {
+ next = w->next;
+ free(w);
+ w = next;
+ }
+ free(file->name);
+ free(file);
+ }
+ return;
+ }
+ prev = &fdp->next;
+ }
+ CHECK(0); /* Didn't find struct fd_info */
+}
+
+static void file_rewrite_data(int fd, struct write_info *w, char *buf)
+{
+ size_t remains, block;
+ ssize_t written;
+ off_t r;
+
+ srand(w->random_seed);
+ for (r = 0; r < w->random_offset; ++r)
+ rand();
+ CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1);
+ remains = w->size;
+ written = BUFFER_SIZE;
+ while (remains) {
+ /* Fill up buffer with random data */
+ if (written < BUFFER_SIZE)
+ memmove(buf, buf + written, BUFFER_SIZE - written);
+ else
+ written = 0;
+ for (; written < BUFFER_SIZE; ++written)
+ buf[written] = rand();
+ /* Write a block of data */
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ CHECK(written == block);
+ remains -= written;
+ }
+}
+
+static void save_file(int fd, struct file_info *file)
+{
+ int w_fd;
+ struct write_info *w;
+ char buf[BUFFER_SIZE];
+ char name[256];
+
+ /* Open file to save contents to */
+ strcpy(name, "/tmp/");
+ strcat(name, file->name);
+ strcat(name, ".integ.sav.read");
+ fprintf(stderr, "Saving %s\n", name);
+ w_fd = open(name, O_CREAT | O_WRONLY, 0777);
+ CHECK(w_fd != -1);
+
+ /* Start at the beginning */
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+
+ for (;;) {
+ ssize_t r = read(fd, buf, BUFFER_SIZE);
+ CHECK(r != -1);
+ if (!r)
+ break;
+ CHECK(write(w_fd, buf, r) == r);
+ }
+ CHECK(close(w_fd) != -1);
+
+ /* Open file to save contents to */
+ strcpy(name, "/tmp/");
+ strcat(name, file->name);
+ strcat(name, ".integ.sav.written");
+ fprintf(stderr, "Saving %s\n", name);
+ w_fd = open(name, O_CREAT | O_WRONLY, 0777);
+ CHECK(w_fd != -1);
+
+ for (w = file->writes; w; w = w->next)
+ file_rewrite_data(w_fd, w, buf);
+
+ CHECK(close(w_fd) != -1);
+}
+
+static void file_check_hole( struct file_info *file,
+ int fd, off_t offset,
+ size_t size)
+{
+ size_t remains, block, i;
+ char buf[BUFFER_SIZE];
+
+ CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1);
+ remains = size;
+ while (remains) {
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ CHECK(read(fd, buf, block) == block);
+ for (i = 0; i < block; ++i) {
+ if (buf[i] != 0) {
+ fprintf(stderr, "file_check_hole failed at %u "
+ "checking hole at %u size %u\n",
+ (unsigned) (size - remains + i),
+ (unsigned) offset,
+ (unsigned) size);
+ file_info_display(file);
+ save_file(fd, file);
+ }
+ CHECK(buf[i] == 0);
+ }
+ remains -= block;
+ }
+}
+
+static void file_check_data( struct file_info *file,
+ int fd,
+ struct write_info *w)
+{
+ size_t remains, block, i;
+ off_t r;
+ char buf[BUFFER_SIZE];
+
+ srand(w->random_seed);
+ for (r = 0; r < w->random_offset; ++r)
+ rand();
+ CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1);
+ remains = w->size;
+ while (remains) {
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ CHECK(read(fd, buf, block) == block);
+ for (i = 0; i < block; ++i) {
+ char c = (char) rand();
+ if (buf[i] != c) {
+ fprintf(stderr, "file_check_data failed at %u "
+ "checking data at %u size %u\n",
+ (unsigned) (w->size - remains + i),
+ (unsigned) w->offset,
+ (unsigned) w->size);
+ file_info_display(file);
+ save_file(fd, file);
+ }
+ CHECK(buf[i] == c);
+ }
+ remains -= block;
+ }
+}
+
+static void file_check(struct file_info *file, int fd)
+{
+ int open_and_close = 0, link_count = 0;
+ char *path = NULL;
+ off_t pos;
+ struct write_info *w;
+ struct dir_entry_info *entry;
+ struct stat st;
+
+ /* Do not check files that have errored */
+ if (!check_nospc_files && file->no_space_error)
+ return;
+ /* Do not check the same file twice */
+ if (file->check_run_no == check_run_no)
+ return;
+ file->check_run_no = check_run_no;
+ if (fd == -1)
+ open_and_close = 1;
+ if (open_and_close) {
+ /* Open file */
+ path = dir_path(file->links->parent, file->links->name);
+ fd = open(path, O_RDONLY);
+ CHECK(fd != -1);
+ }
+ /* Check length */
+ pos = lseek(fd, 0, SEEK_END);
+ if (pos != file->length) {
+ fprintf(stderr, "file_check failed checking length "
+ "expected %u actual %u\n",
+ (unsigned) file->length,
+ (unsigned) pos);
+ file_info_display(file);
+ save_file(fd, file);
+ }
+ CHECK(pos == file->length);
+ /* Check each write */
+ pos = 0;
+ for (w = file->writes; w; w = w->next) {
+ if (w->offset > pos)
+ file_check_hole(file, fd, pos, w->offset - pos);
+ file_check_data(file, fd, w);
+ pos = w->offset + w->size;
+ }
+ if (file->length > pos)
+ file_check_hole(file, fd, pos, file->length - pos);
+ CHECK(fstat(fd, &st) != -1);
+ CHECK(file->link_count == st.st_nlink);
+ if (open_and_close) {
+ CHECK(close(fd) != -1);
+ free(path);
+ }
+ entry = file->links;
+ while (entry) {
+ link_count += 1;
+ entry = entry->next_link;
+ }
+ CHECK(link_count == file->link_count);
+}
+
+static char *symlink_path(const char *path, const char *target_pathname)
+{
+ char *p;
+ size_t len, totlen, tarlen;
+
+ if (target_pathname[0] == '/')
+ return copy_string(target_pathname);
+ p = strrchr(path, '/');
+ len = p - path;
+ len += 1;
+ tarlen = strlen(target_pathname);
+ totlen = len + tarlen + 1;
+ p = malloc(totlen);
+ CHECK(p != NULL);
+ strncpy(p, path, len);
+ p[len] = '\0';
+ strcat(p, target_pathname);
+ return p;
+}
+
+void symlink_check(const struct symlink_info *symlink)
+{
+ char *path, buf[8192], *target;
+ struct stat st1, st2;
+ ssize_t len;
+ int ret1, ret2;
+
+ path = dir_path(symlink->entry->parent, symlink->entry->name);
+ CHECK(lstat(path, &st1) != -1);
+ CHECK(S_ISLNK(st1.st_mode));
+ CHECK(st1.st_nlink == 1);
+ len = readlink(path, buf, 8192);
+ CHECK(len > 0 && len < 8192);
+ buf[len] = '\0';
+ CHECK(strlen(symlink->target_pathname) == len);
+ CHECK(strncmp(symlink->target_pathname, buf, len) == 0);
+ /* Check symlink points where it should */
+ ret1 = stat(path, &st1);
+ target = symlink_path(path, symlink->target_pathname);
+ ret2 = stat(target, &st2);
+ CHECK(ret1 == ret2);
+ if (ret1 != -1) {
+ CHECK(st1.st_dev == st2.st_dev);
+ CHECK(st1.st_ino == st2.st_ino);
+ }
+ free(target);
+ free(path);
+}
+
+static int search_comp(const void *pa, const void *pb)
+{
+ const struct dirent *a = (const struct dirent *) pa;
+ const struct dir_entry_info *b = * (const struct dir_entry_info **) pb;
+ return strcmp(a->d_name, b->name);
+}
+
+static void dir_entry_check(struct dir_entry_info **entry_array,
+ size_t number_of_entries,
+ struct dirent *ent)
+{
+ struct dir_entry_info **found;
+ struct dir_entry_info *entry;
+ size_t sz;
+
+ sz = sizeof(struct dir_entry_info *);
+ found = bsearch(ent, entry_array, number_of_entries, sz, search_comp);
+ CHECK(found != NULL);
+ entry = *found;
+ CHECK(!entry->checked);
+ entry->checked = 1;
+}
+
+static int sort_comp(const void *pa, const void *pb)
+{
+ const struct dir_entry_info *a = * (const struct dir_entry_info **) pa;
+ const struct dir_entry_info *b = * (const struct dir_entry_info **) pb;
+ return strcmp(a->name, b->name);
+}
+
+static void dir_check(struct dir_info *dir)
+{
+ struct dir_entry_info **entry_array, **p;
+ size_t sz, n;
+ struct dir_entry_info *entry;
+ DIR *d;
+ struct dirent *ent;
+ unsigned checked = 0;
+ char *path;
+ int link_count = 2; /* Parent and dot */
+ struct stat st;
+
+ /* Create an array of entries */
+ sz = sizeof(struct dir_entry_info *);
+ n = dir->number_of_entries;
+ entry_array = (struct dir_entry_info **) malloc(sz * n);
+ CHECK(entry_array != NULL);
+
+ entry = dir->first;
+ p = entry_array;
+ while (entry) {
+ *p++ = entry;
+ entry->checked = 0;
+ entry = entry->next;
+ }
+
+ /* Sort it by name */
+ qsort(entry_array, n, sz, sort_comp);
+
+ /* Go through directory on file system checking entries match */
+ path = dir_path(dir->parent, dir->name);
+ d = opendir(path);
+ CHECK(d != NULL);
+ for (;;) {
+ errno = 0;
+ ent = readdir(d);
+ if (ent) {
+ if (strcmp(".",ent->d_name) != 0 &&
+ strcmp("..",ent->d_name) != 0) {
+ dir_entry_check(entry_array, n, ent);
+ checked += 1;
+ }
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ CHECK(closedir(d) != -1);
+ CHECK(checked == dir->number_of_entries);
+
+ /* Now check each entry */
+ entry = dir->first;
+ while (entry) {
+ if (entry->type == 'd') {
+ dir_check(entry->entry.dir);
+ link_count += 1; /* <subdir>/.. */
+ } else if (entry->type == 'f')
+ file_check(entry->entry.file, -1);
+ else if (entry->type == 's')
+ symlink_check(entry->entry.symlink);
+ else
+ CHECK(0);
+ entry = entry->next;
+ }
+
+ CHECK(stat(path, &st) != -1);
+ CHECK(link_count == st.st_nlink);
+
+ free(entry_array);
+ free(path);
+}
+
+static void check_deleted_files(void)
+{
+ struct open_file_info *ofi;
+
+ for (ofi = open_files; ofi; ofi = ofi->next)
+ if (ofi->fdi->file->deleted)
+ file_check(ofi->fdi->file, ofi->fdi->fd);
+}
+
+static void close_open_files(void)
+{
+ struct open_file_info *ofi;
+
+ for (ofi = open_files; ofi; ofi = open_files)
+ file_close(ofi->fdi);
+}
+
+static char *make_name(struct dir_info *dir)
+{
+ static char name[256];
+ struct dir_entry_info *entry;
+ int found;
+
+ do {
+ found = 0;
+ if (tests_random_no(5) == 1) {
+ int i, n = tests_random_no(tests_max_fname_len) + 1;
+
+ CHECK(n > 0 && n < 256);
+ for (i = 0; i < n; i++)
+ name[i] = 'a' + tests_random_no(26);
+ name[i] = '\0';
+ } else
+ sprintf(name, "%u", (unsigned) tests_random_no(1000000));
+ for (entry = dir->first; entry; entry = entry->next) {
+ if (strcmp(entry->name, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ } while (found);
+ return name;
+}
+
+static struct file_info *pick_file(void)
+{
+ struct dir_info *dir = top_dir;
+
+ for (;;) {
+ struct dir_entry_info *entry;
+ size_t r;
+
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ for (;;) {
+ if (!entry)
+ return NULL;
+ if (entry->type == 'f')
+ return entry->entry.file;
+ if (entry->type == 'd')
+ if (entry->entry.dir->number_of_entries != 0)
+ break;
+ entry = entry->next;
+ }
+ dir = entry->entry.dir;
+ }
+}
+
+static struct dir_info *pick_dir(void)
+{
+ struct dir_info *dir = top_dir;
+
+ if (tests_random_no(40) >= 30)
+ return dir;
+ for (;;) {
+ struct dir_entry_info *entry;
+ size_t r;
+
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ for (;;) {
+ if (!entry)
+ break;
+ if (entry->type == 'd')
+ break;
+ entry = entry->next;
+ }
+ if (!entry) {
+ entry = dir->first;
+ for (;;) {
+ if (!entry)
+ break;
+ if (entry->type == 'd')
+ break;
+ entry = entry->next;
+ }
+ }
+ if (!entry)
+ return dir;
+ dir = entry->entry.dir;
+ if (tests_random_no(40) >= 30)
+ return dir;
+ }
+}
+
+static char *pick_rename_name(struct dir_info **parent,
+ struct dir_entry_info **rename_entry, int isdir)
+{
+ struct dir_info *dir = pick_dir();
+ struct dir_entry_info *entry;
+ size_t r;
+
+ *parent = dir;
+ *rename_entry = NULL;
+
+ if (grow || tests_random_no(20) < 10)
+ return copy_string(make_name(dir));
+
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ if (!entry)
+ entry = dir->first;
+ if (!entry ||
+ (entry->type == 'd' && entry->entry.dir->number_of_entries != 0))
+ return copy_string(make_name(dir));
+
+ if ((isdir && entry->type != 'd') ||
+ (!isdir && entry->type == 'd'))
+ return copy_string(make_name(dir));
+
+ *rename_entry = entry;
+ return copy_string(entry->name);
+}
+
+static void rename_entry(struct dir_entry_info *entry)
+{
+ struct dir_entry_info *rename_entry = NULL;
+ struct dir_info *parent;
+ char *path, *to, *name;
+ int ret, isdir, retry;
+
+ if (!entry->parent)
+ return;
+
+ for (retry = 0; retry < 3; retry++) {
+ path = dir_path(entry->parent, entry->name);
+ isdir = entry->type == 'd' ? 1 : 0;
+ name = pick_rename_name(&parent, &rename_entry, isdir);
+ to = dir_path(parent, name);
+ /*
+ * Check we are not trying to move a directory to a subdirectory
+ * of itself.
+ */
+ if (isdir) {
+ struct dir_info *p;
+
+ for (p = parent; p; p = p->parent)
+ if (p == entry->entry.dir)
+ break;
+ if (p == entry->entry.dir) {
+ free(path);
+ free(name);
+ free(to);
+ path = NULL;
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (!path)
+ return;
+
+ ret = rename(path, to);
+ if (ret == -1) {
+ if (errno == ENOSPC)
+ full = 1;
+ CHECK(errno == ENOSPC || errno == EBUSY);
+ free(path);
+ free(name);
+ free(to);
+ return;
+ }
+
+ free(path);
+ free(to);
+
+ if (rename_entry && rename_entry->type == entry->type &&
+ rename_entry->entry.target == entry->entry.target) {
+ free(name);
+ return;
+ }
+
+ add_dir_entry(parent, entry->type, name, entry->entry.target);
+ if (rename_entry)
+ remove_dir_entry(rename_entry);
+ remove_dir_entry(entry);
+ free(name);
+}
+
+static size_t str_count(const char *s, char c)
+{
+ size_t count = 0;
+ char cc;
+
+ while ((cc = *s++) != '\0')
+ if (cc == c)
+ count += 1;
+ return count;
+}
+
+static char *relative_path(const char *path1, const char *path2)
+{
+ const char *p1, *p2;
+ char *rel;
+ size_t up, len, len2, i;
+
+ p1 = path1;
+ p2 = path2;
+ while (*p1 == *p2 && *p1) {
+ p1 += 1;
+ p2 += 1;
+ }
+ len2 = strlen(p2);
+ up = str_count(p1, '/');
+ if (up == 0 && len2 != 0)
+ return copy_string(p2);
+ if (up == 0 && len2 == 0) {
+ p2 = strrchr(path2, '/');
+ return copy_string(p2);
+ }
+ if (up == 1 && len2 == 0)
+ return copy_string(".");
+ if (len2 == 0)
+ up -= 1;
+ len = up * 3 + len2 + 1;
+ rel = malloc(len);
+ CHECK(rel != NULL);
+ rel[0] = '\0';
+ if (up) {
+ strcat(rel, "..");
+ for (i = 1; i < up; i++)
+ strcat(rel, "/..");
+ if (len2)
+ strcat(rel, "/");
+ }
+ if (len2)
+ strcat(rel, p2);
+ return rel;
+}
+
+static char *pick_symlink_target(const char *symlink_path)
+{
+ struct dir_info *dir;
+ struct dir_entry_info *entry;
+ size_t r;
+ char *path, *rel_path;
+
+ dir = pick_dir();
+
+ if (tests_random_no(100) < 10)
+ return dir_path(dir, make_name(dir));
+
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ if (!entry)
+ entry = dir->first;
+ if (!entry)
+ return dir_path(dir, make_name(dir));
+ path = dir_path(dir, entry->name);
+ if (tests_random_no(20) < 10)
+ return path;
+ rel_path = relative_path(symlink_path, path);
+ free(path);
+ return rel_path;
+}
+
+static void symlink_new(struct dir_info *dir, const char *name_)
+{
+ struct symlink_info *s;
+ char *path, *target, *name = copy_string(name_);
+ size_t sz;
+
+ path = dir_path(dir, name);
+ target = pick_symlink_target(path);
+ if (symlink(target, path) == -1) {
+ CHECK(errno == ENOSPC || errno == ENAMETOOLONG);
+ if (errno == ENOSPC)
+ full = 1;
+ free(target);
+ free(path);
+ free(name);
+ return;
+ }
+ free(path);
+
+ sz = sizeof(struct symlink_info);
+ s = malloc(sz);
+ CHECK(s != NULL);
+ memset(s, 0, sz);
+ add_dir_entry(dir, 's', name, s);
+ s->target_pathname = target;
+ free(name);
+}
+
+static void symlink_remove(struct symlink_info *symlink)
+{
+ char *path;
+
+ path = dir_path(symlink->entry->parent, symlink->entry->name);
+
+ remove_dir_entry(symlink->entry);
+
+ CHECK(unlink(path) != -1);
+ free(path);
+}
+
+static void operate_on_dir(struct dir_info *dir);
+static void operate_on_file(struct file_info *file);
+
+/* Randomly select something to do with a directory entry */
+static void operate_on_entry(struct dir_entry_info *entry)
+{
+ /* 1 time in 1000 rename */
+ if (tests_random_no(1000) == 0) {
+ rename_entry(entry);
+ return;
+ }
+ if (entry->type == 's') {
+ symlink_check(entry->entry.symlink);
+ /* If shrinking, 1 time in 50, remove a symlink */
+ if (shrink && tests_random_no(50) == 0)
+ symlink_remove(entry->entry.symlink);
+ return;
+ }
+ if (entry->type == 'd') {
+ /* If shrinking, 1 time in 50, remove a directory */
+ if (shrink && tests_random_no(50) == 0) {
+ dir_remove(entry->entry.dir);
+ return;
+ }
+ operate_on_dir(entry->entry.dir);
+ }
+ if (entry->type == 'f') {
+ /* If shrinking, 1 time in 10, remove a file */
+ if (shrink && tests_random_no(10) == 0) {
+ file_delete(entry->entry.file);
+ return;
+ }
+ /* If not growing, 1 time in 10, unlink a file with links > 1 */
+ if (!grow && entry->entry.file->link_count > 1 &&
+ tests_random_no(10) == 0) {
+ file_unlink_file(entry->entry.file);
+ return;
+ }
+ operate_on_file(entry->entry.file);
+ }
+}
+
+/* Randomly select something to do with a directory */
+static void operate_on_dir(struct dir_info *dir)
+{
+ size_t r;
+ struct dir_entry_info *entry;
+ struct file_info *file;
+
+ r = tests_random_no(14);
+ if (r == 0 && grow)
+ /* When growing, 1 time in 14 create a file */
+ file_new(dir, make_name(dir));
+ else if (r == 1 && grow)
+ /* When growing, 1 time in 14 create a directory */
+ dir_new(dir, make_name(dir));
+ else if (r == 2 && grow && (file = pick_file()) != NULL)
+ /* When growing, 1 time in 14 create a hard link */
+ link_new(dir, make_name(dir), file);
+ else if (r == 3 && grow && tests_random_no(5) == 0)
+ /* When growing, 1 time in 70 create a symbolic link */
+ symlink_new(dir, make_name(dir));
+ else {
+ /* Otherwise randomly select an entry to operate on */
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ if (entry)
+ operate_on_entry(entry);
+ }
+}
+
+/* Randomly select something to do with a file */
+static void operate_on_file(struct file_info *file)
+{
+ /* Try to keep at least 10 files open */
+ if (open_files_count < 10) {
+ file_open(file);
+ return;
+ }
+ /* Try to keep about 20 files open */
+ if (open_files_count < 20 && tests_random_no(2) == 0) {
+ file_open(file);
+ return;
+ }
+ /* Try to keep up to 40 files open */
+ if (open_files_count < 40 && tests_random_no(20) == 0) {
+ file_open(file);
+ return;
+ }
+ /* Occasionly truncate */
+ if (shrink && tests_random_no(100) == 0) {
+ file_truncate_file(file);
+ return;
+ }
+ /* Mostly just write */
+ file_write_file(file);
+ /* Once in a while check it too */
+ if (tests_random_no(100) == 1) {
+ int fd = -2;
+
+ if (file->links)
+ fd = -1;
+ else if (file->fds)
+ fd = file->fds->fd;
+ if (fd != -2) {
+ check_run_no += 1;
+ file_check(file, fd);
+ }
+ }
+}
+
+/* Randomly select something to do with an open file */
+static void operate_on_open_file(struct fd_info *fdi)
+{
+ size_t r;
+
+ r = tests_random_no(1000);
+ if (shrink && r < 5)
+ file_truncate(fdi->file, fdi->fd);
+ else if (r < 21)
+ file_close(fdi);
+ else if (shrink && r < 121 && !fdi->file->deleted)
+ file_delete(fdi->file);
+ else {
+ file_write(fdi->file, fdi->fd);
+ if (r >= 999) {
+ if (tests_random_no(100) >= 50)
+ CHECK(fsync(fdi->fd) != -1);
+ else
+ CHECK(fdatasync(fdi->fd) != -1);
+ }
+ }
+}
+
+/* Select an open file at random */
+static void operate_on_an_open_file(void)
+{
+ size_t r;
+ struct open_file_info *ofi;
+
+ /* When shrinking, close all open files 1 time in 128 */
+ if (shrink) {
+ static int x = 0;
+
+ x += 1;
+ x &= 127;
+ if (x == 0) {
+ close_open_files();
+ return;
+ }
+ }
+ /* Close any open files that have errored */
+ if (!check_nospc_files) {
+ ofi = open_files;
+ while (ofi) {
+ if (ofi->fdi->file->no_space_error) {
+ struct fd_info *fdi;
+
+ fdi = ofi->fdi;
+ ofi = ofi->next;
+ file_close(fdi);
+ } else
+ ofi = ofi->next;
+ }
+ }
+ r = tests_random_no(open_files_count);
+ for (ofi = open_files; ofi; ofi = ofi->next, --r)
+ if (!r) {
+ operate_on_open_file(ofi->fdi);
+ return;
+ }
+}
+
+static void do_an_operation(void)
+{
+ /* Half the time operate on already open files */
+ if (tests_random_no(100) < 50)
+ operate_on_dir(top_dir);
+ else
+ operate_on_an_open_file();
+}
+
+static void create_test_data(void)
+{
+ uint64_t i, n;
+
+ grow = 1;
+ shrink = 0;
+ full = 0;
+ operation_count = 0;
+ while (!full) {
+ do_an_operation();
+ ++operation_count;
+ }
+ grow = 0;
+ shrink = 1;
+ /* Drop to less than 90% full */
+ n = operation_count / 40;
+ while (n--) {
+ uint64_t free;
+ uint64_t total;
+ for (i = 0; i < 10; ++i)
+ do_an_operation();
+ free = tests_get_free_space();
+ total = tests_get_total_space();
+ if ((free * 100) / total >= 10)
+ break;
+ }
+ grow = 0;
+ shrink = 0;
+ full = 0;
+ n = operation_count * 2;
+ for (i = 0; i < n; ++i)
+ do_an_operation();
+}
+
+static void update_test_data(void)
+{
+ uint64_t i, n;
+
+ grow = 1;
+ shrink = 0;
+ full = 0;
+ while (!full)
+ do_an_operation();
+ grow = 0;
+ shrink = 1;
+ /* Drop to less than 50% full */
+ n = operation_count / 10;
+ while (n--) {
+ uint64_t free;
+ uint64_t total;
+ for (i = 0; i < 10; ++i)
+ do_an_operation();
+ free = tests_get_free_space();
+ total = tests_get_total_space();
+ if ((free * 100) / total >= 50)
+ break;
+ }
+ grow = 0;
+ shrink = 0;
+ full = 0;
+ n = operation_count * 2;
+ for (i = 0; i < n; ++i)
+ do_an_operation();
+}
+
+void integck(void)
+{
+ pid_t pid;
+ int64_t rpt;
+ uint64_t z;
+ char dir_name[256];
+
+ /* Get memory page size for mmap */
+ mem_page_size = sysconf(_SC_PAGE_SIZE);
+ CHECK(mem_page_size > 0);
+ /* Make our top directory */
+ pid = getpid();
+ printf("pid is %u\n", (unsigned) pid);
+ tests_cat_pid(dir_name, "integck_test_dir_", pid);
+ if (chdir(dir_name) != -1) {
+ /* Remove it if it is already there */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+ }
+ initial_free_space = tests_get_free_space();
+ log10_initial_free_space = 0;
+ for (z = initial_free_space; z >= 10; z /= 10)
+ ++log10_initial_free_space;
+ top_dir = dir_new(NULL, dir_name);
+
+ if (!top_dir)
+ return;
+
+ srand(pid);
+
+ create_test_data();
+
+ if (!tests_fs_is_rootfs()) {
+ close_open_files();
+ tests_remount(); /* Requires root access */
+ }
+
+ /* Check everything */
+ check_run_no += 1;
+ dir_check(top_dir);
+ check_deleted_files();
+
+ for (rpt = 0; tests_repeat_parameter == 0 ||
+ rpt < tests_repeat_parameter; ++rpt) {
+ update_test_data();
+
+ if (!tests_fs_is_rootfs()) {
+ close_open_files();
+ tests_remount(); /* Requires root access */
+ }
+
+ /* Check everything */
+ check_run_no += 1;
+ dir_check(top_dir);
+ check_deleted_files();
+ }
+
+ /* Tidy up by removing everything */
+ close_open_files();
+ tests_clear_dir(dir_name);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *integck_get_title(void)
+{
+ return "Test file system integrity";
+}
+
+/* Description of this test */
+
+const char *integck_get_description(void)
+{
+ return
+ "Create a directory named integck_test_dir_pid " \
+ "where pid is the process id. " \
+ "Randomly create and delete files and directories. " \
+ "Randomly write to and truncate files. " \
+ "Un-mount and re-mount test file " \
+ "system (if it is not the root file system ). " \
+ "Check data. Make more random changes. " \
+ "Un-mount and re-mount again. Check again. " \
+ "Repeat some number of times. "
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, integck_get_title(),
+ integck_get_description(), "n");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /*
+ * We expect accurate file size from ubifs even after "no space"
+ * errors. And we can mmap.
+ */
+ if (strcmp(tests_file_system_type, "ubifs") == 0) {
+ check_nospc_files = 1;
+ can_mmap = 1;
+ }
+ /* Do the actual test */
+ integck();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/lib/Makefile b/mtd-utils-1.3.1/tests/fs-tests/lib/Makefile
new file mode 100644
index 0000000..8d57824
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/lib/Makefile
@@ -0,0 +1,18 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2
+
+LDFLAGS := $(LDFLAGS)
+
+all: tests.o
+
+tests.o: tests.h
+
+clean:
+ rm -f *.o
+
+tests:
+ echo
diff --git a/mtd-utils-1.3.1/tests/fs-tests/lib/tests.c b/mtd-utils-1.3.1/tests/fs-tests/lib/tests.c
new file mode 100644
index 0000000..587eda8
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/lib/tests.c
@@ -0,0 +1,1212 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
+#include <mntent.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+
+#include "tests.h"
+
+char *tests_file_system_mount_dir = TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR;
+
+char *tests_file_system_type = TESTS_DEFAULT_FILE_SYSTEM_TYPE;
+
+int tests_ok_to_sync = 0; /* Whether to use fsync */
+
+/* General purpose test parameter to specify some aspect of test size.
+ May be used by different tests in different ways or not at all.
+ Set by the -z or --size option. */
+int64_t tests_size_parameter = 0;
+
+/* General purpose test parameter to specify some aspect of test repetition.
+ May be used by different tests in different ways or not at all.
+ Set by the -n, --repeat options. */
+int64_t tests_repeat_parameter = 0;
+
+/* General purpose test parameter to specify some aspect of test sleeping.
+ May be used by different tests in different ways or not at all.
+ Set by the -p, --sleep options. */
+int64_t tests_sleep_parameter = 0;
+
+/* Program name from argv[0] */
+char *program_name = "unknown";
+
+/* General purpose test parameter to specify a file should be unlinked.
+ May be used by different tests in different ways or not at all. */
+int tests_unlink_flag = 0;
+
+/* General purpose test parameter to specify a file should be closed.
+ May be used by different tests in different ways or not at all. */
+int tests_close_flag = 0;
+
+/* General purpose test parameter to specify a file should be deleted.
+ May be used by different tests in different ways or not at all. */
+int tests_delete_flag = 0;
+
+/* General purpose test parameter to specify a file have a hole.
+ May be used by different tests in different ways or not at all. */
+int tests_hole_flag = 0;
+
+/* Whether it is ok to test on the root file system */
+static int rootok = 0;
+
+/* Maximum file name length of test file system (from statfs) */
+long tests_max_fname_len = 255;
+
+/* Function invoked by the CHECK macro */
+void tests_test(int test,const char *msg,const char *file,unsigned line)
+{
+ int eno;
+ time_t t;
+
+ if (test)
+ return;
+ eno = errno;
+ time(&t);
+ fprintf(stderr, "Test failed: %s on %s"
+ "Test failed: %s in %s at line %u\n",
+ program_name, ctime(&t), msg, file, line);
+ if (eno) {
+ fprintf(stderr,"errno = %d\n",eno);
+ fprintf(stderr,"strerror = %s\n",strerror(eno));
+ }
+ exit(1);
+}
+
+static int is_zero(const char *p)
+{
+ for (;*p;++p)
+ if (*p != '0')
+ return 0;
+ return 1;
+}
+
+static void fold(const char *text, int width)
+{
+ int pos, bpos = 0;
+ const char *p;
+ char line[1024];
+
+ if (width > 1023) {
+ printf("%s\n", text);
+ return;
+ }
+ p = text;
+ pos = 0;
+ while (p[pos]) {
+ while (!isspace(p[pos])) {
+ line[pos] = p[pos];
+ if (!p[pos])
+ break;
+ ++pos;
+ if (pos == width) {
+ line[pos] = '\0';
+ printf("%s\n", line);
+ p += pos;
+ pos = 0;
+ }
+ }
+ while (pos < width) {
+ line[pos] = p[pos];
+ if (!p[pos]) {
+ bpos = pos;
+ break;
+ }
+ if (isspace(p[pos]))
+ bpos = pos;
+ ++pos;
+ }
+ line[bpos] = '\0';
+ printf("%s\n", line);
+ p += bpos;
+ pos = 0;
+ while (p[pos] && isspace(p[pos]))
+ ++p;
+ }
+}
+
+/* Handle common program options */
+int tests_get_args(int argc,
+ char *argv[],
+ const char *title,
+ const char *desc,
+ const char *opts)
+{
+ int run_test = 0;
+ int display_help = 0;
+ int display_title = 0;
+ int display_description = 0;
+ int i;
+ char *s;
+
+ program_name = argv[0];
+
+ s = getenv("TEST_FILE_SYSTEM_MOUNT_DIR");
+ if (s)
+ tests_file_system_mount_dir = strdup(s);
+ s = getenv("TEST_FILE_SYSTEM_TYPE");
+ if (s)
+ tests_file_system_type = strdup(s);
+
+ run_test = 1;
+ rootok = 1;
+ for (i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--help") == 0 ||
+ strcmp(argv[i], "-h") == 0)
+ display_help = 1;
+ else if (strcmp(argv[i], "--title") == 0 ||
+ strcmp(argv[i], "-t") == 0)
+ display_title = 1;
+ else if (strcmp(argv[i], "--description") == 0 ||
+ strcmp(argv[i], "-d") == 0)
+ display_description = 1;
+ else if (strcmp(argv[i], "--sync") == 0 ||
+ strcmp(argv[i], "-s") == 0)
+ tests_ok_to_sync = 1;
+ else if (strncmp(argv[i], "--size", 6) == 0 ||
+ strncmp(argv[i], "-z", 2) == 0) {
+ int64_t n;
+ char *p;
+ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1]))
+ ++i;
+ p = argv[i];
+ while (*p && !isdigit(*p))
+ ++p;
+ n = atoll(p);
+ if (n)
+ tests_size_parameter = n;
+ else {
+ int all_zero = 1;
+ for (; all_zero && *p; ++p)
+ if (*p != '0')
+ all_zero = 0;
+ if (all_zero)
+ tests_size_parameter = 0;
+ else
+ display_help = 1;
+ }
+ } else if (strncmp(argv[i], "--repeat", 8) == 0 ||
+ strncmp(argv[i], "-n", 2) == 0) {
+ int64_t n;
+ char *p;
+ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1]))
+ ++i;
+ p = argv[i];
+ while (*p && !isdigit(*p))
+ ++p;
+ n = atoll(p);
+ if (n || is_zero(p))
+ tests_repeat_parameter = n;
+ else
+ display_help = 1;
+ } else if (strncmp(argv[i], "--sleep", 7) == 0 ||
+ strncmp(argv[i], "-p", 2) == 0) {
+ int64_t n;
+ char *p;
+ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1]))
+ ++i;
+ p = argv[i];
+ while (*p && !isdigit(*p))
+ ++p;
+ n = atoll(p);
+ if (n || is_zero(p))
+ tests_sleep_parameter = n;
+ else
+ display_help = 1;
+ } else if (strcmp(argv[i], "--unlink") == 0 ||
+ strcmp(argv[i], "-u") == 0)
+ tests_unlink_flag = 1;
+ else if (strcmp(argv[i], "--hole") == 0 ||
+ strcmp(argv[i], "-o") == 0)
+ tests_hole_flag = 1;
+ else if (strcmp(argv[i], "--close") == 0 ||
+ strcmp(argv[i], "-c") == 0)
+ tests_close_flag = 1;
+ else if (strcmp(argv[i], "--delete") == 0 ||
+ strcmp(argv[i], "-e") == 0)
+ tests_delete_flag = 1;
+ else
+ display_help = 1;
+ }
+
+ if (display_help) {
+ run_test = 0;
+ display_title = 0;
+ display_description = 0;
+ if (!opts)
+ opts = "";
+ printf("File System Test Program\n\n");
+ printf("Test Title: %s\n\n", title);
+ printf("Usage is: %s [ options ]\n",argv[0]);
+ printf(" Options are:\n");
+ printf(" -h, --help ");
+ printf("Display this help\n");
+ printf(" -t, --title ");
+ printf("Display the test title\n");
+ printf(" -d, --description ");
+ printf("Display the test description\n");
+ if (strchr(opts, 's')) {
+ printf(" -s, --sync ");
+ printf("Make use of fsync\n");
+ }
+ if (strchr(opts, 'z')) {
+ printf(" -z, --size ");
+ printf("Set size parameter\n");
+ }
+ if (strchr(opts, 'n')) {
+ printf(" -n, --repeat ");
+ printf("Set repeat parameter\n");
+ }
+ if (strchr(opts, 'p')) {
+ printf(" -p, --sleep ");
+ printf("Set sleep parameter\n");
+ }
+ if (strchr(opts, 'u')) {
+ printf(" -u, --unlink ");
+ printf("Unlink file\n");
+ }
+ if (strchr(opts, 'o')) {
+ printf(" -o, --hole ");
+ printf("Create a hole in a file\n");
+ }
+ if (strchr(opts, 'c')) {
+ printf(" -c, --close ");
+ printf("Close file\n");
+ }
+ if (strchr(opts, 'e')) {
+ printf(" -e, --delete ");
+ printf("Delete file\n");
+ }
+ printf("\nBy default, testing is done in directory ");
+ printf("/mnt/test_file_system. To change this\nuse ");
+ printf("environmental variable ");
+ printf("TEST_FILE_SYSTEM_MOUNT_DIR. By default, ");
+ printf("the file\nsystem tested is jffs2. To change this ");
+ printf("set TEST_FILE_SYSTEM_TYPE.\n\n");
+ printf("Test Description:\n");
+ fold(desc, 80);
+ } else {
+ if (display_title)
+ printf("%s\n", title);
+ if (display_description)
+ printf("%s\n", desc);
+ if (display_title || display_description)
+ if (argc == 2 || (argc == 3 &&
+ display_title &&
+ display_description))
+ run_test = 0;
+ }
+ return run_test;
+}
+
+/* Return the number of files (or directories) in the given directory */
+unsigned tests_count_files_in_dir(const char *dir_name)
+{
+ DIR *dir;
+ struct dirent *entry;
+ unsigned count = 0;
+
+ dir = opendir(dir_name);
+ CHECK(dir != NULL);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ ++count;
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ CHECK(closedir(dir) != -1);
+ return count;
+}
+
+/* Change to the file system mount directory, check that it is empty,
+ matches the file system type, and is not the root file system */
+void tests_check_test_file_system(void)
+{
+ struct statfs fs_info;
+ struct stat f_info;
+ struct stat root_f_info;
+
+ if (chdir(tests_file_system_mount_dir) == -1 ||
+ statfs(tests_file_system_mount_dir, &fs_info) == -1) {
+ fprintf(stderr, "Invalid test file system mount directory:"
+ " %s\n", tests_file_system_mount_dir);
+ fprintf(stderr, "Use environment variable "
+ "TEST_FILE_SYSTEM_MOUNT_DIR\n");
+ CHECK(0);
+ }
+ tests_max_fname_len = fs_info.f_namelen;
+ if (strcmp(tests_file_system_type, "jffs2") == 0 &&
+ fs_info.f_type != JFFS2_SUPER_MAGIC) {
+ fprintf(stderr, "File system type is not jffs2\n");
+ CHECK(0);
+ }
+ /* Check that the test file system is not the root file system */
+ if (!rootok) {
+ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1);
+ CHECK(stat("/", &root_f_info) != -1);
+ CHECK(f_info.st_dev != root_f_info.st_dev);
+ }
+}
+
+/* Get the free space for the file system of the current directory */
+uint64_t tests_get_free_space(void)
+{
+ struct statvfs fs_info;
+
+ CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1);
+ return (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize;
+}
+
+/* Get the total space for the file system of the current directory */
+uint64_t tests_get_total_space(void)
+{
+ struct statvfs fs_info;
+
+ CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1);
+ return (uint64_t) fs_info.f_blocks * (uint64_t) fs_info.f_frsize;
+}
+
+#define WRITE_BUFFER_SIZE 32768
+
+static char write_buffer[WRITE_BUFFER_SIZE];
+
+static void init_write_buffer()
+{
+ static int init = 0;
+
+ if (!init) {
+ int i, d;
+ uint64_t u;
+
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ srand(1);
+ for (i = 0; i < WRITE_BUFFER_SIZE; ++i)
+ write_buffer[i] = rand() / d;
+ init = 1;
+ }
+}
+
+/* Write size random bytes into file descriptor fd at the current position,
+ returning the number of bytes actually written */
+uint64_t tests_fill_file(int fd, uint64_t size)
+{
+ ssize_t written;
+ size_t sz;
+ unsigned start = 0, length;
+ uint64_t remains;
+ uint64_t actual_size = 0;
+
+ init_write_buffer();
+ remains = size;
+ while (remains > 0) {
+ length = WRITE_BUFFER_SIZE - start;
+ if (remains > length)
+ sz = length;
+ else
+ sz = (size_t) remains;
+ written = write(fd, write_buffer + start, sz);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual_size += written;
+ if (written == sz)
+ start = 0;
+ else
+ start += written;
+ }
+ tests_maybe_sync(fd);
+ return actual_size;
+}
+
+/* Write size random bytes into file descriptor fd at offset,
+ returning the number of bytes actually written */
+uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size)
+{
+ ssize_t written;
+ size_t sz;
+ unsigned start = 0, length;
+ uint64_t remains;
+ uint64_t actual_size = 0;
+
+ CHECK(lseek(fd, offset, SEEK_SET) == offset);
+
+ init_write_buffer();
+ remains = size;
+ start = offset % WRITE_BUFFER_SIZE;
+ while (remains > 0) {
+ length = WRITE_BUFFER_SIZE - start;
+ if (remains > length)
+ sz = length;
+ else
+ sz = (size_t) remains;
+ written = write(fd, write_buffer + start, sz);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual_size += written;
+ if (written == sz)
+ start = 0;
+ else
+ start += written;
+ }
+ tests_maybe_sync(fd);
+ return actual_size;
+}
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file_fd(int fd)
+{
+ ssize_t sz;
+ char buf[WRITE_BUFFER_SIZE];
+
+ CHECK(lseek(fd, 0, SEEK_SET) == 0);
+ do {
+ sz = read(fd, buf, WRITE_BUFFER_SIZE);
+ CHECK(sz >= 0);
+ CHECK(memcmp(buf, write_buffer, sz) == 0);
+ } while (sz);
+}
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file(const char *file_name)
+{
+ int fd;
+
+ fd = open(file_name, O_RDONLY);
+ CHECK(fd != -1);
+ tests_check_filled_file_fd(fd);
+ CHECK(close(fd) != -1);
+}
+
+void tests_sync_directory(const char *file_name)
+{
+ char *path;
+ char *dir;
+ int fd;
+
+ if (!tests_ok_to_sync)
+ return;
+
+ path = strdup(file_name);
+ dir = dirname(path);
+ fd = open(dir,O_RDONLY | tests_maybe_sync_flag());
+ CHECK(fd != -1);
+ CHECK(fsync(fd) != -1);
+ CHECK(close(fd) != -1);
+ free(path);
+}
+
+/* Delete a file */
+void tests_delete_file(const char *file_name)
+{
+ CHECK(unlink(file_name) != -1);
+ tests_sync_directory(file_name);
+}
+
+/* Create a file of size file_size */
+uint64_t tests_create_file(const char *file_name, uint64_t file_size)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ uint64_t actual_size; /* Less than size if the file system is full */
+
+ flags = O_CREAT | O_TRUNC | O_WRONLY | tests_maybe_sync_flag();
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ actual_size = tests_fill_file(fd, file_size);
+ CHECK(close(fd) != -1);
+ if (file_size != 0 && actual_size == 0)
+ tests_delete_file(file_name);
+ else
+ tests_sync_directory(file_name);
+ return actual_size;
+}
+
+/* Calculate: free_space * numerator / denominator */
+uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator)
+{
+ if (denominator == 0)
+ denominator = 1;
+ if (numerator > denominator)
+ numerator = denominator;
+ return numerator * (tests_get_free_space() / denominator);
+}
+
+/* Create file "fragment_n" where n is the file_number, and unlink it */
+int tests_create_orphan(unsigned file_number)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ flags = O_CREAT | O_TRUNC | O_RDWR | tests_maybe_sync_flag();
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && (errno == ENOSPC || errno == EMFILE))
+ return fd; /* File system full or too many open files */
+ CHECK(fd != -1);
+ tests_sync_directory(file_name);
+ CHECK(unlink(file_name) != -1);
+ return fd;
+}
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_write_fragment_file(unsigned file_number,
+ int fd,
+ off_t offset,
+ unsigned size)
+{
+ int i, d;
+ uint64_t u;
+ ssize_t written;
+ off_t pos;
+ char buf[WRITE_BUFFER_SIZE];
+
+ if (size > WRITE_BUFFER_SIZE)
+ size = WRITE_BUFFER_SIZE;
+
+ pos = lseek(fd, 0, SEEK_END);
+ CHECK(pos != (off_t) -1);
+ if (offset > pos)
+ offset = pos;
+
+ pos = lseek(fd, offset, SEEK_SET);
+ CHECK(pos != (off_t) -1);
+ CHECK(pos == offset);
+
+ srand(file_number);
+ while (offset--)
+ rand();
+
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ for (i = 0; i < size; ++i)
+ buf[i] = rand() / d;
+
+ written = write(fd, buf, size);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ written = 0;
+ }
+ tests_maybe_sync(fd);
+ return (unsigned) written;
+}
+
+/* Write size bytes to the end of file descriptor fd using file_number
+ to determine the random data written i.e. seed for random numbers */
+unsigned tests_fill_fragment_file(unsigned file_number, int fd, unsigned size)
+{
+ off_t offset;
+
+ offset = lseek(fd, 0, SEEK_END);
+ CHECK(offset != (off_t) -1);
+
+ return tests_write_fragment_file(file_number, fd, offset, size);
+}
+
+/* Write size bytes to the end of file "fragment_n" where n is the file_number
+ and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_append_to_fragment_file(unsigned file_number,
+ unsigned size,
+ int create)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ unsigned actual_growth;
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ if (create)
+ flags = O_CREAT | O_EXCL | O_WRONLY | tests_maybe_sync_flag();
+ else
+ flags = O_WRONLY | tests_maybe_sync_flag();
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ actual_growth = tests_fill_fragment_file(file_number, fd, size);
+ CHECK(close(fd) != -1);
+ if (create && !actual_growth)
+ tests_delete_fragment_file(file_number);
+ return actual_growth;
+}
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_overwite_fragment_file( unsigned file_number,
+ off_t offset,
+ unsigned size)
+{
+ int fd;
+ unsigned actual_size;
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ fd = open(file_name, O_RDWR | tests_maybe_sync_flag());
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ actual_size = tests_write_fragment_file(file_number,
+ fd, offset, size);
+ CHECK(close(fd) != -1);
+ return actual_size;
+}
+
+/* Delete file "fragment_n" where n is the file_number */
+void tests_delete_fragment_file(unsigned file_number)
+{
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ tests_delete_file(file_name);
+}
+
+/* Check the random data in file "fragment_n" is what is expected */
+void tests_check_fragment_file_fd(unsigned file_number, int fd)
+{
+ ssize_t sz, i;
+ int d;
+ uint64_t u;
+ char buf[8192];
+
+ CHECK(lseek(fd, 0, SEEK_SET) == 0);
+ srand(file_number);
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ for (;;) {
+ sz = read(fd, buf, 8192);
+ if (sz == 0)
+ break;
+ CHECK(sz >= 0);
+ for (i = 0; i < sz; ++i)
+ CHECK(buf[i] == (char) (rand() / d));
+ }
+}
+
+/* Check the random data in file "fragment_n" is what is expected */
+void tests_check_fragment_file(unsigned file_number)
+{
+ int fd;
+ ssize_t sz, i;
+ int d;
+ uint64_t u;
+ char file_name[256];
+ char buf[8192];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ fd = open(file_name, O_RDONLY);
+ CHECK(fd != -1);
+ srand(file_number);
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ for (;;) {
+ sz = read(fd, buf, 8192);
+ if (sz == 0)
+ break;
+ CHECK(sz >= 0);
+ for (i = 0; i < sz; ++i)
+ CHECK(buf[i] == (char) (rand() / d));
+ }
+ CHECK(close(fd) != -1);
+}
+
+/* Central point to decide whether to use fsync */
+void tests_maybe_sync(int fd)
+{
+ if (tests_ok_to_sync)
+ CHECK(fsync(fd) != -1);
+}
+
+/* Return O_SYNC if ok to sync otherwise return 0 */
+int tests_maybe_sync_flag(void)
+{
+ if (tests_ok_to_sync)
+ return O_SYNC;
+ return 0;
+}
+
+/* Return random number from 0 to n - 1 */
+size_t tests_random_no(size_t n)
+{
+ uint64_t a, b;
+
+ if (!n)
+ return 0;
+ if (n - 1 <= RAND_MAX) {
+ a = rand();
+ b = RAND_MAX;
+ b += 1;
+ } else {
+ const uint64_t u = 1 + (uint64_t) RAND_MAX;
+ a = rand();
+ a *= u;
+ a += rand();
+ b = u * u;
+ CHECK(n <= b);
+ }
+ if (RAND_MAX <= UINT32_MAX && n <= UINT32_MAX)
+ return a * n / b;
+ else /*if (RAND_MAX <= UINT64_MAX && n <= UINT64_MAX)*/ {
+ uint64_t x, y;
+ if (a < n) {
+ x = a;
+ y = n;
+ } else {
+ x = n;
+ y = a;
+ }
+ return (x * (y / b)) + ((x * (y % b)) / b);
+ }
+}
+
+/* Make a directory empty */
+void tests_clear_dir(const char *dir_name)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char buf[4096];
+
+ dir = opendir(dir_name);
+ CHECK(dir != NULL);
+ CHECK(getcwd(buf, 4096) != NULL);
+ CHECK(chdir(dir_name) != -1);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (entry->d_type == DT_DIR) {
+ tests_clear_dir(entry->d_name);
+ CHECK(rmdir(entry->d_name) != -1);
+ } else
+ CHECK(unlink(entry->d_name) != -1);
+ }
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ CHECK(chdir(buf) != -1);
+ CHECK(closedir(dir) != -1);
+}
+
+/* Create an empty sub-directory or small file in the current directory */
+int64_t tests_create_entry(char *return_name)
+{
+ int fd;
+ char name[256];
+
+ for (;;) {
+ sprintf(name, "%u", (unsigned) tests_random_no(10000000));
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ break;
+ close(fd);
+ }
+ if (return_name)
+ strcpy(return_name, name);
+ if (tests_random_no(2)) {
+ return tests_create_file(name, tests_random_no(4096));
+ } else {
+ if (mkdir(name, 0777) == -1) {
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ return 0;
+ }
+ return TESTS_EMPTY_DIR_SIZE;
+ }
+}
+
+/* Remove a random file of empty sub-directory from the current directory */
+int64_t tests_remove_entry(void)
+{
+ DIR *dir;
+ struct dirent *entry;
+ unsigned count = 0, pos;
+ int64_t result = 0;
+
+ dir = opendir(".");
+ CHECK(dir != NULL);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ ++count;
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ pos = tests_random_no(count);
+ count = 0;
+ rewinddir(dir);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (!entry) {
+ CHECK(errno == 0);
+ break;
+ }
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (count == pos) {
+ if (entry->d_type == DT_DIR) {
+ tests_clear_dir(entry->d_name);
+ CHECK(rmdir(entry->d_name) != -1);
+ result = TESTS_EMPTY_DIR_SIZE;
+ } else {
+ struct stat st;
+ CHECK(stat(entry->d_name, &st) != -1);
+ result = st.st_size;
+ CHECK(unlink(entry->d_name) != -1);
+ }
+ }
+ ++count;
+ }
+ }
+ CHECK(closedir(dir) != -1);
+ return result;
+}
+
+/* Read mount information from /proc/mounts or /etc/mtab */
+int tests_get_mount_info(struct mntent *info)
+{
+ FILE *f;
+ struct mntent *entry;
+ int found = 0;
+
+ f = fopen("/proc/mounts", "rb");
+ if (!f)
+ f = fopen("/etc/mtab", "rb");
+ CHECK(f != NULL);
+ while (!found) {
+ entry = getmntent(f);
+ if (entry) {
+ if (strcmp(entry->mnt_dir,
+ tests_file_system_mount_dir) == 0) {
+ found = 1;
+ *info = *entry;
+ }
+ } else
+ break;
+ }
+ CHECK(fclose(f) == 0);
+ return found;
+}
+
+/*
+ * This funcion parses file-system options string, extracts standard mount
+ * options from there, and saves them in the @flags variable. The non-standard
+ * (fs-specific) mount options are left in @mnt_opts string, while the standard
+ * ones will be removed from it.
+ *
+ * The reason for this perverted function is that we want to preserve mount
+ * options when unmounting the file-system and mounting it again. But we cannot
+ * pass standard* mount optins (like sync, ro, etc) as a string to the
+ * 'mount()' function, because it fails. It accepts standard mount options only
+ * as flags. And only the FS-specific mount options are accepted in form of a
+ * string.
+ */
+static int process_mount_options(char **mnt_opts, unsigned long *flags)
+{
+ char *tmp, *opts, *p;
+ const char *opt;
+
+ /*
+ * We are going to use 'strtok()' which modifies the original string,
+ * so duplicate it.
+ */
+ tmp = strdup(*mnt_opts);
+ if (!tmp)
+ goto out_mem;
+
+ p = opts = calloc(1, strlen(*mnt_opts) + 1);
+ if (!opts) {
+ free(tmp);
+ goto out_mem;
+ }
+
+ *flags = 0;
+ opt = strtok(tmp, ",");
+ while (opt) {
+ if (!strcmp(opt, "rw"))
+ ;
+ else if (!strcmp(opt, "ro"))
+ *flags |= MS_RDONLY;
+ else if (!strcmp(opt, "dirsync"))
+ *flags |= MS_DIRSYNC;
+ else if (!strcmp(opt, "noatime"))
+ *flags |= MS_NOATIME;
+ else if (!strcmp(opt, "nodiratime"))
+ *flags |= MS_NODIRATIME;
+ else if (!strcmp(opt, "noexec"))
+ *flags |= MS_NOEXEC;
+ else if (!strcmp(opt, "nosuid"))
+ *flags |= MS_NOSUID;
+ else if (!strcmp(opt, "relatime"))
+ *flags |= MS_RELATIME;
+ else if (!strcmp(opt, "sync"))
+ *flags |= MS_SYNCHRONOUS;
+ else {
+ int len = strlen(opt);
+
+ if (p != opts)
+ *p++ = ',';
+ memcpy(p, opt, len);
+ p += len;
+ *p = '\0';
+ }
+
+ opt = strtok(NULL, ",");
+ }
+
+ free(tmp);
+ *mnt_opts = opts;
+ return 0;
+
+out_mem:
+ fprintf(stderr, "cannot allocate memory\n");
+ return 1;
+}
+
+/* Un-mount and re-mount test file system */
+void tests_remount(void)
+{
+ struct mntent mount_info;
+ char *source;
+ char *target;
+ char *filesystemtype;
+ unsigned long mountflags;
+ char *data;
+ char cwd[4096];
+
+ CHECK(tests_get_mount_info(&mount_info));
+
+ if (strcmp(mount_info.mnt_dir,"/") == 0)
+ return;
+
+ CHECK(getcwd(cwd, 4096) != NULL);
+ CHECK(chdir("/") != -1);
+
+ CHECK(umount(tests_file_system_mount_dir) != -1);
+
+ source = mount_info.mnt_fsname;
+ target = tests_file_system_mount_dir;
+ filesystemtype = tests_file_system_type;
+ data = mount_info.mnt_opts;
+ process_mount_options(&data, &mountflags);
+
+ CHECK(mount(source, target, filesystemtype, mountflags, data) != -1);
+ CHECK(chdir(cwd) != -1);
+}
+
+/* Un-mount or re-mount test file system */
+static void tests_mnt(int mnt)
+{
+ static struct mntent mount_info;
+ char *source;
+ char *target;
+ char *filesystemtype;
+ unsigned long mountflags;
+ char *data;
+ static char cwd[4096];
+
+ if (mnt == 0) {
+ CHECK(tests_get_mount_info(&mount_info));
+ if (strcmp(mount_info.mnt_dir,"/") == 0)
+ return;
+ CHECK(getcwd(cwd, 4096) != NULL);
+ CHECK(chdir("/") != -1);
+ CHECK(umount(tests_file_system_mount_dir) != -1);
+ } else {
+ source = mount_info.mnt_fsname;
+ target = tests_file_system_mount_dir;
+ filesystemtype = tests_file_system_type;
+ data = mount_info.mnt_opts;
+ process_mount_options(&data, &mountflags);
+
+ CHECK(mount(source, target, filesystemtype, mountflags, data)
+ != -1);
+ CHECK(chdir(cwd) != -1);
+ }
+}
+
+/* Unmount test file system */
+void tests_unmount(void)
+{
+ tests_mnt(0);
+}
+
+/* Mount test file system */
+void tests_mount(void)
+{
+ tests_mnt(1);
+}
+
+/* Check whether the test file system is also the root file system */
+int tests_fs_is_rootfs(void)
+{
+ struct stat f_info;
+ struct stat root_f_info;
+
+ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1);
+ CHECK(stat("/", &root_f_info) != -1);
+ if (f_info.st_dev == root_f_info.st_dev)
+ return 1;
+ else
+ return 0;
+}
+
+/* Try to make a directory empty */
+void tests_try_to_clear_dir(const char *dir_name)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char buf[4096];
+
+ dir = opendir(dir_name);
+ if (dir == NULL)
+ return;
+ if (getcwd(buf, 4096) == NULL || chdir(dir_name) == -1) {
+ closedir(dir);
+ return;
+ }
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (entry->d_type == DT_DIR) {
+ tests_try_to_clear_dir(entry->d_name);
+ rmdir(entry->d_name);
+ } else
+ unlink(entry->d_name);
+ }
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ chdir(buf);
+ closedir(dir);
+}
+
+/* Check whether the test file system is also the current file system */
+int tests_fs_is_currfs(void)
+{
+ struct stat f_info;
+ struct stat curr_f_info;
+
+ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1);
+ CHECK(stat(".", &curr_f_info) != -1);
+ if (f_info.st_dev == curr_f_info.st_dev)
+ return 1;
+ else
+ return 0;
+}
+
+#define PID_BUF_SIZE 64
+
+/* Concatenate a pid to a string in a signal safe way */
+void tests_cat_pid(char *buf, const char *name, pid_t pid)
+{
+ char *p;
+ unsigned x;
+ const char digits[] = "0123456789";
+ char pid_buf[PID_BUF_SIZE];
+
+ x = (unsigned) pid;
+ p = pid_buf + PID_BUF_SIZE;
+ *--p = '\0';
+ if (x)
+ while (x) {
+ *--p = digits[x % 10];
+ x /= 10;
+ }
+ else
+ *--p = '0';
+ buf[0] = '\0';
+ strcat(buf, name);
+ strcat(buf, p);
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/lib/tests.h b/mtd-utils-1.3.1/tests/fs-tests/lib/tests.h
new file mode 100644
index 0000000..01849bc
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/lib/tests.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#ifndef included_tests_tests_h__
+#define included_tests_tests_h__
+
+#include <stdint.h>
+
+/* Main macro for testing */
+#define CHECK(x) tests_test((x),__func__,__FILE__,__LINE__)
+
+/* The default directory in which tests are conducted */
+#define TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR "/mnt/test_file_system"
+
+/* The default file system type to test */
+#define TESTS_DEFAULT_FILE_SYSTEM_TYPE "jffs2"
+
+/* Estimated size of an empty directory */
+#define TESTS_EMPTY_DIR_SIZE 128
+
+/* Function invoked by the CHECK macro */
+void tests_test(int test,const char *msg,const char *file,unsigned line);
+
+/* Handle common program options */
+int tests_get_args(int argc,
+ char *argv[],
+ const char *title,
+ const char *desc,
+ const char *opts);
+
+/* Return the number of files (or directories) in the given directory */
+unsigned tests_count_files_in_dir(const char *dir_name);
+
+/* Change to the file system mount directory, check that it is empty,
+ matches the file system type, and is not the root file system */
+void tests_check_test_file_system(void);
+
+/* Get the free space for the file system of the current directory */
+uint64_t tests_get_free_space(void);
+
+/* Get the total space for the file system of the current directory */
+uint64_t tests_get_total_space(void);
+
+/* Write size random bytes into file descriptor fd at the current position,
+ returning the number of bytes actually written */
+uint64_t tests_fill_file(int fd, uint64_t size);
+
+/* Write size random bytes into file descriptor fd at offset,
+ returning the number of bytes actually written */
+uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size);
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file_fd(int fd);
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file(const char *file_name);
+
+/* Delete a file */
+void tests_delete_file(const char *file_name);
+
+/* Create a file of size file_size */
+uint64_t tests_create_file(const char *file_name, uint64_t file_size);
+
+/* Calculate: free_space * numerator / denominator */
+uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator);
+
+/* Create file "fragment_n" where n is the file_number, and unlink it */
+int tests_create_orphan(unsigned file_number);
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_write_fragment_file(unsigned file_number,
+ int fd,
+ off_t offset,
+ unsigned size);
+
+/* Write size bytes to the end of file descriptor fd using file_number
+ to determine the random data written i.e. seed for random numbers */
+unsigned tests_fill_fragment_file(unsigned file_number,
+ int fd,
+ unsigned size);
+
+/* Write size bytes to the end of file "fragment_n" where n is the file_number
+ and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_append_to_fragment_file(unsigned file_number,
+ unsigned size,
+ int create);
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_overwite_fragment_file( unsigned file_number,
+ off_t offset,
+ unsigned size);
+
+/* Delete file "fragment_n" where n is the file_number */
+void tests_delete_fragment_file(unsigned file_number);
+
+/* Check the random data in file "fragment_n" is what is expected */
+void tests_check_fragment_file_fd(unsigned file_number, int fd);
+
+/* Check the random data in file "fragment_n" is what is expected */
+void tests_check_fragment_file(unsigned file_number);
+
+/* Central point to decide whether to use fsync */
+void tests_maybe_sync(int fd);
+
+/* Return O_SYNC if ok to sync otherwise return 0 */
+int tests_maybe_sync_flag(void);
+
+/* Return random number from 0 to n - 1 */
+size_t tests_random_no(size_t n);
+
+/* Make a directory empty */
+void tests_clear_dir(const char *dir_name);
+
+/* Create an empty sub-directory or small file in the current directory */
+int64_t tests_create_entry(char *return_name);
+
+/* Remove a random file of empty sub-directory from the current directory */
+int64_t tests_remove_entry(void);
+
+/* Un-mount and re-mount test file system */
+void tests_remount(void);
+
+/* Un-mount test file system */
+void tests_unmount(void);
+
+/* Mount test file system */
+void tests_mount(void);
+
+/* Check whether the test file system is also the root file system */
+int tests_fs_is_rootfs(void);
+
+/* Try to make a directory empty */
+void tests_try_to_clear_dir(const char *dir_name);
+
+/* Check whether the test file system is also the current file system */
+int tests_fs_is_currfs(void);
+
+/* Concatenate a pid to a string in a signal safe way */
+void tests_cat_pid(char *buf, const char *name, pid_t pid);
+
+extern char *tests_file_system_mount_dir;
+
+extern char *tests_file_system_type;
+
+/* General purpose test parameter to specify some aspect of test size.
+ May be used by different tests in different ways.
+ Set by the -z, --size options. */
+extern int64_t tests_size_parameter;
+
+/* General purpose test parameter to specify some aspect of test repetition.
+ May be used by different tests in different ways.
+ Set by the -n, --repeat options. */
+extern int64_t tests_repeat_parameter;
+
+/* General purpose test parameter to specify some aspect of test sleeping.
+ May be used by different tests in different ways.
+ Set by the -p, --sleep options. */
+extern int64_t tests_sleep_parameter;
+
+/* General purpose test parameter to specify a file should be unlinked.
+ May be used by different tests in different ways or not at all. */
+extern int tests_unlink_flag;
+
+/* General purpose test parameter to specify a file should be closed.
+ May be used by different tests in different ways or not at all. */
+extern int tests_close_flag;
+
+/* General purpose test parameter to specify a file should be deleted.
+ May be used by different tests in different ways or not at all. */
+extern int tests_delete_flag;
+
+/* General purpose test parameter to specify a file have a hole.
+ May be used by different tests in different ways or not at all. */
+extern int tests_hole_flag;
+
+/* Program name from argv[0] */
+extern char *program_name;
+
+/* Maximum file name length of test file system (from statfs) */
+extern long tests_max_fname_len;
+
+#endif
diff --git a/mtd-utils-1.3.1/tests/fs-tests/run_all.sh b/mtd-utils-1.3.1/tests/fs-tests/run_all.sh
new file mode 100755
index 0000000..e79993a
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/run_all.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR
+if test -z "$TEST_DIR";
+then
+ TEST_DIR="/mnt/test_file_system"
+fi
+
+rm -rf ${TEST_DIR}/*
+
+./simple/test_1 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./simple/test_2 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./integrity/integck || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/rndrm00 -z0 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/rmdir00 -z0 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/stress_1 -z10000000 -e || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/stress_2 -z10000000 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/stress_3 -z1000000000 -e || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+cd stress || exit 1
+
+./stress00.sh 3600 || exit 1
+
+./stress01.sh 3600 || exit 1
+
+cd .. || exit 1
diff --git a/mtd-utils-1.3.1/tests/fs-tests/simple/Makefile b/mtd-utils-1.3.1/tests/fs-tests/simple/Makefile
new file mode 100644
index 0000000..d447da3
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/simple/Makefile
@@ -0,0 +1,30 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = test_1 \
+ test_2 \
+ ftrunc \
+ orph \
+ perf
+
+all: $(TARGETS)
+
+$(TARGETS): ../lib/tests.o
+
+../lib/tests.o: ../lib/tests.h
+
+clean:
+ rm -f *.o $(TARGETS)
+
+tests: all
+ ./test_1 --sync
+ ./test_2 --sync
+ ./ftrunc
+ ./orph --sync
+ ./perf
diff --git a/mtd-utils-1.3.1/tests/fs-tests/simple/ftrunc.c b/mtd-utils-1.3.1/tests/fs-tests/simple/ftrunc.c
new file mode 100644
index 0000000..86edf65
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/simple/ftrunc.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void ftrunc(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char *file_name;
+ off_t actual;
+ char buf[WRITE_BUFFER_SIZE];
+
+ file_name = "ftrunc_test_file";
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ pid = getpid();
+ srand(pid);
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ remains = tests_size_parameter;
+ actual = 0;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual += written;
+ }
+ CHECK(ftruncate(fd, (actual ? actual - 1 : actual)) != -1);
+ CHECK(close(fd) != -1);
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *ftrunc_get_title(void)
+{
+ return "Truncate a large test file";
+}
+
+/* Description of this test */
+
+const char *ftrunc_get_description(void)
+{
+ return
+ "Create a file named ftrunc_test_file. " \
+ "Truncate the file to reduce its length by 1. " \
+ "Then remove the truncated file. "
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, ftrunc_get_title(),
+ ftrunc_get_description(), "z");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ ftrunc();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/simple/orph.c b/mtd-utils-1.3.1/tests/fs-tests/simple/orph.c
new file mode 100644
index 0000000..f6d8956
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/simple/orph.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define MAX_ORPHANS 1000000
+
+void orph(void)
+{
+ pid_t pid;
+ unsigned i, j, k, n;
+ int fd, done, full;
+ int64_t repeat;
+ ssize_t sz;
+ char dir_name[256];
+ int fds[MAX_ORPHANS];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "orph_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ full = 0;
+ done = 0;
+ n = 0;
+ while (n + 100 < MAX_ORPHANS && !done) {
+ /* Make 100 more orphans */
+ for (i = 0; i < 100; i++) {
+ fd = tests_create_orphan(n + i);
+ if (fd < 0) {
+ done = 1;
+ if (errno == ENOSPC)
+ full = 1;
+ else if (errno != EMFILE)
+ CHECK(0);
+ errno = 0;
+ break;
+ }
+ fds[n + i] = fd;
+ }
+ if (!full) {
+ /* Write to orphans just created */
+ k = i;
+ for (i = 0; i < k; i++) {
+ if (tests_write_fragment_file(n + i,
+ fds[n+i],
+ 0, 1000)
+ != 1000) {
+ /*
+ * Out of space, so close
+ * remaining files
+ */
+ for (j = i; j < k; j++)
+ CHECK(close(fds[n + j])
+ != -1);
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (!done)
+ CHECK(tests_count_files_in_dir(".") == 0);
+ n += i;
+ }
+ /* Check the data in the files */
+ for (i = 0; i < n; i++)
+ tests_check_fragment_file_fd(i, fds[i]);
+ if (!full && n) {
+ /* Ensure the file system is full */
+ n -= 1;
+ do {
+ sz = write(fds[n], fds, 4096);
+ if (sz == -1 && errno == ENOSPC) {
+ errno = 0;
+ break;
+ }
+ CHECK(sz >= 0);
+ } while (sz == 4096);
+ CHECK(close(fds[n]) != -1);
+ }
+ /* Check the data in the files */
+ for (i = 0; i < n; i++)
+ tests_check_fragment_file_fd(i, fds[i]);
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ /* Close orphans */
+ for (i = 0; i < n; i++)
+ CHECK(close(fds[i]) != -1);
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ }
+ CHECK(tests_count_files_in_dir(".") == 0);
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *orph_get_title(void)
+{
+ return "Create many open unlinked files";
+}
+
+/* Description of this test */
+
+const char *orph_get_description(void)
+{
+ return
+ "Create a directory named orph_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "create files and keep them open and unlink them. " \
+ "Create as many files as possible until the file system is " \
+ "full or the maximum allowed open files is reached. " \
+ "If a sleep value is specified, the process sleeps. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. " \
+ "Sleep is specified in milliseconds. " \
+ "Then close the files. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "Finally remove the directory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, orph_get_title(),
+ orph_get_description(), "nps");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ orph();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/simple/perf.c b/mtd-utils-1.3.1/tests/fs-tests/simple/perf.c
new file mode 100644
index 0000000..43d708d
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/simple/perf.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+
+#include "tests.h"
+
+#define BLOCK_SIZE 32 * 1024
+
+struct timeval tv_start;
+struct timeval tv_stop;
+
+static inline void start_timer(void)
+{
+ CHECK(gettimeofday(&tv_start, NULL) != -1);
+}
+
+static inline long long stop_timer(void)
+{
+ long long usecs;
+
+ CHECK(gettimeofday(&tv_stop, NULL) != -1);
+ usecs = (tv_stop.tv_sec - tv_start.tv_sec);
+ usecs *= 1000000;
+ usecs += tv_stop.tv_usec;
+ usecs -= tv_start.tv_usec;
+ return usecs;
+}
+
+static unsigned speed(size_t bytes, long long usecs)
+{
+ unsigned long long k;
+
+ k = bytes * 1000000ULL;
+ k /= usecs;
+ k /= 1024;
+ CHECK(k <= UINT_MAX);
+ return (unsigned) k;
+}
+
+void perf(void)
+{
+ pid_t pid;
+ int fd, i;
+ ssize_t written, readsz;
+ size_t remains, block, actual_size;
+ char file_name[256];
+ unsigned char *buf;
+ long long write_time, unmount_time, mount_time, read_time;
+
+ /* Sync all file systems */
+ sync();
+ /* Make random data to write */
+ buf = malloc(BLOCK_SIZE);
+ CHECK(buf != NULL);
+ pid = getpid();
+ srand(pid);
+ for (i = 0; i < BLOCK_SIZE; i++)
+ buf[i] = rand();
+ /* Open file */
+ tests_cat_pid(file_name, "perf_test_file_", pid);
+ start_timer();
+ fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ CHECK(tests_size_parameter > 0);
+ CHECK(tests_size_parameter <= SIZE_MAX);
+ /* Write to file */
+ actual_size = 0;
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > BLOCK_SIZE)
+ block = BLOCK_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual_size += written;
+ }
+ CHECK(fsync(fd) != -1);
+ CHECK(close(fd) != -1);
+ write_time = stop_timer();
+ /* Unmount */
+ start_timer();
+ tests_unmount();
+ unmount_time = stop_timer();
+ /* Mount */
+ start_timer();
+ tests_mount();
+ mount_time = stop_timer();
+ /* Open file, read it, and close it */
+ start_timer();
+ fd = open(file_name, O_RDONLY);
+ CHECK(fd != -1);
+ remains = actual_size;
+ while (remains > 0) {
+ if (remains > BLOCK_SIZE)
+ block = BLOCK_SIZE;
+ else
+ block = remains;
+ readsz = read(fd, buf, block);
+ CHECK(readsz == block);
+ remains -= readsz;
+ }
+ CHECK(close(fd) != -1);
+ read_time = stop_timer();
+ CHECK(unlink(file_name) != -1);
+ /* Display timings */
+ printf("File system read and write speed\n");
+ printf("================================\n");
+ printf("Specfied file size: %lld\n", tests_size_parameter);
+ printf("Actual file size: %zu\n", actual_size);
+ printf("Write time (us): %lld\n", write_time);
+ printf("Unmount time (us): %lld\n", unmount_time);
+ printf("Mount time (us): %lld\n", mount_time);
+ printf("Read time (us): %lld\n", read_time);
+ printf("Write speed (KiB/s): %u\n", speed(actual_size, write_time));
+ printf("Read speed (KiB/s): %u\n", speed(actual_size, read_time));
+ printf("Test completed\n");
+}
+
+/* Title of this test */
+
+const char *perf_get_title(void)
+{
+ return "Measure file system read and write speed";
+}
+
+/* Description of this test */
+
+const char *perf_get_description(void)
+{
+ return
+ "Syncs the file system (a newly created empty file system is " \
+ "preferable). Creates a file named perf_test_file_pid, where " \
+ "pid is the process id. The file is filled with random data. " \
+ "The size of the file is given by the -z or --size option, " \
+ "otherwise it defaults to 10MiB. Unmounts the file system. " \
+ "Mounts the file system. Reads the entire file in 32KiB size " \
+ "blocks. Displays the time taken for each activity. Deletes " \
+ "the file. Note that the file is synced after writing and " \
+ "that time is included in the write time and speed.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 10 * 1024 * 1024;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, perf_get_title(),
+ perf_get_description(), "z");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ perf();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/simple/test_1.c b/mtd-utils-1.3.1/tests/fs-tests/simple/test_1.c
new file mode 100644
index 0000000..69eafe4
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/simple/test_1.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "tests.h"
+
+void test_1(void)
+{
+ int fd;
+ pid_t pid;
+ uint64_t i;
+ uint64_t block;
+ uint64_t actual_size;
+ char name[256];
+ char old[16];
+ char buf[16];
+ off_t old_len;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "test_1_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Create a file that fills half the free space on the file system */
+ tests_create_file("big_file", tests_get_big_file_size(1,2));
+ CHECK(tests_count_files_in_dir(".") == 1);
+ fd = open("big_file", O_RDWR | tests_maybe_sync_flag());
+ CHECK(fd != -1);
+ CHECK(read(fd, old, 5) == 5);
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(write(fd,"start", 5) == 5);
+ CHECK(lseek(fd,0,SEEK_END) != (off_t) -1);
+ CHECK(write(fd, "end", 3) == 3);
+ tests_maybe_sync(fd);
+ /* Delete the file while it is still open */
+ tests_delete_file("big_file");
+ CHECK(tests_count_files_in_dir(".") == 0);
+ /* Create files to file up the file system */
+ for (block = 1000000, i = 1; ; block /= 10) {
+ while (i != 0) {
+ sprintf(name, "fill_up_%llu", i);
+ actual_size = tests_create_file(name, block);
+ if (actual_size != 0)
+ ++i;
+ if (actual_size != block)
+ break;
+ }
+ if (block == 1)
+ break;
+ }
+ /* Check the big file */
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(read(fd, buf, 5) == 5);
+ CHECK(strncmp(buf, "start", 5) == 0);
+ CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1);
+ CHECK(read(fd, buf, 3) == 3);
+ CHECK(strncmp(buf, "end", 3) == 0);
+ /* Check the other files and delete them */
+ i -= 1;
+ CHECK(tests_count_files_in_dir(".") == i);
+ for (; i > 0; --i) {
+ sprintf(name, "fill_up_%llu", i);
+ tests_check_filled_file(name);
+ tests_delete_file(name);
+ }
+ CHECK(tests_count_files_in_dir(".") == 0);
+ /* Check the big file again */
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(read(fd, buf, 5) == 5);
+ CHECK(strncmp(buf, "start", 5) == 0);
+ CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1);
+ CHECK(read(fd, buf, 3) == 3);
+ CHECK(strncmp(buf, "end", 3) == 0);
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(write(fd,old, 5) == 5);
+ old_len = lseek(fd, -3, SEEK_END);
+ CHECK(old_len != (off_t) -1);
+ CHECK(ftruncate(fd,old_len) != -1);
+ tests_check_filled_file_fd(fd);
+ /* Close the big file*/
+ CHECK(close(fd) != -1);
+ CHECK(tests_count_files_in_dir(".") == 0);
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *test_1_get_title(void)
+{
+ return "Fill file system while holding deleted big file descriptor";
+}
+
+/* Description of this test */
+
+const char *test_1_get_description(void)
+{
+ return
+ "Create a directory named test_1_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "create a big file (approx. half the file system in size), " \
+ "open it, and unlink it. " \
+ "Create many smaller files until the file system is full. " \
+ "Check the big file is ok. " \
+ "Delete all the smaller files. " \
+ "Check the big file again. " \
+ "Finally delete the big file and directory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, test_1_get_title(),
+ test_1_get_description(), "s");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ test_1();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/simple/test_2.c b/mtd-utils-1.3.1/tests/fs-tests/simple/test_2.c
new file mode 100644
index 0000000..2094460
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/simple/test_2.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "tests.h"
+
+void test_2(void)
+{
+ pid_t pid;
+ int create, full;
+ unsigned i, number_of_files;
+ unsigned growth;
+ unsigned size;
+ uint64_t big_file_size;
+ int fd;
+ off_t offset;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "test_2_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Create up to 1000 files appending 400 bytes at a time to each file */
+ /* until the file system is full.*/
+ create = 1;
+ full = 0;
+ number_of_files = 1000;
+ while (!full) {
+ for (i = 0; i < number_of_files; ++i) {
+ growth = tests_append_to_fragment_file(i, 400, create);
+ if (!growth) {
+ full = 1;
+ if (create)
+ number_of_files = i;
+ break;
+ }
+ }
+ create = 0;
+ }
+ /* Check the files */
+ CHECK(tests_count_files_in_dir(".") == number_of_files);
+ for (i = 0; i < number_of_files; ++i)
+ tests_check_fragment_file(i);
+ /* Delete half of them */
+ for (i = 1; i < number_of_files; i += 2)
+ tests_delete_fragment_file(i);
+ /* Check them again */
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ /* Create a big file that fills two thirds of the free space */
+ big_file_size = tests_get_big_file_size(2,3);
+ /* Check the big file */
+ tests_create_file("big_file", big_file_size);
+ CHECK(tests_count_files_in_dir(".") == 1 + (number_of_files + 1) / 2);
+ tests_check_filled_file("big_file");
+ /* Open the big file */
+ fd = open("big_file",O_RDWR | tests_maybe_sync_flag());
+ CHECK(fd != -1);
+ /* Delete the big file while it is still open */
+ tests_delete_file("big_file");
+ /* Check the big file again */
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ tests_check_filled_file_fd(fd);
+
+ /* Write parts of the files and check them */
+
+ offset = 100; /* Offset to write at, in the small files */
+ size = 200; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 300; /* Offset to write at, in the small files */
+ size = 400; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 110; /* Offset to write at, in the small files */
+ size = 10; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 10; /* Offset to write at, in the small files */
+ size = 1000; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 0; /* Offset to write at, in the small files */
+ size = 100000; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ /* Close the big file*/
+ CHECK(close(fd) != -1);
+ /* Check the small files */
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ /* Delete the small files */
+ for (i = 0; i < number_of_files; i += 2)
+ tests_delete_fragment_file(i);
+ CHECK(tests_count_files_in_dir(".") == 0);
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *test_2_get_title(void)
+{
+ return "Repeated write many small files and one big deleted file";
+}
+
+/* Description of this test */
+
+const char *test_2_get_description(void)
+{
+ return
+ "Create a directory named test_2_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "create about 1000 files. Append 400 bytes to each until " \
+ "the file system is full. Then delete half of them. Then " \
+ "create a big file that uses about 2/3 of the remaining free " \
+ "space. Get a file descriptor for the big file, and delete " \
+ "the big file. Then repeatedly write to the small files " \
+ "and the big file. " \
+ "Finally delete the big file and directory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, test_2_get_title(),
+ test_2_get_description(), "s");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ test_2();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/Makefile b/mtd-utils-1.3.1/tests/fs-tests/stress/Makefile
new file mode 100644
index 0000000..c24ff3f
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/Makefile
@@ -0,0 +1,11 @@
+
+SUBDIRS = atoms
+
+all tests: $(SUBDIRS)
+
+clean: $(SUBDIRS)
+ rm -rf run_pdf_test_file_*
+
+.PHONY: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/Makefile b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/Makefile
new file mode 100644
index 0000000..9fbfd39
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/Makefile
@@ -0,0 +1,40 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = stress_1 \
+ stress_2 \
+ stress_3 \
+ pdfrun \
+ rndwrite00 \
+ fwrite00 \
+ rmdir00 \
+ rndrm00 \
+ rndrm99 \
+ gcd_hupper
+
+all: $(TARGETS)
+
+$(TARGETS): ../../lib/tests.o
+
+../lib/tests.o: ../../lib/tests.h
+
+clean:
+ rm -f *.o $(TARGETS) run_pdf_test_file
+
+tests: all
+ ./stress_1 -e
+ ./stress_2
+ ./stress_3 -e
+ ./pdfrun
+ ./rndwrite00 -e
+ ./fwrite00
+ ./rmdir00
+ ./rndrm00
+ ./rndrm99
+ ./gcd_hupper
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/fwrite00.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/fwrite00.c
new file mode 100644
index 0000000..fd691be
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/fwrite00.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+#define HOLE_BLOCK_SIZE 10000000
+
+void filestress00(void)
+{
+ int fd, i, deleted;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ int64_t repeat;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ fd = -1;
+ deleted = 1;
+ pid = getpid();
+ tests_cat_pid(file_name, "filestress00_test_file_", pid);
+ srand(pid);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ /* Open the file */
+ if (fd == -1) {
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ if (fd == -1 && errno == ENOSPC) {
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep 2 secs and try again */
+ sleep(2);
+ continue;
+ }
+ CHECK(fd != -1);
+ deleted = 0;
+ if (tests_unlink_flag) {
+ CHECK(unlink(file_name) != -1);
+ deleted = 1;
+ }
+ }
+ /* Get a different set of random data */
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ if (tests_hole_flag) {
+ /* Make a hole */
+ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1);
+ written = write(fd, "!", 1);
+ if (written <= 0) {
+ /* File system full */
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ }
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ /* Write at set points into the hole */
+ remains = tests_size_parameter;
+ while (remains > HOLE_BLOCK_SIZE) {
+ CHECK(lseek(fd, HOLE_BLOCK_SIZE,
+ SEEK_CUR) != -1);
+ written = write(fd, "!", 1);
+ remains -= HOLE_BLOCK_SIZE;
+ if (written <= 0) {
+ /* File system full */
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ break;
+ }
+ }
+ } else {
+ /* Write data into the file */
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ /* File system full */
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ }
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Close if tests_close_flag */
+ if (tests_close_flag) {
+ CHECK(close(fd) != -1);
+ fd = -1;
+ }
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ /* Delete if tests_delete flag */
+ if (!deleted && tests_delete_flag) {
+ CHECK(unlink(file_name) != -1);
+ deleted = 1;
+ }
+ }
+ CHECK(close(fd) != -1);
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ /* Tidy up */
+ if (!deleted)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *filestress00_get_title(void)
+{
+ return "File stress test 00";
+}
+
+/* Description of this test */
+
+const char *filestress00_get_description(void)
+{
+ return
+ "Create a file named filestress00_test_file_pid, where " \
+ "pid is the process id. If the unlink option " \
+ "(-u or --unlink) is specified, " \
+ "unlink the file while holding the open file descriptor. " \
+ "If the hole option (-o or --hole) is specified, " \
+ "write a single character at the end of the file, creating a " \
+ "hole. " \
+ "Write a single character in the hole every 10 million " \
+ "bytes. " \
+ "If the hole option is not specified, then the file is " \
+ "filled with random data. " \
+ "If the close option (-c or --close) is specified the file " \
+ "is closed. " \
+ "If a sleep value is specified, the process sleeps. " \
+ "If the delete option (-e or --delete) is specified, then " \
+ "the file is deleted. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The file size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. " \
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, filestress00_get_title(),
+ filestress00_get_description(), "znpuoce");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ filestress00();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/gcd_hupper.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/gcd_hupper.c
new file mode 100644
index 0000000..31c175d
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/gcd_hupper.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <mntent.h>
+#include <signal.h>
+
+#include "tests.h"
+
+#define MAX_NAME_SIZE 1024
+
+struct gcd_pid
+{
+ struct gcd_pid *next;
+ int pid;
+ char *name;
+ int mtd_index;
+};
+
+struct gcd_pid *gcd_pid_list = NULL;
+
+int add_gcd_pid(const char *number)
+{
+ int pid;
+ FILE *f;
+ char file_name[MAX_NAME_SIZE];
+ char program_name[MAX_NAME_SIZE];
+
+ pid = atoi(number);
+ if (pid <= 0)
+ return 0;
+ snprintf(file_name, MAX_NAME_SIZE, "/proc/%s/stat", number);
+ f = fopen(file_name, "r");
+ if (f == NULL)
+ return 0;
+ if (fscanf(f, "%d %s", &pid, program_name) != 2) {
+ fclose(f);
+ return 0;
+ }
+ if (strncmp(program_name, "(jffs2_gcd_mtd", 14) != 0)
+ pid = 0;
+ if (pid) {
+ size_t sz;
+ struct gcd_pid *g;
+
+ sz = sizeof(struct gcd_pid);
+ g = (struct gcd_pid *) malloc(sz);
+ g->pid = pid;
+ g->name = (char *) malloc(strlen(program_name) + 1);
+ if (g->name)
+ strcpy(g->name, program_name);
+ else
+ exit(1);
+ g->mtd_index = atoi(program_name + 14);
+ g->next = gcd_pid_list;
+ gcd_pid_list = g;
+ }
+ fclose(f);
+ return pid;
+}
+
+int get_pid_list(void)
+{
+ DIR *dir;
+ struct dirent *entry;
+
+ dir = opendir("/proc");
+ if (dir == NULL)
+ return 1;
+ for (;;) {
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ add_gcd_pid(entry->d_name);
+ } else
+ break;
+ }
+ closedir(dir);
+ return 0;
+}
+
+int parse_index_number(const char *name)
+{
+ const char *p, *q;
+ int all_zero;
+ int index;
+
+ p = name;
+ while (*p && !isdigit(*p))
+ ++p;
+ if (!*p)
+ return -1;
+ all_zero = 1;
+ for (q = p; *q; ++q) {
+ if (!isdigit(*q))
+ return -1;
+ if (*q != '0')
+ all_zero = 0;
+ }
+ if (all_zero)
+ return 0;
+ index = atoi(p);
+ if (index <= 0)
+ return -1;
+ return index;
+}
+
+int get_mtd_index(void)
+{
+ FILE *f;
+ struct mntent *entry;
+ struct stat f_info;
+ struct stat curr_f_info;
+ int found;
+ int mtd_index = -1;
+
+ if (stat(tests_file_system_mount_dir, &f_info) == -1)
+ return -1;
+ f = fopen("/proc/mounts", "rb");
+ if (!f)
+ f = fopen("/etc/mtab", "rb");
+ if (f == NULL)
+ return -1;
+ found = 0;
+ for (;;) {
+ entry = getmntent(f);
+ if (!entry)
+ break;
+ if (stat(entry->mnt_dir, &curr_f_info) == -1)
+ continue;
+ if (f_info.st_dev == curr_f_info.st_dev) {
+ int i;
+
+ i = parse_index_number(entry->mnt_fsname);
+ if (i != -1) {
+ if (found && i != mtd_index)
+ return -1;
+ found = 1;
+ mtd_index = i;
+ }
+ }
+ }
+ fclose(f);
+ return mtd_index;
+}
+
+int get_gcd_pid()
+{
+ struct gcd_pid *g;
+ int mtd_index;
+
+ if (get_pid_list())
+ return 0;
+ mtd_index = get_mtd_index();
+ if (mtd_index == -1)
+ return 0;
+ for (g = gcd_pid_list; g; g = g->next)
+ if (g->mtd_index == mtd_index)
+ return g->pid;
+ return 0;
+}
+
+void gcd_hupper(void)
+{
+ int64_t repeat;
+ int pid;
+
+ pid = get_gcd_pid();
+ CHECK(pid != 0);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ CHECK(kill(pid, SIGHUP) != -1);
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+}
+
+/* Title of this test */
+
+const char *gcd_hupper_get_title(void)
+{
+ return "Send HUP signals to gcd";
+}
+
+/* Description of this test */
+
+const char *gcd_hupper_get_description(void)
+{
+ return
+ "Determine the PID of the gcd process. " \
+ "Send it SIGHUP (may require root privileges). " \
+ "If a sleep value is specified, the process sleeps. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 1. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 1;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, gcd_hupper_get_title(),
+ gcd_hupper_get_description(), "np");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ gcd_hupper();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/pdfrun.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/pdfrun.c
new file mode 100644
index 0000000..3536580
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/pdfrun.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void adjust_size(void)
+{
+ char dummy[1024];
+ unsigned long total_memory;
+ FILE *f;
+
+ total_memory = 0;
+ f = fopen("/proc/meminfo", "r");
+ fscanf(f, "%s %lu", dummy, &total_memory);
+ fclose(f);
+ if (total_memory > 0 && tests_size_parameter > total_memory / 2)
+ tests_size_parameter = total_memory / 2;
+}
+
+void run_pdf(void)
+{
+ int fd, i;
+ pid_t pid;
+ int64_t repeat;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ if (tests_fs_is_currfs())
+ return;
+ adjust_size();
+ pid = getpid();
+ tests_cat_pid(file_name, "run_pdf_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ pid = getpid();
+ srand(pid);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ CHECK(lseek(fd, 0, SEEK_SET) == 0);
+ }
+ CHECK(close(fd) != -1);
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *run_pdf_get_title(void)
+{
+ return "Create / overwrite a large file in the current directory";
+}
+
+/* Description of this test */
+
+const char *run_pdf_get_description(void)
+{
+ return
+ "Create a file named run_pdf_test_file_pid, " \
+ "where pid is the process id. The file is created " \
+ "in the current directory, " \
+ "if the current directory is NOT on the test " \
+ "file system, otherwise no action is taken. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The size is adjusted so that it is not more than " \
+ "half the size of total memory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, run_pdf_get_title(),
+ run_pdf_get_description(), "zn");
+ if (!run_test)
+ return 1;
+ /* Do the actual test */
+ run_pdf();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rmdir00.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rmdir00.c
new file mode 100644
index 0000000..c1d0729
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rmdir00.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+void rmdir00(void)
+{
+ int64_t repeat;
+ int64_t size, this_size;
+ pid_t pid;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "rmdir00_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Repeat loop */
+ repeat = tests_repeat_parameter;
+ size = 0;
+ for (;;) {
+ /* Remove everything in the directory */
+ tests_clear_dir(".");
+ /* Fill with sub-dirs and small files */
+ do {
+ this_size = tests_create_entry(NULL);
+ if (!this_size)
+ break;
+ size += this_size;
+ } while (this_size &&
+ (tests_size_parameter == 0 ||
+ size < tests_size_parameter));
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+ /* Tidy up by removing everything */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *rmdir00_get_title(void)
+{
+ return "Create and remove directories and files";
+}
+
+/* Description of this test */
+
+const char *rmdir00_get_description(void)
+{
+ return
+ "Create a directory named rmdir00_test_dir_pid, where " \
+ "pid is the process id. Within that directory, create " \
+ "a number of sub-directories and small files. " \
+ "The total size of all sub-directories and files " \
+ "is specified by the size parameter. " \
+ "The size parameter is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "A size of zero fills the file system until there is no "
+ "space left. " \
+ "The task repeats, sleeping in between each iteration, " \
+ "and then removing the sub-directories and files created " \
+ "during the last iteration. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rmdir00_get_title(),
+ rmdir00_get_description(), "znp");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rmdir00();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndrm00.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndrm00.c
new file mode 100644
index 0000000..724b1c3
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndrm00.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+void rndrm00(void)
+{
+ int64_t repeat;
+ int64_t size, this_size;
+ pid_t pid;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "rndrm00_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Repeat loop */
+ repeat = tests_repeat_parameter;
+ size = 0;
+ for (;;) {
+ /* Create and remove sub-dirs and small files, */
+ /* but tending to grow */
+ do {
+ if (tests_random_no(3)) {
+ this_size = tests_create_entry(NULL);
+ if (!this_size)
+ break;
+ size += this_size;
+ } else {
+ this_size = tests_remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ if (!this_size)
+ this_size = 1;
+ }
+ } while (this_size &&
+ (tests_size_parameter == 0 ||
+ size < tests_size_parameter));
+ /* Create and remove sub-dirs and small files, but */
+ /* but tending to shrink */
+ do {
+ if (!tests_random_no(3)) {
+ this_size = tests_create_entry(NULL);
+ size += this_size;
+ } else {
+ this_size = tests_remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ }
+ } while ((tests_size_parameter != 0 &&
+ size > tests_size_parameter / 10) ||
+ (tests_size_parameter == 0 && size > 100000));
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+ /* Tidy up by removing everything */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *rndrm00_get_title(void)
+{
+ return "Randomly create and remove directories and files";
+}
+
+/* Description of this test */
+
+const char *rndrm00_get_description(void)
+{
+ return
+ "Create a directory named rndrm00_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "randomly create and remove " \
+ "a number of sub-directories and small files, " \
+ "but do more creates than removes. " \
+ "When the total size of all sub-directories and files " \
+ "is greater than the size specified by the size parameter, " \
+ "start to do more removes than creates. " \
+ "The size parameter is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "A size of zero fills the file system until there is no "
+ "space left. " \
+ "The task repeats, sleeping in between each iteration. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rndrm00_get_title(),
+ rndrm00_get_description(), "znp");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rndrm00();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndrm99.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndrm99.c
new file mode 100644
index 0000000..7751839
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndrm99.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "tests.h"
+
+uint32_t files_created = 0;
+uint32_t files_removed = 0;
+uint32_t dirs_created = 0;
+uint32_t dirs_removed = 0;
+int64_t *size_ptr = 0;
+
+void display_stats(void)
+{
+ printf( "\nrndrm99 stats:\n"
+ "\tNumber of files created = %u\n"
+ "\tNumber of files deleted = %u\n"
+ "\tNumber of directories created = %u\n"
+ "\tNumber of directories deleted = %u\n"
+ "\tCurrent net size of creates and deletes = %lld\n",
+ (unsigned) files_created,
+ (unsigned) files_removed,
+ (unsigned) dirs_created,
+ (unsigned) dirs_removed,
+ (long long) (size_ptr ? *size_ptr : 0));
+ fflush(stdout);
+}
+
+struct timeval tv_before;
+struct timeval tv_after;
+
+void before(void)
+{
+ CHECK(gettimeofday(&tv_before, NULL) != -1);
+}
+
+void after(const char *msg)
+{
+ time_t diff;
+ CHECK(gettimeofday(&tv_after, NULL) != -1);
+ diff = tv_after.tv_sec - tv_before.tv_sec;
+ if (diff >= 8) {
+ printf("\nrndrm99: the following fn took more than 8 seconds: %s (took %u secs)\n",msg,(unsigned) diff);
+ fflush(stdout);
+ display_stats();
+ }
+}
+
+#define WRITE_BUFFER_SIZE 32768
+
+static char write_buffer[WRITE_BUFFER_SIZE];
+
+static void init_write_buffer()
+{
+ static int init = 0;
+
+ if (!init) {
+ int i, d;
+ uint64_t u;
+
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ srand(1);
+ for (i = 0; i < WRITE_BUFFER_SIZE; ++i)
+ write_buffer[i] = rand() / d;
+ init = 1;
+ }
+}
+
+/* Write size random bytes into file descriptor fd at the current position,
+ returning the number of bytes actually written */
+uint64_t fill_file(int fd, uint64_t size)
+{
+ ssize_t written;
+ size_t sz;
+ unsigned start = 0, length;
+ uint64_t remains;
+ uint64_t actual_size = 0;
+
+ init_write_buffer();
+ remains = size;
+ while (remains > 0) {
+ length = WRITE_BUFFER_SIZE - start;
+ if (remains > length)
+ sz = length;
+ else
+ sz = (size_t) remains;
+ before();
+ written = write(fd, write_buffer + start, sz);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ after("write");
+ fprintf(stderr,"\nrndrm99: write failed with ENOSPC\n");fflush(stderr);
+ display_stats();
+ break;
+ }
+ after("write");
+ remains -= written;
+ actual_size += written;
+ if ((size_t) written == sz)
+ start = 0;
+ else
+ start += written;
+ }
+ return actual_size;
+}
+
+/* Create a file of size file_size */
+uint64_t create_file(const char *file_name, uint64_t file_size)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ uint64_t actual_size; /* Less than size if the file system is full */
+
+ flags = O_CREAT | O_TRUNC | O_WRONLY;
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ before();
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ after("open");
+ fprintf(stderr,"\nrndrm99: open failed with ENOSPC\n");fflush(stderr);
+ display_stats();
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ after("open");
+ actual_size = fill_file(fd, file_size);
+ before();
+ CHECK(close(fd) != -1);
+ after("close");
+ if (file_size != 0 && actual_size == 0) {
+ printf("\nrndrm99: unlinking zero size file\n");fflush(stdout);
+ before();
+ CHECK(unlink(file_name) != -1);
+ after("unlink (create_file)");
+ }
+ return actual_size;
+}
+
+/* Create an empty sub-directory or small file in the current directory */
+int64_t create_entry(char *return_name)
+{
+ int fd;
+ char name[256];
+ int64_t res;
+
+ for (;;) {
+ sprintf(name, "%u", (unsigned) tests_random_no(10000000));
+ before();
+ fd = open(name, O_RDONLY);
+ after("open (create_entry)");
+ if (fd == -1)
+ break;
+ before();
+ close(fd);
+ after("close (create_entry)");
+ }
+ if (return_name)
+ strcpy(return_name, name);
+ if (tests_random_no(2)) {
+ res = create_file(name, tests_random_no(4096));
+ if (res > 0)
+ files_created += 1;
+ return res;
+ } else {
+ before();
+ if (mkdir(name, 0777) == -1) {
+ CHECK(errno == ENOSPC);
+ after("mkdir");
+ errno = 0;
+ fprintf(stderr,"\nrndrm99: mkdir failed with ENOSPC\n");fflush(stderr);
+ display_stats();
+ return 0;
+ }
+ after("mkdir");
+ dirs_created += 1;
+ return TESTS_EMPTY_DIR_SIZE;
+ }
+}
+
+/* Remove a random file of empty sub-directory from the current directory */
+int64_t remove_entry(void)
+{
+ DIR *dir;
+ struct dirent *entry;
+ unsigned count = 0, pos;
+ int64_t result = 0;
+
+ before();
+ dir = opendir(".");
+ CHECK(dir != NULL);
+ after("opendir");
+ for (;;) {
+ errno = 0;
+ before();
+ entry = readdir(dir);
+ if (entry) {
+ after("readdir 1");
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ ++count;
+ } else {
+ CHECK(errno == 0);
+ after("readdir 1");
+ break;
+ }
+ }
+ pos = tests_random_no(count);
+ count = 0;
+ before();
+ rewinddir(dir);
+ after("rewinddir");
+ for (;;) {
+ errno = 0;
+ before();
+ entry = readdir(dir);
+ if (!entry) {
+ CHECK(errno == 0);
+ after("readdir 2");
+ break;
+ }
+ after("readdir 2");
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (count == pos) {
+ if (entry->d_type == DT_DIR) {
+ before();
+ tests_clear_dir(entry->d_name);
+ after("tests_clear_dir");
+ before();
+ CHECK(rmdir(entry->d_name) != -1);
+ after("rmdir");
+ result = TESTS_EMPTY_DIR_SIZE;
+ dirs_removed += 1;
+ } else {
+ struct stat st;
+ before();
+ CHECK(stat(entry->d_name, &st) != -1);
+ after("stat");
+ result = st.st_size;
+ before();
+ CHECK(unlink(entry->d_name) != -1);
+ after("unlink");
+ files_removed += 1;
+ }
+ }
+ ++count;
+ }
+ }
+ before();
+ CHECK(closedir(dir) != -1);
+ after("closedir");
+ return result;
+}
+
+void rndrm99(void)
+{
+ int64_t repeat, loop_cnt;
+ int64_t size, this_size;
+ pid_t pid;
+ char dir_name[256];
+
+ size_ptr = &size;
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "rndrm99_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Repeat loop */
+ repeat = tests_repeat_parameter;
+ size = 0;
+ for (;;) {
+ /* Create and remove sub-dirs and small files, */
+ /* but tending to grow */
+ printf("\nrndrm99: growing\n");fflush(stdout);
+ loop_cnt = 0;
+ do {
+ if (loop_cnt++ % 2000 == 0)
+ display_stats();
+ if (tests_random_no(3)) {
+ this_size = create_entry(NULL);
+ if (!this_size)
+ break;
+ size += this_size;
+ } else {
+ this_size = remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ if (!this_size)
+ this_size = 1;
+ }
+ } while (this_size &&
+ (tests_size_parameter == 0 ||
+ size < tests_size_parameter));
+ /* Create and remove sub-dirs and small files, but */
+ /* but tending to shrink */
+ printf("\nrndrm99: shrinking\n");fflush(stdout);
+ loop_cnt = 0;
+ do {
+ if (loop_cnt++ % 2000 == 0)
+ display_stats();
+ if (!tests_random_no(3)) {
+ this_size = create_entry(NULL);
+ size += this_size;
+ } else {
+ this_size = remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ }
+ } while ((tests_size_parameter != 0 &&
+ size > tests_size_parameter / 10) ||
+ (tests_size_parameter == 0 && size > 100000));
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ printf("\nrndrm99: sleeping\n");fflush(stdout);
+ usleep(s);
+ }
+ }
+ printf("\nrndrm99: tidying\n");fflush(stdout);
+ display_stats();
+ /* Tidy up by removing everything */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+ size_ptr = 0;
+}
+
+/* Title of this test */
+
+const char *rndrm99_get_title(void)
+{
+ return "Randomly create and remove directories and files";
+}
+
+/* Description of this test */
+
+const char *rndrm99_get_description(void)
+{
+ return
+ "Create a directory named rndrm99_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "randomly create and remove " \
+ "a number of sub-directories and small files, " \
+ "but do more creates than removes. " \
+ "When the total size of all sub-directories and files " \
+ "is greater than the size specified by the size parameter, " \
+ "start to do more removes than creates. " \
+ "The size parameter is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "A size of zero fills the file system until there is no "
+ "space left. " \
+ "The task repeats, sleeping in between each iteration. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rndrm99_get_title(),
+ rndrm99_get_description(), "znp");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rndrm99();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndwrite00.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndwrite00.c
new file mode 100644
index 0000000..655d9cc
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/rndwrite00.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "tests.h"
+
+#define BLOCK_SIZE 32768
+#define BUFFER_SIZE 32768
+
+static void check_file(int fd, char *data, size_t length)
+{
+ size_t n, i;
+ char buf[BUFFER_SIZE];
+
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ n = 0;
+ for (;;) {
+ i = read(fd, buf, BUFFER_SIZE);
+ CHECK(i >= 0);
+ if (i == 0)
+ break;
+ CHECK(memcmp(buf, data + n, i) == 0);
+ n += i;
+ }
+ CHECK(n == length);
+}
+
+void rndwrite00(void)
+{
+ int fd;
+ pid_t pid;
+ ssize_t written;
+ size_t remains;
+ size_t block;
+ size_t actual_size;
+ size_t check_every;
+ char *data, *p, *q;
+ off_t offset;
+ size_t size;
+ int64_t repeat;
+ char file_name[256];
+ char buf[4096];
+
+ /* Create file */
+ pid = getpid();
+ tests_cat_pid(file_name, "rndwrite00_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ /* Allocate memory to hold file data */
+ CHECK(tests_size_parameter > 0);
+ CHECK(tests_size_parameter <= SIZE_MAX);
+ data = (char *) malloc(tests_size_parameter);
+ CHECK(data != NULL);
+ /* Fill with random data */
+ srand(pid);
+ for (p = data, q = data + tests_size_parameter; p != q; ++p)
+ *p = rand();
+ /* Write to file */
+ p = data;
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > BLOCK_SIZE)
+ block = BLOCK_SIZE;
+ else
+ block = remains;
+ written = write(fd, p, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ p += written;
+ }
+ actual_size = p - data;
+ /* Repeating bit */
+ repeat = tests_repeat_parameter;
+ check_every = actual_size / 8192;
+ for (;;) {
+ offset = tests_random_no(actual_size);
+ size = tests_random_no(4096);
+ /* Don't change the file size */
+ if (offset + size > actual_size)
+ size = actual_size - offset;
+ if (!size)
+ continue;
+ for (p = buf, q = p + size; p != q; ++p)
+ *p = rand();
+ CHECK(lseek(fd, offset, SEEK_SET) != -1);
+ written = write(fd, buf, size);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ } else
+ memcpy(data + offset, buf, written);
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ if (repeat % check_every == 0)
+ check_file(fd, data, actual_size);
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+ /* Check and close file */
+ check_file(fd, data, actual_size);
+ CHECK(close(fd) != -1);
+ if (tests_delete_flag)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *rndwrite00_get_title(void)
+{
+ return "Randomly write a large test file";
+}
+
+/* Description of this test */
+
+const char *rndwrite00_get_description(void)
+{
+ return
+ "Create a file named rndwrite00_test_file_pid, where " \
+ "pid is the process id. " \
+ "The file is filled with random data. " \
+ "The size of the file is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "Then a randomly sized block of random data is written at a " \
+ "random location in the file. "\
+ "The block size is always in the range 1 to 4095. " \
+ "If a sleep value is specified, the process sleeps. " \
+ "The number of writes is given by the repeat count. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 10000. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds. " \
+ "Periodically the data in the file is checked with a copy " \
+ "held in memory. " \
+ "If the delete option is specified the file is finally " \
+ "deleted.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 10000;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rndwrite00_get_title(),
+ rndwrite00_get_description(), "zne");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rndwrite00();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_1.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_1.c
new file mode 100644
index 0000000..86f94c2
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_1.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void stress_1(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ pid = getpid();
+ tests_cat_pid(file_name, "stress_1_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ srand(pid);
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ CHECK(close(fd) != -1);
+ if (tests_delete_flag)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *stress_1_get_title(void)
+{
+ return "Create / overwrite a large file";
+}
+
+/* Description of this test */
+
+const char *stress_1_get_description(void)
+{
+ return
+ "Create a file named stress_1_test_file_pid, " \
+ "where pid is the process id. " \
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The file will be deleted if the delete option " \
+ "is specified. ";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, stress_1_get_title(),
+ stress_1_get_description(), "ze");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ stress_1();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_2.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_2.c
new file mode 100644
index 0000000..a9bc31a
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_2.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void stress_2(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ int64_t repeat;
+ size_t block;
+ char *file_name;
+ char buf[WRITE_BUFFER_SIZE];
+
+ file_name = "stress_2_test_file";
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ CHECK(unlink(file_name) != -1);
+ pid = getpid();
+ srand(pid);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ }
+ CHECK(close(fd) != -1);
+}
+
+/* Title of this test */
+
+const char *stress_2_get_title(void)
+{
+ return "Create / overwrite a large deleted file";
+}
+
+/* Description of this test */
+
+const char *stress_2_get_description(void)
+{
+ return
+ "Create a file named stress_2_test_file. " \
+ "Open it, delete it while holding the open file descriptor, " \
+ "then fill it with random data. " \
+ "Repeated re-write the file some number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 10. " \
+ "The file size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 10;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, stress_2_get_title(),
+ stress_2_get_description(), "zn");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ stress_2();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_3.c b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_3.c
new file mode 100644
index 0000000..99fb05d
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/atoms/stress_3.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void stress_3(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ pid = getpid();
+ tests_cat_pid(file_name, "stress_3_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ pid = getpid();
+ srand(pid);
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1);
+ CHECK(write(fd, "!", 1) == 1);
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ if (ftruncate(fd, 0) == -1) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ }
+ CHECK(close(fd) != -1);
+ if (tests_delete_flag)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *stress_3_get_title(void)
+{
+ return "Create a file with a large hole and fill it";
+}
+
+/* Description of this test */
+
+const char *stress_3_get_description(void)
+{
+ return
+ "Create a file named stress_3_test_file_pid, " \
+ "where pid is the process id. " \
+ "Write a single character past the end of the file, " \
+ "based on the specified file size, " \
+ "which creates a hole in the file. "
+ "Fill the hole with random data. " \
+ "Then truncate the file length to zero. " \
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The file will be deleted if the delete option " \
+ "is specified.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, stress_3_get_title(),
+ stress_3_get_description(), "ze");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ stress_3();
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/stress00.sh b/mtd-utils-1.3.1/tests/fs-tests/stress/stress00.sh
new file mode 100755
index 0000000..60f8c0d
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/stress00.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR
+if test -z "$TEST_DIR";
+then
+ TEST_DIR="/mnt/test_file_system"
+fi
+
+FREESPACE=`../utils/free_space "$TEST_DIR"`
+
+if test -z "$FREESPACE";
+then
+ echo "Failed to determine free space"
+ exit 1
+fi
+
+if test -n "$1";
+then
+ DURATION="-d$1";
+else
+ DURATION="";
+fi
+
+FWRITE00=atoms/fwrite00
+RNDWR=atoms/rndwrite00
+GCHUP=atoms/gcd_hupper
+PDFLUSH=atoms/pdfrun
+FSIZE=$(( $FREESPACE/15 ));
+
+../utils/fstest_monitor $DURATION \
+"$FWRITE00 -z $FSIZE -n0 -p 20" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -s" \
+"$FWRITE00 -z $FSIZE -n0 -p 20 -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 70 -o" \
+"$FWRITE00 -z $FSIZE -n0 -p 15 -s -o -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -u -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -u -o -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -o -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o -u -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o" \
+"$RNDWR -z $FSIZE -n0 -p 10 -e" \
+"$RNDWR -z $FSIZE -n0 -p 100 -e" \
+"$PDFLUSH -z 1073741824 -n0"
+
+STATUS=$?
+
+rm -rf ${TEST_DIR}/*
+
+exit $STATUS
diff --git a/mtd-utils-1.3.1/tests/fs-tests/stress/stress01.sh b/mtd-utils-1.3.1/tests/fs-tests/stress/stress01.sh
new file mode 100755
index 0000000..5913c1c
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/stress/stress01.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR
+if test -z "$TEST_DIR";
+then
+ TEST_DIR="/mnt/test_file_system"
+fi
+
+FREESPACE=`../utils/free_space "$TEST_DIR"`
+
+if test -z "$FREESPACE";
+then
+ echo "Failed to determine free space"
+ exit 1
+fi
+
+if test -n "$1";
+then
+ DURATION="-d$1";
+else
+ DURATION="";
+fi
+
+FWRITE00=atoms/fwrite00
+RNDWR=atoms/rndwrite00
+PDFLUSH=atoms/pdfrun
+FSIZE=$(( $FREESPACE/15 ));
+
+../utils/fstest_monitor $DURATION \
+"$FWRITE00 -z $FSIZE -n0 -p 300" \
+"$FWRITE00 -z $FSIZE -n0 -u" \
+"$FWRITE00 -z $FSIZE -n0 -u -c" \
+"$FWRITE00 -z $FSIZE -n0 -s -o" \
+"$RNDWR -z $FSIZE -n0 -e"
+
+STATUS=$?
+
+rm -rf ${TEST_DIR}/*
+
+exit $STATUS
diff --git a/mtd-utils-1.3.1/tests/fs-tests/utils/Makefile b/mtd-utils-1.3.1/tests/fs-tests/utils/Makefile
new file mode 100644
index 0000000..9fb60b5
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/utils/Makefile
@@ -0,0 +1,19 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = fstest_monitor free_space
+
+all: $(TARGETS)
+
+clean:
+ rm -f *.o $(TARGETS)
+
+tests: all
+ ./fstest_monitor
+ ./free_space > /dev/null
diff --git a/mtd-utils-1.3.1/tests/fs-tests/utils/free_space.c b/mtd-utils-1.3.1/tests/fs-tests/utils/free_space.c
new file mode 100644
index 0000000..88036aa
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/utils/free_space.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/statvfs.h>
+
+int main(int argc, char *argv[])
+{
+ char *dir_name = ".";
+ uint64_t free_space;
+ struct statvfs fs_info;
+
+ if (argc > 1) {
+ if (strncmp(argv[1], "--help", 6) == 0 ||
+ strncmp(argv[1], "-h", 2) == 0) {
+ printf( "Usage is: "
+ "free_space [directory]\n"
+ "\n"
+ "Display the free space of the file system "
+ "of the directory given\n"
+ "or the current directory if no "
+ "directory is given.\nThe value output is "
+ "in bytes.\n"
+ );
+ return 1;
+ }
+ dir_name = argv[1];
+ }
+ if (statvfs(dir_name, &fs_info) == -1)
+ return 1;
+
+ free_space = (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize;
+
+ printf("%llu\n", (unsigned long long) free_space);
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/fs-tests/utils/fstest_monitor.c b/mtd-utils-1.3.1/tests/fs-tests/utils/fstest_monitor.c
new file mode 100644
index 0000000..298ee26
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/fs-tests/utils/fstest_monitor.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 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
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+struct child_info {
+ struct child_info *next;
+ pid_t pid;
+ int terminated;
+ int killed;
+ int gone;
+};
+
+struct child_info *children = 0;
+
+void kill_children(void)
+{
+ struct child_info *child;
+
+ child = children;
+ while (child) {
+ if (!child->gone) {
+ if (!child->terminated) {
+ child->terminated = 1;
+ kill(child->pid, SIGTERM);
+ } /*else if (!child->killed) {
+ child->killed = 1;
+ kill(child->pid, SIGKILL);
+ }*/
+ }
+ child = child->next;
+ }
+}
+
+void add_child(pid_t child_pid)
+{
+ struct child_info *child;
+ size_t sz;
+
+ sz = sizeof(struct child_info);
+ child = (struct child_info *) malloc(sz);
+ memset(child, 0, sz);
+ child->pid = child_pid;
+ child->next = children;
+ children = child;
+}
+
+void mark_child_gone(pid_t child_pid)
+{
+ struct child_info *child;
+
+ child = children;
+ while (child) {
+ if (child->pid == child_pid) {
+ child->gone = 1;
+ break;
+ }
+ child = child->next;
+ }
+}
+
+int have_children(void)
+{
+ struct child_info *child;
+
+ child = children;
+ while (child) {
+ if (!child->gone)
+ return 1;
+ child = child->next;
+ }
+ return 0;
+}
+
+int parse_command_line(char *cmdline, int *pargc, char ***pargv)
+{
+ char **tmp;
+ char *p, *v, *q;
+ size_t sz;
+ int argc = 0;
+ int state = 0;
+ char *argv[1024];
+
+ if (!cmdline)
+ return 1;
+ q = v = (char *) malloc(strlen(cmdline) + 1024);
+ if (!v)
+ return 1;
+ p = cmdline;
+ for (;;) {
+ char c = *p++;
+ if (!c) {
+ *v++ = 0;
+ break;
+ }
+ switch (state) {
+ case 0: /* Between args */
+ if (isspace(c))
+ break;
+ argv[argc++] = v;
+ if (c == '"') {
+ state = 2;
+ break;
+ } else if (c == '\'') {
+ state = 3;
+ break;
+ }
+ state = 1;
+ case 1: /* Not quoted */
+ if (c == '\\') {
+ if (*p)
+ *v++ = *p;
+ } else if (isspace(c)) {
+ *v++ = 0;
+ state = 0;
+ } else
+ *v++ = c;
+ break;
+ case 2: /* Double quoted */
+ if (c == '\\' && *p == '"') {
+ *v++ = '"';
+ ++p;
+ } else if (c == '"') {
+ *v++ = 0;
+ state = 0;
+ } else
+ *v++ = c;
+ break;
+ case 3: /* Single quoted */
+ if (c == '\'') {
+ *v++ = 0;
+ state = 0;
+ } else
+ *v++ = c;
+ break;
+ }
+ }
+ argv[argc] = 0;
+ sz = sizeof(char *) * (argc + 1);
+ tmp = (char **) malloc(sz);
+ if (!tmp) {
+ free(q);
+ return 1;
+ }
+ if (argc == 0)
+ free(q);
+ memcpy(tmp, argv, sz);
+ *pargc = argc;
+ *pargv = tmp;
+ return 0;
+}
+
+void signal_handler(int signum)
+{
+ kill_children();
+}
+
+int result = 0;
+int alarm_gone_off = 0;
+
+void alarm_handler(int signum)
+{
+ if (!result)
+ alarm_gone_off = 1;
+ kill_children();
+}
+
+int main(int argc, char *argv[], char **env)
+{
+ int p;
+ pid_t child_pid;
+ int status;
+ int duration = 0;
+
+ p = 1;
+ if (argc > 1) {
+ if (strncmp(argv[p], "--help", 6) == 0 ||
+ strncmp(argv[p], "-h", 2) == 0) {
+ printf( "Usage is: "
+ "fstest_monitor options programs...\n"
+ " Options are:\n"
+ " -h, --help "
+ "This help message\n"
+ " -d, --duration arg "
+ "Stop after arg seconds\n"
+ "\n"
+ "Run programs and wait for them."
+ " If duration is specified,\n"
+ "kill all programs"
+ " after that number of seconds have elapsed.\n"
+ "Example: "
+ "fstest_monitor \"/bin/ls -l\" /bin/date\n"
+ );
+ return 1;
+ }
+ if (strncmp(argv[p], "--duration", 10) == 0 ||
+ strncmp(argv[p], "-d", 2) == 0) {
+ char *s;
+ if (p+1 < argc && !isdigit(argv[p][strlen(argv[p])-1]))
+ ++p;
+ s = argv[p];
+ while (*s && !isdigit(*s))
+ ++s;
+ duration = atoi(s);
+ ++p;
+ }
+ }
+
+ signal(SIGTERM, signal_handler);
+ signal(SIGINT, signal_handler);
+ for (; p < argc; ++p) {
+ child_pid = fork();
+ if (child_pid) {
+ /* Parent */
+ if (child_pid == (pid_t) -1) {
+ kill_children();
+ result = 1;
+ break;
+ }
+ add_child(child_pid);
+ } else {
+ /* Child */
+ int cargc;
+ char **cargv;
+
+ if (parse_command_line(argv[p], &cargc, &cargv))
+ return 1;
+ execve(cargv[0], cargv, env);
+ return 1;
+ }
+ }
+ if (!result && duration > 0) {
+ signal(SIGALRM, alarm_handler);
+ alarm(duration);
+ }
+ while (have_children()) {
+ status = 0;
+ child_pid = wait(&status);
+ if (child_pid == (pid_t) -1) {
+ if (errno == EINTR)
+ continue;
+ kill_children();
+ return 1;
+ }
+ mark_child_gone(child_pid);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ result = 1;
+ kill_children();
+ }
+ }
+
+ if (alarm_gone_off)
+ return 0;
+
+ return result;
+}
diff --git a/mtd-utils-1.3.1/tests/jittertest/COPYING b/mtd-utils-1.3.1/tests/jittertest/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/jittertest/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/mtd-utils-1.3.1/tests/jittertest/JitterTest.c b/mtd-utils-1.3.1/tests/jittertest/JitterTest.c
new file mode 100644
index 0000000..13c64d9
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/jittertest/JitterTest.c
@@ -0,0 +1,1044 @@
+/***********************************************************************
+ *
+ * Copyright: Daniel Measurement and Control, Inc.
+ * 9753 Pine Lake Drive
+ * Houston, TX 77055
+ *
+ * Created by: Vipin Malik and Gail Murray
+ * Released under GPL by permission of Daniel Industries.
+ *
+ * This software is licensed under the GPL version 2. Plese see the file
+ * COPYING for details on the license.
+ *
+ * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose
+ * are made in this software. Please use at your own risk.
+ *
+ * Filename: JitterTest.c
+ *
+ * Description: Program to be used to measure wake jitter.
+ * See README file for more info.
+ *
+ *
+ * Revision History:
+ * $Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $
+ * $Log: JitterTest.c,v $
+ * Revision 1.13 2005/11/07 11:15:20 gleixner
+ * [MTD / JFFS2] Clean up trailing white spaces
+ *
+ * Revision 1.12 2001/08/10 19:23:11 vipin
+ * Ready to be released under GPL! Added proper headers etc.
+ *
+ * Revision 1.11 2001/07/09 15:35:50 vipin
+ * Couple of new features:1. The program runs by default as a "regular"
+ * (SCHED_OTHER) task by default, unless the -p n cmd line parameter is
+ * specified. It then runs as SCHED_RR at that priority.
+ * 2. Added ability to send SIGSTOP/SIGCONT to a specified PID. This
+ * would presumably be the PID of the JFFS2 GC task. SIGSTOP is sent
+ * before writing to the fs, and a SIGCONT after the write is done.
+ * 3. The "pad" data now written to the file on the "fs under test" is
+ * random, not sequential as previously.
+ *
+ * Revision 1.10 2001/06/27 19:14:24 vipin
+ * Now console file to log at can be specified from cmd line. This can enable
+ * you to run two instances of the program- one logging to the /dev/console
+ * and another to a regular file (if you want the data) or /dev/null if you don't.
+ *
+ * Revision 1.9 2001/06/25 20:21:31 vipin
+ * This is the latest version, NOT the one last checked in!
+ *
+ * Revision 1.7 2001/06/18 22:36:19 vipin
+ * Fix minor typo that excluded '\n' from msg on console.
+ *
+ * Revision 1.6 2001/06/18 21:17:50 vipin
+ * Added option to specify the amount of data written to outfile each period.
+ * The regular info is written, but is then padded to the requested size.
+ * This will enable testing of different sized writes to JFFS fs.
+ *
+ * Revision 1.5 2001/06/08 19:36:23 vipin
+ * All write() are now checked for return err code.
+ *
+ * Revision 1.4 2001/06/06 23:10:31 vipin
+ * Added README file.
+ * In JitterTest.c: Changed operation of periodic timer to one shot. The timer is now
+ * reset when the task wakes. This way every "jitter" is from the last time and
+ * jitters from previous times are not accumulated and screw aroud with our display.
+ *
+ * All jitter is now +ve. (as it should be). Also added a "read file" functionality
+ * to test for jitter in task if we have to read from JFFS fs.
+ * The program now also prints data to console- where it can be logged, interspersed with
+ * other "interesting" printk's from the kernel and drivers (flash sector erases etc.)
+ *
+ * Revision 1.3 2001/03/01 19:20:39 gmurray
+ * Add priority scheduling. Shortened name of default output file.
+ * Changed default interrupt period. Output delta time and jitter
+ * instead of time of day.
+ *
+ * Revision 1.2 2001/02/28 16:20:19 vipin
+ * Added version control Id and log fields.
+ *
+ ***********************************************************************/
+/*************************** Included Files ***************************/
+#include <stdio.h> /* fopen, printf, fprintf, fclose */
+#include <string.h> /* strcpy, strcmp */
+#include <stdlib.h> /* exit, atol, atoi */
+#include <sys/time.h> /* setitimer, settimeofday, gettimeofday */
+#include <signal.h> /* signal */
+#include <sched.h> /* sched_setscheduler, sched_get_priority_min,*/
+/* sched_get_priority_max */
+#include <unistd.h> /* gettimeofday, sleep */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+
+
+/**************************** Enumerations ****************************/
+enum timerActions
+ {
+ ONESHOT,
+ AUTORESETTING
+ };
+
+
+
+/****************************** Constants *****************************/
+/* Exit error codes */
+#define EXIT_FILE_OPEN_ERR (1) /* error opening output file*/
+#define EXIT_REG_SIGALRM_ERR (2) /* error registering SIGALRM*/
+#define EXIT_REG_SIGINT_ERR (3) /* error registering SIGINT */
+#define EXIT_INV_INT_PERIOD (4) /* error invalid int. period*/
+#define EXIT_MIN_PRIORITY_ERR (5) /* error, minimum priority */
+#define EXIT_MAX_PRIORITY_ERR (6) /* error, maximum priority */
+#define EXIT_INV_SCHED_PRIORITY (7) /* error, invalid priority */
+#define EXIT_SET_SCHEDULER_ERR (8) /* error, set scheduler */
+#define EXIT_PREV_TIME_OF_DAY_ERR (9) /* error, init. prev. */
+/* time of day */
+
+#define MAX_FILE_NAME_LEN (32) /* maximum file name length */
+
+#define STRINGS_EQUAL ((int) 0) /* strcmp value if equal */
+
+#define MIN_INT_PERIOD_MILLISEC ( 5L) /* minimum interrupt period */
+#define MAX_INT_PERIOD_MILLISEC (5000L) /* maximum interrupt period */
+#define DEF_INT_PERIOD_MILLISEC ( 10L) /* default interrupt period */
+
+#define READ_FILE_MESSAGE "This is a junk file. Must contain at least 1 byte though!\n"
+
+/* The user can specify that the program pad out the write file to
+ a given number of bytes. But a minimum number needs to be written. This
+ will contain the jitter info.
+*/
+#define MIN_WRITE_BYTES 30
+#define DEFAULT_WRITE_BYTES 30
+#define MAX_WRITE_BYTES 4096
+
+/* used for gen a printable ascii random # between spc and '~' */
+#define MIN_ASCII 32 /* <SPACE> can be char*/
+#define MAX_ASCII 126.0 /* needs to be float. See man rand() */
+
+/*----------------------------------------------------------------------
+ * It appears that the preprocessor can't handle multi-line #if
+ * statements. Thus, the check on the default is divided into two
+ * #if statements.
+ *---------------------------------------------------------------------*/
+#if (DEF_INT_PERIOD_MILLISEC < MIN_INT_PERIOD_MILLISEC)
+#error *** Invalid default interrupt period. ***
+#endif
+
+#if (DEF_INT_PERIOD_MILLISEC > MAX_INT_PERIOD_MILLISEC)
+#error *** Invalid default interrupt period. ***
+#endif
+
+
+#define TRUE 1 /* Boolean true value */
+#define FALSE 0
+
+/* Time conversion constants. */
+#define MILLISEC_PER_SEC (1000L) /* milliseconds per second */
+#define MICROSEC_PER_MILLISEC (1000L) /* microsecs per millisec */
+#define MICROSEC_PER_SEC (1000000L) /* microsecs per second */
+
+#define PRIORITY_POLICY ((int) SCHED_RR) /* If specified iwth "-p" */
+
+
+
+/************************** Module Variables **************************/
+/* version identifier (value supplied by CVS)*/
+static const char Version[] = "$Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $";
+
+static char OutFileName[MAX_FILE_NAME_LEN+1]; /* output file name */
+static char LogFile[MAX_FILE_NAME_LEN+1] = "/dev/console"; /* default */
+static char ReadFile[MAX_FILE_NAME_LEN+1]; /* This file is read. Should
+ contain at least 1 byte */
+
+static int WriteBytes = DEFAULT_WRITE_BYTES; /* pad out file to these many bytes. */
+static int Fd1; /* fd where the above buffer if o/p */
+static int Cfd; /* fd to console (or file specified) */
+static int Fd2; /* fd for the ReadFile */
+static int DoRead = FALSE; /* should we attempt to ReadFile?*/
+static long InterruptPeriodMilliSec; /* interrupt period, millisec */
+static int MinPriority; /* minimum scheduler priority */
+static int MaxPriority; /* maximum scheduler priority */
+static int RequestedPriority; /* requested priority */
+static struct itimerval ITimer; /* interrupt timer structure */
+static struct timeval PrevTimeVal; /* previous time structure */
+static struct timeval CurrTimeVal; /* current time structure */
+static long LastMaxDiff = 0; /* keeps track of worst jitter encountered */
+
+static int GrabKProfile = FALSE; /* To help determine system bottle necks
+ this parameter can be set. This causes
+ the /proc/profile file to be read and
+ stored in unique filenames in current
+ dir, and indication to be o/p on the
+ /dev/console also.
+ */
+static long ProfileTriggerMSecs = 15000l; /* Jitter time in seconds that triggers
+ a snapshot of the profile to be taken
+
+ */
+static int SignalGCTask = FALSE; /* should be signal SIGSTOP/SIGCONT to gc task?*/
+static int GCTaskPID;
+
+static int RunAsRTTask = FALSE; /* default action unless priority is
+ specified on cmd line */
+
+
+/********************* Local Function Prototypes **********************/
+void HandleCmdLineArgs(int argc, char *argv[]);
+void SetFileName(char * pFileName);
+void SetInterruptPeriod(char * pASCIIInterruptPeriodMilliSec);
+void SetSchedulerPriority(char * pASCIISchedulerPriority);
+
+void PrintVersionInfo(void);
+void PrintHelpInfo(void);
+
+int Write(int fd, void *buf, size_t bytes, int lineNo);
+
+void InitITimer(struct itimerval * pITimer, int action);
+
+/* For catching timer interrupts (SIGALRM). */
+void AlarmHandler(int sigNum);
+
+/* For catching Ctrl+C SIGINT. */
+void SignalHandler(int sigNum);
+
+
+
+/***********************************************************************
+ * main function
+ * return: exit code
+ ***********************************************************************/
+int main(
+ int argc,
+ char *argv[])
+{
+ struct sched_param schedParam;
+
+ int mypri;
+ char tmpBuf[200];
+
+
+ strcpy(OutFileName,"jitter.dat");
+ InterruptPeriodMilliSec = MIN_INT_PERIOD_MILLISEC;
+
+ /* Get the minimum and maximum priorities. */
+ MinPriority = sched_get_priority_min(PRIORITY_POLICY);
+ MaxPriority = sched_get_priority_max(PRIORITY_POLICY);
+ if (MinPriority == -1) {
+ printf("\n*** Unable to get minimum priority. ***\n");
+ exit(EXIT_MIN_PRIORITY_ERR);
+ }
+ if (MaxPriority == -1) {
+ printf("\n*** Unable to get maximum priority. ***\n");
+ exit(EXIT_MAX_PRIORITY_ERR);
+ }
+
+ /* Set requested priority to minimum value as default. */
+ RequestedPriority = MinPriority;
+
+ HandleCmdLineArgs(argc, argv);
+
+ if(mlockall(MCL_CURRENT|MCL_FUTURE) < 0){
+ printf("Could not lock task into memory. Bye\n");
+ perror("Error");
+ }
+
+ if(RunAsRTTask){
+
+ /* Set the priority. */
+ schedParam.sched_priority = RequestedPriority;
+ if (sched_setscheduler(
+ 0,
+ PRIORITY_POLICY,
+ &schedParam) != (int) 0) {
+ printf("Exiting: Unsuccessful sched_setscheduler.\n");
+ close(Fd1);
+ exit(EXIT_SET_SCHEDULER_ERR);
+ }
+
+
+ /* Double check as I have some doubts that it's really
+ running at realtime priority! */
+ if((mypri = sched_getscheduler(0)) != RequestedPriority)
+ {
+ printf("Not running with request priority %i. running with priority %i instead!\n",
+ RequestedPriority, mypri);
+ }else
+ {
+ printf("Running with %i priority. Good!\n", mypri);
+ }
+
+ }
+
+ /*------------------------- Initializations --------------------------*/
+ if((Fd1 = open(OutFileName, O_RDWR|O_CREAT|O_SYNC)) <= 0)
+ {
+ perror("Cannot open outfile for write:");
+ exit(1);
+ }
+
+ /* If a request is made to read from a specified file, then create that
+ file and fill with junk data so that there is something there to read.
+ */
+ if(DoRead)
+ {
+
+ if((Fd2 = open(ReadFile, O_RDWR|O_CREAT|O_SYNC|O_TRUNC)) <= 0)
+ {
+ perror("cannot open read file:");
+ exit(1);
+ }else
+ {
+
+ /* Don't really care how much we write here */
+ if(write(Fd2, READ_FILE_MESSAGE, strlen(READ_FILE_MESSAGE)) < 0)
+ {
+ perror("Problems writing to readfile:");
+ exit(1);
+ }
+ lseek(Fd2, 0, SEEK_SET); /* position back to byte #0 */
+ }
+ }
+
+
+
+ /* Also log output to console. This way we can capture it
+ on a serial console to a log file.
+ */
+ if((Cfd = open(LogFile, O_WRONLY|O_SYNC)) <= 0)
+ {
+ perror("cannot open o/p logfile:");
+ exit(1);
+ }
+
+
+ /* Set-up handler for periodic interrupt. */
+ if (signal(SIGALRM, &AlarmHandler) == SIG_ERR) {
+ printf("Couldn't register signal handler for SIGALRM.\n");
+ sprintf(tmpBuf,
+ "Couldn't register signal handler for SIGALRM.\n");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ close(Fd1);
+ exit(EXIT_REG_SIGALRM_ERR);
+ }
+
+ /* Set-up handler for Ctrl+C to exit the program. */
+ if (signal(SIGINT, &SignalHandler) == SIG_ERR) {
+ printf("Couldn't register signal handler for SIGINT.\n");
+ sprintf(tmpBuf,
+ "Couldn't register signal handler for SIGINT.\n");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ close(Fd1);
+ exit(EXIT_REG_SIGINT_ERR);
+ }
+
+ printf("Press Ctrl+C to exit the program.\n");
+ printf("Output File: %s\n", OutFileName);
+ printf("Scheduler priority: %d\n", RequestedPriority);
+ sprintf(tmpBuf, "\nScheduler priority: %d\n",
+ RequestedPriority);
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ printf("Interrupt period: %ld milliseconds\n",
+ InterruptPeriodMilliSec);
+ sprintf(tmpBuf, "Interrupt period: %ld milliseconds\n",
+ InterruptPeriodMilliSec);
+
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+
+ fflush(0);
+
+
+
+ /* Initialize the periodic timer. */
+ InitITimer(&ITimer, ONESHOT);
+
+ /* Initialize "previous" time. */
+ if (gettimeofday(&PrevTimeVal, NULL) != (int) 0) {
+ printf("Exiting - ");
+ printf("Unable to initialize previous time of day.\n");
+ sprintf(tmpBuf, "Exiting - ");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ sprintf(tmpBuf,
+ "Unable to initialize previous time of day.\n");
+
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ }
+
+ /* Start the periodic timer. */
+ setitimer(ITIMER_REAL, &ITimer, NULL);
+
+
+ while(TRUE) { /* Intentional infinite loop. */
+ /* Sleep for one second. */
+ sleep((unsigned int) 1);
+ }
+
+ /* Just in case. File should be closed in SignalHandler. */
+ close(Fd1);
+ close(Cfd);
+
+ return 0;
+}
+
+
+
+
+/***********************************************************************
+ * SignalHandler
+ * This is a handler for the SIGINT signal. It is assumed that the
+ * SIGINT is due to the user pressing Ctrl+C to halt the program.
+ * output: N/A
+ ***********************************************************************/
+void SignalHandler(
+ int sigNum)
+{
+
+ char tmpBuf[200];
+
+ /* Note sigNum not used. */
+ printf("Ctrl+C detected. Worst Jitter time was:%fms.\nJitterTest exiting.\n",
+ (float)LastMaxDiff/1000.0);
+
+ sprintf(tmpBuf,
+ "\nCtrl+C detected. Worst Jitter time was:%fms\nJitterTest exiting.\n",
+ (float)LastMaxDiff/1000.0);
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ close(Fd1);
+ close(Cfd);
+ exit(0);
+}
+
+
+
+
+
+/*
+ A snapshot of the /proc/profile needs to be taken.
+ This is stored as a new file every time, and the
+ stats reset by doing a (any) write to the /proc/profile
+ file.
+ */
+void doGrabKProfile(int jitterusec, char *fileName)
+{
+ int fdSnapshot;
+ int fdProfile;
+ int readBytes;
+ char readBuf[4096];
+
+ if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT)) <= 0)
+ {
+ fprintf(stderr, "Could not open file %s.\n", fileName);
+ perror("Error:");
+ return;
+ }
+
+ if((fdProfile = open("/proc/profile", O_RDWR)) <= 0)
+ {
+ fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n");
+ close(fdSnapshot);
+ return;
+ }
+
+ while((readBytes = read(fdProfile, readBuf, sizeof(readBuf))) > 0)
+ {
+ write(fdSnapshot, readBuf, readBytes);
+ }
+
+ close(fdSnapshot);
+
+ if(write(fdProfile, readBuf, 1) != 1)
+ {
+ perror("Could Not clear profile by writing to /proc/profile:");
+ }
+
+ close(fdProfile);
+
+
+
+}/* end doGrabKProfile()*/
+
+
+/*
+ Call this routine to clear the kernel profiling buffer /proc/profile
+*/
+void clearProfileBuf(void){
+
+
+ int fdProfile;
+ char readBuf[10];
+
+
+ if((fdProfile = open("/proc/profile", O_RDWR)) <= 0)
+ {
+ fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n");
+ return;
+ }
+
+
+ if(write(fdProfile, readBuf, 1) != 1)
+ {
+ perror("Could Not clear profile by writing to /proc/profile:");
+ }
+
+ close(fdProfile);
+
+
+}/* end clearProfileBuf() */
+
+
+
+
+
+/***********************************************************************
+ * AlarmHandler
+ * This is a handler for the SIGALRM signal (due to the periodic
+ * timer interrupt). It prints the time (seconds) to
+ * the output file.
+ * output: N/A
+ ***********************************************************************/
+void AlarmHandler(
+ int sigNum) /* signal number (not used) */
+{
+
+ long timeDiffusec; /* diff time in micro seconds */
+ long intervalusec;
+
+
+ char tmpBuf[MAX_WRITE_BYTES];
+ int cntr;
+ char padChar;
+
+ static int profileFileNo = 0;
+ char profileFileName[150];
+
+ static int seedStarter = 0; /* carries over rand info to next time
+ where time() will be the same as this time
+ if invoked < 1sec apart.
+ */
+
+ if (gettimeofday(&CurrTimeVal, NULL) == (int) 0) {
+
+ /*----------------------------------------------------------------
+ * Compute the elapsed time between the current and previous
+ * time of day values and store the result.
+ *
+ * Print the elapsed time to the output file.
+ *---------------------------------------------------------------*/
+
+ timeDiffusec = (long)(((((long long)CurrTimeVal.tv_sec) * 1000000L) + CurrTimeVal.tv_usec) -
+ (((long long)PrevTimeVal.tv_sec * 1000000L) + PrevTimeVal.tv_usec));
+
+ sprintf(tmpBuf," %f ms ", (float)timeDiffusec/1000.0);
+
+ intervalusec = InterruptPeriodMilliSec * 1000L;
+
+ timeDiffusec = timeDiffusec - intervalusec;
+
+ sprintf(&tmpBuf[strlen(tmpBuf)]," %f ms", (float)timeDiffusec/1000.0);
+
+
+ /* should we send a SIGSTOP/SIGCONT to the specified PID? */
+ if(SignalGCTask){
+
+ if(kill(GCTaskPID, SIGSTOP) < 0){
+
+ perror("error:");
+ }
+ }
+
+
+ /* Store some historical #'s */
+ if(abs(timeDiffusec) > LastMaxDiff)
+ {
+ LastMaxDiff = abs(timeDiffusec);
+ sprintf(&tmpBuf[strlen(tmpBuf)],"!");
+
+ if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (abs(timeDiffusec)/1000)))
+ {
+ sprintf(profileFileName, "JitterTest.profilesnap-%i", profileFileNo);
+
+ /* go and grab the kernel performance profile. */
+ doGrabKProfile(timeDiffusec, profileFileName);
+ profileFileNo++; /* unique name next time */
+
+ /* Say so on the console so that a marker gets put in the console log */
+ sprintf(&tmpBuf[strlen(tmpBuf)],"<Profile saved in file:%s>",
+ profileFileName);
+
+ }
+
+ }
+
+
+
+
+ sprintf(&tmpBuf[strlen(tmpBuf)],"\n"); /* CR for the data going out of the console */
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+
+ /* The "-1" below takes out the '\n' at the end that we appended for the msg printed on
+ the console.*/
+ sprintf(&tmpBuf[strlen(tmpBuf)-1]," PadBytes:");
+
+ /* Now pad the output file if requested by user. */
+ if(WriteBytes > MIN_WRITE_BYTES)
+ {
+
+ /* start from a new place every time */
+ srand(time(NULL) + seedStarter);
+
+ /* already written MIN_WRITE_BYTES by now */
+ for(cntr = strlen(tmpBuf); cntr < WriteBytes - 1 ; cntr++) /* "-1" adj for '\n' at end */
+ {
+ /* don't accept any # < 'SPACE' */
+ padChar = (char)(MIN_ASCII+(int)((MAX_ASCII-(float)MIN_ASCII)*rand()/(RAND_MAX+1.0)));
+
+
+ /*
+ padChar = (cntr % (126-33)) + 33;
+ */
+
+ tmpBuf[cntr] = padChar;
+ }
+
+ seedStarter = tmpBuf[cntr-1]; /* save for next time */
+
+ tmpBuf[cntr] = '\n'; /* CR for the data going into the outfile. */
+ tmpBuf[cntr+1] = '\0'; /* NULL terminate the string */
+ }
+
+ /* write out the entire line to the output file. */
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+
+ /* Read a byte from the specified file */
+ if(DoRead)
+ {
+
+ read(Fd2, tmpBuf, 1);
+ lseek(Fd2, 0, SEEK_SET); /* back to start */
+ }
+
+
+ /* Start the periodic timer again. */
+ setitimer(ITIMER_REAL, &ITimer, NULL);
+
+
+ /* Update previous time with current time. */
+ PrevTimeVal.tv_sec = CurrTimeVal.tv_sec;
+ PrevTimeVal.tv_usec = CurrTimeVal.tv_usec;
+ }
+
+ else {
+ sprintf(tmpBuf, "gettimeofday error \n");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ printf("gettimeofday error \n");
+ }
+
+ /* now clear the profiling buffer */
+ if(GrabKProfile == TRUE){
+
+ clearProfileBuf();
+ }
+
+ /* should we send a SIGSTOP/SIGCONT to the specified PID? */
+ if(SignalGCTask){
+
+ if(kill(GCTaskPID, SIGCONT) < 0){
+
+ perror("error:");
+ }
+ }
+
+
+ return;
+}
+
+
+
+/***********************************************************************
+ * InitITimer
+ * This function initializes the 'struct itimerval' structure whose
+ * address is passed to interrupt every InterruptPeriodMilliSec.
+ * output: N/A
+ ***********************************************************************/
+void InitITimer(
+ struct itimerval * pITimer, /* pointer to interrupt timer struct*/
+ int action) /* ONESHOT or autosetting? */
+{
+ long seconds; /* seconds portion of int. period */
+ long microSeconds; /* microsec. portion of int. period */
+
+ /*--------------------------------------------------------------------
+ * Divide the desired interrupt period into its seconds and
+ * microseconds components.
+ *-------------------------------------------------------------------*/
+ if (InterruptPeriodMilliSec < MILLISEC_PER_SEC) {
+ seconds = 0L;
+ microSeconds = InterruptPeriodMilliSec * MICROSEC_PER_MILLISEC;
+ }
+ else {
+ seconds = InterruptPeriodMilliSec / MILLISEC_PER_SEC;
+ microSeconds =
+ (InterruptPeriodMilliSec - (seconds * MILLISEC_PER_SEC)) *
+ MICROSEC_PER_MILLISEC;
+ }
+
+ /* Initialize the interrupt period structure. */
+ pITimer->it_value.tv_sec = seconds;
+ pITimer->it_value.tv_usec = microSeconds;
+
+ if(action == ONESHOT)
+ {
+ /* This will (should) prevent the timer from restarting itself */
+ pITimer->it_interval.tv_sec = 0;
+ pITimer->it_interval.tv_usec = 0;
+ }else
+ {
+ pITimer->it_interval.tv_sec = seconds;
+ pITimer->it_interval.tv_usec = microSeconds;
+
+ }
+
+ return;
+}
+
+
+/***********************************************************************
+ * HandleCmdLineArgs
+ * This function handles the command line arguments.
+ * output: stack size
+ ***********************************************************************/
+void HandleCmdLineArgs(
+ int argc, /* number of command-line arguments */
+ char *argv[]) /* ptrs to command-line arguments */
+{
+ int argNum; /* argument number */
+
+ if (argc > (int) 1) {
+
+ for (argNum = (int) 1; argNum < argc; argNum++) {
+
+ /* The command line contains an argument. */
+
+ if ((strcmp(argv[argNum],"--version") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-v") == STRINGS_EQUAL)) {
+ /* Print version information and exit. */
+ PrintVersionInfo();
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--help") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-h") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-?") == STRINGS_EQUAL)) {
+ /* Print help information and exit. */
+ PrintHelpInfo();
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--file") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-f") == STRINGS_EQUAL)) {
+ /* Set the name of the output file. */
+ ++argNum;
+ if (argNum < argc) {
+ SetFileName(argv[argNum]);
+ }
+ else {
+ printf("*** Output file name not specified. ***\n");
+ printf("Default output file name will be used.\n");
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"--time") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-t") == STRINGS_EQUAL)) {
+ /* Set the interrupt period. */
+ ++argNum;
+ if (argNum < argc) {
+ SetInterruptPeriod(argv[argNum]);
+ }
+ else {
+ printf("*** Interrupt period not specified. ***\n");
+ printf("Default interrupt period will be used.\n");
+ }
+
+ }
+
+ else if ((strcmp(argv[argNum],"--priority") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-p") == STRINGS_EQUAL)) {
+ /* Set the scheduler priority. */
+ ++argNum;
+ if (argNum < argc) {
+ SetSchedulerPriority(argv[argNum]);
+ }
+ else {
+ printf("*** Scheduler priority not specified. ***\n");
+ printf("Default scheduler priority will be used.\n");
+ }
+
+ }
+
+ else if ((strcmp(argv[argNum],"--readfile") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-r") == STRINGS_EQUAL)) {
+ /* Set the file to read*/
+ ++argNum;
+
+ strncpy(ReadFile, argv[argNum], sizeof(ReadFile));
+ DoRead = TRUE;
+ }
+
+ else if ((strcmp(argv[argNum],"--write_bytes") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-w") == STRINGS_EQUAL)) {
+ /* Set the file to read*/
+ ++argNum;
+
+ WriteBytes = atoi(argv[argNum]);
+
+ if(WriteBytes < MIN_WRITE_BYTES)
+ {
+ printf("Writing less than %i bytes is not allowed. Bye.\n",
+ MIN_WRITE_BYTES);
+ exit(0);
+ }
+
+
+ }
+
+ else if ((strcmp(argv[argNum],"--consolefile") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-c") == STRINGS_EQUAL)) {
+ /* Set the file to log console log on. */
+ ++argNum;
+
+ strncpy(LogFile, argv[argNum], sizeof(LogFile));
+ }
+
+ else if ((strcmp(argv[argNum],"--grab_kprofile") ==
+ STRINGS_EQUAL))
+ {
+ /* We will read the /proc/profile file on configurable timeout */
+ GrabKProfile = TRUE;
+
+ ++argNum;
+
+ /* If the jittter is > this #, then the profile is grabbed. */
+ ProfileTriggerMSecs = (long) atoi(argv[argNum]);
+
+ if(ProfileTriggerMSecs <= 0){
+
+ printf("Illegal value for profile trigger threshold.\n");
+ exit(0);
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"--siggc") ==
+ STRINGS_EQUAL))
+ {
+ /* We will SIGSTOP/SIGCONT the specified pid */
+ SignalGCTask = TRUE;
+
+ ++argNum;
+
+ GCTaskPID = atoi(argv[argNum]);
+
+ if(ProfileTriggerMSecs <= 0){
+
+ printf("Illegal value for JFFS(2) GC task pid.\n");
+ exit(0);
+ }
+ }
+
+
+ else {
+ /* Unknown argument. Print help information and exit. */
+ printf("Invalid option %s\n", argv[argNum]);
+ printf("Try 'JitterTest --help' for more information.\n");
+ exit(0);
+ }
+ }
+ }
+
+ return;
+}
+
+
+/***********************************************************************
+ * SetFileName
+ * This function sets the output file name.
+ * output: N/A
+ ***********************************************************************/
+void SetFileName(
+ char * pFileName) /* ptr to desired output file name */
+{
+ size_t fileNameLen; /* file name length (bytes) */
+
+ /* Check file name length. */
+ fileNameLen = strlen(pFileName);
+ if (fileNameLen > (size_t) MAX_FILE_NAME_LEN) {
+ printf("File name %s exceeds maximum length %d.\n",
+ pFileName, MAX_FILE_NAME_LEN);
+ exit(0);
+ }
+
+ /* File name length is OK so save the file name. */
+ strcpy(OutFileName, pFileName);
+
+ return;
+}
+
+
+/***********************************************************************
+ * SetInterruptPeriod
+ * This function sets the interrupt period.
+ * output: N/A
+ ***********************************************************************/
+void SetInterruptPeriod(
+ char * /* ptr to desired interrupt period */
+ pASCIIInterruptPeriodMilliSec)
+{
+ long period; /* interrupt period */
+
+ period = atol(pASCIIInterruptPeriodMilliSec);
+ if ((period < MIN_INT_PERIOD_MILLISEC) ||
+ (period > MAX_INT_PERIOD_MILLISEC)) {
+ printf("Invalid interrupt period: %ld ms.\n", period);
+ exit(EXIT_INV_INT_PERIOD);
+ }
+ else {
+ InterruptPeriodMilliSec = period;
+ }
+ return;
+}
+
+
+/***********************************************************************
+ * SetSchedulerPriority
+ * This function sets the desired scheduler priority.
+ * output: N/A
+ ***********************************************************************/
+void SetSchedulerPriority(
+ char * pASCIISchedulerPriority) /* ptr to desired scheduler priority*/
+{
+ int priority; /* desired scheduler priority value */
+
+ priority = atoi(pASCIISchedulerPriority);
+ if ((priority < MinPriority) ||
+ (priority > MaxPriority)) {
+ printf("Scheduler priority %d outside of range [%d, %d]\n",
+ priority, MinPriority, MaxPriority);
+ exit(EXIT_INV_SCHED_PRIORITY);
+ }
+ else {
+ RequestedPriority = priority;
+ RunAsRTTask = TRUE; /* We shall run as a POSIX real time task */
+ }
+ return;
+}
+
+
+/***********************************************************************
+ * PrintVersionInfo
+ * This function prints version information.
+ * output: N/A
+ ***********************************************************************/
+void PrintVersionInfo(void)
+{
+ printf("JitterTest version %s\n", Version);
+ printf("Copyright (c) 2001, Daniel Industries, Inc.\n");
+ return;
+}
+
+
+/***********************************************************************
+ * PrintHelpInfo
+ * This function prints help information.
+ * output: N/A
+ ***********************************************************************/
+void PrintHelpInfo(void)
+{
+ printf("Usage: JitterTest [options]\n");
+ printf(" *** Requires root privileges. ***\n");
+ printf("Option:\n");
+ printf(" [-h, --help, -?] Print this message and exit.\n");
+ printf(" [-v, --version] ");
+ printf( "Print the version number of JitterTest and exit.\n");
+ printf(" [-f FILE, --file FILE] Set output file name to FILE. Typically you would put this on the fs under test\n");
+ printf(" [-c FILE, --consolefile] Set device or file to write the console log to.\n\tTypically you would set this to /dev/console and save it on another computer.\n");
+ printf(" [-w BYTES, --write_bytes BYTES Write BYTES to FILE each period.\n");
+ printf(" [-r FILE, --readfile FILE] Also read 1 byte every cycle from FILE. FILE will be created and filled with data.\n");
+ printf(" [-t <n>, --time <n>] ");
+ printf( "Set interrupt period to <n> milliseconds.\n");
+ printf(" ");
+ printf( "Range: [%ld, %ld] milliseconds.\n",
+ MIN_INT_PERIOD_MILLISEC, MAX_INT_PERIOD_MILLISEC);
+ printf(" [-p <n>, --priority <n>] ");
+ printf( "Set scheduler priority to <n>.\n");
+ printf(" ");
+ printf( "Range: [%d, %d] (higher number = higher priority)\n",
+ MinPriority, MaxPriority);
+ printf(" [--grab_kprofile <THRESHOLD in ms>] Read the /proc/profile if jitter is > THRESHOLD and store in file.\n");
+ printf(" [--siggc <PID>] Before writing to fs send SIGSTOP to PID. After write send SIGCONT\n");
+ return;
+
+}
+
+
+/* A common write that checks for write errors and exits. Pass it __LINE__ for lineNo */
+int Write(int fd, void *buf, size_t bytes, int lineNo)
+{
+
+ int err;
+
+ err = write(fd, buf, bytes);
+
+ if(err < bytes)
+ {
+
+ printf("Write Error at line %i! Wanted to write %i bytes, but wrote only %i bytes.\n",
+ lineNo, bytes, err);
+ perror("Write did not complete. Error. Bye:"); /* show error from errno. */
+ exit(1);
+
+ }
+
+ return err;
+
+}/* end Write*/
diff --git a/mtd-utils-1.3.1/tests/jittertest/Makefile b/mtd-utils-1.3.1/tests/jittertest/Makefile
new file mode 100644
index 0000000..2f11329
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/jittertest/Makefile
@@ -0,0 +1,46 @@
+CC=gcc
+# uncomment following for performance
+CCFLAGS=-O3 -Wall -m486 -fomit-frame-pointer
+
+# uncomment following for debugging. Uncomment either this or the one above. Not both.
+# CCFLAGS=-Wall -g
+
+
+all: JitterTest plotJittervsFill
+
+JitterTest: JitterTest.c Makefile
+ gcc $(CCFLAGS) -lm JitterTest.c -o JitterTest
+
+plotJittervsFill: plotJittervsFill.c Makefile
+ gcc $(CCFLAGS) plotJittervsFill.c -o plotJittervsFill
+
+clean:
+ rm -rf *~
+ rm -rf core
+ rm -rf *.o
+ rm -rf JitterTest
+
+
+dep:
+ makedepend -I./ *.c
+# DO NOT DELETE
+
+JitterTest.o: /usr/include/stdio.h /usr/include/features.h
+JitterTest.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h
+JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h
+JitterTest.o: /usr/include/bits/types.h /usr/include/libio.h
+JitterTest.o: /usr/include/_G_config.h /usr/include/bits/stdio_lim.h
+JitterTest.o: /usr/include/string.h /usr/include/stdlib.h
+JitterTest.o: /usr/include/sys/types.h /usr/include/time.h
+JitterTest.o: /usr/include/endian.h /usr/include/bits/endian.h
+JitterTest.o: /usr/include/sys/select.h /usr/include/bits/select.h
+JitterTest.o: /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h
+JitterTest.o: /usr/include/alloca.h /usr/include/sys/time.h
+JitterTest.o: /usr/include/bits/time.h /usr/include/signal.h
+JitterTest.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h
+JitterTest.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h
+JitterTest.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h
+JitterTest.o: /usr/include/sched.h /usr/include/bits/sched.h
+JitterTest.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+JitterTest.o: /usr/include/bits/confname.h /usr/include/getopt.h
diff --git a/mtd-utils-1.3.1/tests/jittertest/README b/mtd-utils-1.3.1/tests/jittertest/README
new file mode 100644
index 0000000..f411a2c
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/jittertest/README
@@ -0,0 +1,197 @@
+$Id: README,v 1.2 2001/08/10 19:23:11 vipin Exp $
+
+This is the README file for the JitterTest (and friends)
+program.
+
+This program is used to measure what the jitter of a
+real time task would be under "standard" Linux.
+
+More particularly, what is the effect of running
+a real time task under Linux with background
+JFFS file system activity.
+
+The jitter is measured in milli seconds (ms) from
+the expected time of arrival of a signal from a
+periodic timer (set by the task) to when the
+task actually gets the signal.
+
+This jitter is then stored in a file specified
+(or the default output file "jitter.dat").
+
+The data may also be sent out to the console by
+writing to /dev/console (See help options. This is
+highly desirable specially if you have redirected
+your console to the serial port and are storing it
+as a minicom log on another computer for later analysis
+using some tools provided here).
+
+This is particularly useful if you have a serial
+console and are outputting "interesting" info
+from inside some kernel task or driver.
+(or something as simple as a "df" program running
+periodically and redirecting o/p to the console).
+
+One "interesting" thing that I have measured
+is the effect of FLASH chip erases on the jitter
+of a real time task.
+
+One can do that by putting a printk at the
+beginning of the flash erase routine in the MTD
+flash chip driver.
+
+Now you will get jitter data interspersed with
+flash sector erase events. Other printk's can also
+be placed at suspected jitter causing locations in
+the system.
+
+
+
+EXECUTING THE PROGRAM "JitterTest"
+
+You may specify a file to be read by the
+program every time it wakes up (every cycle).
+This file is created and filled with some junk
+data. The purpose of this is to test the jitter
+of the program if it were reading from- say
+a JFFS (Journaling Flash File System) file system.
+
+By specifying the complete paths of the read and write
+(o/p) files you can test the jitter a POSIX type
+real time task will experience under Linux, under
+various conditions.
+
+These can be as follows:
+
+1. O/P file on ram file system, no i/p file.
+
+ In this case you would presumably generate other
+"typical" background activity for your system and
+examine the worst case jitter experienced by
+a task that is neither reading nor writing to
+a file system.
+
+Other cases could be:
+
+2. O/P to ram fs, I/P from JFFS (type) fs:
+
+ This is specially useful to test the proper
+operation of erase suspend type of operation
+in JFFS file systems (with an MTD layer that
+supports it).
+
+ In this test you would generate some background
+write/erase type activity that would generate
+chip erases. Since this program is reading from
+the same file system, you contrast the latencies
+with those obtained with writes going to the same
+fs.
+
+3. Both read and writes to (or just write to) JFFS
+file system:
+
+ Here you would test for latencies experienced by
+a program if it were writing (and optionally also
+reading) from a JFFS fs.
+
+
+
+
+Grabing a kernel profile:
+
+This program can also conditionally grab a kernel profile.
+Specify --grab_kprofile on the cmd line as well as
+a "threshold" parameter (see help options by -?).
+
+Any jitter greater than this "threshold" will cause the
+program to read the /proc/profile file and dump it in
+a local file with increasing file numbers. It will also
+output the filename at that time to the console file specified.
+This will allow you to corelate later in time the various profiles
+with data in your console file and what was going on at that time.
+
+These profile files may then be later examined by running them through
+ksymoops.
+
+Make sure you specify "profile=2" on the kernel command line
+when you boot the kernel if you want to use this functionality.
+
+
+
+Signalling the JFFS[2] GC task:
+
+You can also force this program to send a SIGSTOP/SIGCONT to the
+JFFS (or JFFS2) gc task by specifing --siggc <pid> on the cmd line.
+
+This will let you investigate the effect of forcing the gc task to
+wake up and do its thing when you are not writing to the fs and to
+force it to sleep when you want to write to the fs.
+
+These are just various tools to investigate the possibility of
+achieving minimal read/write latency when using JFFS[2].
+
+You need to manually do a "ps aux" and look up the PID of the gc
+thread and provide it to the program.
+
+
+
+
+EXECUTING THE PROGRAM "plotJittervsFill"
+
+This program is a post processing tool that will extract the jitter
+times as printed by the JitterTest program in its console log file
+as well as the data printed by the "df" command.
+
+This "df" data happens to be in the console log because you will
+run the shell file fillJffs2.sh on a console when you are doing
+your jitter test.
+
+This shell script copies a specified file to another specified file
+every programmable seconds. It also does a "df" and redirects output
+to /dev/console where it is mixed with the output from JitterTest.
+
+All this console data is stored on another computer, as all this data
+is being outputted to the serial port as you have redirected the console
+to the serial port (that is the only guaranteed way to not loose any
+console log or printk data).
+
+You can then run this saved console log through this program and it
+will output a very nice text file with the %fill in one col and
+corrosponding jitter values in the second. gnuplot then does a
+beautifull plot of this resulting file showing you graphically the
+jitters encountered at different fill % of your JFFS[2] fs.
+
+
+
+OTHER COMMENTS:
+
+Use the "-w BYTES" cmd line parameter to simulate your test case.
+Not everyone has the same requirements. Someone may want to measure
+the jitter of JFFS2 with 500 bytes being written every 500ms. Others
+may want to measure the system performance writing 2048 bytes every
+5 seconds.
+
+RUNNING MULTIPLE INSTANCES:
+
+Things get real interesting when you run multiple instances of this
+program *at the same time*.
+
+You could have one version running as a real time task (by specifing
+the priority with the -p cmd line parameter), not interacting with
+any fs or at the very least not reading and writing to JFFS[2].
+
+At the same time you could have another version running as a regular
+task (by not specifing any priority) but reading and writing to JFFS[2].
+
+This way you can easily measure the blocking performance of the real time
+task while another non-real time task interacts with JFFS[2] in the back ground.
+
+You get the idea.
+
+
+WATCH OUT!
+
+Be particularly careful of running this program as a real time task AND
+writing to JFFS[2]. Any blocks will cause your whole system to block till
+any garbage collect initiated by writes by this task complete. I have measured
+these blocks to be of the order of 40-50 seconds on a reasonably powerful
+32 bit embedded system.
diff --git a/mtd-utils-1.3.1/tests/jittertest/filljffs2.sh b/mtd-utils-1.3.1/tests/jittertest/filljffs2.sh
new file mode 100644
index 0000000..10651f4
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/jittertest/filljffs2.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Pass following cmd line:
+# 1st - file to copy
+# 2nd - file to copy to
+# 3rd - time to sleep between copies
+
+while [ $(( 1 )) -gt $(( 0 )) ]
+do
+ cp $1 $2
+ rm $2
+ df |grep mtd > /dev/console
+ echo "sleeping $3"
+ sleep $3
+done
+
diff --git a/mtd-utils-1.3.1/tests/jittertest/plotJittervsFill.c b/mtd-utils-1.3.1/tests/jittertest/plotJittervsFill.c
new file mode 100644
index 0000000..f8a5660
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/jittertest/plotJittervsFill.c
@@ -0,0 +1,312 @@
+/*
+ ***********************************************************************
+ *
+ * Copyright: Daniel Measurement and Control, Inc.
+ * 9753 Pine Lake Drive
+ * Houston, TX 77055
+ *
+ * Created by: Vipin Malik
+ * Released under GPL by permission of Daniel Industries.
+ *
+ * This software is licensed under the GPL version 2. Plese see the file
+ * COPYING for details on the license.
+ *
+ * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose
+ * are made in this software. Please use at your own risk.
+ *
+ File: plotJittervsFill.c
+ By: Vipin Malik
+
+ About: This program reads in a jitter log file as created
+ by the JitterTest.c program and extracts all the jitters
+ in the file that are greater than a threshold specified
+ as a parameter on the cmd line. It also extracts the
+ amount of disk space at (form the "df" out that should also
+ be present in the log file) after the jitter extracted.
+
+ It writes the data to the stderr (where you may redirect it).
+ It is suitable for plotting, as the data is written as
+ COL1=UsedSpace COL2=Jitter
+
+ $Id: plotJittervsFill.c,v 1.6 2005/11/07 11:15:21 gleixner Exp $
+ $Log: plotJittervsFill.c,v $
+ Revision 1.6 2005/11/07 11:15:21 gleixner
+ [MTD / JFFS2] Clean up trailing white spaces
+
+ Revision 1.5 2001/08/10 19:23:11 vipin
+ Ready to be released under GPL! Added proper headers etc.
+
+ Revision 1.4 2001/07/02 22:25:40 vipin
+ Fixed couple of minor cosmetic typos.
+
+ Revision 1.3 2001/07/02 14:46:46 vipin
+ Added a debug option where it o/p's line numbers to debug funky values.
+
+ Revision 1.2 2001/06/26 19:48:57 vipin
+ Now prints out jitter values found at end of log file, after which
+ no new "df" disk usage values were encountered. The last "df" disk usage
+ encountered is printed for these orphaned values.
+
+ Revision 1.1 2001/06/25 19:13:55 vipin
+ Added new file- plotJittervsFill.c- that mines the data log file
+ outputed from the fillFlash.sh script file and JitterTest.c file
+ and produces output suitable to be plotted.
+ This output plot may be examined to see visually the relationship
+ of the Jitter vs disk usage of the fs under test.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static char Version_string[] = "$Id: plotJittervsFill.c,v 1.6 2005/11/07 11:15:21 gleixner Exp $";
+static char LogFile[250] = "InputLogFile.log";
+
+static int JitterThreshold_ms = 1000;
+static int Debug = 0; /* Debug level. Each "-d" on the cmd line increases the level */
+
+#define TRUE 1
+#define FALSE 0
+
+#define MIN_JITTER_THRESHOLD 1 /* ms minimum jitter threshold */
+
+void PrintHelpInfo(void)
+{
+ printf("Usage: plotJittervsFill [options] -f [--file] <input log file name> -t [--jitter_threshold] <jitter threshold in ms>\n");
+ printf("[options]:\n-v [--version] Print version and exit\n");
+ printf("-d Debug. Prints input file line number for each data point picked up.\n");
+ printf("-h [--help] [-?] Print this help screen and exit.\n");
+}
+
+
+
+/***********************************************************************
+ * HandleCmdLineArgs
+ * This function handles the command line arguments.
+ * output: stack size
+ ***********************************************************************/
+void HandleCmdLineArgs(
+ int argc, /* number of command-line arguments */
+ char *argv[]) /* ptrs to command-line arguments */
+{
+ int argNum; /* argument number */
+
+ if (argc > (int) 1) {
+
+ for (argNum = (int) 1; argNum < argc; argNum++) {
+
+ /* The command line contains an argument. */
+
+ if ((strcmp(argv[argNum],"--version") == 0) ||
+ (strcmp(argv[argNum],"-v") == 0)) {
+ /* Print version information and exit. */
+ printf("%s\n", Version_string);
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--help") == 0) ||
+ (strcmp(argv[argNum],"-h") == 0) ||
+ (strcmp(argv[argNum],"-?") == 0)) {
+ /* Print help information and exit. */
+ PrintHelpInfo();
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--file") == 0) ||
+ (strcmp(argv[argNum],"-f") == 0)) {
+ /* Set the name of the output file. */
+ ++argNum;
+ if (argNum < argc) {
+ strncpy(LogFile, argv[argNum], sizeof(LogFile));
+ }
+ else {
+ printf("*** Input file name not specified. ***\n");
+ exit(0);
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"--jitter_threshold") == 0) ||
+ (strcmp(argv[argNum],"-t") == 0)) {
+ /* Set the file to read*/
+ ++argNum;
+
+ JitterThreshold_ms = atoi(argv[argNum]);
+
+ if(JitterThreshold_ms < MIN_JITTER_THRESHOLD)
+ {
+ printf("A jitter threshold less than %i ms is not allowed. Bye.\n",
+ MIN_JITTER_THRESHOLD);
+ exit(0);
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"-d") == 0))
+ {
+ /* Increment debug level */
+
+ Debug++;
+ }
+
+ else {
+ /* Unknown argument. Print help information and exit. */
+ printf("Invalid option %s\n", argv[argNum]);
+ printf("Try 'plotJittervsFill --help' for more information.\n");
+ exit(0);
+ }
+ }
+ }
+
+ return;
+}
+
+
+
+
+
+int main(
+ int argc,
+ char *argv[])
+{
+
+ char lineBuf[1024]; /* how long a single line be? */
+ int converted;
+ int lineNo = 0;
+ int cnt;
+
+ FILE *fp;
+
+ int junkInt1, junkInt2, junkInt3;
+ float junkFloat1;
+ float jitter_ms;
+
+#define MAX_SAVE_BUFFER 1000 /* How many values will be picked up while searching for
+ a % disk full line (i.e. before they can be printed out)
+ */
+ int saveJitter[MAX_SAVE_BUFFER]; /* lets us record multiple jitter values that exceed
+ our threshold till we find a "df" field- which is when
+ we can o/p all these values.
+ */
+ int dataLineNo[MAX_SAVE_BUFFER]; /* The saved line #'s for the above. Printed if debug specified. */
+
+ int saveJitterCnt = 0;
+ int lookFor_df = FALSE;
+ int dfPercent = -1; /* will be >= 0 if at least one found. The init value is a flag. */
+
+ char junkStr1[500], junkStr2[500];
+
+ HandleCmdLineArgs(argc, argv);
+
+ if((fp = fopen(LogFile, "r")) == NULL)
+ {
+ printf("Unable to open input log file %s for read.\b", LogFile);
+ perror("Error:");
+ exit(1);
+ }
+
+
+
+ while(fgets(lineBuf, sizeof(lineBuf), fp) != NULL)
+ {
+ lineNo++;
+
+
+ /* Are we looking for a "df" o/p line? (to see how full
+ the flash is?)*/
+
+ /* is there a "%" in this line? */
+ if((strstr(lineBuf, "%") != NULL) && (lookFor_df))
+ {
+ converted = sscanf(lineBuf, "%s %i %i %i %i\n",
+ junkStr1, &junkInt1, &junkInt2, &junkInt3, &dfPercent);
+ if(converted < 5)
+ {
+ printf("Line %i contains \"%%\", but expected fileds not found. Skipping.\n", lineNo);
+ }else
+ {
+ /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */
+ for(cnt = 0; cnt < saveJitterCnt; cnt++)
+ {
+ if(Debug)
+ {
+ fprintf(stderr, "%i\t%i\t%i\n", (int)dataLineNo[cnt],
+ dfPercent, (int)saveJitter[cnt]);
+ }else
+ {
+ fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]);
+ }
+
+
+ }
+
+ saveJitterCnt = 0; /* all flushed. Reset for next saves. */
+ lookFor_df = FALSE;
+ }
+
+ }
+
+
+ /* is there a "ms" in this line?*/
+ if(strstr(lineBuf, "ms") == NULL)
+ {
+ continue;
+ }
+
+ /* grab the ms jitter value */
+ converted = sscanf(lineBuf, "%f %s %f %s\n", &junkFloat1, junkStr1, &jitter_ms, junkStr2);
+ if(converted < 4)
+ {
+ printf("Line %i contains \"ms\", but expected fileds not found. Converted %i, Skipping.",
+ lineNo, converted);
+ printf("1=%i, 2=%s.\n", junkInt1, junkStr1);
+ continue; /* not our jitter line*/
+ }
+
+ /* Is the jitter value > threshold value? */
+ if(abs(jitter_ms) > JitterThreshold_ms)
+ {
+ /* Found a jitter line that matches our crietrion.
+ Now set flag to be on the look out for the next
+ "df" output so that we can see how full the flash is.
+ */
+
+ if(saveJitterCnt < MAX_SAVE_BUFFER)
+ {
+ saveJitter[saveJitterCnt] = (int)abs(jitter_ms); /* why keep the (ms) jitter in float */
+ dataLineNo[saveJitterCnt] = lineNo;
+ saveJitterCnt++;
+ lookFor_df = TRUE;
+ }
+ else
+ {
+ printf("Oops! I've run out of buffer space before I found a %% use line. Dropping itter value. Increase MAX_SAVE_BUFFER and recompile.\n");
+ }
+
+
+ }
+
+ }
+
+
+ /* Now print out any saved jitter values that were not printed out because we did not find
+ and "df" after these were picked up. Only print if a "df" disk usage was ever found.
+ */
+ if(lookFor_df && (dfPercent >= 0))
+ {
+ /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */
+ for(cnt = 0; cnt < saveJitterCnt; cnt++)
+ {
+ fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]);
+ }
+ }
+
+ return 0;
+
+
+}/* end main() */
+
+
+
+
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/Makefile b/mtd-utils-1.3.1/tests/ubi-tests/Makefile
new file mode 100644
index 0000000..d6be0dc
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/Makefile
@@ -0,0 +1,25 @@
+LIBUBI_PATH=../../ubi-utils/
+LIBUBI_SRC_PATH=../../ubi-utils/src/
+LIBUBI_HEADER_PATH=../../ubi-utils/include
+UBIUTILS_PATH=../../ubi-utils/
+
+LIBUBI_PATH=../../ubi-utils/
+KERNELHDR := ../../include
+
+LIBS = libubi
+TARGETS=io_update volrefcnt integ io_paral io_read io_basic \
+ mkvol_basic mkvol_bad mkvol_paral rsvol
+
+CFLAGS += -I$(LIBUBI_HEADER_PATH) -I $(KERNELHDR) -lpthread
+
+include ../../common.mk
+
+# Compile ubilib with the udevsettle hack
+libubi.a: $(LIBUBI_SRC_PATH)/libubi.c $(LIBUBI_HEADER_PATH)/libubi.h $(LIBUBI_SRC_PATH)/libubi_int.h
+ $(CC) $(CFLAGS) -I $(LIBUBI_SRC_PATH) -I../../include -DUDEV_SETTLE_HACK -c $(LIBUBI_SRC_PATH)/libubi.c -o libubi.o
+ ar cr libubi.a libubi.o
+
+$(TARGETS): $(addprefix $(BUILDDIR)/, common.o) libubi.a
+
+clean::
+ rm -f $(TARGETS) $(addsuffix .o, $(TESTS)) libubi.*
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/README.udev b/mtd-utils-1.3.1/tests/ubi-tests/README.udev
new file mode 100644
index 0000000..06e71d3
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/README.udev
@@ -0,0 +1,25 @@
+There is a problem with udev: when a volume is created, there is a delay
+before corresponding /dev/ubiX_Y device node is created by udev, so some
+tests fail because of this. The symptom is error messages like
+"cannot open /dev/ubi0_0".
+
+One possible solution of this problem is to pre-create UBI device and volume
+nodes. There is even a script which may be used for this in ubi-utils/scripts/.
+But this is not enough because udev will still remove and re-create the nodes
+and tests will still fail. So you need to stop removing device nodes using
+the following udev rule:
+
+ KERNEL=="ubi*_*", ACTION=="remove", OPTIONS+="ignore_device"
+
+In our Ubuntu distribution we put that to new file:
+/etc/udev/rules.d/50-local.rules
+
+Another possibility is to call udevsettle utility in libubi after the volume
+has been created See src/libubi.c - the call is compiled in only if
+UDEV_SETTLE_HACK is defined. This is anyway an ugly hack, but works, although
+makes the tests slower. Suggestions are welcome.
+
+So, if you have udevsettel unility in your system, you do not have to do
+anyting, and the tests should work, because we compile libubi with
+UDEV_SETTLE_HACK. Otherwise, you should remove -D UDEV_SETTLE_HACK
+from the makefile and pre-create UBI device nodes.
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/common.c b/mtd-utils-1.3.1/tests/ubi-tests/common.c
new file mode 100644
index 0000000..2381e67
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/common.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * The stuff which is common for many tests.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#include "common.h"
+
+/**
+ * __initial_check - check that common prerequisites which are required to run
+ * tests.
+ *
+ * @test test name
+ * @argc count of command-line arguments
+ * @argv command-line arguments
+ *
+ * This function returns %0 if all is fine and test may be run and %-1 if not.
+ */
+int __initial_check(const char *test, int argc, char * const argv[])
+{
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+
+ /*
+ * All tests require UBI character device name as the first parameter,
+ * check this.
+ */
+ if (argc < 2) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "UBI character device node is not specified");
+ return -1;
+ }
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ __failed(test, __FUNCTION__, __LINE__, "libubi_open");
+ return -1;
+ }
+
+ if (ubi_get_dev_info(libubi, argv[1], &dev_info)) {
+ __failed(test, __FUNCTION__, __LINE__, "ubi_get_dev_info");
+ goto close;
+ }
+
+ if (dev_info.avail_lebs < MIN_AVAIL_EBS) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "insufficient available eraseblocks %d on UBI "
+ "device, required %d",
+ dev_info.avail_lebs, MIN_AVAIL_EBS);
+ goto close;
+ }
+
+ if (dev_info.vol_count != 0) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "device %s is not empty", argv[1]);
+ goto close;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return -1;
+}
+
+/**
+ * __err_msg - print a message to stderr.
+ *
+ * @test test name
+ * @func function name
+ * @line line number
+ * @fmt format string
+ */
+void __err_msg(const char *test, const char *func, int line,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "[%s] %s():%d: ", test, func, line);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+}
+
+/**
+ * __failed - print function fail message.
+ *
+ * @test test name
+ * @func calling function name
+ * @line line number
+ * @failed failed function name
+ */
+void __failed(const char *test, const char *func, int line,
+ const char *failed)
+{
+ fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n",
+ test, func, line, failed, errno, strerror(errno));
+}
+
+/**
+ * __check_volume - check volume information.
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @vol_id ID of existing volume to check
+ * @req volume creation request to compare with
+ *
+ * This function checks if a volume created using @req request has exactly the
+ * requested characteristics. Returns 0 in case of success and %-1 in case of
+ * error.
+ */
+int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line, int vol_id,
+ const struct ubi_mkvol_request *req)
+{
+ int ret;
+ struct ubi_vol_info vol_info;
+ int leb_size;
+ long long rsvd_bytes;
+
+ ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info);
+ if (ret) {
+ __failed(test, func, line, "ubi_get_vol_info");
+ return -1;
+ }
+
+ if (req->alignment != vol_info.alignment) {
+ __err_msg(test, func, line,
+ "bad alignment: requested %d, got %d",
+ req->alignment, vol_info.alignment);
+ return -1;
+ }
+ if (req->vol_type != vol_info.type) {
+ __err_msg(test, func, line, "bad type: requested %d, got %d",
+ req->vol_type, vol_info.type);
+ return -1;
+ }
+ if (strlen(req->name) != strlen(vol_info.name) ||
+ strcmp(req->name, vol_info.name) != 0) {
+ __err_msg(test, func, line,
+ "bad name: requested \"%s\", got \"%s\"",
+ req->name, vol_info.name);
+ return -1;
+ }
+ if (vol_info.corrupted) {
+ __err_msg(test, func, line, "corrupted new volume");
+ return -1;
+ }
+
+ leb_size = dev_info->leb_size - (dev_info->leb_size % req->alignment);
+ if (leb_size != vol_info.leb_size) {
+ __err_msg(test, func, line,
+ "bad usable LEB size %d, should be %d",
+ vol_info.leb_size, leb_size);
+ return -1;
+ }
+
+ rsvd_bytes = req->bytes;
+ if (rsvd_bytes % leb_size)
+ rsvd_bytes += leb_size - (rsvd_bytes % leb_size);
+
+ if (rsvd_bytes != vol_info.rsvd_bytes) {
+ __err_msg(test, func, line,
+ "bad reserved bytes %lld, should be %lld",
+ vol_info.rsvd_bytes, rsvd_bytes);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * __check_vol_patt - check that volume contains certain data
+ *
+ * @libubi libubi descriptor
+ * @test test name
+ * @func function name
+ * @line line number
+ * @node volume character device node
+ * @byte data pattern to check
+ *
+ * This function returns %0 if the volume contains only @byte bytes, and %-1 if
+ * not.
+ */
+int __check_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, uint8_t byte)
+{
+ int ret, fd;
+ long long bytes = 0;
+ struct ubi_vol_info vol_info;
+ unsigned char buf[512];
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1) {
+ __failed(test, func, line, "open");
+ __err_msg(test, func, line, "cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ ret = ubi_get_vol_info(libubi, node, &vol_info);
+ if (ret) {
+ __failed(test, func, line, "ubi_get_vol_info");
+ goto close;
+ }
+
+ while (bytes < vol_info.data_bytes) {
+ int i;
+
+ memset(buf, ~byte, 512);
+ ret = read(fd, buf, 512);
+ if (ret == -1) {
+ __failed(test, func, line, "read");
+ __err_msg(test, func, line, "bytes = %lld, ret = %d",
+ bytes, ret);
+ goto close;
+ }
+
+ if (ret == 0 && bytes + ret < vol_info.data_bytes) {
+ __err_msg(test, func, line,
+ "EOF, but read only %lld bytes of %lld",
+ bytes + ret, vol_info.data_bytes);
+ goto close;
+ }
+
+ for (i = 0; i < ret; i++)
+ if (buf[i] != byte) {
+ __err_msg(test, func, line,
+ "byte at %lld is not %#x but %#x",
+ bytes + i, byte, (int)buf[i]);
+ goto close;
+ }
+
+ bytes += ret;
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * __update_vol_patt - update volume using a certain byte pattern
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @node volume character device node
+ * @byte data pattern to check
+ *
+ * This function returns %0 in case of success, and %-1 if in case of failure.
+ */
+int __update_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, long long bytes, uint8_t byte)
+{
+ int ret, fd;
+ long long written = 0;
+ unsigned char buf[512];
+
+ fd = open(node, O_RDWR);
+ if (fd == -1) {
+ __failed(test, func, line, "open");
+ __err_msg(test, func, line, "cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ if (ubi_update_start(libubi, fd, bytes)) {
+ __failed(test, func, line, "ubi_update_start");
+ __err_msg(test, func, line, "bytes = %lld", bytes);
+ goto close;
+ }
+
+ memset(buf, byte, 512);
+
+ while (written != bytes) {
+ ret = write(fd, buf, 512);
+ if (ret == -1) {
+ __failed(test, func, line, "write");
+ __err_msg(test, func, line, "written = %lld, ret = %d",
+ written, ret);
+ goto close;
+ }
+ written += ret;
+
+ if (written > bytes) {
+ __err_msg(test, func, line, "update length %lld bytes, "
+ "but %lld bytes are already written",
+ bytes, written);
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/common.h b/mtd-utils-1.3.1/tests/ubi-tests/common.h
new file mode 100644
index 0000000..faa2865
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/common.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * The stuff which is common for many tests.
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UBI_VOLUME_PATTERN "/dev/ubi%d_%d"
+#define MIN_AVAIL_EBS 5
+#define PAGE_SIZE 4096
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#define err_msg(fmt, ...) \
+ __err_msg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
+
+#define failed(name) \
+ __failed(TESTNAME, __FUNCTION__, __LINE__, name)
+
+#define initial_check(argc, argv) \
+ __initial_check(TESTNAME, argc, argv)
+
+#define check_volume(vol_id, req) \
+ __check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__, \
+ __LINE__, vol_id, req)
+
+#define check_vol_patt(node, byte) \
+ __check_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, node, byte)
+
+#define update_vol_patt(node, bytes, byte) \
+ __update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, \
+ node, bytes, byte)
+
+#define check_failed(ret, error, func, fmt, ...) ({ \
+ int __ret; \
+ \
+ if (!ret) { \
+ err_msg("%s() returned success but should have failed", func); \
+ err_msg(fmt, ##__VA_ARGS__); \
+ __ret = -1; \
+ } \
+ if (errno != (error)) { \
+ err_msg("%s failed with error %d (%s), expected %d (%s)", \
+ func, errno, strerror(errno), error, strerror(error)); \
+ err_msg(fmt, ##__VA_ARGS__); \
+ __ret = -1; \
+ } \
+ __ret = 0; \
+})
+
+/* Alignments to test, @s is eraseblock size */
+#define ALIGNMENTS(s) \
+ {3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1, \
+ (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2, \
+ (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3, \
+ (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9, \
+ (s)-8, (s)-6, (s)-4, (s)-1, (s)};
+
+extern void __err_msg(const char *test, const char *func, int line,
+ const char *fmt, ...);
+void __failed(const char *test, const char *func, int line,
+ const char *failed);
+int __initial_check(const char *test, int argc, char * const argv[]);
+int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line, int vol_id,
+ const struct ubi_mkvol_request *req);
+int __check_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, uint8_t byte);
+int __update_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, long long bytes,
+ uint8_t byte);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__COMMON_H__ */
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/integ.c b/mtd-utils-1.3.1/tests/ubi-tests/integ.c
new file mode 100644
index 0000000..bb60bcc
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/integ.c
@@ -0,0 +1,783 @@
+#define _LARGEFILE64_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "libubi.h"
+
+struct erase_block_info;
+struct volume_info;
+struct ubi_device_info;
+
+struct write_info
+{
+ struct write_info *next;
+ struct erase_block_info *erase_block;
+ int offset_within_block; /* Offset within erase block */
+ off64_t offset; /* Offset within volume */
+ int size;
+ int random_seed;
+};
+
+struct erase_block_info
+{
+ struct volume_info *volume;
+ int block_number;
+ off64_t offset; /* Offset within volume */
+ off64_t top_of_data;
+ int touched; /* Have we done anything at all with this erase block */
+ int erased; /* This erased block is currently erased */
+ struct write_info *writes;
+};
+
+struct volume_fd
+{
+ struct volume_fd *next;
+ struct volume_info *volume;
+ int fd;
+};
+
+struct volume_info
+{
+ struct volume_info *next;
+ struct ubi_device_info *ubi_device;
+ struct volume_fd *fds;
+ struct erase_block_info *erase_blocks;
+ const char *device_file_name;
+ struct ubi_vol_info info;
+};
+
+struct ubi_device_info
+{
+ struct volume_info *volumes;
+ const char *device_file_name;
+ struct ubi_dev_info info;
+};
+
+struct open_volume_fd
+{
+ struct open_volume_fd *next;
+ struct volume_fd *vol_fd;
+};
+
+#define MAX_UBI_DEVICES 64
+
+static libubi_t libubi;
+
+static struct ubi_info info;
+static struct ubi_device_info ubi_array[MAX_UBI_DEVICES];
+
+static uint64_t total_written = 0;
+static uint64_t total_space = 0;
+
+static struct open_volume_fd *open_volumes;
+static int open_volume_count = 0;
+
+static const char *ubi_module_load_string;
+
+static unsigned char *write_buffer = NULL;
+static unsigned char *read_buffer = NULL;
+
+static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */
+
+static unsigned long next_seed = 1;
+
+static unsigned get_next_seed()
+{
+ next_seed = next_seed * 1103515245 + 12345;
+ return ((unsigned) (next_seed / 65536) % 32768);
+}
+
+static void error_exit(const char *msg)
+{
+ int eno = errno;
+ fprintf(stderr,"UBI Integrity Test Error: %s\n",msg);
+ if (eno) {
+ fprintf(stderr, "errno = %d\n", eno);
+ fprintf(stderr, "strerror = %s\n", strerror(eno));
+ }
+ exit(1);
+}
+
+static void *allocate(size_t n)
+{
+ void *p = malloc(n);
+ if (!p)
+ error_exit("Memory allocation failure");
+ memset(p, 0, n);
+ return p;
+}
+
+static unsigned get_random_number(unsigned n)
+{
+ uint64_t r, b;
+
+ if (n < 1)
+ return 0;
+ r = rand();
+ r *= n;
+ b = RAND_MAX;
+ b += 1;
+ r /= b;
+ return r;
+}
+
+static struct volume_fd *open_volume(struct volume_info *vol)
+{
+ struct volume_fd *s;
+ struct open_volume_fd *ofd;
+ int fd;
+
+ if (vol->fds) {
+ /* If already open dup it */
+ fd = dup(vol->fds->fd);
+ if (fd == -1)
+ error_exit("Failed to dup volume device file des");
+ } else {
+ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
+ if (fd == -1)
+ error_exit("Failed to open volume device file");
+ }
+ s = allocate(sizeof(*s));
+ s->fd = fd;
+ s->volume = vol;
+ s->next = vol->fds;
+ vol->fds = s;
+ /* Add to open volumes list */
+ ofd = allocate(sizeof(*ofd));
+ ofd->vol_fd = s;
+ ofd->next = open_volumes;
+ open_volumes = ofd;
+ open_volume_count += 1;
+ return 0;
+}
+
+static void close_volume(struct volume_fd *vol_fd)
+{
+ struct volume_fd *vfd, *vfd_last;
+ struct open_volume_fd *ofd, *ofd_last;
+ int fd = vol_fd->fd;
+
+ /* Remove from open volumes list */
+ ofd_last = NULL;
+ ofd = open_volumes;
+ while (ofd) {
+ if (ofd->vol_fd == vol_fd) {
+ if (ofd_last)
+ ofd_last->next = ofd->next;
+ else
+ open_volumes = ofd->next;
+ free(ofd);
+ open_volume_count -= 1;
+ break;
+ }
+ ofd_last = ofd;
+ ofd = ofd->next;
+ }
+ /* Remove from volume fd list */
+ vfd_last = NULL;
+ vfd = vol_fd->volume->fds;
+ while (vfd) {
+ if (vfd == vol_fd) {
+ if (vfd_last)
+ vfd_last->next = vfd->next;
+ else
+ vol_fd->volume->fds = vfd->next;
+ free(vfd);
+ break;
+ }
+ vfd_last = vfd;
+ vfd = vfd->next;
+ }
+ /* Close volume device file */
+ if (close(fd) == -1)
+ error_exit("Failed to close volume file descriptor");
+}
+
+static void set_random_data(unsigned seed, unsigned char *buf, int size)
+{
+ int i;
+ unsigned r;
+
+ r = rand();
+ srand(seed);
+ for (i = 0; i < size; ++i)
+ buf[i] = rand();
+ srand(r);
+}
+
+#if 0
+static void print_write_info(struct write_info *w)
+{
+ printf("Offset: %lld Size:%d Seed:%u\n", w->offset, w->size, w->random_seed);
+ fflush(stdout);
+}
+#endif
+
+static void check_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ struct write_info *w;
+ off64_t gap_end;
+ int leb_size = erase_block->volume->info.leb_size;
+ ssize_t bytes_read;
+
+ w = erase_block->writes;
+ gap_end = erase_block->offset + leb_size;
+ while (w) {
+ if (w->offset + w->size < gap_end) {
+ /* There is a gap. Check all 0xff */
+ off64_t gap_start = w->offset + w->size;
+ ssize_t size = gap_end - gap_start;
+ if (lseek64(fd, gap_start, SEEK_SET) != gap_start)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, size);
+ if (bytes_read != size)
+ error_exit("read failed in gap");
+ while (size)
+ if (read_buffer[--size] != 0xff) {
+ fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
+ fprintf(stderr, "offset = %lld\n" , (long long) gap_start);
+ fprintf(stderr, "size = %ld\n" , (long) bytes_read);
+ error_exit("verify 0xff failed");
+ }
+ }
+ if (lseek64(fd, w->offset, SEEK_SET) != w->offset)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , w->size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, w->size);
+ if (bytes_read != w->size) {
+ fprintf(stderr, "offset = %lld\n" , (long long) w->offset);
+ fprintf(stderr, "size = %ld\n" , (long) w->size);
+ fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read);
+ error_exit("read failed");
+ }
+ set_random_data(w->random_seed, write_buffer, w->size);
+ if (memcmp(read_buffer, write_buffer, w->size))
+ error_exit("verify failed");
+ gap_end = w->offset;
+ w = w->next;
+ }
+ if (gap_end > erase_block->offset) {
+ /* Check all 0xff */
+ off64_t gap_start = erase_block->offset;
+ ssize_t size = gap_end - gap_start;
+ if (lseek64(fd, gap_start, SEEK_SET) != gap_start)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, size);
+ if (bytes_read != size)
+ error_exit("read failed in gap");
+ while (size)
+ if (read_buffer[--size] != 0xff) {
+ fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
+ fprintf(stderr, "offset = %lld\n" , (long long) gap_start);
+ fprintf(stderr, "size = %ld\n" , (long) bytes_read);
+ error_exit("verify 0xff failed!");
+ }
+ }
+}
+
+static int write_to_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ int page_size = erase_block->volume->ubi_device->info.min_io_size;
+ int leb_size = erase_block->volume->info.leb_size;
+ int next_offset = 0;
+ int space, size;
+ off64_t offset;
+ unsigned seed;
+ struct write_info *w;
+
+ if (erase_block->writes)
+ next_offset = erase_block->writes->offset_within_block + erase_block->writes->size;
+ space = leb_size - next_offset;
+ if (space <= 0)
+ return 0; /* No space */
+ if (!get_random_number(10)) {
+ /* 1 time in 10 leave a gap */
+ next_offset += get_random_number(space);
+ next_offset = (next_offset / page_size) * page_size;
+ space = leb_size - next_offset;
+ }
+ if (get_random_number(2))
+ size = 1 * page_size;
+ else if (get_random_number(2))
+ size = 2 * page_size;
+ else if (get_random_number(2))
+ size = 3 * page_size;
+ else if (get_random_number(2))
+ size = 4 * page_size;
+ else {
+ if (get_random_number(4))
+ size = get_random_number(space);
+ else
+ size = space;
+ size = (size / page_size) * page_size;
+ }
+ if (size == 0 || size > space)
+ size = page_size;
+ if (next_offset + size > leb_size)
+ error_exit("internal error");
+ offset = erase_block->offset + next_offset;
+ if (offset < erase_block->top_of_data)
+ error_exit("internal error!");
+ if (lseek64(fd, offset, SEEK_SET) != offset)
+ error_exit("lseek64 failed");
+ /* Do write */
+ seed = get_next_seed();
+ if (!seed)
+ seed = 1;
+ set_random_data(seed, write_buffer, size);
+ if (write(fd, write_buffer, size) != size)
+ error_exit("write failed");
+ erase_block->top_of_data = offset + size;
+ /* Make write info and add to eb */
+ w = allocate(sizeof(*w));
+ w->offset_within_block = next_offset;
+ w->offset = offset;
+ w->size = size;
+ w->random_seed = seed;
+ w->next = erase_block->writes;
+ erase_block->writes = w;
+ erase_block->touched = 1;
+ erase_block->erased = 0;
+ total_written += size;
+ return 1;
+}
+
+static void erase_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ struct write_info *w;
+ uint32_t eb_no;
+ int res;
+
+ eb_no = erase_block->block_number;
+ res = ioctl(fd, UBI_IOCEBER, &eb_no);
+ if (res)
+ error_exit("Failed to erase an erase block");
+ /* Remove writes from this eb */
+ while (erase_block->writes) {
+ w = erase_block->writes;
+ erase_block->writes = erase_block->writes->next;
+ free(w);
+ }
+ erase_block->erased = 1;
+ erase_block->touched = 1;
+ erase_block->top_of_data = erase_block->offset;
+}
+
+static void operate_on_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ /*
+ Possible operations:
+ read from it and verify
+ write to it
+ erase it
+ */
+ int work_done = 1;
+ static int no_work_done_count = 0;
+
+ if (!get_random_number(10) && no_work_done_count <= 5) {
+ check_erase_block(erase_block, fd);
+ work_done = 0;
+ } else if (get_random_number(100)) {
+ if (!write_to_erase_block(erase_block, fd)) {
+ /* The erase block was full */
+ if (get_random_number(2) || no_work_done_count > 5)
+ erase_erase_block(erase_block, fd);
+ else
+ work_done = 0;
+ }
+ } else
+ erase_erase_block(erase_block, fd);
+ if (work_done)
+ no_work_done_count = 0;
+ else
+ no_work_done_count += 1;
+}
+
+static void operate_on_open_volume(struct volume_fd *vol_fd)
+{
+ /*
+ Possible operations:
+ operate on an erase block
+ close volume
+ */
+ if (get_random_number(100) == 0)
+ close_volume(vol_fd);
+ else {
+ /* Pick an erase block at random */
+ int eb_no = get_random_number(vol_fd->volume->info.rsvd_lebs);
+ operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd);
+ }
+}
+
+static void operate_on_volume(struct volume_info *vol)
+{
+ /*
+ Possible operations:
+ open it
+ resize it (must close fd's first) <- TODO
+ delete it (must close fd's first) <- TODO
+ */
+ open_volume(vol);
+}
+
+static int ubi_major(const char *device_file_name)
+{
+ struct stat buf;
+ static int maj = 0;
+
+ if (maj)
+ return maj;
+ if (stat(device_file_name, &buf) == -1)
+ error_exit("Failed to stat ubi device file");
+ maj = major(buf.st_rdev);
+ return maj;
+}
+
+static void operate_on_ubi_device(struct ubi_device_info *ubi_device)
+{
+ /*
+ TODO:
+ Possible operations:
+ create a new volume
+ operate on existing volume
+ */
+ /*
+ Simplified operation (i.e. only have 1 volume):
+ If there are no volumes create 1 volumne
+ Then operate on the volume
+ */
+ if (ubi_device->info.vol_count == 0) {
+ /* Create the one-and-only volume we will use */
+ char dev_name[1024];
+ int i, n, maj, fd;
+ struct volume_info *s;
+ struct ubi_mkvol_request req;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1; /* TODO: What is this? */
+ req.bytes = ubi_device->info.leb_size * max_ebs_per_vol;
+ if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes)
+ req.bytes = ubi_device->info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = "integ-test-vol";
+ if (ubi_mkvol(libubi, ubi_device->device_file_name, &req))
+ error_exit("ubi_mkvol failed");
+ s = allocate(sizeof(*s));
+ s->ubi_device = ubi_device;
+ if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info))
+ error_exit("ubi_get_vol_info failed");
+ n = s->info.rsvd_lebs;
+ s->erase_blocks = allocate(sizeof(struct erase_block_info) * n);
+ for (i = 0; i < n; ++i) {
+ s->erase_blocks[i].volume = s;
+ s->erase_blocks[i].block_number = i;
+ s->erase_blocks[i].offset = i * (off64_t) s->info.leb_size;
+ s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset;
+ }
+ /* FIXME: Correctly get device file name */
+ sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id);
+ s->device_file_name = strdup(dev_name);
+ ubi_device->volumes = s;
+ ubi_device->info.vol_count += 1;
+ sleep(1);
+ fd = open(s->device_file_name, O_RDONLY);
+ if (fd == -1) {
+ /* FIXME: Correctly make node */
+ maj = ubi_major(ubi_device->device_file_name);
+ sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1);
+ system(dev_name);
+ } else if (close(fd) == -1)
+ error_exit("Failed to close volume device file");
+ }
+ operate_on_volume(ubi_device->volumes);
+}
+
+static void do_an_operation(void)
+{
+ int too_few = (open_volume_count < info.dev_count * 3);
+ int too_many = (open_volume_count > info.dev_count * 5);
+
+ if (too_many || (!too_few && get_random_number(1000) > 0)) {
+ /* Operate on an open volume */
+ size_t pos;
+ struct open_volume_fd *ofd;
+ pos = get_random_number(open_volume_count);
+ for (ofd = open_volumes; pos && ofd && ofd->next; --pos)
+ ofd = ofd->next;
+ operate_on_open_volume(ofd->vol_fd);
+ } else if (info.dev_count > 0) {
+ /* Operate on a ubi device */
+ size_t ubi_pos = 0;
+ if (info.dev_count > 1)
+ ubi_pos = get_random_number(info.dev_count - 1);
+ operate_on_ubi_device(&ubi_array[ubi_pos]);
+ } else
+ error_exit("Internal error");
+}
+
+static void get_ubi_devices_info(void)
+{
+ int i, ubi_pos = 0;
+ char dev_name[1024];
+ ssize_t buf_size = 1024 * 128;
+
+ if (ubi_get_info(libubi, &info))
+ error_exit("ubi_get_info failed");
+ if (info.dev_count > MAX_UBI_DEVICES)
+ error_exit("Too many ubi devices");
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) {
+ struct ubi_device_info *s;
+ s = &ubi_array[ubi_pos++];
+ if (ubi_get_dev_info1(libubi, i, &s->info))
+ error_exit("ubi_get_dev_info1 failed");
+ if (s->info.vol_count)
+ error_exit("There are existing volumes");
+ /* FIXME: Correctly get device file name */
+ sprintf(dev_name, "/dev/ubi%d", i);
+ s->device_file_name = strdup(dev_name);
+ if (buf_size < s->info.leb_size)
+ buf_size = s->info.leb_size;
+ if (max_ebs_per_vol && s->info.leb_size * max_ebs_per_vol < s->info.avail_bytes)
+ total_space += s->info.leb_size * max_ebs_per_vol;
+ else
+ total_space += s->info.avail_bytes;
+ }
+ write_buffer = allocate(buf_size);
+ read_buffer = allocate(buf_size);
+}
+
+static void load_ubi(void)
+{
+ system("rmmod ubi");
+ if (system(ubi_module_load_string) != 0)
+ error_exit("Failed to load UBI module");
+ sleep(1);
+}
+
+static void do_some_operations(void)
+{
+ unsigned i = 0;
+ total_written = 0;
+ printf("Total space: %llu\n", (unsigned long long) total_space);
+ while (total_written < total_space * 3) {
+ do_an_operation();
+ if (i++ % 10000 == 0)
+ printf("Total written: %llu\n", (unsigned long long) total_written);
+ }
+ printf("Total written: %llu\n", (unsigned long long) total_written);
+}
+
+static void reload_ubi(void)
+{
+ /* Remove module */
+ if (system("rmmod ubi") != 0)
+ error_exit("Failed to remove UBI module");
+ /* Install module */
+ if (system(ubi_module_load_string) != 0)
+ error_exit("Failed to load UBI module");
+ sleep(1);
+}
+
+static void check_volume(struct volume_info *vol)
+{
+ struct erase_block_info *eb = vol->erase_blocks;
+ int pos;
+ int fd;
+
+ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
+ if (fd == -1)
+ error_exit("Failed to open volume device file");
+ for (pos = 0; pos < vol->info.rsvd_lebs; ++pos)
+ check_erase_block(eb++, fd);
+ if (close(fd) == -1)
+ error_exit("Failed to close volume device file");
+}
+
+static void check_ubi_device(struct ubi_device_info *ubi_device)
+{
+ struct volume_info *vol;
+
+ vol = ubi_device->volumes;
+ while (vol) {
+ check_volume(vol);
+ vol = vol->next;
+ }
+}
+
+static void check_ubi(void)
+{
+ int i;
+
+ for (i = 0; i < info.dev_count; ++i)
+ check_ubi_device(&ubi_array[i]);
+}
+
+static int is_all_digits(const char *s)
+{
+ const char *digits = "0123456789";
+ if (!s || !*s)
+ return 0;
+ for (;*s;++s)
+ if (!strchr(digits,*s))
+ return 0;
+ return 1;
+}
+
+static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
+{
+ const char *p = NULL;
+ int i = *pos;
+ size_t n = strlen(name);
+
+ if (strlen(argv[i]) > n)
+ p = argv[i] + n;
+ else if (++i < argc)
+ p = argv[i];
+ if (!is_all_digits(p))
+ return 1;
+ *result = atoll(p);
+ *pos = i;
+ return 0;
+}
+
+static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
+{
+ const char *p = NULL;
+ int i = *pos;
+ size_t n = strlen(name);
+
+ if (strlen(argv[i]) > n)
+ p = argv[i] + n;
+ else if (++i < argc)
+ p = argv[i];
+ if (p && *p == '=') {
+ p += 1;
+ if (!*p && ++i < argc)
+ p = argv[i];
+ }
+ if (!is_all_digits(p))
+ return 1;
+ *result = atoll(p);
+ *pos = i;
+ return 0;
+}
+
+static int remove_all_volumes(void)
+{
+ int i;
+
+ for (i = 0; i < info.dev_count; ++i) {
+ struct ubi_device_info *ubi_device = &ubi_array[i];
+ struct volume_info *vol;
+ vol = ubi_device->volumes;
+ while (vol) {
+ int res = ubi_rmvol(libubi,
+ ubi_device->device_file_name,
+ vol->info.vol_id);
+ if (res)
+ return res;
+ vol = vol->next;
+ }
+ }
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int i;
+ long long r, repeat = 1;
+ int initial_seed = 1, args_ok = 1;
+
+ printf("UBI Integrity Test\n");
+
+ /* Get arguments */
+ ubi_module_load_string = 0;
+ for (i = 1; i < argc; ++i) {
+ if (strncmp(argv[i], "-h", 2) == 0)
+ args_ok = 0;
+ else if (strncmp(argv[i], "--help", 6) == 0)
+ args_ok = 0;
+ else if (strncmp(argv[i], "-n", 2) == 0) {
+ if (get_short_arg(&i, "-n", &repeat, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "--repeat", 8) == 0) {
+ if (get_long_arg(&i, "--repeat", &repeat, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "-m", 2) == 0) {
+ if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "--maxebs", 8) == 0) {
+ if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv))
+ args_ok = 0;
+ } else if (!ubi_module_load_string)
+ ubi_module_load_string = argv[i];
+ else
+ args_ok = 0;
+ }
+ if (!args_ok || !ubi_module_load_string) {
+ fprintf(stderr, "Usage is: ubi_integ [<options>] <UBI Module load command>\n");
+ fprintf(stderr, " Options: \n");
+ fprintf(stderr, " -h, --help Help\n");
+ fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n");
+ fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n");
+ return 1;
+ }
+
+ initial_seed = getpid();
+ printf("Initial seed = %u\n", (unsigned) initial_seed);
+ next_seed = initial_seed;
+ srand(initial_seed);
+ load_ubi();
+
+ libubi = libubi_open();
+ if (!libubi)
+ error_exit("Failed to open libubi");
+
+ get_ubi_devices_info();
+
+ r = 0;
+ while (repeat == 0 || r++ < repeat) {
+ printf("Cycle %lld\n", r);
+ do_some_operations();
+
+ /* Close all volumes */
+ while (open_volumes)
+ close_volume(open_volumes->vol_fd);
+
+ check_ubi();
+
+ libubi_close(libubi);
+
+ reload_ubi();
+
+ libubi = libubi_open();
+ if (!libubi)
+ error_exit("Failed to open libubi");
+
+ check_ubi();
+ }
+
+ if (remove_all_volumes())
+ error_exit("Failed to remove all volumes");
+
+ libubi_close(libubi);
+
+ printf("UBI Integrity Test completed ok\n");
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/io_basic.c b/mtd-utils-1.3.1/tests/ubi-tests/io_basic.c
new file mode 100644
index 0000000..566514c
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/io_basic.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test basic UBI volume I/O capabilities.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+/**
+ * test_basic - check basic volume read and update capabilities.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_basic(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_basic()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = type;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ /* Make sure newly created volume contains only 0xFF bytes */
+ if (check_vol_patt(vol_node, 0xFF))
+ goto remove;
+
+ /* Write 0xA5 bytes to the volume */
+ if (update_vol_patt(vol_node, dev_info.avail_bytes, 0xA5))
+ goto remove;
+ if (check_vol_patt(vol_node, 0xA5))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/**
+ * test_aligned - test volume alignment feature.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_aligned(int type)
+{
+ unsigned int i, ebsz;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_aligned()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int alignments[] = ALIGNMENTS(dev_info.leb_size);
+
+ req.vol_type = type;
+ req.name = name;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * ebsz;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ /* Make sure newly created volume contains only 0xFF bytes */
+ if (check_vol_patt(vol_node, 0xFF))
+ goto remove;
+
+ /* Write 0xA5 bytes to the volume */
+ if (update_vol_patt(vol_node, req.bytes, 0xA5))
+ goto remove;
+ if (check_vol_patt(vol_node, 0xA5))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_basic(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_basic(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_aligned(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_aligned(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/io_paral.c b/mtd-utils-1.3.1/tests/ubi-tests/io_paral.c
new file mode 100644
index 0000000..935f46a
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/io_paral.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * This test does a lot of I/O to volumes in parallel.
+ */
+
+#define _XOPEN_SOURCE 500
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_paral"
+#include "common.h"
+
+#define THREADS_NUM 4
+#define ITERATIONS (1024 * 1)
+#define VOL_LEBS 10
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+static const char *node;
+static int vol_size;
+
+static struct ubi_mkvol_request reqests[THREADS_NUM + 1];
+static char vol_name[THREADS_NUM + 1][100];
+static char vol_nodes[THREADS_NUM + 1][strlen(UBI_VOLUME_PATTERN) + 100];
+static unsigned char *wbufs[THREADS_NUM + 1];
+static unsigned char *rbufs[THREADS_NUM + 1];
+
+static int update_volume(int vol_id, int bytes)
+{
+ int i, fd, ret, written = 0, rd = 0;
+ char *vol_node = vol_nodes[vol_id];
+ unsigned char *wbuf = wbufs[vol_id];
+ unsigned char *rbuf = rbufs[vol_id];
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", vol_node);
+ return -1;
+ }
+
+ for (i = 0; i < bytes; i++)
+ wbuf[i] = random() % 255;
+ memset(rbuf, '\0', bytes);
+
+ ret = ubi_update_start(libubi, fd, bytes);
+ if (ret) {
+ failed("ubi_update_start");
+ err_msg("volume id is %d", vol_id);
+ goto err_close;
+ }
+
+ while (written < bytes) {
+ int to_write = random() % (bytes - written);
+
+ if (to_write == 0)
+ to_write = 1;
+
+ ret = write(fd, wbuf + written, to_write);
+ if (ret != to_write) {
+ failed("write");
+ err_msg("failed to write %d bytes at offset %d "
+ "of volume %d", to_write, written,
+ vol_id);
+ err_msg("update: %d bytes", bytes);
+ goto err_close;
+ }
+
+ written += to_write;
+ }
+
+ close(fd);
+
+ fd = open(vol_node, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ /* read data back and check */
+ while (rd < bytes) {
+ int to_read = random() % (bytes - rd);
+
+ if (to_read == 0)
+ to_read = 1;
+
+ ret = read(fd, rbuf + rd, to_read);
+ if (ret != to_read) {
+ failed("read");
+ err_msg("failed to read %d bytes at offset %d "
+ "of volume %d", to_read, rd, vol_id);
+ goto err_close;
+ }
+
+ rd += to_read;
+ }
+
+ if (memcmp(wbuf, rbuf, bytes)) {
+ err_msg("written and read data are different");
+ goto err_close;
+ }
+
+ close(fd);
+ return 0;
+
+err_close:
+ close(fd);
+ return -1;
+}
+
+static void *update_thread(void *ptr)
+{
+ int vol_id = (long)ptr, i;
+
+ for (i = 0; i < ITERATIONS; i++) {
+ int ret, bytes = (random() % (vol_size - 1)) + 1;
+ int remove = !(random() % 16);
+
+ /* From time to time remove the volume */
+ if (remove) {
+ ret = ubi_rmvol(libubi, node, vol_id);
+ if (ret) {
+ failed("ubi_rmvol");
+ err_msg("cannot remove volume %d", vol_id);
+ return NULL;
+ }
+ ret = ubi_mkvol(libubi, node, &reqests[vol_id]);
+ if (ret) {
+ failed("ubi_mkvol");
+ err_msg("cannot create volume %d", vol_id);
+ return NULL;
+ }
+ }
+
+ ret = update_volume(vol_id, bytes);
+ if (ret)
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static void *write_thread(void *ptr)
+{
+ int ret, fd, vol_id = (long)ptr, i;
+ char *vol_node = vol_nodes[vol_id];
+ unsigned char *wbuf = wbufs[vol_id];
+ unsigned char *rbuf = rbufs[vol_id];
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", vol_node);
+ return NULL;
+ }
+
+ ret = ubi_set_property(fd, UBI_PROP_DIRECT_WRITE, 1);
+ if (ret) {
+ failed("ubi_set_property");
+ err_msg("cannot set property for \"%s\"\n", vol_node);
+ }
+
+ for (i = 0; i < ITERATIONS * VOL_LEBS; i++) {
+ int j, leb = random() % VOL_LEBS;
+ off_t offs = dev_info.leb_size * leb;
+
+ ret = ubi_leb_unmap(fd, leb);
+ if (ret) {
+ failed("ubi_leb_unmap");
+ err_msg("cannot unmap LEB %d", leb);
+ break;
+ }
+
+ for (j = 0; j < dev_info.leb_size; j++)
+ wbuf[j] = random() % 255;
+ memset(rbuf, '\0', dev_info.leb_size);
+
+ ret = pwrite(fd, wbuf, dev_info.leb_size, offs);
+ if (ret != dev_info.leb_size) {
+ failed("pwrite");
+ err_msg("cannot write %d bytes to offs %lld, wrote %d",
+ dev_info.leb_size, offs, ret);
+ break;
+ }
+
+ /* read data back and check */
+ ret = pread(fd, rbuf, dev_info.leb_size, offs);
+ if (ret != dev_info.leb_size) {
+ failed("read");
+ err_msg("failed to read %d bytes at offset %d "
+ "of volume %d", dev_info.leb_size, offs,
+ vol_id);
+ break;
+ }
+
+ if (memcmp(wbuf, rbuf, dev_info.leb_size)) {
+ err_msg("written and read data are different");
+ break;
+ }
+ }
+
+ close(fd);
+ return NULL;
+}
+
+int main(int argc, char * const argv[])
+{
+ int i, ret;
+ pthread_t threads[THREADS_NUM];
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ /*
+ * Create 1 volume more than threads count. The last volume
+ * will not change to let WL move more stuff.
+ */
+ vol_size = dev_info.leb_size * VOL_LEBS;
+ for (i = 0; i <= THREADS_NUM; i++) {
+ reqests[i].alignment = 1;
+ reqests[i].bytes = vol_size;
+ reqests[i].vol_id = i;
+ sprintf(vol_name[i], TESTNAME":%d", i);
+ reqests[i].name = vol_name[i];
+ reqests[i].vol_type = UBI_DYNAMIC_VOLUME;
+ if (i == THREADS_NUM)
+ reqests[i].vol_type = UBI_STATIC_VOLUME;
+ sprintf(vol_nodes[i], UBI_VOLUME_PATTERN, dev_info.dev_num, i);
+
+ if (ubi_mkvol(libubi, node, &reqests[i])) {
+ failed("ubi_mkvol");
+ goto remove;
+ }
+
+ wbufs[i] = malloc(vol_size);
+ rbufs[i] = malloc(vol_size);
+ if (!wbufs[i] || !rbufs[i]) {
+ failed("malloc");
+ goto remove;
+ }
+
+ ret = update_volume(i, vol_size);
+ if (ret)
+ goto remove;
+ }
+
+ for (i = 0; i < THREADS_NUM / 2; i++) {
+ ret = pthread_create(&threads[i], NULL, &write_thread, (void *)(long)i);
+ if (ret) {
+ failed("pthread_create");
+ goto remove;
+ }
+ }
+
+ for (i = THREADS_NUM / 2; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL, &update_thread, (void *)(long)i);
+ if (ret) {
+ failed("pthread_create");
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < THREADS_NUM; i++)
+ pthread_join(threads[i], NULL);
+
+ for (i = 0; i <= THREADS_NUM; i++) {
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ goto remove;
+ }
+ if (wbufs[i])
+ free(wbufs[i]);
+ if (rbufs[i])
+ free(rbufs[i]);
+ wbufs[i] = NULL;
+ rbufs[i] = NULL;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+remove:
+ for (i = 0; i <= THREADS_NUM; i++) {
+ ubi_rmvol(libubi, node, i);
+ if (wbufs[i])
+ free(wbufs[i]);
+ if (rbufs[i])
+ free(rbufs[i]);
+ wbufs[i] = NULL;
+ rbufs[i] = NULL;
+ }
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/io_read.c b/mtd-utils-1.3.1/tests/ubi-tests/io_read.c
new file mode 100644
index 0000000..57a8da7
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/io_read.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test UBI volume read.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+static int fd;
+
+/* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */
+#define LENGTHES(io, s) \
+ {1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io), \
+ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \
+ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \
+ 2*(s)+(io), 3*(s), 3*(s)+(io)};
+
+/*
+ * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz -
+ * volume size.
+ */
+#define OFFSETS(io, s, sz) \
+ {0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io), \
+ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \
+ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \
+ 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1};
+
+/**
+ * test_static - test static volume-specific features.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_static(void)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_basic()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_vol_info vol_info;
+ int fd, ret;
+ char buf[20];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto close;
+ }
+
+ /* Make sure new static volume contains no data */
+ if (vol_info.data_bytes != 0) {
+ err_msg("data_bytes = %lld, not zero", vol_info.data_bytes);
+ goto close;
+ }
+
+ /* Ensure read returns EOF */
+ ret = read(fd, buf, 1);
+ if (ret < 0) {
+ failed("read");
+ goto close;
+ }
+ if (ret != 0) {
+ err_msg("read data from free static volume");
+ goto close;
+ }
+
+ if (ubi_update_start(libubi, fd, 10)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ ret = write(fd, buf, 10);
+ if (ret < 0) {
+ failed("write");
+ goto close;
+ }
+ if (ret != 10) {
+ err_msg("written %d bytes", ret);
+ goto close;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ failed("seek");
+ goto close;
+ }
+ ret = read(fd, buf, 20);
+ if (ret < 0) {
+ failed("read");
+ goto close;
+ }
+ if (ret != 10) {
+ err_msg("read %d bytes", ret);
+ goto close;
+ }
+
+ close(fd);
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+close:
+ close(fd);
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/*
+ * A helper function for test_read2().
+ */
+static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off)
+{
+ int i, len1;
+ unsigned char ck_buf[len], buf[len];
+ off_t new_off;
+
+ if (off + len > vol_info->data_bytes)
+ len1 = vol_info->data_bytes - off;
+ else
+ len1 = len;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ failed("seek");
+ err_msg("len = %d", len);
+ return -1;
+ }
+ if (read(fd, buf, len) != len1) {
+ failed("read");
+ err_msg("len = %d", len);
+ return -1;
+ }
+
+ new_off = lseek(fd, 0, SEEK_CUR);
+ if (new_off != off + len1) {
+ if (new_off == -1)
+ failed("lseek");
+ else
+ err_msg("read %d bytes from %lld, but resulting "
+ "offset is %lld", len1, (long long) off, (long long) new_off);
+ return -1;
+ }
+
+ for (i = 0; i < len1; i++)
+ ck_buf[i] = (unsigned char)(off + i);
+
+ if (memcmp(buf, ck_buf, len1)) {
+ err_msg("incorrect data read from offset %lld",
+ (long long)off);
+ err_msg("len = %d", len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * A helper function for test_read1().
+ */
+static int test_read2(const struct ubi_vol_info *vol_info, int len)
+{
+ int i;
+ off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->leb_size,
+ vol_info->data_bytes);
+
+ for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) {
+ if (test_read3(vol_info, len, offsets[i])) {
+ err_msg("offset = %d", offsets[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * A helper function for test_read().
+ */
+static int test_read1(struct ubi_vol_info *vol_info)
+{
+ int i, written = 0;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->leb_size);
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ /* Write some pattern to the volume */
+ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) {
+ failed("ubi_update_start");
+ err_msg("bytes = %lld", vol_info->rsvd_bytes);
+ goto close;
+ }
+
+ while (written < vol_info->rsvd_bytes) {
+ int i, ret;
+ unsigned char buf[512];
+
+ for (i = 0; i < 512; i++)
+ buf[i] = (unsigned char)(written + i);
+
+ ret = write(fd, buf, 512);
+ if (ret == -1) {
+ failed("write");
+ err_msg("written = %d, ret = %d", written, ret);
+ goto close;
+ }
+ written += ret;
+ }
+
+ close(fd);
+
+ if (ubi_get_vol_info(libubi, vol_node, vol_info)) {
+ failed("ubi_get_vol_info");
+ return -1;
+ }
+
+ fd = open(vol_node, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) {
+ if (test_read2(vol_info, lengthes[i])) {
+ err_msg("length = %d", lengthes[i]);
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * test_read - test UBI volume reading from different offsets.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_read(int type)
+{
+ const char *name = TESTNAME ":test_read()";
+ int alignments[] = ALIGNMENTS(dev_info.leb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_mkvol_request req;
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int leb_size;
+ struct ubi_vol_info vol_info;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * leb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_read1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_static())
+ goto close;
+ if (test_read(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_read(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/io_update.c b/mtd-utils-1.3.1/tests/ubi-tests/io_update.c
new file mode 100644
index 0000000..dce4ee2
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/io_update.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test UBI volume update and atomic LEB change
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libubi.h>
+#include <mtd/ubi-user.h>
+#define TESTNAME "io_update"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+#define SEQUENCES(io, s) { \
+ {3*(s)-(io)-1, 1}, \
+ {512}, \
+ {666}, \
+ {2048}, \
+ {(io), (io), PAGE_SIZE}, \
+ {(io)+1, (io)+1, PAGE_SIZE}, \
+ {PAGE_SIZE}, \
+ {PAGE_SIZE-1}, \
+ {PAGE_SIZE+(io)}, \
+ {(s)}, \
+ {(s)-1}, \
+ {(s)+1}, \
+ {(io), (s)+1}, \
+ {(s)+(io), PAGE_SIZE}, \
+ {2*(s), PAGE_SIZE}, \
+ {PAGE_SIZE, 2*(s), 1}, \
+ {PAGE_SIZE, 2*(s)}, \
+ {2*(s)-1, 2*(s)-1}, \
+ {3*(s), PAGE_SIZE + 1}, \
+ {1, PAGE_SIZE}, \
+ {(io), (s)} \
+}
+
+#define SEQ_SZ 21
+
+/*
+ * test_update1 - helper function for test_update().
+ */
+static int test_update1(struct ubi_vol_info *vol_info, int leb_change)
+{
+ long long total_len = leb_change ? vol_info->leb_size
+ : vol_info->rsvd_bytes;
+ int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size,
+ leb_change ? dev_info.min_io_size * 2
+ : vol_info->leb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned char buf[total_len];
+ int fd, i, j;
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ for (i = 0; i < SEQ_SZ; i++) {
+ int ret, stop = 0, len = 0;
+ off_t off = 0;
+ long long test_len;
+ unsigned char buf1[total_len];
+
+ /*
+ * test_len is LEB size (if we test atomic LEB change) or
+ * volume size (if we test update). For better test coverage,
+ * use a little smaller LEB change/update length.
+ */
+ test_len = total_len - (rand() % (total_len / 10));
+
+ if (leb_change) {
+ if (ubi_leb_change_start(libubi, fd, 0, test_len,
+ UBI_SHORTTERM)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+ } else {
+ if (ubi_update_start(libubi, fd, test_len)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+ }
+
+ for (j = 0; off < test_len; j++) {
+ int n, rnd_len, l;
+
+ if (!stop) {
+ if (sequences[i][j] != 0)
+ l = len = sequences[i][j];
+ else
+ stop = 1;
+ }
+
+ /*
+ * Fill some part of the write buffer with random data,
+ * and the other part with 0xFFs to test how UBI
+ * stripes 0xFFs multiple of I/O unit size.
+ */
+ if (off + l > test_len)
+ l = test_len - off;
+ rnd_len = rand() % (l + 1);
+ for (n = 0; n < rnd_len; n++)
+ buf[off + n] = (unsigned char)rand();
+ memset(buf + off + rnd_len, 0xFF, l - rnd_len);
+
+ /*
+ * Deliberately pass len instead of l (len may be
+ * greater then l if this is the last chunk) because
+ * UBI have to read only l bytes anyway.
+ */
+ ret = write(fd, buf + off, len);
+ if (ret < 0) {
+ failed("write");
+ err_msg("failed to write %d bytes at offset "
+ "%lld", len, (long long)off);
+ goto close;
+ }
+ len = l;
+ if (ret != len) {
+ err_msg("failed to write %d bytes at offset "
+ "%lld, wrote %d", len, (long long)off, ret);
+ goto close;
+ }
+ off += len;
+ }
+
+ /* Check data */
+ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) {
+ failed("lseek");
+ err_msg("cannot seek to 0");
+ goto close;
+ }
+
+ memset(buf1, 0x01, test_len);
+
+ if (vol_info->type == UBI_STATIC_VOLUME)
+ /*
+ * Static volume must not let use read more then it
+ * contains.
+ */
+ ret = read(fd, buf1, test_len + 100);
+ else
+ ret = read(fd, buf1, test_len);
+ if (ret < 0) {
+ failed("read");
+ err_msg("failed to read %d bytes", test_len);
+ goto close;
+ }
+ if (ret != test_len) {
+ err_msg("failed to read %d bytes, read %d", test_len, ret);
+ goto close;
+ }
+ if (memcmp(buf, buf1, test_len)) {
+ err_msg("data corruption");
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * test_update - check volume update and atomic LEB change capabilities.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_update(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_update()";
+ int alignments[] = ALIGNMENTS(dev_info.leb_size);
+ struct ubi_vol_info vol_info;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int leb_size;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * leb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_update1(&vol_info, 0)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (vol_info.type != UBI_STATIC_VOLUME) {
+ if (test_update1(&vol_info, 1)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_update(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_update(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/mkvol_bad.c b/mtd-utils-1.3.1/tests/ubi-tests/mkvol_bad.c
new file mode 100644
index 0000000..2e3c450
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/mkvol_bad.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test UBI volume creation and deletion ioctl()s with bad input and in case of
+ * incorrect usage.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_bad"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+/**
+ * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters.
+ *
+ * This function returns %0 if the test passed and %-1 if not.
+ */
+static int test_mkvol(void)
+{
+ int ret, i;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_mkvol()";
+
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ /* Bad volume ID */
+ req.vol_id = -2;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id))
+ return -1;
+
+ req.vol_id = dev_info.max_vol_count;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id))
+ return -1;
+
+ /* Bad alignment */
+ req.vol_id = 0;
+ req.alignment = 0;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ req.alignment = -1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ req.alignment = dev_info.leb_size + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ if (dev_info.min_io_size > 1) {
+ req.alignment = dev_info.min_io_size + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+ }
+
+ /* Bad bytes */
+ req.alignment = 1;
+ req.bytes = -1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.bytes = 0;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.bytes = dev_info.avail_bytes + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.alignment = dev_info.leb_size - dev_info.min_io_size;
+ req.bytes = (dev_info.leb_size - dev_info.leb_size % req.alignment) *
+ dev_info.avail_lebs + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ /* Bad vol_type */
+ req.alignment = 1;
+ req.bytes = dev_info.leb_size;
+ req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d",
+ req.vol_type))
+ return -1;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+
+ /* Too long name */
+ {
+ char name[UBI_VOL_NAME_MAX + 5];
+
+ memset(name, 'x', UBI_VOL_NAME_MAX + 1);
+ name[UBI_VOL_NAME_MAX + 1] = '\0';
+
+ req.name = name;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d",
+ UBI_VOL_NAME_MAX + 1))
+ return -1;
+ }
+
+ /* Try to create 2 volumes with the same ID and name */
+ req.name = name;
+ req.vol_id = 0;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "volume with ID 0 created twice"))
+ return -1;
+
+ req.vol_id = 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "volume with name \"%s\" created twice", name))
+ return -1;
+
+ if (ubi_rmvol(libubi, node, 0)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Try to use too much space */
+ req.vol_id = 0;
+ req.bytes = dev_info.avail_bytes;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ req.bytes = 1;
+ req.vol_id = 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "created volume of maximum size %lld, but still "
+ "can create more volumes", dev_info.avail_bytes))
+ return -1;
+
+ if (ubi_rmvol(libubi, node, 0)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Try to create too many volumes */
+ for (i = 0; i < dev_info.max_vol_count; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(nm, "%s:%d", name, i);
+ req.name = nm;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ /*
+ * Note, because of gluebi we may be unable to create
+ * dev_info.max_vol_count devices (MTD restrictions).
+ */
+ if (errno == ENFILE)
+ break;
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
+
+/**
+ * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters.
+ *
+ * This function returns %0 if the test passed and %-1 if not.
+ */
+static int test_rmvol(void)
+{
+ int ret;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_rmvol()";
+
+ /* Bad vol_id */
+ ret = ubi_rmvol(libubi, node, -1);
+ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1"))
+ return -1;
+
+ ret = ubi_rmvol(libubi, node, dev_info.max_vol_count);
+ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d",
+ dev_info.max_vol_count))
+ return -1;
+
+ /* Try to remove non-existing volume */
+ ret = ubi_rmvol(libubi, node, 0);
+ if (check_failed(ret, ENODEV, "ubi_rmvol",
+ "removed non-existing volume 0"))
+ return -1;
+
+ /* Try to remove volume twice */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ ret = ubi_rmvol(libubi, node, req.vol_id);
+ if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice",
+ req.vol_id))
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_mkvol())
+ goto close;
+
+ if (test_rmvol())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/mkvol_basic.c b/mtd-utils-1.3.1/tests/ubi-tests/mkvol_basic.c
new file mode 100644
index 0000000..880c149
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/mkvol_basic.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test test checks basic volume creation and deletion capabilities.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+/**
+ * mkvol_alignment - create volumes with different alignments.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_alignment(void)
+{
+ struct ubi_mkvol_request req;
+ int i, vol_id, ebsz;
+ const char *name = TESTNAME ":mkvol_alignment()";
+ int alignments[] = ALIGNMENTS(dev_info.leb_size);
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ /* Alignment should actually be multiple of min. I/O size */
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ /* Bear in mind alignment reduces EB size */
+ ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment;
+ req.bytes = dev_info.avail_lebs * ebsz;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ err_msg("alignment %d", req.alignment);
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_basic - simple test that checks basic volume creation capability.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_basic(void)
+{
+ struct ubi_mkvol_request req;
+ struct ubi_vol_info vol_info;
+ int vol_id, ret;
+ const char *name = TESTNAME ":mkvol_basic()";
+
+ /* Create dynamic volume of maximum size */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Create static volume of maximum size */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", vol_id);
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_multiple - test multiple volumes creation
+ *
+ * Thus function returns %0 if the test passed and %-1 if not.
+ */
+static int mkvol_multiple(void)
+{
+ struct ubi_mkvol_request req;
+ int i, ret, max = dev_info.max_vol_count;
+ const char *name = TESTNAME ":mkvol_multiple()";
+
+ /* Create maximum number of volumes */
+ for (i = 0; i < max; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(nm, "%s:%d", name, i);
+ req.name = nm;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ if (errno == ENFILE) {
+ max = i;
+ break;
+ }
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req)) {
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < max; i++) {
+ struct ubi_vol_info vol_info;
+
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", i);
+ goto remove;
+ }
+ }
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (mkvol_basic())
+ goto close;
+
+ if (mkvol_alignment())
+ goto close;
+
+ if (mkvol_multiple())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/mkvol_paral.c b/mtd-utils-1.3.1/tests/ubi-tests/mkvol_paral.c
new file mode 100644
index 0000000..8c6e02f
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/mkvol_paral.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * This test creates and deletes volumes in parallel.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_paral"
+#include "common.h"
+
+#define THREADS_NUM 4
+#define ITERATIONS 500
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+static int iterations = ITERATIONS;
+
+/**
+ * the_thread - the testing thread.
+ *
+ * @ptr thread number
+ */
+static void * the_thread(void *ptr)
+{
+ int n = (long)ptr, iter = iterations;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":the_thread()";
+ char nm[strlen(name) + 50];
+
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes/ITERATIONS;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ sprintf(nm, "%s:%d", name, n);
+ req.name = nm;
+
+ while (iter--) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return NULL;
+ }
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+int main(int argc, char * const argv[])
+{
+ int i, ret;
+ pthread_t threads[THREADS_NUM];
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)(long)i);
+ if (ret) {
+ failed("pthread_create");
+ goto close;
+ }
+ }
+
+ for (i = 0; i < THREADS_NUM; i++)
+ pthread_join(threads[i], NULL);
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/rsvol.c b/mtd-utils-1.3.1/tests/ubi-tests/rsvol.c
new file mode 100644
index 0000000..8ec93bc
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/rsvol.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Tes UBI volume re-size.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "rsvol"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+/**
+ * test_basic - check volume re-size capability.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_basic(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_basic()";
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = MIN_AVAIL_EBS * dev_info.leb_size;
+ req.vol_type = type;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ req.bytes = dev_info.leb_size;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.leb_size;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ req.bytes -= 1;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/*
+ * Helper function for test_rsvol().
+ */
+static int test_rsvol1(struct ubi_vol_info *vol_info)
+{
+ long long bytes;
+ struct ubi_vol_info vol_info1;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned char buf[vol_info->rsvd_bytes];
+ int fd, i, ret;
+
+ /* Make the volume smaller and check basic volume I/O */
+ bytes = vol_info->rsvd_bytes - vol_info->leb_size;
+ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id,
+ &vol_info1)) {
+ failed("ubi_get_vol_info");
+ return -1;
+ }
+
+ if (vol_info1.rsvd_bytes != bytes) {
+ err_msg("rsvd_bytes %lld, must be %lld",
+ vol_info1.rsvd_bytes, bytes);
+ return -1;
+ }
+
+ if (vol_info1.rsvd_lebs != vol_info->rsvd_lebs - 1) {
+ err_msg("rsvd_lebs %d, must be %d",
+ vol_info1.rsvd_lebs, vol_info->rsvd_lebs - 1);
+ return -1;
+ }
+
+ /* Write data to the volume */
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", vol_node);
+ return -1;
+ }
+
+ bytes = vol_info->rsvd_bytes - vol_info->leb_size - 1;
+ if (ubi_update_start(libubi, fd, bytes)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ for (i = 0; i < bytes; i++)
+ buf[i] = (unsigned char)i;
+
+ ret = write(fd, buf, bytes);
+ if (ret != bytes) {
+ failed("write");
+ goto close;
+ }
+
+ close(fd);
+
+ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ if (ubi_rsvol(libubi, node, vol_info->vol_id,
+ vol_info->leb_size * dev_info.avail_lebs)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", vol_node);
+ return -1;
+ }
+
+ /* Read data back */
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ failed("seek");
+ goto close;
+ }
+ memset(buf, 0, bytes);
+ ret = read(fd, buf, bytes);
+ if (ret != bytes) {
+ failed("read");
+ goto close;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ if (buf[i] != (unsigned char)i) {
+ err_msg("bad data");
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * test_rsvol - test UBI volume re-size.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_rsvol(int type)
+{
+ const char *name = TESTNAME "test_rsvol:()";
+ int alignments[] = ALIGNMENTS(dev_info.leb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_mkvol_request req;
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int leb_size;
+ struct ubi_vol_info vol_info;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * leb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_rsvol1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_basic(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_basic(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_rsvol(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_rsvol(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/runtests.sh b/mtd-utils-1.3.1/tests/ubi-tests/runtests.sh
new file mode 100755
index 0000000..708cce4
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/runtests.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+ubidev="$1"
+tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update
+io_paral volrefcnt"
+
+if test -z "$ubidev";
+then
+ echo "Usage:"
+ echo "$0 <UBI device>"
+ exit 1
+fi
+
+ubiname=`echo $ubidev | cut -d/ -f3`
+
+major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1`
+
+for minor in `seq 0 4`; do
+ if test ! -e ${ubidev}_${minor} ;
+ then
+ mknod ${ubidev}_${minor} c $major $(($minor + 1))
+ fi
+done
+
+if ! test -c "$ubidev";
+then
+ echo "Error: $ubidev is not character device"
+ exit 1
+fi
+
+for t in `echo $tests`;
+do
+ echo "Running $t $ubidev"
+ "./$t" "$ubidev" || exit 1
+done
+
+echo SUCCESS
+
+exit 0
diff --git a/mtd-utils-1.3.1/tests/ubi-tests/volrefcnt.c b/mtd-utils-1.3.1/tests/ubi-tests/volrefcnt.c
new file mode 100644
index 0000000..a56deae
--- /dev/null
+++ b/mtd-utils-1.3.1/tests/ubi-tests/volrefcnt.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) Nokia Corporation, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test volume reference counting - create a volume, open a sysfs file
+ * belonging to the volume, delete the volume but do not close the file, make
+ * sure the file cannot be read, close the file, make sure the volume
+ * disappeard, make sure its sysfs subtree disappeared.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "libubi.h"
+#define TESTNAME "rmvol"
+#include "common.h"
+
+#define SYSFS_FILE "/sys/class/ubi/ubi%d_%d/usable_eb_size"
+
+int main(int argc, char * const argv[])
+{
+ int ret, fd;
+ char fname[sizeof(SYSFS_FILE) + 20];
+ const char *node;
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+ struct ubi_mkvol_request req;
+ char tmp[100];
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto out_libubi;
+ }
+
+ /* Create a small dynamic volume */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = dev_info.min_io_size;
+ req.bytes = dev_info.leb_size;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = "rmvol";
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ goto out_libubi;
+ }
+
+ /* Open volume-related sysfs file */
+ sprintf(fname, SYSFS_FILE, dev_info.dev_num, req.vol_id);
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ err_msg("cannot open %s", fname);
+ failed("open");
+ goto out_rmvol;
+ }
+
+ /* Remove the volume, but do not close the file */
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ perror("ubi_rmvol");
+ goto out_close;
+ }
+
+ /* Try to read from the file, this should fail */
+ ret = read(fd, tmp, 100);
+ if (ret != -1) {
+ err_msg("read returned %d, expected -1", ret);
+ failed("read");
+ goto out_close;
+ }
+
+ /* Close the file and try to open it again, should fail */
+ close(fd);
+ fd = open(fname, O_RDONLY);
+ if (fd != -1) {
+ err_msg("opened %s again, open returned %d, expected -1",
+ fname, fd);
+ failed("open");
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_rmvol:
+ ubi_rmvol(libubi, node, req.vol_id);
+out_libubi:
+ libubi_close(libubi);
+ return 1;
+
+out_close:
+ close(fd);
+ libubi_close(libubi);
+ return 1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/LICENSE.libiniparser b/mtd-utils-1.3.1/ubi-utils/LICENSE.libiniparser
new file mode 100644
index 0000000..dbfa45d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/LICENSE.libiniparser
@@ -0,0 +1,21 @@
+Copyright (c) 2000-2007 by Nicolas Devillard.
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/mtd-utils-1.3.1/ubi-utils/Makefile b/mtd-utils-1.3.1/ubi-utils/Makefile
new file mode 100644
index 0000000..b3d0471
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/Makefile
@@ -0,0 +1,61 @@
+#
+# Makefile for ubi-utils
+#
+
+KERNELHDR := ../include
+
+# CFLAGS += -Werror
+CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR)
+
+LIBS = libubi libmtd libubigen libiniparser libscan
+TARGETS = ubicpvol ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
+ ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol
+
+VPATH = src
+
+include ../common.mk
+
+# And the below is the rule to get final executable from its .o and common.o
+$(TARGETS): $(addprefix $(BUILDDIR)/,\
+ libubi.a common.o)
+# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lubi -o $@
+
+$(BUILDDIR)/ubicrc32: $(addprefix $(BUILDDIR)/,\
+ ubicrc32.o crc32.o)
+# $(CC) $(CFLAGS) -o $@ $^
+
+$(BUILDDIR)/ubinize: $(addprefix $(BUILDDIR)/,\
+ ubinize.o common.o crc32.o libiniparser.a libubigen.a)
+# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -liniparser -lubigen -o $@
+
+$(BUILDDIR)/mtdinfo: $(addprefix $(BUILDDIR)/,\
+ libmtd.a libubigen.a crc32.o common.o)
+# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lubigen -o $@
+
+$(BUILDDIR)/ubiformat: $(addprefix $(BUILDDIR)/,\
+ ubiformat.o common.o crc32.o libmtd.a libscan.a libubi.a libubigen.a)
+# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@
+
+$(BUILDDIR)/libubi.a: $(BUILDDIR)/libubi.o
+
+$(BUILDDIR)/libmtd.a: $(BUILDDIR)/libmtd.o $(BUILDDIR)/libmtd_legacy.o
+
+$(BUILDDIR)/libubigen.a: $(BUILDDIR)/libubigen.o
+
+$(BUILDDIR)/libiniparser.a: $(addprefix $(BUILDDIR)/,\
+ libiniparser.o dictionary.o)
+
+$(BUILDDIR)/libscan.a: $(addprefix $(BUILDDIR)/,\
+ libscan.o crc32.o)
+
+clean::
+ rm -f $(addsuffix .a, $(LIBS))
+
+install::
+ mkdir -p ${DESTDIR}/${SBINDIR}
+ install -m 0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
+
+uninstall:
+ for file in ${TARGETS}; do \
+ $(RM) ${DESTDIR}/${SBINDIR}/$$file; \
+ done
diff --git a/mtd-utils-1.3.1/ubi-utils/include/libiniparser.h b/mtd-utils-1.3.1/ubi-utils/include/libiniparser.h
new file mode 100644
index 0000000..be3c667
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/include/libiniparser.h
@@ -0,0 +1,280 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.h
+ @author N. Devillard
+ @date Sep 2007
+ @version 3.0
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+/*
+ $Id: iniparser.h,v 1.24 2007-11-23 21:38:19 ndevilla Exp $
+ $Revision: 1.24 $
+*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include <unistd.h> */
+
+#include "dictionary.h"
+
+/*---------------------------------------------------------------------------
+ Macros
+ ---------------------------------------------------------------------------*/
+/** For backwards compatibility only */
+#define iniparser_getstr(d, k) iniparser_getstring(d, k, NULL)
+#define iniparser_setstr iniparser_setstring
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_setstring(dictionary * ini, char * entry, char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#endif
diff --git a/mtd-utils-1.3.1/ubi-utils/include/libmtd.h b/mtd-utils-1.3.1/ubi-utils/include/libmtd.h
new file mode 100644
index 0000000..c8b44e8
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/include/libmtd.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2008, 2009 Nokia Corporation
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * MTD library.
+ */
+
+#ifndef __LIBMTD_H__
+#define __LIBMTD_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Maximum MTD device name length */
+#define MTD_NAME_MAX 127
+/* Maximum MTD device type string length */
+#define MTD_TYPE_MAX 64
+
+/* MTD library descriptor */
+typedef void * libmtd_t;
+
+/**
+ * @dev_count: count of MTD devices in system
+ * @lowest_dev_num: lowest MTD device number
+ * @highest_dev_num: highest MTD device number
+ * @sysfs_supported: non-zero if sysfs is supported by MTD
+ */
+struct mtd_info
+{
+ int dev_count;
+ int lowest_dev_num;
+ int highest_dev_num;
+ unsigned int sysfs_supported:1;
+};
+
+/**
+ * struct mtd_dev_info - information about an MTD device.
+ * @dev_num: MTD device number
+ * @major: major number of corresponding character device
+ * @minor: minor number of corresponding character device
+ * @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h)
+ * @type_str: static R/O flash type string
+ * @name: device name
+ * @size: device size in bytes
+ * @eb_cnt: count of eraseblocks
+ * @eb_size: eraseblock size
+ * @min_io_size: minimum input/output unit size
+ * @subpage_size: sub-page size
+ * @oob_size: OOB size (zero if the device does not have OOB area)
+ * @region_cnt: count of additional erase regions
+ * @writable: zero if the device is read-only
+ * @bb_allowed: non-zero if the MTD device may have bad eraseblocks
+ */
+struct mtd_dev_info
+{
+ int dev_num;
+ int major;
+ int minor;
+ int type;
+ const char type_str[MTD_TYPE_MAX + 1];
+ const char name[MTD_NAME_MAX + 1];
+ long long size;
+ int eb_cnt;
+ int eb_size;
+ int min_io_size;
+ int subpage_size;
+ int oob_size;
+ int region_cnt;
+ unsigned int writable:1;
+ unsigned int bb_allowed:1;
+};
+
+/**
+ * libmtd_open - open MTD library.
+ *
+ * This function initializes and opens the MTD library and returns MTD library
+ * descriptor in case of success and %NULL in case of failure. In case of
+ * failure, errno contains zero if MTD is not present in the system, or
+ * contains the error code if a real error happened.
+ */
+libmtd_t libmtd_open(void);
+
+/**
+ * libmtd_close - close MTD library.
+ * @desc: MTD library descriptor
+ */
+void libmtd_close(libmtd_t desc);
+
+/**
+ * mtd_get_info - get general MTD information.
+ * @desc: MTD library descriptor
+ * @info: the MTD device information is returned here
+ *
+ * This function fills the passed @info object with general MTD information and
+ * returns %0 in case of success and %-1 in case of failure. If MTD subsystem is
+ * not present in the system, errno is set to @ENODEV.
+ */
+int mtd_get_info(libmtd_t desc, struct mtd_info *info);
+
+/**
+ * mtd_get_dev_info - get information about an MTD device.
+ * @desc: MTD library descriptor
+ * @node: name of the MTD device node
+ * @mtd: the MTD device information is returned here
+ *
+ * This function gets information about MTD device defined by the @node device
+ * node file and saves this information in the @mtd object. Returns %0 in case
+ * of success and %-1 in case of failure. If MTD subsystem is not present in the
+ * system, or the MTD device does not exist, errno is set to @ENODEV.
+ */
+int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd);
+
+/**
+ * mtd_get_dev_info1 - get information about an MTD device.
+ * @desc: MTD library descriptor
+ * @dev_num: MTD device number to fetch information about
+ * @mtd: the MTD device information is returned here
+ *
+ * This function is identical to 'mtd_get_dev_info()' except that it accepts
+ * MTD device number, not MTD character device.
+ */
+int mtd_get_dev_info1(libmtd_t desc, int dev_num, struct mtd_dev_info *mtd);
+
+/**
+ * mtd_erase - erase an eraseblock.
+ * @mtd: MTD device description object
+ * @fd: MTD device node file descriptor
+ * @eb: eraseblock to erase
+ *
+ * This function erases eraseblock @eb of MTD device described by @fd. Returns
+ * %0 in case of success and %-1 in case of failure.
+ */
+int mtd_erase(const struct mtd_dev_info *mtd, int fd, int eb);
+
+/**
+ * mtd_torture - torture an eraseblock.
+ * @mtd: MTD device description object
+ * @fd: MTD device node file descriptor
+ * @eb: eraseblock to torture
+ *
+ * This function tortures eraseblock @eb. Returns %0 in case of success and %-1
+ * in case of failure.
+ */
+int mtd_torture(const struct mtd_dev_info *mtd, int fd, int eb);
+
+/**
+ * mtd_is_bad - check if eraseblock is bad.
+ * @mtd: MTD device description object
+ * @fd: MTD device node file descriptor
+ * @eb: eraseblock to check
+ *
+ * This function checks if eraseblock @eb is bad. Returns %0 if not, %1 if yes,
+ * and %-1 in case of failure.
+ */
+int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb);
+
+/**
+ * mtd_mark_bad - mark an eraseblock as bad.
+ * @mtd: MTD device description object
+ * @fd: MTD device node file descriptor
+ * @eb: eraseblock to mark as bad
+ *
+ * This function marks eraseblock @eb as bad. Returns %0 in case of success and
+ * %-1 in case of failure.
+ */
+int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb);
+
+/**
+ * mtd_read - read data from an MTD device.
+ * @mtd: MTD device description object
+ * @fd: MTD device node file descriptor
+ * @eb: eraseblock to read from
+ * @offs: offset withing the eraseblock to read from
+ * @buf: buffer to read data to
+ * @len: how many bytes to read
+ *
+ * This function reads @len bytes of data from eraseblock @eb and offset @offs
+ * of the MTD device defined by @mtd and stores the read data at buffer @buf.
+ * Returns %0 in case of success and %-1 in case of failure.
+ */
+int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
+ void *buf, int len);
+
+/**
+ * mtd_write - write data to an MTD device.
+ * @mtd: MTD device description object
+ * @fd: MTD device node file descriptor
+ * @eb: eraseblock to write to
+ * @offs: offset withing the eraseblock to write to
+ * @buf: buffer to write
+ * @len: how many bytes to write
+ *
+ * This function writes @len bytes of data to eraseblock @eb and offset @offs
+ * of the MTD device defined by @mtd. Returns %0 in case of success and %-1 in
+ * case of failure.
+ */
+int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
+ void *buf, int len);
+
+/**
+ * mtd_probe_node - test MTD node.
+ * @desc: MTD library descriptor
+ * @node: the node to test
+ *
+ * This function tests whether @node is an MTD device node and returns %1 if it
+ * is, and %-1 if it is not (errno is ENODEV in this case) or if an error
+ * occurred.
+ */
+int mtd_probe_node(libmtd_t desc, const char *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBMTD_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/include/libscan.h b/mtd-utils-1.3.1/ubi-utils/include/libscan.h
new file mode 100644
index 0000000..a2b8657
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/include/libscan.h
@@ -0,0 +1,113 @@
+/*
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI scanning library.
+ */
+
+#ifndef __LIBSCAN_H__
+#define __LIBSCAN_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * If an eraseblock does not contain an erase counter, this value is used
+ * instead of the erase counter.
+ */
+#define NO_EC 0xFFFFFFFF
+
+/*
+ * If an eraseblock contains a corrupted erase counter, this value is used
+ * instead of the erase counter.
+ */
+#define CORRUPT_EC 0xFFFFFFFE
+
+/*
+ * If an eraseblock does not contain an erase counter, one of these values is
+ * used.
+ *
+ * @EB_EMPTY: the eraseblock appeared to be empty
+ * @EB_CORRUPTED: the eraseblock contains corrupted erase counter header
+ * @EB_ALIEN: the eraseblock contains some non-UBI data
+ * @EC_MAX: maximum allowed erase counter value
+ */
+enum
+{
+ EB_EMPTY = 0xFFFFFFFF,
+ EB_CORRUPTED = 0xFFFFFFFE,
+ EB_ALIEN = 0xFFFFFFFD,
+ EB_BAD = 0xFFFFFFFC,
+ EC_MAX = UBI_MAX_ERASECOUNTER,
+};
+
+/**
+ * struct ubi_scan_info - UBI scanning information.
+ * @ec: erase counters or eraseblock status for all eraseblocks
+ * @mean_ec: mean erase counter
+ * @ok_cnt: count of eraseblock with correct erase counter header
+ * @empty_cnt: count of supposedly eraseblocks
+ * @corrupted_cnt: count of eraseblocks with corrupted erase counter header
+ * @alien_cnt: count of eraseblock containing non-ubi data
+ * @bad_cnt: count of bad eraseblocks
+ * @bad_cnt: count of non-bad eraseblocks
+ * @vid_hdr_offs: volume ID header offset from the found EC headers (%-1 means
+ * undefined)
+ * @data_offs: data offset from the found EC headers (%-1 means undefined)
+ */
+struct ubi_scan_info
+{
+ uint32_t *ec;
+ long long mean_ec;
+ int ok_cnt;
+ int empty_cnt;
+ int corrupted_cnt;
+ int alien_cnt;
+ int bad_cnt;
+ int good_cnt;
+ int vid_hdr_offs;
+ int data_offs;
+};
+
+struct mtd_dev_info;
+
+/**
+ * ubi_scan - scan an MTD device.
+ * @mtd: information about the MTD device to scan
+ * @fd: MTD device node file descriptor
+ * @info: the result of the scanning is returned here
+ * @verbose: verbose mode: %0 - be silent, %1 - output progress information,
+ * 2 - debugging output mode
+ */
+int ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info,
+ int verbose);
+
+/**
+ * ubi_scan_free - free scanning information.
+ * @si: scanning information to free
+ */
+void ubi_scan_free(struct ubi_scan_info *si);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBSCAN_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/include/libubi.h b/mtd-utils-1.3.1/ubi-utils/include/libubi.h
new file mode 100644
index 0000000..f52904d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/include/libubi.h
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_H__
+#define __LIBUBI_H__
+
+#include <ctype.h>
+#include <stdint.h>
+#include <mtd/ubi-user.h>
+#include <mtd/ubi-media.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* UBI version libubi is made for */
+#define LIBUBI_UBI_VERSION 1
+
+/* UBI library descriptor */
+typedef void * libubi_t;
+
+/**
+ * struct ubi_attach_request - MTD device attachment request.
+ * @dev_num: number to assign to the newly created UBI device
+ * (%UBI_DEV_NUM_AUTO should be used to automatically assign the
+ * number)
+ * @mtd_num: MTD device number to attach
+ * @vid_hdr_offset: VID header offset (%0 means default offset and this is what
+ * most of the users want)
+ */
+struct ubi_attach_request
+{
+ int dev_num;
+ int mtd_num;
+ int vid_hdr_offset;
+};
+
+/**
+ * struct ubi_mkvol_request - volume creation request.
+ * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to
+ * automatically assign ID)
+ * @alignment: volume alignment
+ * @bytes: volume size in bytes
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @name: volume name
+ */
+struct ubi_mkvol_request
+{
+ int vol_id;
+ int alignment;
+ long long bytes;
+ int vol_type;
+ const char *name;
+};
+
+/**
+ * struct ubi_info - general UBI information.
+ * @dev_count: count of UBI devices in system
+ * @lowest_dev_num: lowest UBI device number
+ * @highest_dev_num: highest UBI device number
+ * @version: UBI version
+ * @ctrl_major: major number of the UBI control device
+ * @ctrl_minor: minor number of the UBI control device
+ */
+struct ubi_info
+{
+ int dev_count;
+ int lowest_dev_num;
+ int highest_dev_num;
+ int version;
+ int ctrl_major;
+ int ctrl_minor;
+};
+
+/**
+ * struct ubi_dev_info - UBI device information.
+ * @vol_count: count of volumes on this UBI device
+ * @lowest_vol_id: lowest volume ID
+ * @highest_vol_id: highest volume ID
+ * @major: major number of corresponding character device
+ * @minor: minor number of corresponding character device
+ * @total_lebs: total number of logical eraseblocks on this UBI device
+ * @avail_lebs: how many logical eraseblocks are not used and available for new
+ * volumes
+ * @total_bytes: @total_lebs * @leb_size
+ * @avail_bytes: @avail_lebs * @leb_size
+ * @bad_count: count of bad physical eraseblocks
+ * @leb_size: logical eraseblock size
+ * @max_ec: current highest erase counter value
+ * @bad_rsvd: how many physical eraseblocks of the underlying flash device are
+ * reserved for bad eraseblocks handling
+ * @max_vol_count: maximum possible number of volumes on this UBI device
+ * @min_io_size: minimum input/output unit size of the UBI device
+ */
+struct ubi_dev_info
+{
+ int dev_num;
+ int vol_count;
+ int lowest_vol_id;
+ int highest_vol_id;
+ int major;
+ int minor;
+ int total_lebs;
+ int avail_lebs;
+ long long total_bytes;
+ long long avail_bytes;
+ int bad_count;
+ int leb_size;
+ long long max_ec;
+ int bad_rsvd;
+ int max_vol_count;
+ int min_io_size;
+};
+
+/**
+ * struct ubi_vol_info - UBI volume information.
+ * @dev_num: UBI device number the volume resides on
+ * @vol_id: ID of this volume
+ * @major: major number of corresponding volume character device
+ * @minor: minor number of corresponding volume character device
+ * @dev_major: major number of corresponding UBI device character device
+ * @dev_minor: minor number of corresponding UBI device character device
+ * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @alignment: alignment of this volume
+ * @data_bytes: how many data bytes are stored on this volume (equivalent to
+ * @rsvd_bytes for dynamic volumes)
+ * @rsvd_bytes: how many bytes are reserved for this volume
+ * @rsvd_lebs: how many logical eraseblocks are reserved for this volume
+ * @leb_size: logical eraseblock size of this volume (may be less then
+ * device's logical eraseblock size due to alignment)
+ * @corrupted: non-zero if the volume is corrupted
+ * @name: volume name (null-terminated)
+ */
+struct ubi_vol_info
+{
+ int dev_num;
+ int vol_id;
+ int major;
+ int minor;
+ int dev_major;
+ int dev_minor;
+ int type;
+ int alignment;
+ long long data_bytes;
+ long long rsvd_bytes;
+ int rsvd_lebs;
+ int leb_size;
+ int corrupted;
+ char name[UBI_VOL_NAME_MAX + 1];
+};
+
+/**
+ * libubi_open - open UBI library.
+ *
+ * This function initializes and opens the UBI library and returns UBI library
+ * descriptor in case of success and %NULL in case of failure. In case of
+ * failure, errno contains the error code or zero if UBI is not present in the
+ * system.
+ */
+libubi_t libubi_open(void);
+
+/**
+ * libubi_close - close UBI library.
+ * @desc: UBI library descriptor
+ */
+void libubi_close(libubi_t desc);
+
+/**
+ * ubi_get_info - get general UBI information.
+ * @desc: UBI library descriptor
+ * @info: pointer to the &struct ubi_info object to fill
+ *
+ * This function fills the passed @info object with general UBI information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_info(libubi_t desc, struct ubi_info *info);
+
+/**
+ * mtd_num2ubi_dev - find UBI device by attached MTD device.
+ * @@desc: UBI library descriptor
+ * @mtd_num: MTD device number
+ * @dev_num: UBI device number is returned here
+ *
+ * This function finds UBI device to which MTD device @mtd_num is attached.
+ * Returns %0 if the UBI device was found and %-1 if not.
+ */
+int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num);
+
+/**
+ * ubi_attach_mtd - attach MTD device to UBI.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI control character device node
+ * @req: MTD attach request.
+ *
+ * This function creates a new UBI device by attaching an MTD device as
+ * described by @req. Returns %0 in case of success and %-1 in case of failure.
+ * The newly created UBI device number is returned in @req->dev_num.
+ */
+int ubi_attach_mtd(libubi_t desc, const char *node,
+ struct ubi_attach_request *req);
+
+/**
+ * ubi_detach_mtd - detach an MTD device.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI control character device node
+ * @mtd_num: MTD device number to detach
+ *
+ * This function detaches MTD device number @mtd_num from UBI, which means the
+ * corresponding UBI device is removed. Returns zero in case of success and %-1
+ * in case of failure.
+ */
+int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num);
+
+/**
+ * ubi_remove_dev - remove an UBI device.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI control character device node
+ * @ubi_dev: UBI device number to remove
+ *
+ * This function removes UBI device number @ubi_dev and returns zero in case of
+ * success and %-1 in case of failure.
+ */
+int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev);
+
+/**
+ * ubi_mkvol - create an UBI volume.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to create a volume at
+ * @req: UBI volume creation request
+ *
+ * This function creates a UBI volume as described at @req and returns %0 in
+ * case of success and %-1 in case of failure. The assigned volume ID is
+ * returned in @req->vol_id.
+ */
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req);
+
+/**
+ * ubi_rmvol - remove a UBI volume.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to remove a volume from
+ * @vol_id: ID of the volume to remove
+ *
+ * This function removes volume @vol_id from UBI device @node and returns %0 in
+ * case of success and %-1 in case of failure.
+ */
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id);
+
+
+/**
+ * ubi_rnvols - rename UBI volumes.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to remove a volume from
+ * @rnvol: description of volumes to rename
+ *
+ * This function removes volume @vol_id from UBI device @node and returns %0 in
+ * case of success and %-1 in case of failure.
+ */
+int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol);
+
+/**
+ * ubi_rsvol - re-size UBI volume.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device owning the volume which should be
+ * re-sized
+ * @vol_id: volume ID to re-size
+ * @bytes: new volume size in bytes
+ *
+ * This function returns %0 in case of success and %-1 in case of error.
+ */
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes);
+
+/**
+ * ubi_probe_node - test UBI node.
+ * @desc: UBI library descriptor
+ * @node: the node to test
+ *
+ * This function tests whether @node is a UBI device or volume node and returns
+ * %1 if this is an UBI device node, %2 if this is a volume node, and %-1 if
+ * this is not an UBI device or volume node (errno is ENODEV in this case) or
+ * if an error occurred.
+ */
+int ubi_probe_node(libubi_t desc, const char *node);
+
+/**
+ * ubi_get_dev_info - get UBI device information.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to fetch information about
+ * @info: pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function fills the passed @info object with UBI device information and
+ * returns %0 in case of success and %-1 in case of failure. If the UBI device
+ * corresponding to @node does not exist, errno is set to @ENODEV.
+ */
+int ubi_get_dev_info(libubi_t desc, const char *node,
+ struct ubi_dev_info *info);
+
+/**
+ * ubi_get_dev_info1 - get UBI device information.
+ * @desc: UBI library descriptor
+ * @dev_num: UBI device number to fetch information about
+ * @info: pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI
+ * device number, not UBI character device. If the UBI device @dev_num does not
+ * exist, errno is set to @ENODEV.
+ */
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info);
+
+/**
+ * ubi_get_vol_info - get UBI volume information.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI volume character device to fetch information about
+ * @info: pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function fills the passed @info object with UBI volume information and
+ * returns %0 in case of success and %-1 in case of failure. If the UBI volume
+ * corresponding to @node does not exist, errno is set to @ENODEV.
+ */
+int ubi_get_vol_info(libubi_t desc, const char *node,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_get_vol_info1 - get UBI volume information.
+ * @desc: UBI library descriptor
+ * @dev_num: UBI device number
+ * @vol_id: ID of the UBI volume to fetch information about
+ * @info: pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI
+ * volume ID, not UBI volume character device. If the UBI device @dev_num does
+ * not exist, or if the UBI volume @vol_id does not exist, errno is set to
+ * @ENODEV.
+ */
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_get_vol_info1_nm - get UBI volume information by volume name.
+ * @desc: UBI library descriptor
+ * @dev_num: UBI device number
+ * @name: name of the UBI volume to fetch information about
+ * @info: pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI
+ * volume name, not UBI volume ID. If the UBI device @dev_num does not exist,
+ * or if the UBI volume @name does not exist, errno is set to @ENODEV.
+ */
+int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_update_start - start UBI volume update.
+ * @desc: UBI library descriptor
+ * @fd: volume character device file descriptor
+ * @bytes: how many bytes will be written to the volume
+ *
+ * This function initiates UBI volume update and returns %0 in case of success
+ * and %-1 in case of error. The caller is assumed to write @bytes data to the
+ * volume @fd afterward.
+ */
+int ubi_update_start(libubi_t desc, int fd, long long bytes);
+
+/**
+ * ubi_leb_change_start - start atomic LEB change.
+ * @desc: UBI library descriptor
+ * @fd: volume character device file descriptor
+ * @lnum: LEB number to change
+ * @bytes: how many bytes of new data will be written to the LEB
+ * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
+ *
+ * This function initiates atomic LEB change operation and returns %0 in case
+ * of success and %-1 in case of error. he caller is assumed to write @bytes
+ * data to the volume @fd afterward.
+ */
+int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype);
+
+/**
+ * ubi_set_property - set volume propety.
+ * @fd: volume character device file descriptor
+ * @property: the property to change (%UBI_PROP_DIRECT_WRITE, etc)
+ * @value: new value of the changed property
+ *
+ * This function changes a property of a volume. Returns zero in case of
+ * success and a negative error code in case of error.
+ */
+int ubi_set_property(int fd, uint8_t property, uint64_t value);
+
+/**
+ * ubi_leb_unmap - unmap a logical eraseblock.
+ * @fd: volume character device file descriptor
+ * @lnum: logical eraseblock to unmap
+ *
+ * This function unmaps LEB @lnum and returns zero in case of success and a
+ * negative error code in case of error.
+ */
+int ubi_leb_unmap(int fd, int lnum);
+
+/**
+ * ubi_is_mapped - check if logical eraseblock is mapped.
+ * @fd: volume character device file descriptor
+ * @lnum: logical eraseblock number
+ *
+ * This function checks if logical eraseblock @lnum is mapped to a physical
+ * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily
+ * mean it will still be un-mapped after the UBI device is re-attached. The
+ * logical eraseblock may become mapped to the physical eraseblock it was last
+ * mapped to.
+ *
+ * This function returns %1 if the LEB is mapped, %0 if not, and %-1 in case of
+ * failure. If the volume is damaged because of an interrupted update errno
+ * set with %EBADF error code.
+ */
+int ubi_is_mapped(int fd, int lnum);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/include/libubigen.h b/mtd-utils-1.3.1/ubi-utils/include/libubigen.h
new file mode 100644
index 0000000..2b567d3
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/include/libubigen.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Authors: Frank Haverkamp
+ * Artem Bityutskiy
+ */
+
+#ifndef __LIBUBIGEN_H__
+#define __LIBUBIGEN_H__
+
+#include <stdint.h>
+#include <mtd/ubi-media.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct ubigen_info - libubigen information.
+ * @leb_size: logical eraseblock size
+ * @peb_size: size of the physical eraseblock
+ * @min_io_size: minimum input/output unit size
+ * @vid_hdr_offs: offset of the VID header
+ * @data_offs: data offset
+ * @ubi_ver: UBI version
+ * @vtbl_size: volume table size
+ * @max_volumes: maximum amount of volumes
+ * @image_seq: UBI image sequence number
+ */
+struct ubigen_info
+{
+ int leb_size;
+ int peb_size;
+ int min_io_size;
+ int vid_hdr_offs;
+ int data_offs;
+ int ubi_ver;
+ int vtbl_size;
+ int max_volumes;
+ uint32_t image_seq;
+};
+
+/**
+ * struct ubigen_vol_info - information about a volume.
+ * @id: volume id
+ * @type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are unused at the end of the each physical
+ * eraseblock to satisfy the requested alignment
+ * @usable_leb_size: LEB size accessible for volume users
+ * @name: volume name
+ * @name_len: volume name length
+ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
+ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
+ * @used_ebs: total number of used logical eraseblocks in this volume (relevant
+ * for static volumes only)
+ * @bytes: size of the volume contents in bytes (relevant for static volumes
+ * only)
+ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
+ */
+struct ubigen_vol_info
+{
+ int id;
+ int type;
+ int alignment;
+ int data_pad;
+ int usable_leb_size;
+ const char *name;
+ int name_len;
+ int compat;
+ int used_ebs;
+ long long bytes;
+ uint8_t flags;
+};
+
+void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
+ int subpage_size, int vid_hdr_offs, int ubi_ver,
+ uint32_t image_seq);
+struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui);
+void ubigen_init_ec_hdr(const struct ubigen_info *ui,
+ struct ubi_ec_hdr *hdr, long long ec);
+int ubigen_get_vtbl_size(const struct ubigen_info *ui);
+int ubigen_add_volume(const struct ubigen_info *ui,
+ const struct ubigen_vol_info *vi,
+ struct ubi_vtbl_record *vtbl);
+int ubigen_write_volume(const struct ubigen_info *ui,
+ const struct ubigen_vol_info *vi, long long ec,
+ long long bytes, int in, int out);
+int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
+ long long ec1, long long ec2,
+ struct ubi_vtbl_record *vtbl, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBIGEN_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/Makefile b/mtd-utils-1.3.1/ubi-utils/old-utils/Makefile
new file mode 100644
index 0000000..acdb31a
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/Makefile
@@ -0,0 +1,59 @@
+#
+# Makefile for ubi-utils
+#
+
+KERNELHDR := ../../include
+
+CFLAGS ?= -O2 -g -Werror
+CPPFLAGS := -I./inc -I./src -I$(KERNELHDR) \
+ -std=gnu99 -DPACKAGE_VERSION=\"1.0\" $(CPPFLAGS)
+
+PERLPROGS = mkpfi ubicrc32.pl
+
+TARGETS = pfiflash pddcustomize ubimirror bin2nand nand2bin ubigen \
+ mkbootenv unubi pfi2bin
+
+vpath %.c ./src
+
+include ../../common.mk
+
+$(BUILDDIR)/pddcustomize: $(addprefix $(BUILDDIR)/,\
+ pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \
+ libubi.o crc32.o)
+
+$(BUILDDIR)/pfiflash: $(addprefix $(BUILDDIR)/,\
+ pfiflash.o libpfiflash.o list.o reader.o error.o libubimirror.o \
+ bootenv.o hashmap.o pfi.o libubi.o crc32.o)
+
+$(BUILDDIR)/ubimirror: $(addprefix $(BUILDDIR)/,\
+ ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \
+ libubi.o crc32.o)
+
+$(BUILDDIR)/nand2bin: $(addprefix $(BUILDDIR)/,\
+ nand2bin.o nandecc.o nandcorr.o)
+
+$(BUILDDIR)/bin2nand: $(addprefix $(BUILDDIR)/,\
+ bin2nand.o error.o nandecc.o)
+
+$(BUILDDIR)/ubigen: $(addprefix $(BUILDDIR)/,\
+ ubigen.o libubigen.o crc32.o)
+
+$(BUILDDIR)/mkbootenv: $(addprefix $(BUILDDIR)/,\
+ mkbootenv.o bootenv.o hashmap.o error.o crc32.o)
+
+$(BUILDDIR)/unubi: $(addprefix $(BUILDDIR)/,\
+ unubi.o crc32.o unubi_analyze.o eb_chain.o)
+
+$(BUILDDIR)/pfi2bin: $(addprefix $(BUILDDIR)/,\
+ pfi2bin.o peb.o error.o list.o crc32.o libubigen.o bootenv.o \
+ hashmap.o reader.o pfi.o)
+
+install::
+ mkdir -p ${DESTDIR}/${SBINDIR}
+ install -m 0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
+ (cd perl && install ${PERLPROGS} ${DESTDIR}/${SBINDIR}/)
+
+uninstall:
+ for file in ${TARGETS} ${PERLPROGS}; do \
+ $(RM) ${DESTDIR}/${SBINDIR}/$$file; \
+ done
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/README b/mtd-utils-1.3.1/ubi-utils/old-utils/README
new file mode 100644
index 0000000..d976a76
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/README
@@ -0,0 +1,236 @@
+README
+======
+
+The programs and libraries in this directory provide a tool-chain to
+generate binary data for embedded systems which can be flashed either
+by a hardware flash programmer, e.g. JTAG debugger, or on the target
+system directly using pfiflash, or ubimkvol, ubirmvol, ubiwritevol.
+
+The latter is the case when there is already Linux running which has
+build in UBI support.
+
+Authors: Oliver Lohmann
+ Frank Haverkamp
+ Andreas Arnez
+
+mkpfi - tool for flash content generation in PFI
+ format
+pfi2bin - conversion tool to transfer a PFI file into a
+ binary image
+pfiflash - tool to update the embedded systems flash using
+ pfi files created by mkpfi
+libbootenv - library for boot-parameter processing
+libpfi - library for partial flash image (PFI) creation
+ and handling
+ubigen - tool to create binary UBI images e.g. for a
+ jtag flashing tool
+nandimg - tool to add OOB data to binary images intended
+ for NAND flash systems
+ubilib - UBI library
+
+!!! NOTICE !!!
+If you execute ./configure in the top_level directory the helper Makefile
+gets overwritten. Thats actually no problem, but be aware of that.
+
+1. Build Process
+
+1.1 Build, install and forget
+ o Build all and everything
+ $make all (takes a while, builds ppc and x86 binaries/libs)
+ o Installation:
+ $make install
+ o Uninstallation:
+ $make uninstall
+
+ o x86 only would be:
+ $make x86 && make install_x86
+
+1.2 Usage for a developer
+
+ 1.2.1 The build process in detail
+
+ o If you've checked out the sources from the CVS repository you'll find a
+ directory setup like this:
+
+ flashutils/
+ -rw-r--r-- 1 olli olli 1.3K Mar 14 11:53 Makefile
+ -rw-r--r-- 1 olli olli 1.9K Mar 14 10:50 Makefile.am
+ -rwxr-xr-x 1 olli olli 265 Mar 9 00:47 bootstrap
+ -rw-r--r-- 1 olli olli 1.1K Mar 9 16:55 configure.ac
+ drwxr-xr-x 2 olli olli 4.0K Mar 9 00:28 doc
+ drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 inc
+ drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 lib
+ drwxr-xr-x 17 olli olli 4.0K Mar 13 16:50 src
+
+ o To generate the initial build templates you have to call the bootstrap
+ script:
+ $ ./bootstrap
+ o Create a directory for the target platform
+ $ mkdir build_x86
+ o Descend into the directory and call the top-level configure script
+ with the desired options.
+ $ cd build_x86
+ $ ../configure --prefix=/usr/local [...]
+ o Now you'll find a directory structure like this:
+
+ flashutils/build_x86/
+ -rw-r--r-- 1 olli olli 47K Mar 14 13:33 Makefile
+ -rw-r--r-- 1 olli olli 33K Mar 14 13:33 config.log
+ -rwxr-xr-x 1 olli olli 38K Mar 14 13:33 config.status
+ drwxr-xr-x 2 olli olli 4.0K Mar 14 13:33 inc
+ drwxr-xr-x 3 olli olli 4.0K Mar 14 13:33 lib
+ -rwxr-xr-x 1 olli olli 202K Mar 14 13:33 libtool
+
+ o The config.guess script can be used to update the Makefiles in the
+ target directory after a change of the top-level template files
+ (i.e. the Makefile.in files).
+ $ ./config.guess
+ o To compile everything for this platform just invoke make in
+ flashutils/build_x86:
+ $ make
+ or from toplevel:
+ $ make -C ./build_x86
+ o The build process creates a new directory "bin":
+ flashutils/build_x86/
+ [...]
+ drwxr-xr-x 3 olli olli 4.0K Mar 14 13:41 bin
+ [...]
+
+ This directory contains all binary files which will be installed
+ by make install, e.g.:
+
+ flashutils/build_x86/bin/
+ -rwxr-xr-x 1 olli olli 7.2K Mar 14 13:41 bin2nand
+ -rwxr-xr-x 1 olli olli 15K Mar 14 13:41 mkbootenv
+ -rwxr-xr-x 1 olli olli 16K Mar 14 13:41 pddcustomize
+ -rwxr-xr-x 1 olli olli 36K Mar 14 13:41 pfi2bin
+ -rwxr-xr-x 1 olli olli 6.8K Mar 14 13:41 pfiflash
+ -rwxr-xr-x 1 olli olli 5.0K Mar 14 13:41 ubicrc32
+ -rwxr-xr-x 1 olli olli 13K Mar 14 13:41 ubigen
+ -rwxr-xr-x 1 olli olli 6.3K Mar 14 13:41 ubimirror
+
+
+ 1.2.2 Modifying and Adding Sources
+
+ o There is a dedicated directory which contains all source code
+ of the flashutils package, e.g.:
+
+ flashutils/src/
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 libbootenv
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 liberror
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 16:48 mkpfi
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 16:12 pddcustomize
+
+
+
+ The prefix "lib" is used to mark directories as part of a convenience
+ library. Binaries have no special prefix.
+
+ o How to add sources?
+
+ Just create a new directory at flashutils/src/, e.g.:
+
+ For a binary:
+ $ mkdir rider
+ $ cd rider
+ $ vi rider.c
+ /* do sth with that file... */
+
+ For a convenience library (as well as for "normal libs")
+ $ mkdir libworld
+ $ cd libworld
+ $ vi world.c
+ /* do sth with that file... */
+
+ o How to register sources in the build process (for binaries)?
+
+ You have to register your sources at the top-level automake Makefile:
+
+ In directory flashutils/
+ $ vi Makefile.am
+
+ Binaries have to be registered at "bin_PROGRAMS", e.g.:
+ bin_PROGRAMS = bin/pddcustomize \
+ bin/rider
+
+ Add the rule how the binary is assembled, e.g.:
+ bin_pddcustomize_SOURCES = \
+ $(top_srcdir)/src/pddcustomize/pddcustomize.c
+ bin_pddcustomize_LDADD = \
+ $(top_builddir)/lib/libbootenv.la \
+ $(top_builddir)/lib/liberror.la
+
+ bin_rider_SOURCES = \
+ $(top_srcdir)/src/rider/rider.c
+
+ This example reflects a simple build process for "rider". "rider"
+ is built without any other dependencies or convenience libraries.
+ The example for pddcustomize is a bit more complicated.
+ "_LDADD" adds some convenience libraris into the link process of
+ "pddcustomize". Imagine, that your "rider" has common code
+ with "dragon_bin" which is held in a library called "libworld".
+ The build rules would like like the following:
+
+ bin_rider_SOURCES = \
+ $(top_srcdir)/src/rider/rider.c
+ bin_rider_LDADD = \
+ $(top_builddir)/lib/libworld.la
+
+ bin_dragon_SOURCES = \
+ $(top_srcdir)/src/dragon_bin/dragon_bin.c
+ bin_dragon_LDADD = \
+ $(top_builddir)/lib/libworld.la
+
+ Don't forget to add "dragon" to "bin_PROGRAMS"!
+ Don't forget to set the build rule for the "libworld" itself!
+ This is documented in the next section.
+
+
+ o How to register sources in the build process (for libraries)?
+
+ Until now we didn't care about the build process of "libworld".
+ Libraries are handled special in this build process because
+ they are handled as "modules", i.e. they are able to be built
+ without building the binaries in the same step. Additionally,
+ it is possible to assemble complex libraries out of simple ones.
+ That especially makes sense if you want to export (install) a
+ library on a system which uses some common code and makes
+ some adoptions for usability and presents a comfortable interface to
+ the user (see libpfiflash in the sources for an example).
+
+ o Registering "libworld" as convenience library.
+
+ Instead of editing the "Makefile.am" in "flashtools/", we have to
+ edit now the "Makefile.am" in "flashtools/lib/":
+
+ noinst_LTLIBRARIES = libworld.la
+
+ libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c
+
+ o Registering "libworld" as library which gets installed.
+
+ lib_LTLIBRARIES = libworld.la
+ libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c
+ libworld_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+ o Header files
+
+ All header files are stored at "flashutils/inc", regardless
+ if convenience library or not.
+
+ If you want to export headers you have to specify this in the Makefile.am
+ located at "flashutils/inc", e.g. (this should not be done
+ for convenience libraries):
+
+ nobase_include_HEADERS = world.h
+
+
+
+Appendix
+
+A.1. FAQ
+
+ Q How to call configure to setup a cross-platform build?
+ A $ ./configure --build=i686-pc-linux-gnu --host=ppc-linux \
+ --prefix=/opt/.../ppcnf/crossroot/ \
+ --exec-prefix=/opt/..../ppcnf/crossroot/usr
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/UBI.TXT b/mtd-utils-1.3.1/ubi-utils/old-utils/UBI.TXT
new file mode 100644
index 0000000..9a1c3c7
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/UBI.TXT
@@ -0,0 +1,108 @@
+UBI - Unsorted Block Images
+
+UBI (Latin: "where?") manages multiple logical volumes on a single
+flash device, specifically supporting NAND flash devices. UBI provides
+a flexible partitioning concept which still allows for wear-levelling
+across the whole flash device.
+
+In a sense, UBI may be compared to the Logical Volume Manager
+(LVM). Whereas LVM maps logical sector numbers to physical HDD sector
+numbers, UBI maps logical eraseblocks to physical eraseblocks.
+
+More information may be found in the UBI design documentation:
+ubidesign.pdf. Which can be found here:
+http://www.linux-mtd.infradead.org/doc/ubi.html
+
+Partitioning/Re-partitioning
+
+ An UBI volume occupies a certain number of erase blocks. This is
+ limited by a configured maximum volume size, which could also be
+ viewed as the partition size. Each individual UBI volume's size can
+ be changed independently of the other UBI volumes, provided that the
+ sum of all volume sizes doesn't exceed a certain limit.
+
+ UBI supports dynamic volumes and static volumes. Static volumes are
+ read-only and their contents are protected by CRC check sums.
+
+Bad eraseblocks handling
+
+ UBI transparently handles bad eraseblocks. When a physical
+ eraseblock becomes bad, it is substituted by a good physical
+ eraseblock, and the user does not even notice this.
+
+Scrubbing
+
+ On a NAND flash bit flips can occur on any write operation,
+ sometimes also on read. If bit flips persist on the device, at first
+ they can still be corrected by ECC, but once they accumulate,
+ correction will become impossible. Thus it is best to actively scrub
+ the affected eraseblock, by first copying it to a free eraseblock
+ and then erasing the original. The UBI layer performs this type of
+ scrubbing under the covers, transparently to the UBI volume users.
+
+Erase Counts
+
+ UBI maintains an erase count header per eraseblock. This frees
+ higher-level layers (like file systems) from doing this and allows
+ for centralized erase count management instead. The erase counts are
+ used by the wear-levelling algorithm in the UBI layer. The algorithm
+ itself is exchangeable.
+
+Booting from NAND
+
+ For booting directly from NAND flash the hardware must at least be
+ capable of fetching and executing a small portion of the NAND
+ flash. Some NAND flash controllers have this kind of support. They
+ usually limit the window to a few kilobytes in erase block 0. This
+ "initial program loader" (IPL) must then contain sufficient logic to
+ load and execute the next boot phase.
+
+ Due to bad eraseblocks, which may be randomly scattered over the
+ flash device, it is problematic to store the "secondary program
+ loader" (SPL) statically. Also, due to bit-flips it may become
+ corrupted over time. UBI allows to solve this problem gracefully by
+ storing the SPL in a small static UBI volume.
+
+UBI volumes vs. static partitions
+
+ UBI volumes are still very similar to static MTD partitions:
+
+ * both consist of eraseblocks (logical eraseblocks in case of UBI
+ volumes, and physical eraseblocks in case of static partitions;
+ * both support three basic operations - read, write, erase.
+
+ But UBI volumes have the following advantages over traditional
+ static MTD partitions:
+
+ * there are no eraseblock wear-leveling constraints in case of UBI
+ volumes, so the user should not care about this;
+ * there are no bit-flips and bad eraseblocks in case of UBI volumes.
+
+ So, UBI volumes may be considered as flash devices with relaxed
+ restrictions.
+
+Where can it be found?
+
+ Documentation, kernel code and applications can be found in the MTD
+ gits.
+
+What are the applications for?
+
+ The applications help to create binary flash images for two
+ purposes: pfi files (partial flash images) for in-system update of
+ UBI volumes, and plain binary images, with or without OOB data in
+ case of NAND, for a manufacturing step. Furthermore some tools
+ are/and will be created that allow flash content analysis after a
+ system has crashed.
+
+Who did UBI?
+
+ The original ideas, where UBI is based on, were developed by Andreas
+ Arnez, Frank Haverkamp and Thomas Gleixner. Josh W. Boyer and
+ some others were involved too. The implementation of the kernel
+ layer was done by Artem B. Bityutskiy. The user-space applications
+ and tools were written by Oliver Lohmann with contributions from
+ Frank Haverkamp, Andreas Arnez, and Artem. Joern Engel contributed a
+ patch which modifies JFFS2 so that it can be run on a UBI
+ volume. Thomas Gleixner did modifications to the NAND layer and also
+ some to JFFS2 to make it work.
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/doc/unubi.roff b/mtd-utils-1.3.1/ubi-utils/old-utils/doc/unubi.roff
new file mode 100644
index 0000000..6cebc46
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/doc/unubi.roff
@@ -0,0 +1,123 @@
+.TH UNUBI 1 "NOVEMBER 2006" FSP "FSP Flashutils"
+.SH NAME
+unubi \- extract volumes/eraseblocks from a raw\-UBI image
+.SH SYNOPSIS
+\fBunubi [\-aevEV] [\-d \fIout\-dir\fB] [\-r \fIvolume\-id\fB]
+[\-b \fIblock\-size\fB] \fIimage\-file
+.SH DESCRIPTION
+.PP
+\fBunubi\fR reads an image file containing blocks of UBI headers and data
+(such as produced from \fBnand2bin\fR) and rebuilds the volumes within.
+The default operation (when no flags are given) is to rebuild all valid
+volumes found in the image. \fBunubi\fR can also read straight from the
+onboard MTD device (ex. /dev/mtdblock/NAND).
+.SH OPTIONS
+.IP "\-a, \-\-analyze"
+When flagged, analysis files are generated within the output directory. These
+may include tables and or graphs detailing statistics gathered from the
+eraseblock data. Files are prefixed `analysis_'.
+
+See \fBANALYSIS\fR.
+.IP "\-b, \-\-blocksize \fIblock\-size\fR"
+Specify in bytes the \fIimage\-file\fR eraseblock size. Sizes may be
+postfixed with `KiB' or `MiB' to indicate mebibytes or kibibytes
+respectively. Default is 128KiB.
+.IP "\-d, \-\-dir \fIoutput\-dir\fR"
+Specify the output directory. If no directory is specified, the default
+is `unubi_\fIimage\-file\fR' within the curent working directory. If the
+attempt to create the output directory fails,
+.B unubi
+will try to create it in /tmp before aborting.
+.IP "\-e, \-\-eb\-split"
+When flagged, images are created for each eraseblock in \fIimage\-file\fR
+regardless of its validity. Each image is the complete eraseblock, including
+headers and any space to the end of the eraseblock after where the data may
+end.
+
+Invalid images are named `ebEEEE', where EEEE is the physical index of the
+eraseblock in the image. Valid images are named `ebEEEE_VVV_NNN_RRR' where
+VVV is the known volume ID, NNN is the logical number and RRR is the version
+of the eraseblock data. Note that the version number is in hexadecimal.
+
+Invalid images may also contain this postfix, if the data in the header
+could be valid (ie. the header contains a resonable volume ID, but the
+header and/or data CRCs are not valid). If this is the case, images are named
+`ebEEEE_VVV_NNN_RRR.reason', so as to distinguish known values from
+non\-definite ones.
+
+See \fBREASON SUFFIXES\fR.
+.IP "\-r, \-\-rebuild \fIvolume\-id\fR"
+Specify a volume to rebuild. Can be used successively to specify
+several volumes to be rebuilt.
+
+Images are named `volumeVVV' where VVV is the volume ID. For each missing
+eraseblock, an error message will be printed.
+.IP "\-v, \-\-vol\-split"
+When flagged, images are created for each valid eraseblock in
+\fIimage\-file\fR. Since a vaild eraseblock will have a defined data start and
+data length, only this range will make up the image.
+
+Images are named `volVVV_NNN_RRR_EEEE', where, for the data in the eraseblock,
+VVV is the volume ID, NNN is the logical number, RRR is the version and EEEE
+is the phyisical index of the eraseblock in the image.
+.IP "\-V, \-\-vol\-split!"
+Same as above, only all images are the complete eraseblock (including headers,
+and raw data, even past the point where the data is supposed to end).
+Overrides \-v when both \-v and \-V are flagged.
+.SH ANALYSIS
+The following files will be generated during the analysis:
+.IP "analysis_ec_hdr.data"
+A space delimited table with these two columns for each eraseblock: the
+eraseblock's index or physical position in the image, and the eraseblock's
+erase count. The third column contains the erase count data sorted.
+.IP "analysis_vid_hdr.data"
+A space delimited table with these four colums for each eraseblock: the
+volume ID, the volume logical number, the leb version, and the data size.
+In addition there are a normalized column representing the volume ID and
+volume logical number, a normalized column representing the leb version, and
+a normalized column representing the data_size. These normalized columns are
+used to better draw the the gnuplot image.
+.IP "analysis_ec_hdr.plot"
+A gnuplot script for quickly viewing a sample output from the respective .data
+file.
+.IP "analysis_vid_hdr.plot"
+A gnuplot script for quickly viewing a sample output from the respective .data
+file.
+.SH REASONS SUFFIXES
+When \-\-eb\-split produces possibly invalid, though usable, eraseblocks, the
+known reason suffixes are:
+.IP ".ec_magic"
+The erase counter header did not contain a valid magic field.
+.IP ".ec_hdr_crc"
+The erase counter header did not contain a vaild header CRC field.
+.IP ".vid_magic"
+The volume ID header did not contain a valid magic field.
+.IP ".vid_hdr_crc"
+The volume ID header did not contain a valid header CRC field.
+.IP ".data_crc"
+The volume ID header did not contain a valid data CRC field.
+.SH EXAMPLES
+To extract and rebuild all valid volumes from demo.img (note the output
+directory will be /home/user/unubi_demo.img):
+.sp 1
+.RS
+.B /home/user# unubi demo.img
+.sp 1
+.RE
+To analyze demo.img as well as extract and rebuild volume 7:
+.sp 1
+.RS
+.B /home/user# unubi \-a \-r 7 demo.img
+.sp 1
+.RE
+To split demo.img into raw images for each eraseblock into the folder
+/var/eraseblocks:
+.sp 1
+.RS
+.B /home/user# unubi \-e \-d /var/eraseblocks demo.img
+.SH AUTHORS
+Frank Haverkamp <haver@vnet.ibm.com>
+.sp 0
+Drake Dowsett <dowsett@de.ibm.com>
+.SH CONTACT
+Andreas Arnez <arnez@de.ibm.com>
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/inc/libubi.h b/mtd-utils-1.3.1/ubi-utils/old-utils/inc/libubi.h
new file mode 100644
index 0000000..82824bd
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/inc/libubi.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_H__
+#define __LIBUBI_H__
+
+#include <stdint.h>
+#include <mtd/ubi-user.h>
+#include <ctype.h>
+#include <mtd/ubi-media.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* UBI version libubi is made for */
+#define LIBUBI_UBI_VERSION 1
+
+/* UBI library descriptor */
+typedef void * libubi_t;
+
+/**
+ * struct ubi_mkvol_request - volume creation request.
+ * */
+struct ubi_mkvol_request
+{
+ int vol_id;
+ int alignment;
+ long long bytes;
+ int vol_type;
+ const char *name;
+};
+
+/**
+ * struct ubi_info - general UBI information.
+ *
+ * @dev_count count of UBI devices in system
+ * @lowest_dev_num lowest UBI device number
+ * @highest_dev_num highest UBI device number
+ * @version UBI version
+ */
+struct ubi_info
+{
+ int dev_count;
+ int lowest_dev_num;
+ int highest_dev_num;
+ int version;
+};
+
+/**
+ * struct ubi_dev_info - UBI device information.
+ *
+ * @vol_count count of volumes on this UBI device
+ * @lowest_vol_num lowest volume number
+ * @highest_vol_num highest volume number
+ * @total_ebs total number of eraseblocks on this UBI device
+ * @avail_ebs how many eraseblocks are not used and available for new
+ * volumes
+ * @total_bytes @total_ebs * @eb_size
+ * @avail_bytes @avail_ebs * @eb_size
+ * @bad_count count of bad eraseblocks
+ * @eb_size size of UBI eraseblock
+ * @max_ec current highest erase counter value
+ * @bad_rsvd how many physical eraseblocks of the underlying flash
+ * device are reserved for bad eraseblocks handling
+ * @max_vol_count maximum count of volumes on this UBI device
+ * @min_io_size minimum input/output size of the UBI device
+ */
+struct ubi_dev_info
+{
+ int dev_num;
+ int vol_count;
+ int lowest_vol_num;
+ int highest_vol_num;
+ int total_ebs;
+ int avail_ebs;
+ long long total_bytes;
+ long long avail_bytes;
+ int bad_count;
+ int eb_size;
+ long long max_ec;
+ int bad_rsvd;
+ int max_vol_count;
+ int min_io_size;
+};
+
+/**
+ * struct ubi_vol_info - UBI volume information.
+ *
+ * @dev_num UBI device number the volume resides on
+ * @vol_id ID of this volume
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @alignment alignemnt of this volume
+ * @data_bytes how many data bytes are stored on this volume (equivalent to
+ * @rsvd_bytes for dynamic volumes)
+ * @rsvd_bytes how many bytes are reserved for this volume
+ * @rsvd_ebs how many eraseblocks are reserved for this volume
+ * @eb_size logical eraseblock size of this volume (may be less then
+ * device's logical eraseblock size due to alignment)
+ * @corrupted the volume is corrupted if this flag is not zero
+ * @name volume name (null-terminated)
+ */
+struct ubi_vol_info
+{
+ int dev_num;
+ int vol_id;
+ int type;
+ int alignment;
+ long long data_bytes;
+ long long rsvd_bytes;
+ int rsvd_ebs;
+ int eb_size;
+ int corrupted;
+ char name[UBI_VOL_NAME_MAX + 1];
+};
+
+/**
+ * libubi_open - open UBI library.
+ *
+ * This function initializes and opens the UBI library and returns UBI library
+ * descriptor in case of success and %NULL in case of failure.
+ */
+libubi_t libubi_open(void);
+
+/**
+ * libubi_close - close UBI library
+ *
+ * @desc UBI library descriptor
+ */
+void libubi_close(libubi_t desc);
+
+/**
+ * ubi_get_info - get general UBI information.
+ *
+ * @info pointer to the &struct ubi_info object to fill
+ * @desc UBI library descriptor
+ *
+ * This function fills the passed @info object with general UBI information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_info(libubi_t desc, struct ubi_info *info);
+
+/**
+ * ubi_mkvol - create an UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device to create a volume at
+ * @req UBI volume creation request (defined at <mtd/ubi-user.h>)
+ *
+ * This function creates a UBI volume as described at @req and returns %0 in
+ * case of success and %-1 in case of failure. The assigned volume ID is
+ * returned in @req->vol_id.
+ */
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req);
+
+/**
+ * ubi_rmvol - remove a UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device to remove a volume from
+ * @vol_id ID of the volume to remove
+ *
+ * This function removes volume @vol_id from UBI device @node and returns %0 in
+ * case of success and %-1 in case of failure.
+ */
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id);
+
+/**
+ * ubi_rsvol - re-size UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device owning the volume which should be
+ * re-sized
+ * @vol_id volume ID to re-size
+ * @bytes new volume size in bytes
+ *
+ * This function returns %0 in case of success and %-1 in case of error.
+ */
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes);
+
+/**
+ * ubi_get_dev_info - get UBI device information.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device to fetch information about
+ * @info pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function fills the passed @info object with UBI device information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_dev_info(libubi_t desc, const char *node,
+ struct ubi_dev_info *info);
+
+/**
+ * ubi_get_dev_info1 - get UBI device information.
+ *
+ * @desc UBI library descriptor
+ * @dev_num UBI device number to fetch information about
+ * @info pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI
+ * device number, not UBI character device.
+ */
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info);
+
+/**
+ * ubi_get_vol_info - get UBI volume information.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI volume character device to fetch information about
+ * @info pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function fills the passed @info object with UBI volume information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_vol_info(libubi_t desc, const char *node,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_get_vol_info1 - get UBI volume information.
+ *
+ * @desc UBI library descriptor
+ * @dev_num UBI device number
+ * @vol_id ID of the UBI volume to fetch information about
+ * @info pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI
+ * volume number, not UBI volume character device.
+ */
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_update_start - start UBI volume update.
+ *
+ * @desc UBI library descriptor
+ * @fd volume character devie file descriptor
+ * @bytes how many bytes will be written to the volume
+ *
+ * This function initiates UBI volume update and returns %0 in case of success
+ * and %-1 in case of error.
+ */
+int ubi_update_start(libubi_t desc, int fd, long long bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/lib/Makefile.am b/mtd-utils-1.3.1/ubi-utils/old-utils/lib/Makefile.am
new file mode 100644
index 0000000..1b0dc01
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/lib/Makefile.am
@@ -0,0 +1,58 @@
+AUTOMAKE_OPTIONS = foreign
+INCLUDES=-I$(top_srcdir)/inc -I$(top_srcdir)/../../kernel/include
+
+# -----------------------------------------------------------------------------
+# all export libs which shall be generated
+lib_LTLIBRARIES = libubi.la \
+ libpfiflash.la
+
+# -----------------------------------------------------------------------------
+# all convinence libs which shall be generated
+noinst_LTLIBRARIES = libcrc32.la \
+ libubigen.la \
+ liberror.la \
+ liblist.la \
+ libbootenv.la \
+ libpfi.la \
+ libpeb.la \
+ libreader.la \
+ libubimirror.la
+
+# -----------------------------------------------------------------------------
+# exported libs
+libpfiflash_la_SOURCES = $(top_srcdir)/src/libpfiflash/pfiflash.c
+libpfiflash_la_LDFLAGS = -no-undefined -version-info 1:0:0
+libpfiflash_la_LIBADD = libreader.la \
+ libubimirror.la \
+ libubi.la
+
+libubi_la_SOURCES = $(top_srcdir)/src/libubi/libubi.c \
+ $(top_srcdir)/src/libubi/libubi_sysfs.c
+libubi_la_LDFLAGS = -no-undefined -version-info 1:0:0
+
+# -----------------------------------------------------------------------------
+# complex convinence libs, beware for double includes.
+libreader_la_SOURCES = $(top_srcdir)/src/libreader/reader.c
+libreader_la_LIBADD = libpfi.la \
+ liblist.la \
+ libpeb.la \
+ libbootenv.la
+
+libubigen_la_SOURCES = $(top_srcdir)/src/libubigen/ubigen.c
+libubigen_la_LIBADD = libcrc32.la
+
+libbootenv_la_SOURCES = $(top_srcdir)/src/libbootenv/bootenv.c \
+ $(top_srcdir)/src/libbootenv/hashmap.c
+libbootenv_la_LIBADD = libcrc32.la
+
+libubimirror_la_SOURCES = $(top_srcdir)/src/libubimirror/ubimirror.c
+libubimirror_la_LIBADD = libubi.la
+
+
+# -----------------------------------------------------------------------------
+# simple convinence libs
+libcrc32_la_SOURCES = $(top_srcdir)/src/libcrc32/crc32.c
+liberror_la_SOURCES = $(top_srcdir)/src/liberror/error.c
+liblist_la_SOURCES = $(top_srcdir)/src/liblist/list.c
+libpeb_la_SOURCES = $(top_srcdir)/src/libpeb/peb.c
+libpfi_la_SOURCES = $(top_srcdir)/src/libpfi/pfi.c
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/perl/f128_nand_sample.cfg b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/f128_nand_sample.cfg
new file mode 100644
index 0000000..e468d9d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/f128_nand_sample.cfg
@@ -0,0 +1,38 @@
+[targets]
+complete=ipl,spl,bootenv,kernel,rootfs
+bootcode=spl,bootenv
+
+# Build sections
+[ipl]
+image=ipl.bin
+raw_starts=0x00000000
+raw_total_size=128kiB
+
+[spl]
+image=u-boot.bin
+ubi_ids=2,3
+ubi_size=2MiB
+ubi_type=static
+ubi_names=spl_0,spl_1
+
+[bootenv]
+bootenv_file=bootenv_complete.txt
+ubi_ids=4,5
+ubi_size=128kiB
+ubi_type=static
+ubi_names=bootenv_0,bootenv_1
+
+[kernel]
+image=vmlinux.bin
+ubi_ids=6,7
+ubi_size=6MiB
+ubi_type=static
+ubi_names=kernel_0,kernel_1
+
+[rootfs]
+image=rootfs.bin
+ubi_ids=8,9
+ubi_alignment=2kiB
+ubi_size=16MiB
+ubi_type=dynamic
+ubi_names=rootfs_0,rootfs_1
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/perl/f64_nor_sample.cfg b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/f64_nor_sample.cfg
new file mode 100644
index 0000000..fd44e27
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/f64_nor_sample.cfg
@@ -0,0 +1,39 @@
+[targets]
+complete=ipl,spl,bootenv,kernel,rootfs
+bootcode=spl,bootenv
+rootfs=rootfs
+
+# Build sections
+[ipl]
+image=ipl.bin
+raw_starts=0x02FE0000, 0x03FE0000
+raw_total_size=128kiB
+
+[spl]
+image=u-boot.bin
+ubi_ids=2,3
+ubi_size=2MiB
+ubi_type=static
+ubi_names=spl_0,spl_1
+
+[bootenv]
+bootenv_file=bootenv_complete.txt
+ubi_ids=4,5
+ubi_size=128kiB
+ubi_type=static
+ubi_names=bootenv_0,bootenv_1
+
+[kernel]
+image=vmlinux.bin
+ubi_ids=6,7
+ubi_size=6MiB
+ubi_type=static
+ubi_names=kernel_0,kernel_1
+
+[rootfs]
+image=rootfs.bin
+ubi_ids=8,9
+ubi_alignment=2kiB
+ubi_size=16128kiB
+ubi_type=dynamic
+ubi_names=rootfs_0,rootfs_1
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/perl/mkpfi b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/mkpfi
new file mode 100755
index 0000000..2cce587
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/mkpfi
@@ -0,0 +1,723 @@
+#!/usr/bin/perl
+#
+# Copyright (c) International Business Machines Corp., 2006
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+#
+# mkpfi
+#
+# This perl program is assembles PFI files from a config file.
+#
+# Author: Oliver Lohmann (oliloh@de.ibm.com)
+#
+use warnings;
+use strict;
+use lib "/usr/lib/perl5"; # Please change this path as you need it, or
+ # make a proposal how this could be done
+ # nicer.
+use Getopt::Long;
+use Pod::Usage;
+use Config::IniFiles;
+use File::Temp;
+
+# ----------------------------------------------------------------------------
+# Versions
+our $version : unique = "0.1";
+our $pfi_version : unique = "0x1";
+
+# ----------------------------------------------------------------------------
+# Globals
+my $verbose = 0;
+my $cfg;
+
+my %opts = ();
+my %files = (config => "");
+my @tmp_files;
+
+my %tools = (ubicrc32 => "ubicrc32");
+
+# ----------------------------------------------------------------------------
+# Processing the input sections
+#
+# The idea is to combine each section entry with a function
+# in order to allow some kind of preprocessing for the values
+# before they are written into the PFI file.
+# This is especially useful to be more verbose and
+# user-friendly in the layout file.
+#
+# All key-function hashes are applied after the general
+# validation of the configuration file.
+# If any mandatory key is missing in a section the user
+# will be informed and the PFI creation process is aborted.
+#
+# Default keys will be checked for their presence inside the config
+# file. If they are missing, they will be generated with appr. values.
+
+# Mandatory keys for UBI volumes.
+my %ubi_keys = ("ubi_ids" => \&check_id_list,
+ "ubi_size" => \&replace_num,
+ "ubi_type" => \&replace_type,
+ "ubi_names" => \&remove_spaces,
+ "ubi_alignment" => \&replace_num);
+
+# Mandatory keys for RAW sections.
+my %raw_keys = ("raw_starts" => \&expand_starts,
+ "raw_total_size" => \&replace_num);
+
+# Common default keys for documentation and control purposes.
+my %common_keys = ("flags" => \&replace_num,
+ "label" => \&do_nothing);
+
+# Define any defaults here. Values which maintained in this default
+# region need not to be specified by the user explicitly.
+my %def_ubi_keys = ("ubi_alignment" => [\&set_default, "0x1"]);
+my %def_raw_keys = ();
+my %def_common_keys = ("flags" => [\&set_default, "0x0"],
+ "label" => [\&generate_label, ""]);
+
+# ----------------------------------------------------------------------------
+# Input keys, actually the path to the input data.
+
+my %input_keys = ("image" => \&do_nothing);
+
+# Placeholder keys allow the replacement via a special
+# purpose function. E.g. the bootenv_file key will be used
+# to generate bootenv binary data from an text file and
+# replace the bootenv_file key with an image key to handle it
+# in the same way in the further creation process.
+my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image);
+
+# ----------------------------------------------------------------------------
+# Helper
+
+# @brief Get current time string.
+sub get_date {
+ my $tmp = scalar localtime;
+ $tmp =~ s/ /_/g;
+ return $tmp;
+}
+
+# @brief Print an info message to stdout.
+sub INFO($) {
+ my $str = shift;
+
+ if (!$verbose) {
+ return;
+ }
+
+ print STDOUT $str;
+}
+
+# @brief Print an error message to stderr.
+sub ERR($) {
+ my $str = shift;
+ print STDERR $str;
+}
+
+# @brief Print a warning message to stderr.
+sub WARN($) {
+ my $str = shift;
+ print STDERR $str;
+}
+
+sub parse_command_line($) {
+ my $opt = shift;
+ my $result = GetOptions( "help" => \$$opt{'help'},
+ "man" => \$$opt{'man'},
+ "config=s" => \$$opt{'config'},
+ "verbose" => \$$opt{'verbose'},
+ ) or pod2usage(2);
+ pod2usage(1) if defined ($$opt{help});
+ pod2usage(-verbose => 2) if defined ($$opt{man});
+
+ $verbose = $$opt{verbose} if defined $$opt{verbose};
+
+ if (!defined $$opt{config}) {
+ ERR("[ ERROR: No config file specified. Aborting...\n");
+ exit 1;
+ }
+
+}
+
+# @brief Check if all needed tools are in PATH.
+sub check_tools {
+ my $err = 0;
+ my $key;
+
+ foreach $key (keys %tools) {
+ if (`which $tools{$key}` eq "") {
+ ERR("\n") if ($err == 0);
+ ERR("! Please add the tool \'$tools{$key}\' " .
+ "to your path!\n");
+ $err = 1;
+ }
+ }
+ die "[ ERROR: Did not find all needed tools!\n" if $err;
+}
+
+sub open_cfg_file($) {
+ my $fname = shift;
+ my $res = new Config::IniFiles( -file => $fname );
+
+ die "[ ERROR: Cannot load your config file!\n" if (!defined $res);
+ return $res;
+}
+
+sub set_default($$$$) {
+ my ($cfg, $section, $parameter, $def_value) = @_;
+ $cfg->newval($section, $parameter, $def_value);
+ return;
+}
+
+sub generate_label($$$$) {
+ my ($cfg, $section, $parameter, $def_value) = @_;
+ my $new_label = $def_value . $section;
+ $new_label .= "_" . get_date;
+ $cfg->newval($section, $parameter, $new_label);
+ return;
+}
+
+# @brief Converts any num to a unified hex string, i.e the resulting value
+# always starts with "0x" and is aligned to 8 hexdigits.
+# @return Returns 0 on success, otherwise an error occured.
+#
+sub any_num_to_hex($$) {
+ my $val = shift;
+ my $res = shift;
+
+ # M(iB)
+ if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) {
+ $$res = sprintf("0x%08x", $1 * 1024 * 1024);
+ }
+ # k(iB)
+ elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) {
+ $$res = sprintf("0x%08x", $1 * 1024);
+ }
+ # hex
+ elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) {
+ $$res = sprintf("0x%08x", hex $1);
+ }
+ # decimal
+ elsif ($val =~ m/^([0-9]+)$/g) {
+ $$res = sprintf("0x%08x", $1);
+ }
+ else {
+ $$res = "";
+ return -1;
+ }
+
+ return 0;
+}
+
+sub remove_spaces($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ my ($start, @starts, @new_starts);
+ my $val = $cfg->val($section, $parameter);
+ my $res;
+
+ $val =~ s/ //g; # spaces
+ $cfg->newval($section, $parameter, $val);
+}
+
+sub expand_starts($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ my ($start, @starts, @new_starts);
+ my $val = $cfg->val($section, $parameter);
+ my $res;
+
+ $val =~ s/ //g; # spaces
+ @starts = split(/,/, $val);
+
+ foreach $start (@starts) {
+ if (any_num_to_hex($start, \$res) != 0) {
+ ERR("[ ERROR: [$section]\n");
+ ERR("[ Expecting a list of numeric " .
+ "values for parameter: $parameter\n");
+ exit 1;
+ }
+ push (@new_starts, $res);
+ }
+ $res = join(',', @starts);
+
+ $cfg->newval($section, $parameter, $res);
+}
+
+sub check_id_list($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ my $val = $cfg->val($section, $parameter);
+ my $res;
+
+ if (!($val =~ m/^[0-9]+[,0-9]*/)) {
+ ERR("[ ERROR: Syntax error in 'ubi_ids' in " .
+ "section '$section': $val\n");
+ ERR("[ Aborting... ");
+ exit 1;
+ }
+}
+
+sub replace_type($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ my $val = $cfg->val($section, $parameter);
+ my $res;
+
+ $res = lc($val);
+ grep {$res eq $_} ('static', 'dynamic')
+ or die "[ ERROR: Unknown UBI Volume Type in " .
+ "section '$section': $val\n";
+
+ $cfg->newval($section, $parameter, $res);
+}
+
+
+sub replace_num($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ my $val = $cfg->val($section, $parameter);
+ my $res = "";
+
+ if (any_num_to_hex($val, \$res) != 0) {
+ ERR("[ ERROR: [$section]\n");
+ ERR("[ Expecting a numeric value " .
+ "for parameter: $parameter\n");
+ exit 1;
+ }
+ $cfg->newval($section, $parameter, $res);
+}
+
+sub do_nothing($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ return;
+}
+
+sub bootenv_sanity_check($) {
+ my $env = shift; # hash array containing bootenv
+ my %pdd = ();
+
+ defined($$env{'pdd'}) or return "'pdd' not defined";
+ foreach (split /,/, $$env{'pdd'}) {
+ defined($$env{$_}) or return "undefined '$_' in pdd";
+ $pdd{$_} = 1;
+ }
+
+ defined $$env{'pdd_preserve'} or
+ return "";
+ foreach (split /,/, $$env{'pdd_preserve'}) {
+ defined($pdd{$_})
+ or return "pdd_preserve field '$_' not in pdd";
+ }
+ return "";
+}
+
+sub create_bootenv_image($$$) {
+ my ($cfg, $section, $parameter) = @_;
+ my $txt_fn = $cfg->val($section, "bootenv_file");
+ my $in;
+
+ my %value = ();
+ my @key = ();
+
+ open $in, "<", $txt_fn
+ or die "[ ERROR: can't open bootenv file '$txt_fn'.\n";
+ while (<$in>) {
+ next if (/^\s*(\#.*)?$/); # Skip comments/whitespace.
+
+ if (/^(\S+?)\+\=(.*)$/) {
+ defined($value{$1}) or
+ die "$txt_fn:$.: error: appending to" .
+ " non-existent '$1'\n";
+ $value{$1} .= $2;
+ } elsif (/^(\S+?)\=(.*)$/) {
+ not defined($value{$1}) or
+ die "$txt_fn:$.: error: trying to" .
+ " redefine '$1'\n";
+ push @key, $1;
+ $value{$1} = $2;
+ } else {
+ die "$txt_fn:$.: error: unrecognized syntax\n";
+ }
+ }
+ close $in;
+
+ $_ = &bootenv_sanity_check(\%value)
+ and die "$txt_fn: error: $_\n";
+
+ my $tmp_file = new File::Temp();
+ push (@tmp_files, $tmp_file);
+
+ foreach (@key) {
+ print $tmp_file "$_=", $value{$_}, "\0";
+ }
+ close $tmp_file;
+
+ $cfg->newval($section, "image", $tmp_file-> filename);
+}
+
+sub process_keys($$$) {
+ my ($cfg, $section, $keys) = @_;
+ my @parameters = $cfg->Parameters($section);
+ my $i;
+
+ for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) {
+ if (defined($$keys{$parameters[$i]})) {
+ $$keys{$parameters[$i]}->($cfg, $section,
+ $parameters[$i]);
+ }
+ }
+
+}
+
+sub is_in_keylist($$) {
+ my ($key, $keys) = @_;
+ my $i;
+
+ for ($i = 0; $i < scalar(@$keys); $i++) {
+ if ($$keys[$i] eq $key) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+sub check_default_keys($$$) {
+ my ($cfg, $section, $keys) = @_;
+ my @parameters = $cfg->Parameters($section);
+ my $key;
+
+ foreach $key (keys %$keys) {
+ if (!is_in_keylist($key, \@parameters)) {
+ $$keys{$key}[0]->
+ ($cfg, $section, $key, $$keys{$key}[1]);
+ }
+ }
+
+}
+
+
+
+sub check_keys($$$) {
+ my ($cfg, $section, $keys) = @_;
+ my @parameters = $cfg->Parameters($section);
+ my ($i, $key, $err);
+
+ $err = 0;
+ for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) {
+ if (!is_in_keylist($$keys[$i], \@parameters)) {
+ ERR("[ ERROR: [$section]\n") if $err == 0;
+ $err = 1;
+ ERR("[ Missing key '$$keys[$i]'\n");
+ }
+ }
+
+ if ($err) {
+ ERR("[ Aborting...\n");
+ exit 1;
+ }
+}
+
+sub push_pfi_data($$$$$) {
+ my ($cfg, $section, $pfi_infos, $keys, $mode) = @_;
+ my ($tmp, $i, $hdr);
+
+ my %pfi_info = ();
+ $pfi_info{'mode'} = $mode;
+ $pfi_info{'image'} = $cfg->val($section, "image");
+
+ # Build the PFI header
+ $hdr = sprintf("PFI!\n");
+ $hdr .= sprintf("version=0x%08x\n", hex $pfi_version);
+ $hdr .= sprintf("mode=$mode\n");
+
+ # calculate the size of the binary data part
+ $tmp = -s $cfg->val($section, "image");
+ if (!defined $tmp) {
+ ERR("[ ERROR: [$section]\n");
+ ERR("[ Missing input image: "
+ . $cfg->val($section, "image") . "\n");
+ exit 1;
+ }
+ # Check for the image to fit into the given space
+ my $quota;
+ if ($mode eq 'raw') {
+ $quota = oct $cfg->val($section, "raw_total_size");
+ } elsif ($mode eq 'ubi') {
+ $quota = oct $cfg->val($section, "ubi_size");
+ }
+ $tmp <= $quota
+ or die "[ERROR: image file too big: " .
+ $cfg->val($section, "image") . "\n";
+ $pfi_info{'size'} = $tmp;
+
+ $hdr .= sprintf("size=0x%08x\n", $tmp);
+
+ my $img_file = $cfg->val($section, "image");
+ my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`;
+ if (any_num_to_hex($crc32, \$tmp) != 0) {
+ die "[ ERROR: $tools{'ubicrc32'} returned with errors";
+ }
+ $hdr .= sprintf("crc=$tmp\n");
+
+
+ # Process all remaining keys
+ for ($i = 0; $i < scalar (@$keys); $i++) {
+ if ($$keys[$i] eq "image") { # special case image input file
+ if (! -e ($tmp = $cfg->val($section, "image"))) {
+ ERR("[ ERROR: [$section]\n");
+ ERR("[ Cannot find input file $tmp\n");
+ exit 1;
+ }
+ next;
+ }
+ $hdr .= sprintf("%s=%s\n", $$keys[$i],
+ $cfg->val($section, $$keys[$i]));
+ }
+
+ $hdr .= sprintf("\n"); # end marker for PFI-header
+
+ $pfi_info{'header'} = $hdr;
+
+ # store in the header list
+ push @$pfi_infos, \%pfi_info;
+}
+
+sub process_section($$$$$$) {
+ my ($cfg, $section, $pfi_infos, $custom_keys,
+ $def_custom_keys, $mode) = @_;
+ my @keys = (keys %common_keys, keys %$custom_keys);
+ my @complete_keys = (@keys, keys %input_keys);
+
+ # set defaults if necessary
+ check_default_keys($cfg, $section, $def_custom_keys);
+ check_default_keys($cfg, $section, \%def_common_keys);
+
+ # check for placeholders...
+ process_keys($cfg, $section, \%input_placeholder_keys);
+
+ # VALIDATE layout.cfg entries
+ check_keys($cfg, $section, \@complete_keys);
+
+ # execute linked functions (if any)
+ process_keys($cfg, $section, \%common_keys);
+ process_keys($cfg, $section, $custom_keys);
+
+ push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode);
+}
+
+sub get_section_info($$) {
+ my ($cfg, $section) = @_;
+ my @parameters = $cfg->Parameters($section);
+ my ($ubi, $raw, $i, @res);
+
+ $ubi = $raw = 0;
+ for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) {
+ if ($parameters[$i] =~ m/ubi_/gi) {
+ $ubi = 1;
+ @res = (\%ubi_keys, \%def_ubi_keys, "ubi");
+ }
+ if ($parameters[$i] =~ m/raw_/gi) {
+ $raw = 1;
+ @res = (\%raw_keys, \%def_raw_keys, "raw");
+ }
+ }
+
+ if (($ubi + $raw) != 1) { # double definition in section
+ ERR("[ ERROR: Layout error in section '$section'\n");
+ exit 1;
+ }
+
+ return @res;
+}
+
+sub mk_target_list($$) {
+ my $val = shift;
+ my $tmp = shift;
+ my $complete = 0;
+
+ if ($val =~ m/\((.*)\)/g) {
+ $val = $1;
+ $complete = 1;
+ }
+ $val =~ s/ //g; # spaces
+
+ @$tmp = split(/,/, $val);
+
+ return $complete;
+}
+
+sub copy_bytes($$$) {
+ my ($in, $out, $to_copy) = @_;
+
+ while ($to_copy) {
+ my $buf;
+ my $bufsize = 1024*1024;
+
+ $bufsize < $to_copy or $bufsize = $to_copy;
+ read($in, $buf, $bufsize) == $bufsize
+ or die "[ ERROR: Image file shrunk during operation\n";
+ print $out $buf;
+ $to_copy -= $bufsize;
+ }
+}
+
+sub write_target($$) {
+ my ($pfi_infos, $target) = @_;
+ my ($pfi_info);
+
+ INFO("[ Writting target pfi file: '$target.pfi'...\n");
+ if (-e "$target.pfi") {
+ WARN("! Replaced old pfi...\n");
+ `rm -f $target.pfi`;
+ }
+ open(FILE, ">", "$target.pfi")
+ or die "[ ERROR: Cannot create output file: $target.pfi\n";
+ binmode(FILE);
+
+ # @FIXME sort by mode (first raw, then ubi)
+ # Currently this ordering is based on a string comparism. :-)
+ @$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos;
+
+ # Print all headers first
+ foreach $pfi_info (@$pfi_infos) {
+ print FILE $$pfi_info{'header'};
+
+ }
+ # Print the linked data sections
+ print FILE "DATA\n";
+ foreach $pfi_info (@$pfi_infos) {
+ open(IMAGE, "<", $$pfi_info{'image'})
+ or die "[ ERROR: Cannot open input image: " .
+ "$$pfi_info{'image'}" . "\n";
+ binmode(IMAGE);
+ ©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'});
+ close(IMAGE) or die "[ ERROR: Cannot close input image: " .
+ "$$pfi_info{'image'}" . "\n";
+ }
+ close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n";
+}
+
+sub process_config($) {
+ my $cfg = shift;
+ my @sections = $cfg->Sections;
+ my ($i, $j, $keylist, $def_keylist, $mode, $tmp,
+ @tlist, $complete,@pfi_infos);
+
+ my @parameters = $cfg->Parameters("targets") or
+ die "[ ERROR: Config file has no 'targets' section!\n";
+
+ for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) {
+ INFO("[ Processing target '$parameters[$i]'...\n");
+ @pfi_infos = ();
+
+ # get a list of subtargets
+ $complete = mk_target_list($cfg->val("targets",
+ $parameters[$i]), \@tlist);
+ # build all subtargets
+ for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) {
+ ($keylist, $def_keylist, $mode)
+ = get_section_info($cfg, $tlist[$j]);
+ process_section($cfg, $tlist[$j],
+ \@pfi_infos,
+ $keylist, $def_keylist, $mode);
+ }
+
+ write_target(\@pfi_infos, $parameters[$i]);
+ }
+
+ INFO("[ Success.\n");
+
+
+}
+
+sub clear_files() {
+ # @FIXME:
+ # Works implicitly and Fedora seems to have removed
+ # the cleanup call. Thus for now, inactive.
+ # File::Temp::cleanup();
+}
+
+require 5.008_000; # Tested with version 5.8.0.
+select STDOUT; $| = 1; # make STDOUT output unbuffered
+select STDERR; $| = 1; # make STDERR output unbuffered
+
+parse_command_line(\%opts);
+check_tools;
+$cfg = open_cfg_file($opts{config});
+process_config($cfg);
+clear_files;
+
+__END__
+
+
+=head1 NAME
+
+mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles
+
+
+=head1 SYNOPSIS
+
+mkpfi [OPTIONS ...]
+
+
+ OPTION
+
+ [--config] [--help] [--man]
+
+
+=head1 ABSTRACT
+
+Perl script for generating pdd pfi files from given config files.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--help>
+
+Print out brief help message.
+
+=item B<--usage>
+
+Print usage.
+
+=item B<--config>
+
+Config input file.
+
+=item B<--man>
+
+Print manual page, same as 'perldoc mkpfi'.
+
+=item B<--verbose>
+
+Be verbose!
+
+=back
+
+=head1 BUGS
+
+Report via MTD mailing list
+
+
+=head1 SEE ALSO
+
+http://www.linux-mtd.infradead.org/
+
+
+=head1 AUTHOR
+
+Oliver Lohmann (oliloh@de.ibm.com)
+
+=cut
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/perl/ubicrc32.pl b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/ubicrc32.pl
new file mode 100755
index 0000000..add5f9d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/perl/ubicrc32.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+# Subroutine crc32(): Calculates the CRC on a given string.
+
+{
+ my @table = ();
+
+ # @brief Calculate CRC32 for a given string.
+ sub crc32
+ {
+ unless (@table) {
+ # Initialize the CRC table
+ my $poly = 0xEDB88320;
+ @table = ();
+
+ for my $i (0..255) {
+ my $c = $i;
+
+ for my $j (0..7) {
+ $c = ($c & 1) ? (($c >> 1) ^ $poly) : ($c >> 1);
+ }
+ $table[$i] = $c;
+ }
+ }
+ my $s = shift; # string to calculate the CRC for
+ my $crc = shift; # CRC start value
+
+ defined($crc)
+ or $crc = 0xffffffff; # Default CRC start value
+
+ for (my $i = 0; $i < length($s); $i++) {
+ $crc = $table[($crc ^ ord(substr($s, $i, 1))) & 0xff]
+ ^ ($crc >> 8);
+ }
+ return $crc;
+ }
+}
+
+sub crc32_on_file
+{
+ my $file = shift;
+
+ my $crc32 = crc32('');
+ my $buf = '';
+ my $ret = 0;
+
+ while ($ret = read($file, $buf, 8192)) {
+ $crc32 = crc32($buf, $crc32);
+ }
+ defined($ret)
+ or return undef;
+ printf("0x%x\n", $crc32);
+}
+
+
+# Main routine: Calculate the CRCs on the given files and print the
+# results.
+
+{
+ if (@ARGV) {
+ while (my $path = shift) {
+ my $file;
+ open $file, "<", $path
+ or die "Error opening '$path'.\n";
+
+ &crc32_on_file($file)
+ or die "Error reading from '$path'.\n";
+ close $file;
+ }
+ } else {
+ &crc32_on_file(\*STDIN)
+ or die "Error reading from stdin.\n";
+ }
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/Makefile b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/Makefile
new file mode 100644
index 0000000..ebd9bc6
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/Makefile
@@ -0,0 +1,75 @@
+#
+# Makefile
+#
+# Testcase for UBI pfi update.
+#
+# Author: Frank Haverkamp <haverkam@de.ibm.com>
+#
+
+card = test
+mkpfi_cfg = test.cfg
+
+#
+# Some default values you might want to overwrite. Try it if you need
+# it and add more if needed. Note that no real sanity checking is done
+# on those values. If you do it wrong your card has no valid PDD data.
+#
+
+PATH := $(PATH):/opt/ppc/usr/bin:../perl:..
+
+dd = dd
+sed = sed
+bin2nand = bin2nand
+ubigen = ubigen
+mkpfi = mkpfi -v
+pfi2bin = pfi2bin -v
+
+vmlinux_bin ?= test_vmlinux.bin
+rootfs_bin ?= test_rootfs.bin
+spl_bin ?= test_u-boot.bin
+pdd_txt ?= pdd.txt
+
+flashtype ?= nand
+pagesize ?= 2048
+
+compl ?= $(card)_complete
+compl_pfi ?= $(compl).pfi
+compl_img ?= $(compl).img
+
+compl_nand2048_mif=$(compl).$(flashtype)$(pagesize).mif
+compl_nand2048_img=$(compl).$(flashtype)$(pagesize).img
+
+all: $(compl_pfi) $(compl_nand2048_mif)
+
+$(compl_pfi): $(vmlinux_bin) $(rootfs_bin) $(spl_bin)
+ $(mkpfi) -c $(mkpfi_cfg)
+
+# Binary data and out of band data (OOB)
+#
+$(compl_nand2048_mif): $(compl_img)
+ $(bin2nand) -p $(pagesize) -o $(compl_nand2048_mif) $<
+
+# Binary data only
+#
+$(compl_img): $(compl_pfi)
+ $(pfi2bin) -j $(pdd_txt) -o $@ $<
+
+#
+# Default data
+#
+# If the binary data is not available in the current working directory
+# we try to create symlinks to our test data.
+#
+$(vmlinux_bin) $(rootfs_bin) $(spl_bin):
+ @echo
+ @echo "No $@ found, will use defaults !"
+ @echo
+ @echo "OR press CTRL-C to provide your own $@" && \
+ sleep 1 && \
+ $(dd) if=/dev/urandom of=$@ bs=1M count=1
+
+clean:
+ $(RM) *.pfi *~
+
+distclean: clean
+ $(RM) *.bin *.mif *.oob *.img
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/README b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/README
new file mode 100644
index 0000000..899b4a1
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/README
@@ -0,0 +1,11 @@
+README
+======
+
+This procedure creates a test pfi which should be flashed to our
+system with pfiflash. The testcase should read the data back and
+compare with the original.
+
+We should try not forget to run these tests before we release
+a new version of UBI.
+
+Frank
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/TODO b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/TODO
new file mode 100644
index 0000000..f093e77
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/TODO
@@ -0,0 +1,5 @@
+TODO
+====
+
+ * Range checking is broken, reserving 2M and offering 3M binary data
+ ... works!? No!
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/bin2nand2bin_test.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/bin2nand2bin_test.sh
new file mode 100644
index 0000000..a17c91b
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/bin2nand2bin_test.sh
@@ -0,0 +1,184 @@
+#!/bin/sh
+#
+# Testcase for nand2bin and bin2nand. Generate testdata and inject
+# biterrors. Convert data back and compare with original data.
+#
+# Conversion:
+# bin -> bin2nand -> mif -> nand2bin -> img
+#
+
+inject_biterror=./scripts/inject_biterror.pl
+
+pagesize=2048
+oobsize=64
+
+# Create test data
+dd if=/dev/urandom of=testblock.bin bs=131072 count=1
+
+echo "Test conversion without bitflips ..."
+
+echo -n "Convert bin to mif ... "
+bin2nand --pagesize=${pagesize} -o testblock.mif testblock.bin
+if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+else
+ echo "ok"
+fi
+
+echo -n "Convert mif to bin ... "
+nand2bin --pagesize=${pagesize} -o testblock.img testblock.mif
+if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+else
+ echo "ok"
+fi
+
+echo -n "Comparing data ... "
+diff testblock.bin testblock.img
+if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+else
+ echo "ok"
+fi
+
+echo "Test conversion with uncorrectable ECC erors ..."
+echo -n "Inject biterror at offset $ioffs ... "
+${inject_biterror} --offset=0 --bitmask=0x81 \
+ --input=testblock.mif \
+ --output=testblock_bitflip.mif
+if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+else
+ echo "ok"
+fi
+
+echo "Convert mif to bin ... "
+rm testblock.img
+nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \
+ testblock_bitflip.mif
+if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+else
+ echo "ok"
+fi
+
+echo -n "Comparing data, must fail due to uncorrectable ECC ... "
+diff testblock.bin testblock.img
+if [ $? -ne "0" ]; then
+ echo "ok" # Must fail!
+else
+ echo "failed!"
+ exit 1
+fi
+
+echo "Test bitflips in data ... "
+for offs in `seq 0 255` ; do
+
+ cp testblock.mif testblock_bitflip.mif
+
+ for xoffs in 0 256 512 768 ; do
+ let ioffs=$offs+$xoffs
+
+ cp testblock_bitflip.mif testblock_bitflip_tmp.mif
+ echo -n "Inject biterror at offset $ioffs ... "
+ ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \
+ --input=testblock_bitflip_tmp.mif \
+ --output=testblock_bitflip.mif
+ if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+ done
+
+ echo "Convert mif to bin ... "
+ rm testblock.img
+ nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \
+ testblock_bitflip.mif
+ if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+
+ echo -n "Comparing data ... "
+ diff testblock.bin testblock.img
+ if [ $? -ne "0" ]; then
+ hexdump testblock.bin > testblock.bin.txt
+ hexdump testblock.img > testblock.img.txt
+ echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare"
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+
+ # Without correction
+ echo "Convert mif to bin ... "
+ rm testblock.img
+ nand2bin --pagesize=${pagesize} -o testblock.img \
+ testblock_bitflip.mif
+ if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+
+ echo -n "Comparing data must differ, correction is disabled ... "
+ diff testblock.bin testblock.img
+ if [ $? -ne "0" ]; then
+ echo "ok" # must fail
+ else
+ echo "failed!"
+ exit 1
+ fi
+done
+
+echo "Test bitflips in OOB data ... "
+for offs in `seq 0 $oobsize` ; do
+
+ let ioffs=$pagesize+$offs
+
+ echo -n "Inject biterror at offset $ioffs ... "
+ ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \
+ --input=testblock.mif \
+ --output=testblock_bitflip.mif
+ if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+
+ echo "Convert mif to bin ... "
+ rm testblock.img
+ nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \
+ testblock_bitflip.mif
+ if [ $? -ne "0" ]; then
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+
+ echo -n "Comparing data ... "
+ diff testblock.bin testblock.img
+ if [ $? -ne "0" ]; then
+ hexdump testblock.bin > testblock.bin.txt
+ hexdump testblock.img > testblock.img.txt
+ echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare"
+ echo "failed!"
+ exit 1
+ else
+ echo "ok"
+ fi
+done
+
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/inject_biterror.pl b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/inject_biterror.pl
new file mode 100644
index 0000000..b4a862a
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/inject_biterror.pl
@@ -0,0 +1,94 @@
+#!/usr/bin/perl -w
+#
+# 2007 Frank Haverkamp <haver@vnet.ibm.com>
+#
+# Program for bit-error injection. I am sure that perl experts do it
+# in 1 line. Please let me know how it is done right ;-).
+#
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+
+my $i;
+my $help;
+my $result;
+my $offset = 0;
+my $bitmask = 0x01;
+my $in = "input.mif";
+my $out = "output.mif";
+
+$result = GetOptions ("offset=i" => \$offset, # numeric
+ "bitmask=o" => \$bitmask, # numeric
+ "input=s" => \$in, # string
+ "output=s" => \$out, # string
+ "help|?" => \$help) or pod2usage(2);
+
+pod2usage(1) if $help;
+
+my $buf;
+
+open(my $in_fh, "<", $in)
+ or die "Cannot open file $in: $!";
+binmode $in_fh;
+
+open(my $out_fh, ">", $out) or
+ die "Cannot open file $out: $!";
+binmode $out_fh;
+
+$i = 0;
+while (sysread($in_fh, $buf, 1)) {
+
+ $buf = pack('C', unpack('C', $buf) ^ $bitmask) if ($i == $offset);
+ syswrite($out_fh, $buf, 1) or
+ die "Cannot write to offset $offset: $!";
+ $i++;
+}
+
+close $in_fh;
+close $out_fh;
+
+__END__
+
+=head1 NAME
+
+inject_biterrors.pl
+
+=head1 SYNOPSIS
+
+inject_biterror.pl [options]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--offset>=I<offset>
+
+Byte-offset where bit-error should be injected.
+
+=item B<--bitmask>=I<bitmask>
+
+Bit-mask where to inject errors in the byte.
+
+=item B<--input>=I<input-file>
+
+Input file.
+
+=item B<--output>=I<output-file>
+
+Output file.
+
+=back
+
+=head1 DESCRIPTION
+
+B<inject_biterrors.pl> will read the given input file and inject
+biterrors at the I<offset> specified. The location of the biterrors
+are defined by the I<bitmask> parameter.
+
+=cut
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/jffs2_test.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/jffs2_test.sh
new file mode 100755
index 0000000..0cc9f0c
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/jffs2_test.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Testcase for JFFS2 verification. We do not want to see any
+# kernel errors occuring when this is executed.
+#
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+echo "***********************************************************************"
+echo "* jffs2 testing ... *"
+echo "***********************************************************************"
+
+ulimit -c unlimited
+
+for i in `seq 5000`; do
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ dd if=/dev/urandom of=test.bin bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "Copy to different file ... "
+ dd if=test.bin of=new.bin bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing files ... "
+ cmp test.bin new.bin
+ dd if=test.bin of=new.bin bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+done
+
+for i in `seq 5000`; do
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ dd if=/dev/urandom of=foo bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+done
+
+for i in `seq 5000`; do
+ echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1) ... "
+ dd if=/dev/zero of=foo bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+done
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool JFFS2 using system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/mkdevs.pl b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/mkdevs.pl
new file mode 100755
index 0000000..f0fd464
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/mkdevs.pl
@@ -0,0 +1,32 @@
+#!/usr/bin/perl -w
+
+#
+# Author: Artem B. Bityutskiy <dedekind@oktetlabs.ru>
+#
+# A small scrip which creates UBI device nodes in /dev. UBI allocates
+# major number dynamically, so the script looks at /proc/devices to find
+# out UBI's major number.
+#
+
+
+my $proc = '/proc/devices';
+my $regexp = '(\d+) (ubi\d+)$';
+
+
+open FILE, "<", $proc or die "Cannot open $proc file: $!\n";
+my @file = <FILE>;
+close FILE;
+
+foreach (@file) {
+ next if not m/$regexp/g;
+ print "found $2\n";
+
+ system("rm -rf /dev/$2");
+ system("mknod /dev/$2 c $1 0");
+
+ for (my $i = 0; $i < 128; $i += 1) {
+ system("rm -rf /dev/$2_$i");
+ my $j = $i + 1;
+ system("mknod /dev/$2_$i c $1 $j");
+ }
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/pdd.txt b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/pdd.txt
new file mode 100644
index 0000000..a3ad915
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/pdd.txt
@@ -0,0 +1,16 @@
+pdd=flash_type,flash_size,flash_eraseblock_size,flash_page_size,card_serialnumber,card_type,ethaddr,eth1addr,eth0,eth1,total,card_hardwarelevel
+pdd_preserve=ethaddr,eth1addr,card_serialnumber
+# To be personalized
+ethaddr=00:04:34:56:78:9A
+eth1addr=00:04:34:56:78:9B
+card_serialnumber=SN0
+# Static for this card type
+total=102M
+card_type=nand_driven_testcard
+card_hardwarelevel=0
+eth0=bcm5222,eth0,0
+eth1=bcm5222,eth0,1
+flash_type=NAND
+flash_size=0x08000000
+flash_eraseblock_size=0x00020000
+flash_page_size=0x00000800
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/run_all.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/run_all.sh
new file mode 100755
index 0000000..040bcbd
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/run_all.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+exit_success ()
+{
+ echo "UBI Utils Test Scripts - SUCCESS!"
+ exit 0
+}
+
+exit_failure ()
+{
+ echo $1
+ echo "UBI Utils Test Scripts - FAILED!"
+ exit 1
+}
+
+echo UBI Utils Test Scripts
+
+devno=$1
+logfile=temp-test-log.txt
+
+if test -z "$devno";
+then
+ echo "Usage is $0 <mtd device number>"
+ exit 1
+fi
+
+cwd=`pwd` || exit_failure "pwd failed"
+
+log="${cwd}/${logfile}"
+
+PATH=$PATH:$cwd:..
+
+cat /dev/null > $log || exit_failure "Failed to create $log"
+
+echo "Setting up for jffs2_test.sh" | tee -a $log
+
+avail=`cat /sys/class/ubi/ubi${devno}/avail_eraseblocks`
+size=`cat /sys/class/ubi/ubi${devno}/eraseblock_size`
+
+bytes=`expr $avail \* $size`
+
+ubimkvol -d$devno -s$bytes -n0 -Njtstvol || exit_failure "ubimkvol failed"
+
+mkdir -p /mnt/test_file_system || exit_failure "mkdir failed"
+
+mtd=`cat /proc/mtd | grep jtstvol | cut -d: -f1`
+
+if test -z "$mtd";
+then
+ exit_failure "mtd device not found"
+fi
+
+mount -t jffs2 $mtd /mnt/test_file_system || exit_failure "mount failed"
+
+cd /mnt/test_file_system || exit_failure "cd failed"
+
+echo Running jffs2_test.sh | tee -a $log
+
+jffs2_test.sh >> $log 2>&1 || exit_failure "jffs2_test.sh failed"
+
+rm -f *
+
+cd $cwd || exit_failure "cd failed"
+
+umount /mnt/test_file_system || exit_failure "umount failed"
+
+ubirmvol -d$devno -n0 || exit_failure "ubirmvol failed"
+
+major=`cat /sys/class/ubi/ubi${devno}/dev | cut -d: -f1`
+
+for minor in `seq 0 32`; do
+ if test ! -e /dev/ubi${devno}_$minor ;
+ then
+ mknod /dev/ubi${devno}_$minor c $major $(($minor + 1))
+ fi
+done
+
+rm -f testdata.bin readdata.bin
+
+echo Running ubi_jffs2_test.sh | tee -a $log
+
+ubi_jffs2_test.sh >> $log 2>&1 || exit_failure "ubi_jffs2_test.sh failed"
+
+echo Running ubi_test.sh | tee -a $log
+
+ubi_test.sh >> $log 2>&1 || exit_failure "ubi_test.sh failed"
+
+for minor in `seq 0 32`; do
+ if test -e /sys/class/ubi/ubi${devno}/$minor;
+ then
+ ubirmvol -d$devno -n$minor || exit_failure "ubirmvol failed"
+ fi
+done
+
+echo Running ubi_tools_test.sh | tee -a $log
+
+ubi_tools_test.sh >> $log 2>&1 || exit_failure "ubi_tools_test failed"
+
+rm -f $log
+
+exit_success
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/test.cfg b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/test.cfg
new file mode 100644
index 0000000..0b5ec48
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/test.cfg
@@ -0,0 +1,23 @@
+[targets]
+test_complete=spl,kernel,rootfs
+
+[spl]
+image=test_u-boot.bin
+ubi_ids=10,11
+ubi_size=1MiB
+ubi_type=static
+ubi_names=test_spl_0,test_spl_1
+
+[kernel]
+image=test_vmlinux.bin
+ubi_ids=12,13
+ubi_size=2MiB
+ubi_type=static
+ubi_names=test_kernel_0,test_kernel_1
+
+[rootfs]
+image=test_rootfs.bin
+ubi_ids=14,15
+ubi_size=2MiB
+ubi_type=dynamic
+ubi_names=test_rootfs_0,test_rootfs_1
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_jffs2_test.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_jffs2_test.sh
new file mode 100755
index 0000000..883903d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_jffs2_test.sh
@@ -0,0 +1,411 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read and JFFS2 on top of UBI
+# testcases.
+#
+# Written in shell language to reduce dependencies to more sophisticated
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+# 1.1 Added fixup for delayed device node creation by udev
+# This points to a problem in the tools, mabe in the desing
+# Tue Oct 31 14:14:54 CET 2006
+#
+
+VERSION="1.1"
+
+export PATH=$PATH:/bin:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+ITERATIONS=250
+ALIGNMENT=2048
+
+UBIMKVOL="ubimkvol -a $ALIGNMENT"
+UBIRMVOL=ubirmvol
+UBIUPDATEVOL=ubiupdatevol
+
+SIZE_512K=524288
+SIZE_1M=1310720
+
+MINVOL=10
+MAXVOL=12
+
+TLOG=/dev/null
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+ echo "*** Fixing the sysfs issue with the /dev nodes ... "
+
+ minor=0
+ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+ rm -rf /dev/ubi0
+ mknod /dev/ubi0 c $major 0
+
+ for minor in `seq $MINVOL $MAXVOL`; do
+ echo " -> mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+ rm -rf /dev/ubi0_$minor
+ mknod /dev/ubi0_$minor c $major $(($minor + 1))
+ done
+ passed
+}
+
+#
+# FIXME Udev needs some time until the device nodes are created.
+# This will cause trouble if after ubimkvol an update attempt
+# is started immediately, since the device node is not yet
+# available. We should either fix the tools with inotify or
+# other ideas or figure out a different way to solve the problem
+# e.g. to use ubi0 and make the volume device nodes obsolete...
+#
+udev_wait ()
+{
+ echo -n "FIXME Waiting for udev to create/delete device node "
+ grep 2\.6\.5 /proc/version > /dev/null
+ if [ $? -eq "0" ]; then
+ for i in `seq 0 5`; do
+ sleep 1; echo -n ".";
+ done
+ echo " ok"
+ fi
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+# to delete it.
+# @id: volume id
+#
+delete_volume ()
+{
+ volume=$1
+
+ ### FIXME broken sysfs!!!!
+ if [ -e /sys/class/ubi/$volume -o \
+ -e /sys/class/ubi/ubi0/$volume -o \
+ -e /sys/class/ubi/ubi0_$volume ]; then
+
+ echo "*** Truncate volume if it exists ... "
+ echo " $UBIUPDATEVOL -d0 -n$volume -t"
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Delete volume if it exists ... "
+ $UBIRMVOL -d0 -n$volume
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ # udev_wait
+ fi
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @volume: Volume number
+# @size: Size of random data to write
+# @type: Volume type static or dynamic
+#
+writevol_test ()
+{
+ volume=$1
+ size=$2
+ type=$3
+
+ echo "*** Write volume test with size $size"
+
+### Make sure that volume exist, delete existing volume, create new
+
+ delete_volume $volume
+
+ echo "*** Try to create volume"
+ echo " $UBIMKVOL -d0 -n$volume -t$type -NNEW$volume -s $size ... "
+ $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ udev_wait
+
+### Try to create same volume again
+ echo -n "*** Try to create some volume again, this must fail ... "
+ $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+### Now create test data, write it, read it, compare it
+ echo -n "*** Create test data ... "
+ dd if=/dev/urandom of=testdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "*** Now writing data to volume ... "
+ echo " $UBIUPDATEVOL -d0 -n$volume testdata.bin"
+ ls -l testdata.bin
+ $UBIUPDATEVOL -d0 -n$volume testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "*** Download data with dd bs=1 ... "
+ dd if=/dev/ubi0_$volume of=readdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Comparing data ... "
+ cmp readdata.bin testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Now truncate volume ... "
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+}
+
+jffs2_torture ()
+{
+ cat /dev/null > TLOG
+
+ echo "*** Torture test ... "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/urandom of=test.bin bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ exit_failure
+ fi
+ #passed
+
+ dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG"
+ exit_failure
+ fi
+ #passed
+
+ #echo "Comparing files ... "
+ cmp test.bin new.bin
+ dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ #passed
+ #echo -n "."
+ done
+
+ echo -n "step0:ok "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/urandom of=foo bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ exit_failure
+ fi
+ #passed
+ done
+
+ echo -n "step1:ok "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/zero of=foo bs=1 count=$i 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/zero of=foo bs=1 count=$i) ... "
+ exit_failure
+ fi
+ #passed
+ done
+
+ echo -n "step2:ok "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/zero of=foo bs=$i count=16 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1024) ... "
+ exit_failure
+ fi
+ #passed
+ done
+
+ echo -n "step3:ok "
+
+ passed
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @volume: Volume number
+# @size: Size of random data to write
+# @type: Volume type static or dynamic
+#
+jffs2_test ()
+{
+ name=$1
+ iterations=$2
+ directory=`pwd`
+
+ ### Setup
+ ulimit -c unlimited
+
+ echo -n "*** Create directory /mnt/$name ... "
+ mkdir -p /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** mount -t jffs2 mtd:$name /mnt/$name ... "
+ mount -t jffs2 mtd:$name /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** change directory ... "
+ cd /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ls
+ echo "*** list directory ... "
+ ls -la
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ### Torture
+ echo -n "*** touch I_WAS_HERE ... "
+ touch I_WAS_HERE
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ jffs2_torture
+
+ echo "*** list directory ... "
+ ls -la
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ### Cleanup
+ echo -n "*** go back ... "
+ cd $directory
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ### Still mounted, ubiupdatevol must fail!
+
+ echo -n "*** $UBIUPDATEVOL -d0 -n$volume -t must fail! ..."
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** umount /mnt/$name ... "
+ umount /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ return
+}
+
+echo "***********************************************************************"
+echo "* UBI JFFS2 Testing starts now ... *"
+echo "* Good luck! *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+ echo "No UBI found in /proc/devices! I am broken!"
+ exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+ echo "Running on example hardware"
+ mount -o remount,rw / /
+ sleep 1
+ fix_sysfs_issue
+else
+ echo "Running on Artems hardware"
+fi
+
+for volume in `seq $MINVOL $MAXVOL`; do
+ echo -n "************ VOLUME $volume NEW$volume "
+ echo "******************************************"
+ writevol_test $volume $SIZE_1M dynamic
+ jffs2_test NEW$volume $ITERATIONS
+ delete_volume $volume
+done
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool UBI system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_test.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_test.sh
new file mode 100755
index 0000000..73e4b19
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_test.sh
@@ -0,0 +1,328 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read test script
+#
+# Written in shell language to reduce dependencies to more sophisticated
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+# 1.1 Use ubiupdatevol instead of ubiwritevol
+#
+
+VERSION="1.1"
+
+export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+UBIMKVOL=ubimkvol
+UBIRMVOL=ubirmvol
+UBIUPDATEVOL=ubiupdatevol
+
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+SIZE_512K=524288
+SIZE_1M=1310720
+
+SELF=$0
+MINVOL=10
+MAXVOL=12
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+ echo -n "*** Fixing the sysfs issue with the /dev nodes ... "
+
+ minor=0
+ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+ rm -rf /dev/ubi0
+ mknod /dev/ubi0 c $major 0
+
+ for minor in `seq 0 $MAXVOL`; do
+ ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+ rm -rf /dev/ubi0_$minor
+ mknod /dev/ubi0_$minor c $major $(($minor + 1))
+ done
+ passed
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+# to delete it.
+# @id: volume id
+#
+delete_volume ()
+{
+ volume=$1
+
+ ### FIXME broken sysfs!!!!
+ if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then
+
+ echo -n "*** Truncate volume if it exists ... "
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Delete volume if it exists ... "
+ $UBIRMVOL -d0 -n$volume
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ fi
+}
+
+mkvol_rmvol_test ()
+{
+ type=$1
+
+### Test if volume delete on non-existing volumes fails nicely
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Delete if exist or not $i ... "
+
+ delete_volume $i
+ passed
+ done
+
+### Now deleting volumes must fail
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Trying to delete non existing UBI Volume $i ... "
+
+ $UBIRMVOL -d0 -n$i
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+ done
+
+### Test if volume creation works ok
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Creating UBI Volume $i ... "
+ echo " $UBIMKVOL -d0 -n$i -t$type -NNEW$i -s $SIZE_512K"
+
+ $UBIMKVOL -d0 -n$i -t$type -N"NEW$i" -s $SIZE_512K
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ done
+
+### Now deleting volumes must be ok
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Trying to delete UBI Volume $i ... "
+
+ $UBIRMVOL -d0 -n$i
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ done
+
+### Now allocate too large volume
+
+ echo -n "*** Try to create too large volume"
+ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s 800000000
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @size: Size of random data to write
+# @type: Volume type static or dynamic
+#
+writevol_test ()
+{
+ size=$1
+ type=$2
+
+ echo "*** Write volume test with size $size"
+
+### Make sure that volume exist, delete existing volume, create new
+
+ delete_volume $MINVOL
+
+ echo -n "*** Try to create volume ... "
+ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+### Try to create same volume again
+ echo -n "*** Try to create some volume again, this must fail ... "
+ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+### Now create test data, write it, read it, compare it
+ echo -n "*** Create test data ... "
+ dd if=/dev/urandom of=testdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "*** Now writing data to volume ... "
+ # sleep 5
+ ls -l testdata.bin
+ echo " $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin"
+ $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ if [ $type = "static" ] ; then
+ echo "*** Download data with cat ... "
+ cat /dev/ubi0_$MINVOL > readdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ else
+ echo "*** Download data with dd bs=1 ... "
+ dd if=/dev/ubi0_$MINVOL of=readdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ # Size 1 does not work with this test ...
+ #
+ #echo "*** Download data with dd bs=$size ... "
+ #dd if=/dev/ubi0_$MINVOL of=readdata2.bin bs=$size count=1
+ #if [ $? -ne "0" ] ; then
+ # exit_failure
+ #fi
+ #passed
+
+ #echo -n "*** Comparing data (1) ... "
+ #cmp readdata.bin readdata2.bin
+ #if [ $? -ne "0" ] ; then
+ # exit_failure
+ #fi
+ #passed
+ fi
+
+ echo -n "*** Comparing data ... "
+ cmp readdata.bin testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+}
+
+echo "***********************************************************************"
+echo "* UBI Testing starts now ... *"
+echo "* Good luck! *"
+echo "***********************************************************************"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+ echo "No UBI found in /proc/devices! I am broken!"
+ exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+ echo "Running on example hardware"
+ mount -o remount,rw / /
+ sleep 1
+ fix_sysfs_issue
+else
+ echo "Running on Artems hardware"
+fi
+
+echo "***********************************************************************"
+echo "* mkvol/rmvol testing for static volumes ... *"
+echo "***********************************************************************"
+
+mkvol_rmvol_test static
+
+echo "***********************************************************************"
+echo "* mkvol/rmvol testing for dynamic volumes ... *"
+echo "***********************************************************************"
+
+mkvol_rmvol_test dynamic
+
+echo "***********************************************************************"
+echo "* write to static volumes ... *"
+echo "***********************************************************************"
+
+# 10 Erase blocks = (128 KiB - 64 * 2) * 10
+# = 1309440 bytes
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+for size in 262144 131073 131072 2048 1 4096 12800 31313 ; do
+ writevol_test $size static
+done
+
+echo "***********************************************************************"
+echo "* write to dynamic volumes ... *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+for size in 131073 131072 2048 1 4096 12800 31313 262144 ; do
+ writevol_test $size dynamic
+done
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool UBI system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_tools_test.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_tools_test.sh
new file mode 100755
index 0000000..7f121f1
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/ubi_tools_test.sh
@@ -0,0 +1,252 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read test script.
+# Uses our flash update tools and the associated toolchain for flash
+# image creation.
+#
+# Written in shell language to reduce dependencies to more sophisticated
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+#
+
+VERSION="1.0"
+
+export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+UBIMKVOL=ubimkvol
+UBIRMVOL=ubirmvol
+UBIWRITEVOL=ubiupdatevol
+PFIFLASH=pfiflash
+CMP=cmp
+
+MAXVOL=32
+
+test_pfi=test_complete.pfi
+real_pfi=example_complete.pfi
+
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+ echo -n "*** Fixing the sysfs issue with the /dev nodes ... "
+
+ minor=0
+ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+ rm -rf /dev/ubi0
+ mknod /dev/ubi0 c $major 0
+
+ for minor in `seq 0 $MAXVOL`; do
+ ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+ rm -rf /dev/ubi0_$minor
+ mknod /dev/ubi0_$minor c $major $(($minor + 1))
+ done
+ passed
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+# to delete it.
+# @id: volume id
+#
+delete_volume ()
+{
+ volume=$1
+
+ ### FIXME broken sysfs!!!!
+ if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then
+
+ echo -n "*** Truncate volume if it exists ... "
+ $UBIWRITEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Delete volume if it exists ... "
+ $UBIRMVOL -d0 -n$volume
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ fi
+}
+
+echo "***********************************************************************"
+echo "* UBI Tools Testing starts now ... *"
+echo "* Good luck! *"
+echo "***********************************************************************"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+ echo "No UBI found in /proc/devices! I am broken!"
+ exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+ echo "Running on example hardware"
+ mount -o remount,rw / /
+ sleep 1
+ fix_sysfs_issue
+else
+ echo "Running on other hardware"
+fi
+
+### Test basic stuff
+pfiflash_basic ()
+{
+ echo "Calling pfiflash with test-data ... "
+ echo " $PFIFLASH $test_pfi"
+ $PFIFLASH $test_pfi
+ if [ $? -ne "0" ]; then
+ echo "Uhhh something went wrong!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 10 and 11 ... "
+ $CMP /dev/ubi0_10 /dev/ubi0_11
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_10 test_u-boot.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 12 and 13 ... "
+ $CMP /dev/ubi0_12 /dev/ubi0_13
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_12 test_vmlinux.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 14 and 15 ... "
+ $CMP /dev/ubi0_14 /dev/ubi0_15
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+}
+
+### Test each and everything
+pfiflash_advanced ()
+{
+ if [ -e example_complete.pfi ]; then
+ echo "Calling pfiflash with real data ... "
+ $PFIFLASH -p overwrite --complete example_complete.pfi
+ if [ $? -ne "0" ]; then
+ echo "Uhhh something went wrong!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 2 and 3 ... "
+ $CMP /dev/ubi0_2 /dev/ubi0_3
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_2 u-boot.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 6 and 7 ... "
+ $CMP /dev/ubi0_6 /dev/ubi0_7
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_6 vmlinux.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+ fi
+}
+
+echo "***********************************************************************"
+echo "* Testing pfiflash ... *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+pfiflash_basic
+pfiflash_advanced
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool UBI system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/unubi_test.sh b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/unubi_test.sh
new file mode 100644
index 0000000..40dc2e2
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/scripts/unubi_test.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Use raw NAND data, extract UBI image and apply tool to it.
+# Test basic functionality.
+#
+# 2007 Frank Haverkamp <haver@vnet.ibm.com>
+#
+
+version=1.1
+
+image=data.mif
+oob=oob.bin
+data=data.bin
+pagesize=2048
+volmax=31
+datadir=unubi_data
+
+# general arguments e.g. debug enablement
+# unubi_args="-D"
+
+echo "------------------------------------------------------------------------"
+echo "Testcase: ${0} Version: ${version}"
+echo "------------------------------------------------------------------------"
+echo "Testing nand2bin ..."
+echo " Input: ${image}"
+echo " Data: ${data}"
+echo " OOB: ${oob}"
+echo " Pagesize: ${pagesize}"
+nand2bin --pagesize ${pagesize} -o ${data} -O ${oob} ${image}
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Testing unubi ..."
+echo "------------------------------------------------------------------------"
+unubi --version
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Trying to extract first ${volmax} volumes ..."
+echo "------------------------------------------------------------------------"
+mkdir -p ${datadir}/volumes
+for v in `seq 0 ${volmax}` ; do
+ unubi ${unubi_args} -r${v} -d${datadir}/volumes ${data}
+ echo -n "."
+done
+echo "ok"
+ls -l ${datadir}/volumes
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Extracting graphics ..."
+echo "------------------------------------------------------------------------"
+unubi -a -d${datadir} ${data}
+echo "Use gnuplot to display:"
+ls ${datadir}/*.plot
+ls ${datadir}/*.data
+echo
+
+echo "------------------------------------------------------------------------"
+echo "eb-split"
+echo "------------------------------------------------------------------------"
+unubi -e -d${datadir}/eb-split ${data}
+ls -l ${datadir}/eb-split
+echo
+
+echo "------------------------------------------------------------------------"
+echo "vol-split"
+echo "------------------------------------------------------------------------"
+unubi -v -d${datadir}/vol-split ${data}
+ls -l ${datadir}/vol-split
+echo
+echo "The generated images contain only the data (126KiB in our "
+echo "case) not including the UBI erase count and volume info "
+echo "header. For dynamic volumes the data should be the full "
+echo "126KiB. Unubi cannot know how much of the data is valid. "
+echo
+
+echo "------------------------------------------------------------------------"
+echo "!vol-split"
+echo "------------------------------------------------------------------------"
+unubi -V -d${datadir}/vol-split! ${data}
+ls -l ${datadir}/vol-split\!
+echo
+echo "The generated images contain the full block data of 128KiB "
+echo "including the UBI erase count and volume information header."
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Extracting volume info table ..."
+echo "------------------------------------------------------------------------"
+unubi -i -d${datadir} ${data}
+echo "I strongly hope that empty ubi blocks are filled with 0xff! "
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Table 0"
+echo "------------------------------------------------------------------------"
+cat ${datadir}/vol_info_table0
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Table 1"
+echo "------------------------------------------------------------------------"
+cat ${datadir}/vol_info_table1
+echo
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/bin2nand.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/bin2nand.c
new file mode 100644
index 0000000..c7c7ccc
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/bin2nand.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+/*
+ * Create a flashable NAND image from a binary image
+ *
+ * History:
+ * 1.0 Initial release (tglx)
+ * 1.1 Understands hex and dec input parameters (tglx)
+ * 1.2 Generates separated OOB data, if needed. (oloh)
+ * 1.3 Padds data/oob to a given size. (oloh)
+ * 1.4 Removed argp because we want to use uClibc.
+ * 1.5 Minor cleanup
+ * 1.6 written variable not initialized (-j did not work) (haver)
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "error.h"
+#include "config.h"
+#include "nandecc.h"
+
+#define PROGRAM_VERSION "1.6"
+
+#define CHECK_ENDP(option, endp) do { \
+ if (*endp) { \
+ fprintf(stderr, \
+ "Parse error option \'%s\'. " \
+ "No correct numeric value.\n" \
+ , option); \
+ exit(EXIT_FAILURE); \
+ } \
+} while(0)
+
+typedef enum action_t {
+ ACT_NORMAL = 0x00000001,
+} action_t;
+
+#define PAGESIZE 2048
+#define PADDING 0 /* 0 means, do not adjust anything */
+#define BUFSIZE 4096
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "bin2nand - a tool for adding OOB information to a "
+ "binary input file.\n";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright informatoin.\n"
+" -j, --padding=<num> Padding in Byte/Mi/ki. Default = no padding\n"
+" -p, --pagesize=<num> Pagesize in Byte/Mi/ki. Default = 2048\n"
+" -o, --output=<fname> Output filename. Interleaved Data/OOB if\n"
+" output-oob not specified.\n"
+" -q, --output-oob=<fname> Write OOB data in separate file.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: bin2nand [-c?V] [-j <num>] [-p <num>] [-o <fname>] [-q <fname>]\n"
+" [--copyright] [--padding=<num>] [--pagesize=<num>]\n"
+" [--output=<fname>] [--output-oob=<fname>] [--help] [--usage]\n"
+" [--version]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp. 2006";
+
+typedef struct myargs {
+ action_t action;
+
+ size_t pagesize;
+ size_t padding;
+
+ FILE* fp_in;
+ char *file_out_data; /* Either: Data and OOB interleaved
+ or plain data */
+ char *file_out_oob; /* OOB Data only. */
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+
+static int ustrtoull(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long long res = strtoull(cp, endp, base);
+
+ switch (**endp) {
+ case 'G':
+ res *= 1024;
+ case 'M':
+ res *= 1024;
+ case 'k':
+ case 'K':
+ res *= 1024;
+ /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+ if ((*endp)[1] == 'i')
+ (*endp) += 2;
+ }
+ return res;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ char* endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'p': /* pagesize */
+ args->pagesize = (size_t)
+ ustrtoull(optarg, &endp, 0);
+ CHECK_ENDP("p", endp);
+ break;
+ case 'j': /* padding */
+ args->padding = (size_t)
+ ustrtoull(optarg, &endp, 0);
+ CHECK_ENDP("j", endp);
+ break;
+ case 'o': /* output */
+ args->file_out_data = optarg;
+ break;
+ case 'q': /* output oob */
+ args->file_out_oob = optarg;
+ break;
+ case '?': /* help */
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ exit(0);
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ case 'c':
+ printf("%s\n", copyright);
+ exit(0);
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ err_quit("Cannot open file %s for input\n",
+ argv[optind++]);
+ }
+ }
+
+ return 0;
+}
+
+static int
+process_page(uint8_t* buf, size_t pagesize,
+ FILE *fp_data, FILE* fp_oob, size_t* written)
+{
+ int eccpoi, oobsize;
+ size_t i;
+ uint8_t oobbuf[64];
+
+ memset(oobbuf, 0xff, sizeof(oobbuf));
+
+ switch(pagesize) {
+ case 2048: oobsize = 64; eccpoi = 64 / 2; break;
+ case 512: oobsize = 16; eccpoi = 16 / 2; break;
+ default:
+ err_msg("Unsupported page size: %d\n", pagesize);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < pagesize; i += 256, eccpoi += 3) {
+ oobbuf[eccpoi++] = 0x0;
+ /* Calculate ECC */
+ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]);
+ }
+
+ /* write data */
+ *written += fwrite(buf, 1, pagesize, fp_data);
+
+ /* either separate oob or interleave with data */
+ if (fp_oob) {
+ i = fwrite(oobbuf, 1, oobsize, fp_oob);
+ if (ferror(fp_oob)) {
+ err_msg("IO error\n");
+ return -EIO;
+ }
+ }
+ else {
+ i = fwrite(oobbuf, 1, oobsize, fp_data);
+ if (ferror(fp_data)) {
+ err_msg("IO error\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int main (int argc, char** argv)
+{
+ int rc = -1;
+ int res = 0;
+ size_t written = 0, read;
+ myargs args = {
+ .action = ACT_NORMAL,
+ .pagesize = PAGESIZE,
+ .padding = PADDING,
+ .fp_in = NULL,
+ .file_out_data = NULL,
+ .file_out_oob = NULL,
+ };
+
+ FILE* fp_out_data = stdout;
+ FILE* fp_out_oob = NULL;
+
+ parse_opt(argc, argv, &args);
+
+ uint8_t* buf = calloc(1, BUFSIZE);
+ if (!buf) {
+ err_quit("Cannot allocate page buffer.\n");
+ }
+
+ if (!args.fp_in) {
+ err_msg("No input image specified!\n");
+ goto err;
+ }
+
+ if (args.file_out_data) {
+ fp_out_data = fopen(args.file_out_data, "wb");
+ if (fp_out_data == NULL) {
+ err_sys("Cannot open file %s for output\n",
+ args.file_out_data);
+ goto err;
+ }
+ }
+
+ if (args.file_out_oob) {
+ fp_out_oob = fopen(args.file_out_oob, "wb");
+ if (fp_out_oob == NULL) {
+ err_sys("Cannot open file %s for output\n",
+ args.file_out_oob);
+ goto err;
+ }
+ }
+
+
+ while(1) {
+ read = fread(buf, 1, args.pagesize, args.fp_in);
+ if (feof(args.fp_in) && read == 0)
+ break;
+
+ if (read < args.pagesize) {
+ err_msg("Image not page aligned\n");
+ goto err;
+ }
+
+ if (ferror(args.fp_in)) {
+ err_msg("Read error\n");
+ goto err;
+ }
+
+ res = process_page(buf, args.pagesize, fp_out_data,
+ fp_out_oob, &written);
+ if (res != 0)
+ goto err;
+ }
+
+ while (written < args.padding) {
+ memset(buf, 0xff, args.pagesize);
+ res = process_page(buf, args.pagesize, fp_out_data,
+ fp_out_oob, &written);
+ if (res != 0)
+ goto err;
+ }
+
+ rc = 0;
+err:
+ free(buf);
+
+ if (args.fp_in)
+ fclose(args.fp_in);
+
+ if (fp_out_oob)
+ fclose(fp_out_oob);
+
+ if (fp_out_data && fp_out_data != stdout)
+ fclose(fp_out_data);
+
+ if (rc != 0) {
+ err_msg("Error during conversion. rc: %d\n", rc);
+ if (args.file_out_data)
+ remove(args.file_out_data);
+ if (args.file_out_oob)
+ remove(args.file_out_oob);
+ }
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/bootenv.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/bootenv.c
new file mode 100644
index 0000000..78198fe
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/bootenv.c
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2008
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <bootenv.h>
+
+#include "hashmap.h"
+#include "error.h"
+
+#include <mtd/ubi-media.h>
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */
+
+/* Structures */
+struct bootenv {
+ hashmap_t map; ///< Pointer to hashmap which holds data structure.
+};
+
+struct bootenv_list {
+ hashmap_t head; ///< Pointer to list which holds the data structure.
+};
+
+/**
+ * @brief Remove the '\n' from a given line.
+ * @param line Input/Output line.
+ * @param size Size of the line.
+ * @param fp File Pointer.
+ * @return 0
+ * @return or error
+ */
+static int
+remove_lf(char *line, size_t size, FILE* fp)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (line[i] == '\n') {
+ line[i] = '\0';
+ return 0;
+ }
+ }
+
+ if (!feof(fp)) {
+ return BOOTENV_EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Determine if a line contains only WS.
+ * @param line The line to process.
+ * @param size Size of input line.
+ * @return 1 Yes, only WS.
+ * @return 0 No, contains data.
+ */
+static int
+is_ws(const char *line, size_t size)
+{
+ size_t i = 0;
+
+ while (i < size) {
+ switch (line[i]) {
+ case '\n':
+ return 1;
+ case '#':
+ return 1;
+ case ' ':
+ i++;
+ continue;
+ case '\t':
+ i++;
+ continue;
+ default: /* any other char -> no cmnt */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief Build a list from a comma seperated value string.
+ * @param list Pointer to hashmap structure which shall store
+ * the list.
+ * @param value Comma seperated value string.
+ * @return 0
+ * @return or error.
+ */
+static int
+build_list_definition(hashmap_t list, const char *value)
+{
+ int rc = 0;
+ char *str = NULL;
+ char *ptr = NULL;
+ size_t len, i, j;
+
+ /* str: val1,val2 , val4,...,valN */
+ len = strlen(value);
+ str = (char*) malloc((len+1) * sizeof(char));
+
+ /* 1. reformat string: remove spaces */
+ for (i = 0, j = 0; i < len; i++) {
+ if (value[i] == ' ')
+ continue;
+
+ str[j] = value[i];
+ j++;
+ }
+ str[j] = '\0';
+
+ /* str: val1,val2,val4,...,valN\0*/
+ /* 2. replace ',' seperator with '\0' */
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (str[i] == ',') {
+ str[i] = '\0';
+ }
+ }
+
+ /* str: val1\0val2\0val4\0...\0valN\0*/
+ /* 3. insert definitions into a hash map, using it like a list */
+ i = j = 0;
+ ptr = str;
+ while (((i = strlen(ptr)) > 0) && (j < len)) {
+ rc = hashmap_add(list, ptr, "");
+ if (rc != 0) {
+ free(str);
+ return rc;
+ }
+ j += i+1;
+ if (j < len)
+ ptr += i+1;
+ }
+
+ free(str);
+ return rc;
+}
+
+/**
+ * @brief Extract a key value pair and add it to a hashmap
+ * @param str Input string which contains a key value pair.
+ * @param env The updated handle which contains the new pair.
+ * @return 0
+ * @return or error
+ * @note The input string format is: "key=value"
+ */
+static int
+extract_pair(const char *str, bootenv_t env)
+{
+ int rc = 0;
+ char *key = NULL;
+ char *val = NULL;
+
+ key = strdup(str);
+ if (key == NULL)
+ return -ENOMEM;
+
+ val = strstr(key, "=");
+ if (val == NULL) {
+ rc = BOOTENV_EBADENTRY;
+ goto err;
+ }
+
+ *val = '\0'; /* split strings */
+ val++;
+
+ rc = bootenv_set(env, key, val);
+
+ err:
+ free(key);
+ return rc;
+}
+
+int
+bootenv_destroy(bootenv_t* env)
+{
+ int rc = 0;
+
+ if (env == NULL || *env == NULL)
+ return -EINVAL;
+
+ bootenv_t tmp = *env;
+
+ rc = hashmap_free(tmp->map);
+ if (rc != 0)
+ return rc;
+
+ free(tmp);
+ return rc;
+}
+
+int
+bootenv_create(bootenv_t* env)
+{
+ bootenv_t res;
+ res = (bootenv_t) calloc(1, sizeof(struct bootenv));
+
+ if (res == NULL)
+ return -ENOMEM;
+
+ res->map = hashmap_new();
+
+ if (res->map == NULL) {
+ free(res);
+ return -ENOMEM;
+ }
+
+ *env = res;
+
+ return 0;
+}
+
+
+/**
+ * @brief Read a formatted buffer and scan it for valid bootenv
+ * key/value pairs. Add those pairs into a hashmap.
+ * @param env Hashmap which shall be used to hold the data.
+ * @param buf Formatted buffer.
+ * @param size Size of the buffer.
+ * @return 0
+ * @return or error
+ */
+static int
+rd_buffer(bootenv_t env, const char *buf, size_t size)
+{
+ const char *curr = buf; /* ptr to current key/value pair */
+ uint32_t i, j; /* current length, chars processed */
+
+ if (buf[size - 1] != '\0') /* must end in '\0' */
+ return BOOTENV_EFMT;
+
+ for (j = 0; j < size; j += i, curr += i) {
+ /* strlen returns the size of the string upto
+ but not including the null terminator;
+ adding 1 to account for '\0' */
+ i = strlen(curr) + 1;
+
+ if (i == 1)
+ return 0; /* no string found */
+
+ if (extract_pair(curr, env) != 0)
+ return BOOTENV_EINVAL;
+ }
+
+ return 0;
+}
+
+
+int
+bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc)
+{
+ int rc;
+ char *buf = NULL;
+ size_t i = 0;
+ uint32_t crc32_table[256];
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ /* allocate temp buffer */
+ buf = (char*) calloc(1, size * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* FIXME Andreas, please review this I removed size-1 and
+ * replaced it by just size, I saw the kernel image starting
+ * with a 0x0060.... and not with the 0x60.... what it should
+ * be. Is this a tools problem or is it a problem here where
+ * fp is moved not to the right place due to the former size-1
+ * here.
+ */
+ while((i < size) && (!feof(fp))) {
+ int c = fgetc(fp);
+ if (c == EOF) {
+ /* FIXME isn't this dangerous, to update
+ the boot envs with incomplete data? */
+ buf[i++] = '\0';
+ break; /* we have enough */
+ }
+ if (ferror(fp)) {
+ rc = -EIO;
+ goto err;
+ }
+
+ buf[i++] = (char)c;
+ }
+
+ /* calculate crc to return */
+ if (ret_crc != NULL) {
+ init_crc32_table(crc32_table);
+ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+ }
+
+ /* transfer to hashmap */
+ rc = rd_buffer(env, buf, size);
+
+err:
+ free(buf);
+ return rc;
+}
+
+
+/**
+ * If we have a single file containing the boot-parameter size should
+ * be specified either as the size of the file or as BOOTENV_MAXSIZE.
+ * If the bootparameter are in the middle of a file we need the exact
+ * length of the data.
+ */
+int
+bootenv_read(FILE* fp, bootenv_t env, size_t size)
+{
+ return bootenv_read_crc(fp, env, size, NULL);
+}
+
+
+int
+bootenv_read_txt(FILE* fp, bootenv_t env)
+{
+ int rc = 0;
+ char *buf = NULL;
+ char *line = NULL;
+ char *lstart = NULL;
+ char *curr = NULL;
+ size_t len;
+ size_t size;
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ size = BOOTENV_MAXSIZE;
+
+ /* allocate temp buffers */
+ buf = (char*) calloc(1, size * sizeof(char));
+ lstart = line = (char*) calloc(1, size * sizeof(char));
+ if ((buf == NULL) || (line == NULL)) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ curr = buf;
+ while ((line = fgets(line, size, fp)) != NULL) {
+ if (is_ws(line, size)) {
+ continue;
+ }
+ rc = remove_lf(line, BOOTENV_MAXSIZE, fp);
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* copy new line to binary buffer */
+ len = strlen(line);
+ if (len > size) {
+ rc = -EFBIG;
+ goto err;
+ }
+ size -= len; /* track remaining space */
+
+ memcpy(curr, line, len);
+ curr += len + 1; /* for \0 seperator */
+ }
+
+ rc = rd_buffer(env, buf, BOOTENV_MAXSIZE);
+err:
+ if (buf != NULL)
+ free(buf);
+ if (lstart != NULL)
+ free(lstart);
+ return rc;
+}
+
+static int
+fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused,
+ size_t *written)
+{
+ int rc = 0;
+ size_t keys_size, i;
+ size_t wr = 0;
+ const char **keys = NULL;
+ const char *val = NULL;
+
+ rc = bootenv_get_key_vector(env, &keys_size, 1, &keys);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < keys_size; i++) {
+ if (wr > BOOTENV_MAXSIZE) {
+ rc = -ENOSPC;
+ goto err;
+ }
+
+ rc = bootenv_get(env, keys[i], &val);
+ if (rc != 0)
+ goto err;
+
+ wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr,
+ "%s=%s", keys[i], val);
+ wr++; /* for \0 */
+ }
+
+ *written = wr;
+
+err:
+ if (keys != NULL)
+ free(keys);
+
+ return rc;
+}
+
+int
+bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc)
+{
+ int rc = 0;
+ size_t size = 0;
+ char *buf = NULL;
+ uint32_t crc32_table[256];
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+
+ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size);
+ if (rc != 0)
+ goto err;
+
+ /* calculate crc to return */
+ if (ret_crc != NULL) {
+ init_crc32_table(crc32_table);
+ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+ }
+
+ if (fwrite(buf, size, 1, fp) != 1) {
+ rc = -EIO;
+ goto err;
+ }
+
+err:
+ if (buf != NULL)
+ free(buf);
+ return rc;
+}
+
+int
+bootenv_write(FILE* fp, bootenv_t env)
+{
+ return bootenv_write_crc(fp, env, NULL);
+}
+
+int
+bootenv_compare(bootenv_t first, bootenv_t second)
+{
+ int rc;
+ size_t written_first, written_second;
+ char *buf_first, *buf_second;
+
+ if (first == NULL || second == NULL)
+ return -EINVAL;
+
+ buf_first = malloc(BOOTENV_MAXSIZE);
+ if (!buf_first)
+ return -ENOMEM;
+ buf_second = malloc(BOOTENV_MAXSIZE);
+ if (!buf_second) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE,
+ &written_first);
+ if (rc < 0)
+ goto err;
+ rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE,
+ &written_second);
+ if (rc < 0)
+ goto err;
+
+ if (written_first != written_second) {
+ rc = 1;
+ goto err;
+ }
+
+ rc = memcmp(buf_first, buf_second, written_first);
+ if (rc != 0) {
+ rc = 2;
+ goto err;
+ }
+
+err:
+ if (buf_first)
+ free(buf_first);
+ if (buf_second)
+ free(buf_second);
+
+ return rc;
+}
+
+int
+bootenv_size(bootenv_t env, size_t *size)
+{
+ int rc = 0;
+ char *buf = NULL;
+
+ if (env == NULL)
+ return -EINVAL;
+
+ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size);
+ if (rc != 0)
+ goto err;
+
+err:
+ if (buf != NULL)
+ free(buf);
+ return rc;
+}
+
+int
+bootenv_write_txt(FILE* fp, bootenv_t env)
+{
+ int rc = 0;
+ size_t size, wr, i;
+ const char **keys = NULL;
+ const char *key = NULL;
+ const char *val = NULL;
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ rc = bootenv_get_key_vector(env, &size, 1, &keys);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < size; i++) {
+ key = keys[i];
+ rc = bootenv_get(env, key, &val);
+ if (rc != 0)
+ goto err;
+
+ wr = fprintf(fp, "%s=%s\n", key, val);
+ if (wr != strlen(key) + strlen(val) + 2) {
+ rc = -EIO;
+ goto err;
+ }
+ }
+
+err:
+ if (keys != NULL)
+ free(keys);
+ return rc;
+}
+
+int
+bootenv_valid(bootenv_t env ubi_unused)
+{
+ /* @FIXME No sanity check implemented. */
+ return 0;
+}
+
+int
+bootenv_copy_bootenv(bootenv_t in, bootenv_t *out)
+{
+ int rc = 0;
+ const char *tmp = NULL;
+ const char **keys = NULL;
+ size_t vec_size, i;
+
+ if ((in == NULL) || (out == NULL))
+ return -EINVAL;
+
+ /* purge output var for sure... */
+ rc = bootenv_destroy(out);
+ if (rc != 0)
+ return rc;
+
+ /* create the new map */
+ rc = bootenv_create(out);
+ if (rc != 0)
+ goto err;
+
+ /* get the key list from the input map */
+ rc = bootenv_get_key_vector(in, &vec_size, 0, &keys);
+ if (rc != 0)
+ goto err;
+
+ if (vec_size != hashmap_size(in->map)) {
+ rc = BOOTENV_ECOPY;
+ goto err;
+ }
+
+ /* make a deep copy of the hashmap */
+ for (i = 0; i < vec_size; i++) {
+ rc = bootenv_get(in, keys[i], &tmp);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_set(*out, keys[i], tmp);
+ if (rc != 0)
+ goto err;
+ }
+
+err:
+ if (keys != NULL)
+ free(keys);
+
+ return rc;
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+int
+bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+ int *warnings, char *err_buf ubi_unused,
+ size_t err_buf_size ubi_unused)
+{
+ bootenv_list_t l_old = NULL;
+ bootenv_list_t l_new = NULL;
+ const char *pdd_old = NULL;
+ const char *pdd_new = NULL;
+ const char *tmp = NULL;
+ const char **vec_old = NULL;
+ const char **vec_new = NULL;
+ const char **pdd_up_vec = NULL;
+ size_t vec_old_size, vec_new_size, pdd_up_vec_size, i;
+ int rc = 0;
+
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ /* get the pdd strings, e.g.:
+ * pdd_old=a,b,c
+ * pdd_new=a,c,d,e */
+ rc = bootenv_get(env_old, "pdd", &pdd_old);
+ if (rc != 0)
+ goto err;
+ rc = bootenv_get(env_new, "pdd", &pdd_new);
+ if (rc != 0)
+ goto err;
+
+ /* put it into a list and then convert it to an vector */
+ rc = bootenv_list_create(&l_old);
+ if (rc != 0)
+ goto err;
+ rc = bootenv_list_create(&l_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_import(l_old, pdd_old);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_import(l_new, pdd_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_copy_bootenv(env_new, env_res);
+ if (rc != 0)
+ goto err;
+
+ /* calculate the update vector between the old and new pdd */
+ pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size,
+ vec_new, vec_new_size, &pdd_up_vec_size);
+
+ if (pdd_up_vec == NULL) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ if (pdd_up_vec_size != 0) {
+ /* need to warn the user about the unset of
+ * some pdd/bootenv values */
+ *warnings = BOOTENV_WPDD_STRING_DIFFERS;
+
+ /* remove all entries in the new bootenv load */
+ for (i = 0; i < pdd_up_vec_size; i++) {
+ bootenv_unset(*env_res, pdd_up_vec[i]);
+ }
+ }
+
+ /* generate the keep array and copy old pdd values to new bootenv */
+ for (i = 0; i < vec_old_size; i++) {
+ rc = bootenv_get(env_old, vec_old[i], &tmp);
+ if (rc != 0) {
+ rc = BOOTENV_EPDDINVAL;
+ goto err;
+ }
+ rc = bootenv_set(*env_res, vec_old[i], tmp);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+ /* put the old pdd string into the result map */
+ rc = bootenv_set(*env_res, "pdd", pdd_old);
+ if (rc != 0) {
+ goto err;
+ }
+
+
+err:
+ if (vec_old != NULL)
+ free(vec_old);
+ if (vec_new != NULL)
+ free(vec_new);
+ if (pdd_up_vec != NULL)
+ free(pdd_up_vec);
+
+ bootenv_list_destroy(&l_old);
+ bootenv_list_destroy(&l_new);
+ return rc;
+}
+
+
+int
+bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings ubi_unused,
+ char *err_buf ubi_unused, size_t err_buf_size ubi_unused)
+{
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ return bootenv_copy_bootenv(env_new, env_res);
+}
+
+int
+bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+ int *warnings ubi_unused, char *err_buf, size_t err_buf_size)
+{
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ snprintf(err_buf, err_buf_size, "The PDD merge operation is not "
+ "implemented. Contact: <oliloh@de.ibm.com>");
+
+ return BOOTENV_ENOTIMPL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+bootenv_get(bootenv_t env, const char *key, const char **value)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ *value = hashmap_lookup(env->map, key);
+ if (*value == NULL)
+ return BOOTENV_ENOTFOUND;
+
+ return 0;
+}
+
+int
+bootenv_get_num(bootenv_t env, const char *key, uint32_t *value)
+{
+ char *endptr = NULL;
+ const char *str;
+
+ if (env == NULL)
+ return 0;
+
+ str = hashmap_lookup(env->map, key);
+ if (!str)
+ return -EINVAL;
+
+ *value = strtoul(str, &endptr, 0);
+
+ if (*endptr == '\0') {
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int
+bootenv_set(bootenv_t env, const char *key, const char *value)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_add(env->map, key, value);
+}
+
+int
+bootenv_unset(bootenv_t env, const char *key)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_remove(env->map, key);
+}
+
+int
+bootenv_get_key_vector(bootenv_t env, size_t* size, int sort,
+ const char ***vector)
+{
+ if ((env == NULL) || (size == NULL))
+ return -EINVAL;
+
+ *vector = hashmap_get_key_vector(env->map, size, sort);
+
+ if (*vector == NULL)
+ return -EINVAL;
+
+ return 0;
+}
+
+int
+bootenv_dump(bootenv_t env)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_dump(env->map);
+}
+
+int
+bootenv_list_create(bootenv_list_t *list)
+{
+ bootenv_list_t res;
+ res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list));
+
+ if (res == NULL)
+ return -ENOMEM;
+
+ res->head = hashmap_new();
+
+ if (res->head == NULL) {
+ free(res);
+ return -ENOMEM;
+ }
+
+ *list = res;
+ return 0;
+}
+
+int
+bootenv_list_destroy(bootenv_list_t *list)
+{
+ int rc = 0;
+
+ if (list == NULL)
+ return -EINVAL;
+
+ bootenv_list_t tmp = *list;
+ if (tmp == 0)
+ return 0;
+
+ rc = hashmap_free(tmp->head);
+ if (rc != 0)
+ return rc;
+
+ free(tmp);
+ *list = NULL;
+ return 0;
+}
+
+int
+bootenv_list_import(bootenv_list_t list, const char *str)
+{
+ if (list == NULL)
+ return -EINVAL;
+
+ return build_list_definition(list->head, str);
+}
+
+int
+bootenv_list_export(bootenv_list_t list, char **string)
+{
+ size_t size, i, j, bufsize, tmp, rc = 0;
+ const char **items;
+
+ if (list == NULL)
+ return -EINVAL;
+
+ bufsize = BOOTENV_MAXLINE;
+ char *res = (char*) malloc(bufsize * sizeof(char));
+ if (res == NULL)
+ return -ENOMEM;
+
+ rc = bootenv_list_to_vector(list, &size, &items);
+ if (rc != 0) {
+ goto err;
+ }
+
+ j = 0;
+ for (i = 0; i < size; i++) {
+ tmp = strlen(items[i]);
+ if (j >= bufsize) {
+ bufsize += BOOTENV_MAXLINE;
+ res = (char*) realloc(res, bufsize * sizeof(char));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
+ memcpy(res + j, items[i], tmp);
+ j += tmp;
+ if (i < (size - 1)) {
+ res[j] = ',';
+ j++;
+ }
+ }
+ j++;
+ res[j] = '\0';
+ free(items);
+ *string = res;
+ return 0;
+err:
+ free(items);
+ return rc;
+}
+
+int
+bootenv_list_add(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_add(list->head, item, "");
+}
+
+int
+bootenv_list_remove(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_remove(list->head, item);
+}
+
+int
+bootenv_list_is_in(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_lookup(list->head, item) != NULL ? 1 : 0;
+}
+
+int
+bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector)
+{
+ if ((list == NULL) || (size == NULL))
+ return -EINVAL;
+
+ *vector = hashmap_get_key_vector(list->head, size, 1);
+ if (*vector == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int
+bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+ uint32_t **vector)
+{
+ int rc = 0;
+ size_t i;
+ uint32_t* res = NULL;
+ char *endptr = NULL;
+ const char **a = NULL;
+
+ rc = bootenv_list_to_vector(list, size, &a);
+ if (rc != 0)
+ goto err;
+
+ res = (uint32_t*) malloc (*size * sizeof(uint32_t));
+ if (!res)
+ goto err;
+
+ for (i = 0; i < *size; i++) {
+ res[i] = strtoul(a[i], &endptr, 0);
+ if (*endptr != '\0')
+ goto err;
+ }
+
+ if (a)
+ free(a);
+ *vector = res;
+ return 0;
+
+err:
+ if (a)
+ free(a);
+ if (res)
+ free(res);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/bootenv.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/bootenv.h
new file mode 100644
index 0000000..8fecdbf
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/bootenv.h
@@ -0,0 +1,434 @@
+#ifndef __BOOTENV_H__
+#define __BOOTENV_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <pfiflash.h>
+
+/* DOXYGEN DOCUMENTATION */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file bootenv.h
+ * @author oliloh@de.ibm.com
+ * @version 1.3
+ *
+ * 1.3 Some renaming
+ */
+
+/**
+ * @mainpage Usage
+ *
+ * @section intro Introduction
+ * This library provides all functionality to handle with the so-called
+ * platform description data (PDD) and the bootparameters defined in
+ * U-Boot. It is able to apply the defined PDD operations in PDD update
+ * scenarios. For more information about the PDD and bootparameter
+ * environment "bootenv" confer the PDD documentation.
+ *
+ * @section ret Return codes
+ * This library defines some return codes which will be delivered classified
+ * as warnings or errors. See the "Defines" section for details and numeric
+ * values.
+ *
+ * @section benv Bootenv format description
+ * There are two different input formats:
+ * - text files
+ * - binary files
+ *
+ * @subsection txt Text Files
+ * Text files have to be specified like:
+ * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim
+ *
+ * @subsection bin Binary files
+ * Binary files have to be specified like:
+ * @verbatim<CRC32-bit>key1=value1,value2,value7\0key2=value55,value1\0... @endverbatim
+ * You can confer the U-Boot documentation for more details.
+ *
+ * @section benvlists Bootenv lists format description.
+ * Values referenced in the preceeding subsection can be
+ * defined like lists:
+ * @verbatim value1,value2,value3 @endverbatim
+ * There are some situation where a conversion of a comma
+ * seperated list can be useful, e.g. to get a list
+ * of defined PDD entries.
+ */
+
+#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */
+
+/**
+ * @def BOOTENV_ECRC
+ * @brief Given binary file is to large.
+ * @def BOOTENV_EFMT
+ * @brief Given bootenv section has an invalid format
+ * @def BOOTENV_EBADENTRY
+ * @brief Bad entry in the bootenv section.
+ * @def BOOTENV_EINVAL
+ * @brief Invalid bootenv defintion.
+ * @def BOOTENV_ENOPDD
+ * @brief Given bootenv sectoin has no PDD defintion string (pdd=...).
+ * @def BOOTENV_EPDDINVAL
+ * @brief Given bootenv section has an invalid PDD defintion.
+ * @def BOOTENV_ENOTIMPL
+ * @brief Functionality not implemented.
+ * @def BOOTENV_ECOPY
+ * @brief Bootenv memory copy error
+ * @def BOOTENV_ENOTFOUND
+ * @brief Given key has has no value.
+ * @def BOOTENV_EMAX
+ * @brief Highest error value.
+ */
+#define BOOTENV_ETOOBIG 1
+#define BOOTENV_EFMT 2
+#define BOOTENV_EBADENTRY 3
+#define BOOTENV_EINVAL 4
+#define BOOTENV_ENOPDD 5
+#define BOOTENV_EPDDINVAL 6
+#define BOOTENV_ENOTIMPL 7
+#define BOOTENV_ECOPY 8
+#define BOOTENV_ENOTFOUND 9
+#define BOOTENV_EMAX 10
+
+/**
+ * @def BOOTENV_W
+ * @brief A warning which is handled internally as an error
+ * but can be recovered by manual effort.
+ * @def BOOTENV_WPDD_STRING_DIFFERS
+ * @brief The PDD strings of old and new PDD differ and
+ * can cause update problems, because new PDD values
+ * are removed from the bootenv section completely.
+ */
+#define BOOTENV_W 20
+#define BOOTENV_WPDD_STRING_DIFFERS 21
+#define BOOTENV_WMAX 22 /* highest warning value */
+
+
+typedef struct bootenv *bootenv_t;
+ /**< A bootenv library handle. */
+
+typedef struct bootenv_list *bootenv_list_t;
+ /**< A handle for a value list. */
+
+typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*,
+ int*, char*, size_t);
+
+
+/**
+ * @brief Get a new handle.
+ * @return 0
+ * @return or error
+ * */
+int bootenv_create(bootenv_t *env);
+
+/**
+ * @brief Cleanup structure.
+ * @param env Bootenv structure which shall be destroyed.
+ * @return 0
+ * @return or error
+ */
+int bootenv_destroy(bootenv_t *env);
+
+/**
+ * @brief Copy a bootenv handle.
+ * @param in The input bootenv.
+ * @param out The copied output bootenv. Discards old data.
+ * @return 0
+ * @return or error
+ */
+int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out);
+
+/**
+ * @brief Looks for a value inside the bootenv data.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @return NULL key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get(bootenv_t env, const char *key, const char **value);
+
+
+/**
+ * @brief Looks for a value inside the bootenv data and converts it to num.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @param value A pointer to the resulting numerical value
+ * @return NULL key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value);
+
+/**
+ * @brief Set a bootenv value by key.
+ * @param env Handle to a bootenv structure.
+ * @param key Key.
+ * @param value Value to set.
+ * @return 0
+ * @return or error
+ */
+int bootenv_set(bootenv_t env, const char *key, const char *value);
+
+/**
+ * @brief Remove the given key (and its value) from a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @param key Key.
+ * @return 0
+ * @return or error
+ */
+int bootenv_unset(bootenv_t env, const char *key);
+
+
+/**
+ * @brief Get a vector of all keys which are currently set
+ * within a bootenv handle.
+ * @param env Handle to a bootenv structure.
+ * @param size The size of the allocated array structure.
+ * @param sort Flag, if set the vector is sorted ascending.
+ * @return NULL on error.
+ * @return !NULL a pointer to the first element the allocated vector.
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort,
+ const char ***vector);
+
+/**
+ * @brief Calculate the size in bytes which are necessary to write the
+ * current bootenv section in a *binary file.
+ * @param env bootenv handle.
+ * @param size The size in bytes of the bootenv handle.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_size(bootenv_t env, size_t *size);
+
+/**
+ * @brief Read a binary bootenv file.
+ * @param fp File pointer to input stream.
+ * @param env bootenv handle.
+ * @param size maximum data size.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read(FILE* fp, bootenv_t env, size_t size);
+
+/**
+ * @param ret_crc return value of crc of read data
+ */
+int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc);
+
+/**
+ * @brief Read bootenv data from an text/ascii file.
+ * @param fp File pointer to ascii PDD file.
+ * @param env bootenv handle
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Write a bootenv structure to the given location (binary).
+ * @param fp Filepointer to binary file.
+ * @param env Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write(FILE* fp, bootenv_t env);
+
+/**
+ * @param ret_crc return value of crc of read data
+ */
+int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc);
+
+/**
+ * @brief Write a bootenv structure to the given location (text).
+ * @param fp Filepointer to text file.
+ * @param env Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Compare bootenvs using memcmp().
+ * @param first First bootenv.
+ * @param second Second bootenv.
+ * @return 0 if bootenvs are equal
+ * @return < 0 if error
+ * @return > 0 if unequal
+ */
+int bootenv_compare(bootenv_t first, bootenv_t second);
+
+/**
+ * @brief Prototype for a PDD handling funtion
+ */
+
+/**
+ * @brief The PDD keep operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of PDD keep.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+
+/**
+ * @brief The PDD merge operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of merge-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief The PDD overwrite operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of overwrite-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_overwrite(bootenv_t env_new,
+ bootenv_t env_old, bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Dump a bootenv structure to stdout. (Debug)
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_dump(bootenv_t env);
+
+/**
+ * @brief Validate a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_valid(bootenv_t env);
+
+/**
+ * @brief Create a new bootenv list structure.
+ * @return NULL on error
+ * @return or a new list handle.
+ * @note This structure is used to store values in a list.
+ * A useful addition when handling PDD strings.
+ */
+int bootenv_list_create(bootenv_list_t *list);
+
+/**
+ * @brief Destroy a bootenv list structure
+ * @param list Handle to a bootenv list structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_destroy(bootenv_list_t *list);
+
+/**
+ * @brief Import a list from a comma seperated string
+ * @param list Handle to a bootenv list structure.
+ * @param str Comma seperated string list.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_import(bootenv_list_t list, const char *str);
+
+/**
+ * @brief Export a list to a string of comma seperated values.
+ * @param list Handle to a bootenv list structure.
+ * @return NULL one error
+ * @return or pointer to a newly allocated string.
+ * @warning Free the allocated memory by yourself!
+ */
+int bootenv_list_export(bootenv_list_t list, char **string);
+
+/**
+ * @brief Add an item to the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_add(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Remove an item from the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_remove(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Check if a given item is in a given list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 1 Item is in list.
+ * @return 0 Item is not in list.
+ */
+int bootenv_list_is_in(bootenv_list_t list, const char *item);
+
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_vector(bootenv_list_t list, size_t *size,
+ const char ***vector);
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+ uint32_t **vector);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*__BOOTENV_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/config.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/config.h
new file mode 100644
index 0000000..55e60f3
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/config.h
@@ -0,0 +1,28 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Frank Haverkamp
+ */
+
+#define PACKAGE_BUGREPORT \
+ "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de"
+
+#define ubi_unused __attribute__((unused))
+
+#endif
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/crc32.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/crc32.c
new file mode 100644
index 0000000..666e217
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/crc32.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Thomas Gleixner
+ */
+
+/*
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+
+#include <stdint.h>
+#include <crc32.h>
+
+/* CRC polynomial */
+#define CRC_POLY 0xEDB88320
+
+/**
+ * init_crc32_table - Initialize crc table
+ *
+ * @table: pointer to the CRC table which must be initialized
+ *
+ * Create CRC32 table for given polynomial. The table is created with
+ * the lowest order term in the highest order bit. So the x^32 term
+ * has to implied in the crc calculation function.
+ */
+void init_crc32_table(uint32_t *table)
+{
+ uint32_t crc;
+ int i, j;
+
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1)
+ crc = (crc >> 1) ^ CRC_POLY;
+ else
+ crc >>= 1;
+ }
+ table[i] = crc;
+ }
+}
+
+/**
+ * clc_crc32 - Calculate CRC32 over a buffer
+ *
+ * @table: pointer to the CRC table
+ * @crc: initial crc value
+ * @buf: pointer to the buffer
+ * @len: number of bytes to calc
+ *
+ * Returns the updated crc value.
+ *
+ * The algorithm resembles a hardware shift register, but calculates 8
+ * bit at once.
+ */
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf,
+ int len)
+{
+ const unsigned char *p = buf;
+
+ while(--len >= 0)
+ crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ return crc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/crc32.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/crc32.h
new file mode 100644
index 0000000..31362b0
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/crc32.h
@@ -0,0 +1,36 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Author: Thomas Gleixner
+ *
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+#include <stdint.h>
+
+void init_crc32_table(uint32_t *table);
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len);
+
+#endif /* __CRC32_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/eb_chain.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/eb_chain.c
new file mode 100644
index 0000000..da5c2e3
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/eb_chain.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Author: Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ */
+
+/* see eb_chain.h */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mtd_swab.h>
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define COPY(dst, src) \
+ do { \
+ dst = malloc(sizeof(*dst)); \
+ if (dst == NULL) \
+ return -ENOMEM; \
+ memcpy(dst, src, sizeof(*dst)); \
+ } while (0)
+
+
+/**
+ * inserts an eb_info into the chain starting at head, then searching
+ * linearly for the correct position;
+ * new should contain valid vid and ec headers and the data_crc should
+ * already have been checked before insertion, otherwise the chain
+ * could be have un an undesired manner;
+ * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0,
+ * if not, the code reached the last line and returned -EAGAIN,
+ * meaning there is a bug or a case not being handled here;
+ **/
+int
+eb_chain_insert(struct eb_info **head, struct eb_info *new)
+{
+ uint32_t vol, num, ver;
+ uint32_t new_vol, new_num, new_ver;
+ struct eb_info *prev, *cur, *hist, *ins;
+ struct eb_info **prev_ptr;
+
+ if ((head == NULL) || (new == NULL))
+ return 0;
+
+ if (*head == NULL) {
+ COPY(*head, new);
+ (*head)->next = NULL;
+ return 0;
+ }
+
+ new_vol = be32_to_cpu(new->vid.vol_id);
+ new_num = be32_to_cpu(new->vid.lnum);
+ new_ver = be32_to_cpu(new->vid.leb_ver);
+
+ /** TRAVERSE HORIZONTALY **/
+
+ cur = *head;
+ prev = NULL;
+
+ /* traverse until vol_id/lnum align */
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = be32_to_cpu(cur->vid.lnum);
+ while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) {
+ /* insert new at end of chain */
+ if (cur->next == NULL) {
+ COPY(ins, new);
+ ins->next = NULL;
+ cur->next = ins;
+ return 0;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = be32_to_cpu(cur->vid.lnum);
+ }
+
+ if (prev == NULL)
+ prev_ptr = head;
+ else
+ prev_ptr = &(prev->next);
+
+ /* insert new into the middle of chain */
+ if ((new_vol != vol) || (new_num != num)) {
+ COPY(ins, new);
+ ins->next = cur;
+ *prev_ptr = ins;
+ return 0;
+ }
+
+ /** TRAVERSE VERTICALY **/
+
+ hist = cur;
+ prev = NULL;
+
+ /* traverse until versions align */
+ ver = be32_to_cpu(cur->vid.leb_ver);
+ while (new_ver < ver) {
+ /* insert new at bottom of history */
+ if (hist->older == NULL) {
+ COPY(ins, new);
+ ins->next = NULL;
+ ins->older = NULL;
+ hist->older = ins;
+ return 0;
+ }
+
+ prev = hist;
+ hist = hist->older;
+ ver = be32_to_cpu(hist->vid.leb_ver);
+ }
+
+ if (prev == NULL) {
+ /* replace active version */
+ COPY(ins, new);
+ ins->next = hist->next;
+ *prev_ptr = ins;
+
+ /* place cur in vertical histroy */
+ ins->older = hist;
+ hist->next = NULL;
+ return 0;
+ }
+
+ /* insert between versions, beneath active version */
+ COPY(ins, new);
+ ins->next = NULL;
+ ins->older = prev->older;
+ prev->older = ins;
+ return 0;
+}
+
+
+/**
+ * sets the pointer at pos to the position of the first entry in the chain
+ * with of vol_id and, if given, with the same lnum as *lnum;
+ * if there is no entry in the chain, then *pos is NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+ struct eb_info **pos)
+{
+ uint32_t vol, num;
+ struct eb_info *cur;
+
+ if ((head == NULL) || (*head == NULL) || (pos == NULL))
+ return 0;
+
+ *pos = NULL;
+
+ cur = *head;
+ while (cur != NULL) {
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = be32_to_cpu(cur->vid.lnum);
+
+ if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) {
+ *pos = cur;
+ return 0;
+ }
+
+ cur = cur->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * prints to stream, the vol_id, lnum and leb_ver for each entry in the
+ * chain, starting at head;
+ * this is intended for debuging purposes;
+ * always returns 0;
+ *
+ * FIXME I do not like the double list traversion ...
+ **/
+int
+eb_chain_print(FILE* stream, struct eb_info *head)
+{
+ struct eb_info *cur;
+
+ if (stream == NULL)
+ stream = stdout;
+
+ if (head == NULL) {
+ fprintf(stream, "EMPTY\n");
+ return 0;
+ }
+ /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/
+ fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n");
+ cur = head;
+ while (cur != NULL) {
+ struct eb_info *hist;
+
+ fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ be32_to_cpu(cur->vid.leb_ver),
+ cur->ec_crc_ok ? "ok":"bad",
+ cur->vid_crc_ok ? "ok":"bad");
+ if (cur->vid.vol_type == UBI_VID_STATIC)
+ fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad");
+ else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign");
+ fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block,
+ cur->phys_addr, be32_to_cpu(cur->vid.data_size),
+ (unsigned long long)be64_to_cpu(cur->ec.ec));
+
+ hist = cur->older;
+ while (hist != NULL) {
+ fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+ be32_to_cpu(hist->vid.vol_id),
+ be32_to_cpu(hist->vid.lnum),
+ be32_to_cpu(hist->vid.leb_ver),
+ hist->ec_crc_ok ? "ok":"bad",
+ hist->vid_crc_ok ? "ok":"bad");
+ if (hist->vid.vol_type == UBI_VID_STATIC)
+ fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad");
+ else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign");
+ fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n",
+ hist->phys_block, hist->phys_addr,
+ be32_to_cpu(hist->vid.data_size),
+ (unsigned long long)be64_to_cpu(hist->ec.ec));
+
+ hist = hist->older;
+ }
+ cur = cur->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * frees the memory of the entire chain, starting at head;
+ * head will be NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_destroy(struct eb_info **head)
+{
+ if (head == NULL)
+ return 0;
+
+ while (*head != NULL) {
+ struct eb_info *cur;
+ struct eb_info *hist;
+
+ cur = *head;
+ *head = (*head)->next;
+
+ hist = cur->older;
+ while (hist != NULL) {
+ struct eb_info *temp;
+
+ temp = hist;
+ hist = hist->older;
+ free(temp);
+ }
+ free(cur);
+ }
+ return 0;
+}
+
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/error.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/error.c
new file mode 100644
index 0000000..4aaedad
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/error.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <string.h>
+#include "error.h"
+
+#define MAXLINE 4096
+#define MAXWIDTH 80
+
+static FILE *logfp = NULL;
+
+static void err_doit(int, int, const char *, va_list);
+
+int
+read_procfile(FILE *fp_out, const char *procfile)
+{
+ FILE *fp;
+
+ if (!fp_out)
+ return -ENXIO;
+
+ fp = fopen(procfile, "r");
+ if (!fp)
+ return -ENOENT;
+
+ while(!feof(fp)) {
+ int c = fgetc(fp);
+
+ if (c == EOF)
+ return 0;
+
+ if (putc(c, fp_out) == EOF)
+ return -EIO;
+
+ if (ferror(fp))
+ return -EIO;
+ }
+ return fclose(fp);
+}
+
+void
+error_initlog(const char *logfile)
+{
+ if (!logfile)
+ return;
+
+ logfp = fopen(logfile, "a+");
+ read_procfile(logfp, "/proc/cpuinfo");
+}
+
+void
+info_msg(const char *fmt, ...)
+{
+ FILE* fpout;
+ char buf[MAXLINE + 1];
+ va_list ap;
+ int n;
+
+ fpout = stdout;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ n = strlen(buf);
+ strcat(buf, "\n");
+
+ fputs(buf, fpout);
+ fflush(fpout);
+ if (fpout != stdout)
+ fclose(fpout);
+
+ va_end(ap);
+ return;
+}
+
+void
+__err_ret(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_INFO, fmt, ap);
+ va_end(ap);
+ return;
+}
+
+void
+__err_sys(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+
+void
+__err_msg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(0, LOG_INFO, fmt, ap);
+ va_end(ap);
+
+ return;
+}
+
+void
+__err_quit(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(0, LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+void
+__err_dump(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_ERR, fmt, ap);
+ va_end(ap);
+ abort(); /* dump core and terminate */
+ exit(EXIT_FAILURE); /* shouldn't get here */
+}
+
+/**
+ * If a logfile is used we must not print on stderr and stdout
+ * anymore. Since pfilfash might be used in a server context, it is
+ * even dangerous to write to those descriptors.
+ */
+static void
+err_doit(int errnoflag, int level __attribute__((unused)),
+ const char *fmt, va_list ap)
+{
+ FILE* fpout;
+ int errno_save, n;
+ char buf[MAXLINE + 1];
+ fpout = stderr;
+
+ errno_save = errno; /* value caller might want printed */
+
+ vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
+
+ n = strlen(buf);
+
+ if (errnoflag)
+ snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
+ strcat(buf, "\n");
+
+ if (logfp) {
+ fputs(buf, logfp);
+ fflush(logfp);
+ return; /* exit when logging completes */
+ }
+
+ if (fpout == stderr) {
+ /* perform line wrap when outputting to stderr */
+ int word_len, post_len, chars;
+ char *buf_ptr;
+ const char *frmt = "%*s%n %n";
+
+ chars = 0;
+ buf_ptr = buf;
+ while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) {
+ int i;
+ char word[word_len + 1];
+ char post[post_len + 1];
+
+ strncpy(word, buf_ptr, word_len);
+ word[word_len] = '\0';
+ buf_ptr += word_len;
+ post_len -= word_len;
+
+ if (chars + word_len > MAXWIDTH) {
+ fputc('\n', fpout);
+ chars = 0;
+ }
+ fputs(word, fpout);
+ chars += word_len;
+
+ if (post_len > 0) {
+ strncpy(post, buf_ptr, post_len);
+ post[post_len] = '\0';
+ buf_ptr += post_len;
+ }
+ for (i = 0; i < post_len; i++) {
+ int inc = 1, chars_new;
+
+ if (post[i] == '\t')
+ inc = 8;
+ if (post[i] == '\n') {
+ inc = 0;
+ chars_new = 0;
+ } else
+ chars_new = chars + inc;
+
+ if (chars_new > MAXWIDTH) {
+ fputc('\n', fpout);
+ chars_new = inc;
+ }
+ fputc(post[i], fpout);
+ chars = chars_new;
+ }
+ }
+ }
+ else
+ fputs(buf, fpout);
+ fflush(fpout);
+ if (fpout != stderr)
+ fclose(fpout);
+
+ return;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/error.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/error.h
new file mode 100644
index 0000000..05d8078
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/error.h
@@ -0,0 +1,84 @@
+#ifndef __ERROR_H__
+#define __ERROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+
+void error_initlog(const char *logfile);
+int read_procfile(FILE *fp_out, const char *procfile);
+
+void __err_ret(const char *fmt, ...);
+void __err_sys(const char *fmt, ...);
+void __err_msg(const char *fmt, ...);
+void __err_quit(const char *fmt, ...);
+void __err_dump(const char *fmt, ...);
+
+void info_msg(const char *fmt, ...);
+
+#ifdef DEBUG
+#define __loc_msg(str) do { \
+ __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n", \
+ str, __FILE__, __FUNCTION__, __LINE__); \
+} while (0)
+#else
+#define __loc_msg(str)
+#endif
+
+
+#define err_dump(fmt, ...) do { \
+ __loc_msg("ErrDump"); \
+ __err_dump(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_quit(fmt, ...) do { \
+ __loc_msg("ErrQuit"); \
+ __err_quit(fmt, ##__VA_ARGS__); \
+} while (0)
+
+
+#define err_ret(fmt, ...) do { \
+ __loc_msg("ErrRet"); \
+ __err_ret(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_sys(fmt, ...) do { \
+ __loc_msg("ErrSys"); \
+ __err_sys(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_msg(fmt, ...) do { \
+ __loc_msg("ErrMsg"); \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define log_msg(fmt, ...) do { \
+ /* __loc_msg("LogMsg"); */ \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#ifdef DEBUG
+#define dbg_msg(fmt, ...) do { \
+ __loc_msg("DbgMsg"); \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+#define dbg_msg(fmt, ...) do {} while (0)
+#endif
+
+#endif /* __ERROR_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/example_ubi.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/example_ubi.h
new file mode 100644
index 0000000..23c7b54
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/example_ubi.h
@@ -0,0 +1,28 @@
+#ifndef __EXAMPLE_UBI_H__
+#define __EXAMPLE_UBI_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * Defaults for our cards.
+ */
+#define EXAMPLE_UBI_DEVICE 0
+#define EXAMPLE_BOOTENV_VOL_ID_1 4
+#define EXAMPLE_BOOTENV_VOL_ID_2 5
+
+#endif /* __EXAMPLE_UBI_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/hashmap.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/hashmap.c
new file mode 100644
index 0000000..3511d56
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/hashmap.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "hashmap.h"
+#define DEFAULT_BUCKETS 4096
+
+#if 0
+#define INFO_MSG(fmt...) do { \
+ info_msg(fmt); \
+} while (0)
+#else
+#define INFO_MSG(fmt...)
+#endif
+
+struct hashentry {
+ char* key; /* key '0' term. str */
+ char* value; /* payload '0' term. str */
+
+ hashentry_t next;
+};
+
+struct hashmap {
+ size_t entries; /* current #entries */
+ size_t maxsize; /* no. of hash buckets */
+ hashentry_t* data; /* array of buckets */
+};
+
+static int
+is_empty(hashentry_t l)
+{
+ return l == NULL ? 1 : 0;
+}
+
+hashmap_t
+hashmap_new(void)
+{
+ hashmap_t res;
+ res = (hashmap_t) calloc(1, sizeof(struct hashmap));
+
+ if (res == NULL)
+ return NULL;
+
+ res->maxsize = DEFAULT_BUCKETS;
+ res->entries = 0;
+
+ res->data = (hashentry_t*)
+ calloc(1, res->maxsize * sizeof(struct hashentry));
+
+ if (res->data == NULL)
+ return NULL;
+
+ return res;
+}
+
+static hashentry_t
+new_entry(const char* key, const char* value)
+{
+ hashentry_t res;
+
+ res = (hashentry_t) calloc(1, sizeof(struct hashentry));
+
+ if (res == NULL)
+ return NULL;
+
+ /* allocate key and value and copy them */
+ res->key = strdup(key);
+ if (res->key == NULL) {
+ free(res);
+ return NULL;
+ }
+
+ res->value = strdup(value);
+ if (res->value == NULL) {
+ free(res->key);
+ free(res);
+ return NULL;
+ }
+
+ res->next = NULL;
+
+ return res;
+}
+
+static hashentry_t
+free_entry(hashentry_t e)
+{
+ if (!is_empty(e)) {
+ if(e->key != NULL) {
+ free(e->key);
+ }
+ if(e->value != NULL)
+ free(e->value);
+ free(e);
+ }
+
+ return NULL;
+}
+
+static hashentry_t
+remove_entry(hashentry_t l, const char* key, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ if(strcmp(l->key,key) == 0) {
+ lnext = l->next;
+ l = free_entry(l);
+ (*entries)--;
+ return lnext;
+ }
+
+ l->next = remove_entry(l->next, key, entries);
+
+ return l;
+}
+
+static hashentry_t
+insert_entry(hashentry_t l, hashentry_t e, size_t* entries)
+{
+ if (is_empty(l)) {
+ (*entries)++;
+ return e;
+ }
+
+ /* check for update */
+ if (strcmp(l->key, e->key) == 0) {
+ e->next = l->next;
+ l = free_entry(l);
+ return e;
+ }
+
+ l->next = insert_entry(l->next, e, entries);
+ return l;
+}
+
+static hashentry_t
+remove_all(hashentry_t l, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ lnext = l->next;
+ free_entry(l);
+ (*entries)--;
+
+ return remove_all(lnext, entries);
+}
+
+static const char*
+value_lookup(hashentry_t l, const char* key)
+{
+ if (is_empty(l))
+ return NULL;
+
+ if (strcmp(l->key, key) == 0)
+ return l->value;
+
+ return value_lookup(l->next, key);
+}
+
+static void
+print_all(hashentry_t l)
+{
+ if (is_empty(l)) {
+ printf("\n");
+ return;
+ }
+
+ printf("%s=%s", l->key, l->value);
+ if (!is_empty(l->next)) {
+ printf(",");
+ }
+
+ print_all(l->next);
+}
+
+static void
+keys_to_array(hashentry_t l, const char** a, size_t* i)
+{
+ if (is_empty(l))
+ return;
+
+ a[*i] = l->key;
+ (*i)++;
+
+ keys_to_array(l->next, a, i);
+}
+
+uint32_t
+hash_str(const char* str, uint32_t mapsize)
+{
+ uint32_t hash = 0;
+ uint32_t x = 0;
+ uint32_t i = 0;
+ size_t len = strlen(str);
+
+ for(i = 0; i < len; str++, i++) {
+ hash = (hash << 4) + (*str);
+ if((x = hash & 0xF0000000L) != 0) {
+ hash ^= (x >> 24);
+ hash &= ~x;
+ }
+ }
+
+ return (hash & 0x7FFFFFFF) % mapsize;
+}
+
+
+int
+hashmap_is_empty(hashmap_t map)
+{
+ if (map == NULL)
+ return -EINVAL;
+
+ return map->entries > 0 ? 1 : 0;
+}
+
+const char*
+hashmap_lookup(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return NULL;
+
+ i = hash_str(key, map->maxsize);
+
+ return value_lookup(map->data[i], key);
+}
+
+int
+hashmap_add(hashmap_t map, const char* key, const char* value)
+{
+ uint32_t i;
+ hashentry_t entry;
+
+ if ((map == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ entry = new_entry(key, value);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ map->data[i] = insert_entry(map->data[i],
+ entry, &map->entries);
+
+ INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value);
+ return 0;
+}
+
+int
+hashmap_remove(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ map->data[i] = remove_entry(map->data[i], key, &map->entries);
+
+ return 0;
+}
+
+size_t
+hashmap_size(hashmap_t map)
+{
+ if (map != NULL)
+ return map->entries;
+ else
+ return 0;
+}
+
+int
+hashmap_free(hashmap_t map)
+{
+ size_t i;
+
+ if (map == NULL)
+ return -EINVAL;
+
+ /* "children" first */
+ for(i = 0; i < map->maxsize; i++) {
+ map->data[i] = remove_all(map->data[i], &map->entries);
+ }
+ free(map->data);
+ free(map);
+
+ return 0;
+}
+
+int
+hashmap_dump(hashmap_t map)
+{
+ size_t i;
+ if (map == NULL)
+ return -EINVAL;
+
+ for(i = 0; i < map->maxsize; i++) {
+ if (map->data[i] != NULL) {
+ printf("[%zd]: ", i);
+ print_all(map->data[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const char**
+sort_key_vector(const char** a, size_t size)
+{
+ /* uses bubblesort */
+ size_t i, j;
+ const char* tmp;
+
+ if (size <= 0)
+ return a;
+
+ for (i = size - 1; i > 0; i--) {
+ for (j = 0; j < i; j++) {
+ if (strcmp(a[j], a[j+1]) > 0) {
+ tmp = a[j];
+ a[j] = a[j+1];
+ a[j+1] = tmp;
+ }
+ }
+ }
+ return a;
+}
+
+const char**
+hashmap_get_key_vector(hashmap_t map, size_t* size, int sort)
+{
+ const char** res;
+ size_t i, j;
+ *size = map->entries;
+
+ res = (const char**) malloc(*size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ j = 0;
+ for(i=0; i < map->maxsize; i++) {
+ keys_to_array(map->data[i], res, &j);
+ }
+
+ if (sort)
+ res = sort_key_vector(res, *size);
+
+ return res;
+}
+
+int
+hashmap_key_is_in_vector(const char** vec, size_t size, const char* key)
+{
+ size_t i;
+ for (i = 0; i < size; i++) {
+ if (strcmp(vec[i], key) == 0) /* found */
+ return 1;
+ }
+
+ return 0;
+}
+
+const char**
+hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size)
+{
+ const char** res;
+ size_t i, j;
+
+ *res_size = vec2_size;
+
+ res = (const char**) malloc(*res_size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ /* get all keys from vec2 which are not set in vec1 */
+ j = 0;
+ for (i = 0; i < vec2_size; i++) {
+ if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i]))
+ res[j++] = vec2[i];
+ }
+
+ *res_size = j;
+ return res;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/hashmap.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/hashmap.h
new file mode 100644
index 0000000..1b13e95
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/hashmap.h
@@ -0,0 +1,49 @@
+#ifndef __HASHMAP_H__
+#define __HASHMAP_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+typedef struct hashentry *hashentry_t;
+typedef struct hashmap *hashmap_t;
+
+hashmap_t hashmap_new(void);
+int hashmap_free(hashmap_t map);
+
+int hashmap_add(hashmap_t map, const char* key, const char* value);
+int hashmap_update(hashmap_t map, const char* key, const char* value);
+int hashmap_remove(hashmap_t map, const char* key);
+const char* hashmap_lookup(hashmap_t map, const char* key);
+
+const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort);
+int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key);
+const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size);
+
+int hashmap_dump(hashmap_t map);
+
+int hashmap_is_empty(hashmap_t map);
+size_t hashmap_size(hashmap_t map);
+
+uint32_t hash_str(const char* str, uint32_t mapsize);
+
+#endif /* __HASHMAP_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/libpfiflash.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libpfiflash.c
new file mode 100644
index 0000000..b0d454a
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libpfiflash.c
@@ -0,0 +1,1325 @@
+/*
+ * Copyright International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Authors: Oliver Lohmann <oliloh@de.ibm.com>
+ * Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <anrez@de.ibm.com>
+ */
+
+/* TODO Compare data before writing it. This implies that the volume
+ * parameters are compared first: size, alignment, name, type, ...,
+ * this is the same, compare the data. Volume deletion is deffered
+ * until the difference has been found out.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define __USE_GNU
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+
+#include <libubi.h>
+#include <pfiflash.h>
+
+#include <mtd/ubi-user.h> /* FIXME Is this ok here? */
+#include <mtd/mtd-user.h>
+
+#include "pfiflash_error.h"
+#include "ubimirror.h"
+#include "error.h"
+#include "reader.h"
+#include "example_ubi.h"
+#include "bootenv.h"
+
+/* ubi-header.h and crc32.h needed for CRC checking */
+#include <mtd/ubi-media.h> /* FIXME Is this ok here? */
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define COMPARE_BUFFER_SIZE 2048
+
+#define DEFAULT_DEV_PATTERN "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d"
+
+static const char copyright [] ubi_unused =
+ "Copyright International Business Machines Corp., 2006, 2007";
+
+/* simply clear buffer, then write into front of it */
+#define EBUF(fmt...) \
+ snprintf(err_buf, err_buf_size, fmt);
+
+/* make a history of buffer and then prepend something in front */
+#define EBUF_PREPEND(fmt) \
+ do { \
+ int EBUF_HISTORY_LENGTH = strlen(err_buf); \
+ char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1]; \
+ strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\
+ EBUF(fmt ": %s", EBUF_HISTORY); \
+ } while (0)
+
+/* An array of PDD function pointers indexed by the algorithm. */
+static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] =
+ {
+ &bootenv_pdd_keep,
+ &bootenv_pdd_merge,
+ &bootenv_pdd_overwrite
+ };
+
+typedef enum ubi_update_process_t {
+ UBI_REMOVE = 0,
+ UBI_WRITE,
+ UBI_COMPARE,
+} ubi_update_process_t;
+
+
+/**
+ * skip_raw_volumes - reads data from pfi to advance fp past raw block
+ * @pfi: fp to pfi data
+ * @pfi_raws: header information
+ *
+ * Error handling):
+ * when early EOF in pfi data
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ **/
+static int
+skip_raw_volumes(FILE* pfi, list_t pfi_raws,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc;
+ void *i;
+ list_t ptr;
+
+ if (is_empty(pfi_raws))
+ return 0;
+
+ rc = 0;
+ foreach(i, ptr, pfi_raws) {
+ size_t j;
+ pfi_raw_t raw;
+
+ raw = (pfi_raw_t)i;
+ for(j = 0; j < raw->data_size; j++) {
+ int c;
+
+ c = fgetc(pfi);
+ if (c == EOF)
+ rc = -PFIFLASH_ERR_EOF;
+ else if (ferror(pfi))
+ rc = -PFIFLASH_ERR_FIO;
+
+ if (rc != 0)
+ goto err;
+ }
+ }
+
+ err:
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ return rc;
+}
+
+
+/**
+ * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook
+ * @devno: UBI device number.
+ * @s: Current seqnum.
+ * @u: Information about the UBI volume from the PFI.
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't create a volume
+ * - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_mkvol(int devno, int s, pfi_ubi_t u,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, type;
+ char path[PATH_MAX];
+ libubi_t ulib;
+ struct ubi_mkvol_request req;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, "
+ "alig=%d, nlen=%d, name=%s",
+ u->ids[s], u->size, u->data_size, u->type, u->alignment,
+ strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]);
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ switch (u->type) {
+ case pfi_ubi_static:
+ type = UBI_STATIC_VOLUME; break;
+ case pfi_ubi_dynamic:
+ default:
+ type = UBI_DYNAMIC_VOLUME;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno);
+
+ req.vol_id = u->ids[s];
+ req.alignment = u->alignment;
+ req.bytes = u->size;
+ req.vol_type = type;
+ req.name = u->names[s];
+
+ rc = ubi_mkvol(ulib, path, &req);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_MKVOL;
+ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]);
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol
+ * @devno UBI device number
+ * @id UBI volume id to remove
+ *
+ * If the volume does not exist, the function will return success.
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't update (truncate) a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err
+ * when UBI system couldn't remove a volume
+ * - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_rmvol(int devno, uint32_t id,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, fd;
+ char path[PATH_MAX];
+ libubi_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ ubirmvol id=%d", id);
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ /* truncate whether it exist or not */
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ libubi_close(ulib);
+ return 0; /* not existent, return 0 */
+ }
+
+ rc = ubi_update_start(ulib, fd, 0);
+ close(fd);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err; /* if EBUSY than empty device, continue */
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno);
+
+ rc = ubi_rmvol(ulib, path, id);
+ if (rc != 0) {
+#ifdef DEBUG
+ int rc_old = rc;
+ dbg_msg("Remove UBI volume %d returned with error: %d "
+ "errno=%d", id, rc_old, errno);
+#endif
+
+ rc = -PFIFLASH_ERR_UBI_RMVOL;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+
+ /* TODO Define a ubi_rmvol return value which says
+ * sth like EUBI_NOSUCHDEV. In this case, a failed
+ * operation is acceptable. Everything else has to be
+ * classified as real error. But talk to Andreas Arnez
+ * before defining something odd...
+ */
+ /* if ((errno == EINVAL) || (errno == ENODEV))
+ return 0; */ /* currently it is EINVAL or ENODEV */
+
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * read_bootenv_volume - reads the current bootenv data from id into be_old
+ * @devno UBI device number
+ * @id UBI volume id to remove
+ * @bootenv_old to hold old boot_env data
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't open a volume to read
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when couldn't read bootenv data
+ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ **/
+static int
+read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ FILE* fp_in;
+ char path[PATH_MAX];
+ libubi_t ulib;
+
+ rc = 0;
+ fp_in = NULL;
+ ulib = NULL;
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fp_in = fopen(path, "r");
+ if (!fp_in) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ log_msg("[ reading old bootenvs ...");
+
+ /* Save old bootenvs for reference */
+ rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ err:
+ if (fp_in)
+ fclose(fp_in);
+ if (ulib)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume
+ * @devno UBI device number
+ * @id UBI volume id
+ * @bootend_old old PDD data from machine
+ * @pdd_f function to handle PDD with
+ * @fp_in new pdd data contained in PFI
+ * @fp_in_size data size of new pdd data in PFI
+ * @pfi_crc crc value from PFI header
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when bootenv can't be created
+ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ * when bootenv can't be read
+ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ * when PDD handling function returns and error
+ * - passes rc and err_buf data
+ * when CRC check fails
+ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ * when bootenv can't be resized
+ * - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err
+ * when UBI system couldn't open a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when couldn't write bootenv data
+ * - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err
+ **/
+static int
+write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+ pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size,
+ uint32_t pfi_crc,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, warnings, fd_out;
+ uint32_t crc;
+ char path[PATH_MAX];
+ size_t update_size;
+ FILE *fp_out;
+ bootenv_t bootenv_new, bootenv_res;
+ libubi_t ulib;
+
+ rc = 0;
+ warnings = 0;
+ crc = 0;
+ update_size = 0;
+ fp_out = NULL;
+ bootenv_new = NULL;
+ bootenv_res = NULL;
+ ulib = NULL;
+
+ log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", id, fp_in);
+
+ /* Workflow:
+ * 1. Apply PDD operation and get the size of the returning
+ * bootenv_res section. Without the correct size it wouldn't
+ * be possible to call UBI update vol.
+ * 2. Call UBI update vol
+ * 3. Get FILE* to vol dev
+ * 4. Write to FILE*
+ */
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_new);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], " 'new'");
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], " 'res'");
+ goto err;
+ }
+
+ rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ } else if (crc != pfi_crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+ goto err;
+ }
+
+ rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("handling PDD");
+ goto err;
+ }
+ else if (warnings)
+ /* TODO do something with warnings */
+ dbg_msg("A warning in the PDD operation occured: %d",
+ warnings);
+
+ rc = bootenv_size(bootenv_res, &update_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_SIZE;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fd_out = open(path, O_RDWR);
+ if (fd_out < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ fp_out = fdopen(fd_out, "r+");
+ if (!fp_out) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ rc = ubi_update_start(ulib, fd_out, update_size);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ rc = bootenv_write(fp_out, bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_WRITE;
+ EBUF(PFIFLASH_ERRSTR[-rc], devno, id);
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+ if (bootenv_new != NULL)
+ bootenv_destroy(&bootenv_new);
+ if (bootenv_res != NULL)
+ bootenv_destroy(&bootenv_res);
+ if (fp_out)
+ fclose(fp_out);
+
+ return rc;
+}
+
+
+/**
+ * write_normal_volume - writes data from PFI file int to regular UBI volume
+ * @devno UBI device number
+ * @id UBI volume id
+ * @update_size size of data stream
+ * @fp_in PFI data file pointer
+ * @pfi_crc CRC data from PFI header
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't open a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when unexpected EOF is encountered
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ * when CRC check fails
+ * - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ **/
+static int
+write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in,
+ uint32_t pfi_crc,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, fd_out;
+ uint32_t crc, crc32_table[256];
+ char path[PATH_MAX];
+ size_t bytes_left;
+ FILE* fp_out;
+ libubi_t ulib;
+
+ rc = 0;
+ crc = UBI_CRC32_INIT;
+ bytes_left = update_size;
+ fp_out = NULL;
+ ulib = NULL;
+
+ log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p",
+ id, update_size, fp_in);
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fd_out = open(path, O_RDWR);
+ if (fd_out < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ fp_out = fdopen(fd_out, "r+");
+ if (!fp_out) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ rc = ubi_update_start(ulib, fd_out, update_size);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ init_crc32_table(crc32_table);
+ while (bytes_left) {
+ char buf[1024];
+ size_t to_rw = sizeof buf > bytes_left ?
+ bytes_left : sizeof buf;
+ if (fread(buf, 1, to_rw, fp_in) != to_rw) {
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ crc = clc_crc32(crc32_table, crc, buf, to_rw);
+ if (fwrite(buf, 1, to_rw, fp_out) != to_rw) {
+ rc = -PFIFLASH_ERR_FIO;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ bytes_left -= to_rw;
+ }
+
+ if (crc != pfi_crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+ goto err;
+ }
+
+ err:
+ if (fp_out)
+ fclose(fp_out);
+ if (ulib)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+ uint32_t data_size, pdd_func_t pdd_f, char *err_buf,
+ size_t err_buf_size)
+{
+ int rc, warnings = 0;
+ unsigned int i;
+ bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL;
+
+ rc = bootenv_create(&bootenv_pfi);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ goto err;
+ }
+
+ rc = bootenv_read(fp_pfi, bootenv_pfi, data_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ rc = bootenv_create(&bootenv_flash);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ goto err;
+ }
+
+ rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ goto err;
+ }
+
+ rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res,
+ &warnings, err_buf, err_buf_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+ goto err;
+ }
+
+ rc = bootenv_compare(bootenv_flash, bootenv_res);
+ if (rc > 0) {
+ rc = -PFIFLASH_CMP_DIFF;
+ goto err;
+ } else if (rc < 0) {
+ rc = -PFIFLASH_ERR_COMPARE;
+ goto err;
+ }
+
+ bootenv_destroy(&bootenv_flash);
+ bootenv_flash = NULL;
+ }
+
+err:
+ if (bootenv_pfi)
+ bootenv_destroy(&bootenv_pfi);
+ if (bootenv_res)
+ bootenv_destroy(&bootenv_res);
+ if (bootenv_flash)
+ bootenv_destroy(&bootenv_flash);
+
+ return rc;
+}
+
+static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+ uint32_t bytes_left)
+{
+ unsigned int i;
+ size_t read_bytes, rc = 0;
+ char buf_pfi[COMPARE_BUFFER_SIZE];
+ char *buf_flash[ids_size];
+
+ for (i = 0; i < ids_size; i++) {
+ buf_flash[i] = malloc(COMPARE_BUFFER_SIZE);
+ if (!buf_flash[i])
+ return -PFIFLASH_ERR_COMPARE;
+ }
+
+ while (bytes_left) {
+ if (bytes_left > COMPARE_BUFFER_SIZE)
+ read_bytes = COMPARE_BUFFER_SIZE;
+ else
+ read_bytes = bytes_left;
+
+ rc = fread(buf_pfi, 1, read_bytes, fp_pfi);
+ if (rc != read_bytes) {
+ rc = -PFIFLASH_ERR_COMPARE;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]);
+ if (rc != read_bytes) {
+ rc = -PFIFLASH_CMP_DIFF;
+ goto err;
+ }
+
+ rc = memcmp(buf_pfi, buf_flash[i], read_bytes);
+ if (rc != 0) {
+ rc = -PFIFLASH_CMP_DIFF;
+ goto err;
+ }
+ }
+
+ bytes_left -= read_bytes;
+ }
+
+err:
+ for (i = 0; i < ids_size; i++)
+ free(buf_flash[i]);
+
+ return rc;
+}
+
+static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi,
+ pdd_func_t pdd_f, char *err_buf, size_t err_buf_size)
+{
+ int rc, is_bootenv = 0;
+ unsigned int i;
+ char path[PATH_MAX];
+ libubi_t ulib = NULL;
+ FILE *fp_flash[u->ids_size];
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ goto err;
+ }
+
+ for (i = 0; i < u->ids_size; i++) {
+ if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 ||
+ u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2)
+ is_bootenv = 1;
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, u->ids[i]);
+
+ fp_flash[i] = fopen(path, "r");
+ if (fp_flash[i] == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ goto err;
+ }
+ }
+
+ if (is_bootenv)
+ rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size,
+ u->data_size, pdd_f, err_buf, err_buf_size);
+ else
+ rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size);
+
+err:
+ if (rc < 0)
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+
+ for (i = 0; i < u->ids_size; i++)
+ fclose(fp_flash[i]);
+ if (ulib)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+static int
+erase_mtd_region(FILE* file_p, int start, int length)
+{
+ int rc, fd;
+ erase_info_t erase;
+ mtd_info_t mtdinfo;
+ loff_t offset = start;
+ loff_t end = offset + length;
+
+ fd = fileno(file_p);
+ if (fd < 0)
+ return -PFIFLASH_ERR_MTD_ERASE;
+
+ rc = ioctl(fd, MEMGETINFO, &mtdinfo);
+ if (rc)
+ return -PFIFLASH_ERR_MTD_ERASE;
+
+ /* check for bad blocks in case of NAND flash */
+ if (mtdinfo.type == MTD_NANDFLASH) {
+ while (offset < end) {
+ rc = ioctl(fd, MEMGETBADBLOCK, &offset);
+ if (rc > 0) {
+ return -PFIFLASH_ERR_MTD_ERASE;
+ }
+
+ offset += mtdinfo.erasesize;
+ }
+ }
+
+ erase.start = start;
+ erase.length = length;
+
+ rc = ioctl(fd, MEMERASE, &erase);
+ if (rc) {
+ return -PFIFLASH_ERR_MTD_ERASE;
+ }
+
+ return rc;
+}
+
+/**
+ * process_raw_volumes - writes the raw sections of the PFI data
+ * @pfi PFI data file pointer
+ * @pfi_raws list of PFI raw headers
+ * @rawdev device to use to write raw data
+ *
+ * Error handling:
+ * when early EOF in PFI data
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ * when CRC check fails
+ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ * when opening MTD device fails
+ * - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err
+ * when closing MTD device fails
+ * - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err
+ **/
+static int
+process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc;
+ char *pfi_data;
+ void *i;
+ uint32_t crc, crc32_table[256];
+ size_t j, k;
+ FILE* mtd = NULL;
+ list_t ptr;
+
+ if (is_empty(pfi_raws))
+ return 0;
+
+ if (rawdev == NULL)
+ return 0;
+
+ rc = 0;
+
+ pfi_data = NULL;
+
+ log_msg("[ rawupdate dev=%s", rawdev);
+
+ crc = UBI_CRC32_INIT;
+ init_crc32_table(crc32_table);
+
+ /* most likely only one element in list, but just in case */
+ foreach(i, ptr, pfi_raws) {
+ pfi_raw_t r = (pfi_raw_t)i;
+
+ /* read in pfi data */
+ if (pfi_data != NULL)
+ free(pfi_data);
+ pfi_data = malloc(r->data_size * sizeof(char));
+ for (j = 0; j < r->data_size; j++) {
+ int c = fgetc(pfi);
+ if (c == EOF) {
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ } else if (ferror(pfi)) {
+ rc = -PFIFLASH_ERR_FIO;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ pfi_data[j] = (char)c;
+ }
+ crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size);
+
+ /* check crc */
+ if (crc != r->crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc);
+ goto err;
+ }
+
+ /* open device */
+ mtd = fopen(rawdev, "r+");
+ if (mtd == NULL) {
+ rc = -PFIFLASH_ERR_MTD_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+ goto err;
+ }
+
+ for (j = 0; j < r->starts_size; j++) {
+ rc = erase_mtd_region(mtd, r->starts[j], r->data_size);
+ if (rc) {
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ fseek(mtd, r->starts[j], SEEK_SET);
+ for (k = 0; k < r->data_size; k++) {
+ int c = fputc((int)pfi_data[k], mtd);
+ if (c == EOF) {
+ fclose(mtd);
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ if ((char)c != pfi_data[k]) {
+ fclose(mtd);
+ rc = -1;
+ goto err;
+ }
+ }
+ }
+ rc = fclose(mtd);
+ mtd = NULL;
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_MTD_CLOSE;
+ EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+ goto err;
+ }
+ }
+
+ err:
+ if (mtd != NULL)
+ fclose(mtd);
+ if (pfi_data != NULL)
+ free(pfi_data);
+ return rc;
+}
+
+
+/**
+ * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest
+ * @devno UBI device number
+ * @pfi_ubis list of UBI header data
+ *
+ * Error handling:
+ * when UBI id is out of bounds
+ * - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err
+ * when UBI volume can't be removed
+ * - passes rc, prepends err_buf with contextual aid
+ **/
+static int
+erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES];
+ size_t i;
+ list_t ptr;
+ pfi_ubi_t u;
+
+ rc = 0;
+
+ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++)
+ ubi_volumes[i] = 1;
+
+ foreach(u, ptr, pfi_ubis) {
+ /* iterate over each vol_id */
+ for(i = 0; i < u->ids_size; i++) {
+ if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) {
+ rc = -PFIFLASH_ERR_UBI_VID_OOB;
+ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]);
+ goto err;
+ }
+ /* remove from removal list */
+ ubi_volumes[u->ids[i]] = 0;
+ }
+ }
+
+ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) {
+ if (ubi_volumes[i]) {
+ rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("remove volume failed");
+ goto err;
+ }
+ }
+ }
+
+ err:
+ return rc;
+}
+
+
+/**
+ * process_ubi_volumes - delegate tasks regarding UBI volumes
+ * @pfi PFI data file pointer
+ * @seqnum sequence number
+ * @pfi_ubis list of UBI header data
+ * @bootenv_old storage for current system PDD
+ * @pdd_f function to handle PDD
+ * @ubi_update_process whether reading or writing
+ *
+ * Error handling:
+ * when and unknown ubi_update_process is given
+ * - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err
+ * otherwise
+ * - passes rc and err_buf
+ **/
+static int
+process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis,
+ bootenv_t bootenv_old, pdd_func_t pdd_f,
+ ubi_update_process_t ubi_update_process,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ pfi_ubi_t u;
+ list_t ptr;
+
+ rc = 0;
+
+ foreach(u, ptr, pfi_ubis) {
+ int s = seqnum;
+
+ if (s > ((int)u->ids_size - 1))
+ s = 0; /* per default use the first */
+ u->curr_seqnum = s;
+
+ switch (ubi_update_process) {
+ case UBI_REMOVE:
+ /* TODO are all these "EXAMPLE" vars okay? */
+ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+ rc = read_bootenv_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s], bootenv_old,
+ err_buf, err_buf_size);
+ /* it's okay if there is no bootenv
+ * we're going to write one */
+ if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) ||
+ (rc == -PFIFLASH_ERR_BOOTENV_READ))
+ rc = 0;
+ if (rc != 0)
+ goto err;
+ }
+
+ rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE, u->ids[s],
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+
+ break;
+ case UBI_WRITE:
+ rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("creating volume");
+ goto err;
+ }
+
+ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+ rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s],
+ bootenv_old, pdd_f,
+ pfi,
+ u->data_size,
+ u->crc,
+ err_buf,
+ err_buf_size);
+ if (rc != 0)
+ EBUF_PREPEND("bootenv volume");
+ } else {
+ rc = write_normal_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s],
+ u->data_size, pfi,
+ u->crc,
+ err_buf,
+ err_buf_size);
+ if (rc != 0)
+ EBUF_PREPEND("normal volume");
+ }
+ if (rc != 0)
+ goto err;
+
+ break;
+ case UBI_COMPARE:
+ rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("compare volume");
+ goto err;
+ }
+
+ break;
+ default:
+ rc = -PFIFLASH_ERR_UBI_UNKNOWN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ }
+
+ err:
+ return rc;
+}
+
+
+/**
+ * mirror_ubi_volumes - mirror redundant pairs of volumes
+ * @devno UBI device number
+ * @pfi_ubis list of PFI header data
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ **/
+static int
+mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint32_t j;
+ list_t ptr;
+ pfi_ubi_t i;
+ libubi_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ mirror ...");
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ /**
+ * Execute all mirror operations on redundant groups.
+ * Create a volume within a redundant group if it does
+ * not exist already (this is a precondition of
+ * ubimirror).
+ */
+ foreach(i, ptr, pfi_ubis) {
+ for (j = 0; j < i->ids_size; j++) {
+ /* skip self-match */
+ if (i->ids[j] == i->ids[i->curr_seqnum])
+ continue;
+
+ rc = my_ubi_rmvol(devno, i->ids[j],
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+
+ rc = my_ubi_mkvol(devno, j, i,
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+ }
+ }
+
+ foreach(i, ptr, pfi_ubis) {
+ rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size,
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+ }
+
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * pfiflash_with_options - exposed func to flash memory with a PFI file
+ * @pfi PFI data file pointer
+ * @complete flag to erase unmapped volumes
+ * @seqnum sequence number
+ * @compare flag to compare
+ * @pdd_handling method to handle pdd (keep, merge, overwrite...)
+ *
+ * Error handling:
+ * when bootenv can't be created
+ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ * when PFI headers can't be read, or
+ * when fail to skip raw sections, or
+ * when error occurs while processing raw volumes, or
+ * when fail to erase unmapped UBI vols, or
+ * when error occurs while processing UBI volumes, or
+ * when error occurs while mirroring UBI volumes
+ * - passes rc, prepends err_buf with contextual aid
+ **/
+int
+pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
+ pdd_handling_t pdd_handling, const char* rawdev,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ bootenv_t bootenv;
+ pdd_func_t pdd_f;
+
+ if (pfi == NULL)
+ return -EINVAL;
+
+ rc = 0;
+ pdd_f = NULL;
+
+ /* If the user didnt specify a seqnum we start per default
+ * with the index 0 */
+ int curr_seqnum = seqnum < 0 ? 0 : seqnum;
+
+ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+
+ rc = bootenv_create(&bootenv);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], "");
+ goto err;
+ }
+
+ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("reading PFI header");
+ goto err;
+ }
+
+ if (rawdev == NULL || compare)
+ rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size);
+ else
+ rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf,
+ err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("handling raw section");
+ goto err;
+ }
+
+ if (complete && !compare) {
+ rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("deleting unmapped UBI volumes");
+ goto err;
+ }
+ }
+
+ if (((int)pdd_handling >= 0) &&
+ (pdd_handling < PDD_HANDLING_NUM))
+ pdd_f = pdd_funcs[pdd_handling];
+ else {
+ rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+ EBUF("%s", PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ if (!compare) {
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+ pdd_f, UBI_REMOVE, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("removing UBI volumes");
+ goto err;
+ }
+
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+ pdd_f, UBI_WRITE, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("writing UBI volumes");
+ goto err;
+ }
+
+ if (seqnum < 0) { /* mirror redundant pairs */
+ rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("mirroring UBI volumes");
+ goto err;
+ }
+ }
+ } else {
+ /* only compare volumes, don't alter the content */
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+ pdd_f, UBI_COMPARE, err_buf, err_buf_size);
+
+ if (rc == -PFIFLASH_CMP_DIFF)
+ /* update is necessary, return positive value */
+ rc = 1;
+
+ if (rc < 0) {
+ EBUF_PREPEND("comparing UBI volumes");
+ goto err;
+ }
+ }
+
+ err:
+ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+ bootenv_destroy(&bootenv);
+ return rc;
+}
+
+
+/**
+ * pfiflash - passes to pfiflash_with_options
+ * @pfi PFI data file pointer
+ * @complete flag to erase unmapped volumes
+ * @seqnum sequence number
+ * @pdd_handling method to handle pdd (keep, merge, overwrite...)
+ **/
+int
+pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+ char *err_buf, size_t err_buf_size)
+{
+ return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling,
+ NULL, err_buf, err_buf_size);
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubi.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubi.c
new file mode 100644
index 0000000..a536b47
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubi.c
@@ -0,0 +1,915 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include "libubi.h"
+#include "libubi_int.h"
+
+libubi_t libubi_open(void)
+{
+ int fd, version;
+ struct libubi *lib;
+
+ lib = calloc(1, sizeof(struct libubi));
+ if (!lib)
+ return NULL;
+
+ /* TODO: this must be discovered instead */
+ lib->sysfs = strdup("/sys");
+ if (!lib->sysfs)
+ goto error;
+
+ lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI);
+ if (!lib->sysfs_ubi)
+ goto error;
+
+ /* Make sure UBI is present */
+ fd = open(lib->sysfs_ubi, O_RDONLY);
+ if (fd == -1)
+ goto error;
+ close(fd);
+
+ lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT);
+ if (!lib->ubi_dev)
+ goto error;
+
+ lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER);
+ if (!lib->ubi_version)
+ goto error;
+
+ lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV);
+ if (!lib->dev_dev)
+ goto error;
+
+ lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS);
+ if (!lib->dev_avail_ebs)
+ goto error;
+
+ lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS);
+ if (!lib->dev_total_ebs)
+ goto error;
+
+ lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT);
+ if (!lib->dev_bad_count)
+ goto error;
+
+ lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE);
+ if (!lib->dev_eb_size)
+ goto error;
+
+ lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC);
+ if (!lib->dev_max_ec)
+ goto error;
+
+ lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD);
+ if (!lib->dev_bad_rsvd)
+ goto error;
+
+ lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS);
+ if (!lib->dev_max_vols)
+ goto error;
+
+ lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE);
+ if (!lib->dev_min_io_size)
+ goto error;
+
+ lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT);
+ if (!lib->ubi_vol)
+ goto error;
+
+ lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE);
+ if (!lib->vol_type)
+ goto error;
+
+ lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV);
+ if (!lib->vol_dev)
+ goto error;
+
+ lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT);
+ if (!lib->vol_alignment)
+ goto error;
+
+ lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES);
+ if (!lib->vol_data_bytes)
+ goto error;
+
+ lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS);
+ if (!lib->vol_rsvd_ebs)
+ goto error;
+
+ lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE);
+ if (!lib->vol_eb_size)
+ goto error;
+
+ lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED);
+ if (!lib->vol_corrupted)
+ goto error;
+
+ lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME);
+ if (!lib->vol_name)
+ goto error;
+
+ if (read_int(lib->ubi_version, &version))
+ goto error;
+ if (version != LIBUBI_UBI_VERSION) {
+ fprintf(stderr, "LIBUBI: this library was made for UBI version "
+ "%d, but UBI version %d is detected\n",
+ LIBUBI_UBI_VERSION, version);
+ goto error;
+ }
+
+ return lib;
+
+error:
+ free(lib->vol_corrupted);
+ free(lib->vol_eb_size);
+ free(lib->vol_rsvd_ebs);
+ free(lib->vol_data_bytes);
+ free(lib->vol_alignment);
+ free(lib->vol_dev);
+ free(lib->vol_type);
+ free(lib->ubi_vol);
+ free(lib->dev_min_io_size);
+ free(lib->dev_max_vols);
+ free(lib->dev_bad_rsvd);
+ free(lib->dev_max_ec);
+ free(lib->dev_eb_size);
+ free(lib->dev_bad_count);
+ free(lib->dev_total_ebs);
+ free(lib->dev_avail_ebs);
+ free(lib->dev_dev);
+ free(lib->ubi_version);
+ free(lib->ubi_dev);
+ free(lib->sysfs_ubi);
+ free(lib->sysfs);
+ free(lib);
+ return NULL;
+}
+
+void libubi_close(libubi_t desc)
+{
+ struct libubi *lib = (struct libubi *)desc;
+
+ free(lib->vol_name);
+ free(lib->vol_corrupted);
+ free(lib->vol_eb_size);
+ free(lib->vol_rsvd_ebs);
+ free(lib->vol_data_bytes);
+ free(lib->vol_alignment);
+ free(lib->vol_dev);
+ free(lib->vol_type);
+ free(lib->ubi_vol);
+ free(lib->dev_min_io_size);
+ free(lib->dev_max_vols);
+ free(lib->dev_bad_rsvd);
+ free(lib->dev_max_ec);
+ free(lib->dev_eb_size);
+ free(lib->dev_bad_count);
+ free(lib->dev_total_ebs);
+ free(lib->dev_avail_ebs);
+ free(lib->dev_dev);
+ free(lib->ubi_version);
+ free(lib->ubi_dev);
+ free(lib->sysfs_ubi);
+ free(lib->sysfs);
+ free(lib);
+}
+
+int ubi_get_info(libubi_t desc, struct ubi_info *info)
+{
+ DIR *sysfs_ubi;
+ struct dirent *dirent;
+ struct libubi *lib = (struct libubi *)desc;
+
+ memset(info, '\0', sizeof(struct ubi_info));
+
+ /*
+ * We have to scan the UBI sysfs directory to identify how many UBI
+ * devices are present.
+ */
+ sysfs_ubi = opendir(lib->sysfs_ubi);
+ if (!sysfs_ubi)
+ return -1;
+
+ info->lowest_dev_num = INT_MAX;
+ while ((dirent = readdir(sysfs_ubi))) {
+ char *name = &dirent->d_name[0];
+ int dev_num, ret;
+
+ ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num);
+ if (ret == 1) {
+ info->dev_count += 1;
+ if (dev_num > info->highest_dev_num)
+ info->highest_dev_num = dev_num;
+ if (dev_num < info->lowest_dev_num)
+ info->lowest_dev_num = dev_num;
+ }
+ }
+
+ if (info->lowest_dev_num == INT_MAX)
+ info->lowest_dev_num = 0;
+
+ if (read_int(lib->ubi_version, &info->version))
+ goto close;
+
+ return closedir(sysfs_ubi);
+
+close:
+ closedir(sysfs_ubi);
+ return -1;
+}
+
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req)
+{
+ int fd, ret;
+ struct ubi_mkvol_req r;
+ size_t n;
+
+ desc = desc;
+ r.vol_id = req->vol_id;
+ r.alignment = req->alignment;
+ r.bytes = req->bytes;
+ r.vol_type = req->vol_type;
+
+ n = strlen(req->name);
+ if (n > UBI_MAX_VOLUME_NAME)
+ return -1;
+
+ strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1);
+ r.name_len = n;
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, UBI_IOCMKVOL, &r);
+ if (!ret)
+ req->vol_id = r.vol_id;
+
+ close(fd);
+ return ret;
+}
+
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id)
+{
+ int fd, ret;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, UBI_IOCRMVOL, &vol_id);
+ close(fd);
+ return ret;
+}
+
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes)
+{
+ int fd, ret;
+ struct ubi_rsvol_req req;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ req.bytes = bytes;
+ req.vol_id = vol_id;
+
+ ret = ioctl(fd, UBI_IOCRSVOL, &req);
+ close(fd);
+ return ret;
+}
+
+int ubi_update_start(libubi_t desc, int fd, long long bytes)
+{
+ desc = desc;
+ if (ioctl(fd, UBI_IOCVOLUP, &bytes))
+ return -1;
+ return 0;
+}
+
+int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info)
+{
+ int dev_num;
+ struct libubi *lib = (struct libubi *)desc;
+
+ dev_num = find_dev_num(lib, node);
+ if (dev_num == -1)
+ return -1;
+
+ return ubi_get_dev_info1(desc, dev_num, info);
+}
+
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
+{
+ DIR *sysfs_ubi;
+ struct dirent *dirent;
+ struct libubi *lib = (struct libubi *)desc;
+
+ memset(info, '\0', sizeof(struct ubi_dev_info));
+ info->dev_num = dev_num;
+
+ sysfs_ubi = opendir(lib->sysfs_ubi);
+ if (!sysfs_ubi)
+ return -1;
+
+ info->lowest_vol_num = INT_MAX;
+ while ((dirent = readdir(sysfs_ubi))) {
+ char *name = &dirent->d_name[0];
+ int vol_id, ret, devno;
+
+ ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id);
+ if (ret == 2 && devno == dev_num) {
+ info->vol_count += 1;
+ if (vol_id > info->highest_vol_num)
+ info->highest_vol_num = vol_id;
+ if (vol_id < info->lowest_vol_num)
+ info->lowest_vol_num = vol_id;
+ }
+ }
+
+ closedir(sysfs_ubi);
+
+ if (info->lowest_vol_num == INT_MAX)
+ info->lowest_vol_num = 0;
+
+ if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs))
+ return -1;
+ if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs))
+ return -1;
+ if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count))
+ return -1;
+ if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size))
+ return -1;
+ if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd))
+ return -1;
+ if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec))
+ return -1;
+ if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count))
+ return -1;
+ if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size))
+ return -1;
+
+ info->avail_bytes = info->avail_ebs * info->eb_size;
+ info->total_bytes = info->total_ebs * info->eb_size;
+
+ return 0;
+}
+
+int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info)
+{
+ int vol_id, dev_num;
+ struct libubi *lib = (struct libubi *)desc;
+
+ dev_num = find_dev_num_vol(lib, node);
+ if (dev_num == -1)
+ return -1;
+
+ vol_id = find_vol_num(lib, dev_num, node);
+ if (vol_id == -1)
+ return -1;
+
+ return ubi_get_vol_info1(desc, dev_num, vol_id, info);
+}
+
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+ struct ubi_vol_info *info)
+{
+ int ret;
+ struct libubi *lib = (struct libubi *)desc;
+ char buf[50];
+
+ memset(info, '\0', sizeof(struct ubi_vol_info));
+ info->dev_num = dev_num;
+ info->vol_id = vol_id;
+
+ ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ if (strncmp(&buf[0], "static\n", ret) == 0)
+ info->type = UBI_STATIC_VOLUME;
+ else if (strncmp(&buf[0], "dynamic\n", ret) == 0)
+ info->type = UBI_DYNAMIC_VOLUME;
+ else {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = vol_read_int(lib->vol_alignment, dev_num, vol_id,
+ &info->alignment);
+ if (ret)
+ return -1;
+ ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id,
+ &info->data_bytes);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id,
+ &info->corrupted);
+ if (ret)
+ return -1;
+ info->rsvd_bytes = info->eb_size * info->rsvd_ebs;
+
+ ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name,
+ UBI_VOL_NAME_MAX + 2);
+ if (ret < 0)
+ return -1;
+
+ info->name[ret - 1] = '\0';
+ return 0;
+}
+
+/**
+ * read_int - read an 'int' value from a file.
+ *
+ * @file the file to read from
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int read_int(const char *file, int *value)
+{
+ int fd, rd;
+ char buf[50];
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_int - read an 'int' value from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_int(const char *patt, int dev_num, int *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_ll - read a 'long long' value from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_ll(const char *patt, int dev_num, long long *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%lld\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_data - read data from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @buf buffer to read data to
+ * @buf_len buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len)
+{
+ int fd, rd;
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return rd;
+}
+
+/**
+ * vol_read_int - read an 'int' value from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%lld\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * vol_read_data - read data from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @buf buffer to read to
+ * @buf_len buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len)
+{
+ int fd, rd;
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return rd;
+}
+
+/**
+ * mkpath - compose full path from 2 given components.
+ *
+ * @path first component
+ * @name second component
+ *
+ * This function returns the resulting path in case of success and %NULL in
+ * case of failure.
+ */
+static char *mkpath(const char *path, const char *name)
+{
+ char *n;
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ n = malloc(len1 + len2 + 2);
+ if (!n)
+ return NULL;
+
+ memcpy(n, path, len1);
+ if (n[len1 - 1] != '/')
+ n[len1++] = '/';
+
+ memcpy(n + len1, name, len2 + 1);
+ return n;
+}
+
+/**
+ * find_dev_num - find UBI device number by its character device node.
+ *
+ * @lib UBI library descriptor
+ * @node UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int find_dev_num(struct libubi *lib, const char *node)
+{
+ struct stat st;
+ struct ubi_info info;
+ int i, major, minor;
+
+ if (stat(node, &st))
+ return -1;
+
+ if (!S_ISCHR(st.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (minor != 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (minor1 == minor && major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * find_dev_num_vol - find UBI device number by volume character device node.
+ *
+ * @lib UBI library descriptor
+ * @node UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int find_dev_num_vol(struct libubi *lib, const char *node)
+{
+ struct stat st;
+ struct ubi_info info;
+ int i, major;
+
+ if (stat(node, &st))
+ return -1;
+
+ if (!S_ISCHR(st.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(st.st_rdev);
+
+ if (minor(st.st_rdev) == 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * find_vol_num - find UBI volume number by its character device node.
+ *
+ * @lib UBI library descriptor
+ * @dev_num UBI device number
+ * @node UBI volume character device node name
+ *
+ * This function returns positive UBI volume number in case of success and %-1
+ * in case of failure.
+ */
+static int find_vol_num(struct libubi *lib, int dev_num, const char *node)
+{
+ struct stat st;
+ struct ubi_dev_info info;
+ int i, major, minor;
+
+ if (stat(node, &st))
+ return -1;
+
+ if (!S_ISCHR(st.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (minor == 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info))
+ return -1;
+
+ for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (minor1 == minor && major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubi_int.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubi_int.h
new file mode 100644
index 0000000..e68b791
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubi_int.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_INT_H__
+#define __LIBUBI_INT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * UBI heavily makes use of the sysfs file system to interact with users-pace.
+ * The below are pre-define UBI file and directory names.
+ */
+
+#define SYSFS_UBI "class/ubi"
+#define UBI_DEV_NAME_PATT "ubi%d"
+#define UBI_VER "version"
+#define DEV_DEV "dev"
+#define UBI_VOL_NAME_PATT "ubi%d_%d"
+#define DEV_AVAIL_EBS "avail_eraseblocks"
+#define DEV_TOTAL_EBS "total_eraseblocks"
+#define DEV_BAD_COUNT "bad_peb_count"
+#define DEV_EB_SIZE "eraseblock_size"
+#define DEV_MAX_EC "max_ec"
+#define DEV_MAX_RSVD "reserved_for_bad"
+#define DEV_MAX_VOLS "max_vol_count"
+#define DEV_MIN_IO_SIZE "min_io_size"
+#define VOL_TYPE "type"
+#define VOL_DEV "dev"
+#define VOL_ALIGNMENT "alignment"
+#define VOL_DATA_BYTES "data_bytes"
+#define VOL_RSVD_EBS "reserved_ebs"
+#define VOL_EB_SIZE "usable_eb_size"
+#define VOL_CORRUPTED "corrupted"
+#define VOL_NAME "name"
+
+/**
+ * libubi - UBI library description data structure.
+ *
+ * @sysfs sysfs file system path
+ * @sysfs_ubi UBI directory in sysfs
+ * @ubi_dev UBI device sysfs directory pattern
+ * @ubi_version UBI version file sysfs path
+ * @dev_dev UBI device's major/minor numbers file pattern
+ * @dev_avail_ebs count of available eraseblocks sysfs path pattern
+ * @dev_total_ebs total eraseblocks count sysfs path pattern
+ * @dev_bad_count count of bad eraseblocks sysfs path pattern
+ * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern
+ * @dev_max_ec maximum erase counter sysfs path pattern
+ * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks
+ * handling
+ * @dev_max_vols maximum volumes number count sysfs path pattern
+ * @dev_min_io_size minimum I/O unit size sysfs path pattern
+ * @ubi_vol UBI volume sysfs directory pattern
+ * @vol_type volume type sysfs path pattern
+ * @vol_dev volume's major/minor numbers file pattern
+ * @vol_alignment volume alignment sysfs path pattern
+ * @vol_data_bytes volume data size sysfs path pattern
+ * @vol_rsvd_ebs volume reserved size sysfs path pattern
+ * @vol_eb_size volume eraseblock size sysfs path pattern
+ * @vol_corrupted volume corruption flag sysfs path pattern
+ * @vol_name volume name sysfs path pattern
+ */
+struct libubi
+{
+ char *sysfs;
+ char *sysfs_ubi;
+ char *ubi_dev;
+ char *ubi_version;
+ char *dev_dev;
+ char *dev_avail_ebs;
+ char *dev_total_ebs;
+ char *dev_bad_count;
+ char *dev_eb_size;
+ char *dev_max_ec;
+ char *dev_bad_rsvd;
+ char *dev_max_vols;
+ char *dev_min_io_size;
+ char *ubi_vol;
+ char *vol_type;
+ char *vol_dev;
+ char *vol_alignment;
+ char *vol_data_bytes;
+ char *vol_rsvd_ebs;
+ char *vol_eb_size;
+ char *vol_corrupted;
+ char *vol_name;
+ char *vol_max_count;
+};
+
+static int read_int(const char *file, int *value);
+static int dev_read_int(const char *patt, int dev_num, int *value);
+static int dev_read_ll(const char *patt, int dev_num, long long *value);
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len);
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value);
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value);
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len);
+static char *mkpath(const char *path, const char *name);
+static int find_dev_num(struct libubi *lib, const char *node);
+static int find_dev_num_vol(struct libubi *lib, const char *node);
+static int find_vol_num(struct libubi *lib, int dev_num, const char *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_INT_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubigen.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubigen.c
new file mode 100644
index 0000000..1793009
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubigen.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Add UBI headers to binary data.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <mtd/ubi-media.h>
+#include <mtd_swab.h>
+
+#include "config.h"
+#include "ubigen.h"
+#include "crc32.h"
+
+#define UBI_NAME_SIZE 256
+#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+
+struct ubi_info {
+ struct ubi_vid_hdr* v; /* Volume ID header */
+ struct ubi_ec_hdr* ec; /* Erase count header */
+
+ FILE* fp_in; /* Input Stream */
+ FILE* fp_out; /* Output stream */
+
+ size_t eb_size; /* Physical EB size in bytes */
+ size_t leb_size; /* Size of a logical EB in a physical EB */
+ size_t leb_total; /* Total input size in logical EB */
+ size_t alignment; /* Block alignment */
+ size_t data_pad; /* Size of padding in each physical EB */
+
+ size_t bytes_total; /* Total input size in bytes */
+ size_t bytes_read; /* Nymber of read bytes (total) */
+
+ uint32_t blks_written; /* Number of written logical EB */
+
+ uint8_t* buf; /* Allocated buffer */
+ uint8_t* ptr_ec_hdr; /* Pointer to EC hdr in buf */
+ uint8_t* ptr_vid_hdr; /* Pointer to VID hdr in buf */
+ uint8_t* ptr_data; /* Pointer to data region in buf */
+};
+
+
+static uint32_t
+byte_to_blk(uint64_t byte, uint32_t eb_size)
+{
+ return (byte % eb_size) == 0
+ ? (byte / eb_size)
+ : (byte / eb_size) + 1;
+}
+
+static int
+validate_ubi_info(ubi_info_t u)
+{
+ if ((u->v->vol_type != UBI_VID_DYNAMIC) &&
+ (u->v->vol_type != UBI_VID_STATIC)) {
+ return EUBIGEN_INVALID_TYPE;
+ }
+
+ if (be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) {
+ return EUBIGEN_INVALID_HDR_OFFSET;
+ }
+
+ return 0;
+}
+
+static int
+skip_blks(ubi_info_t u, uint32_t blks)
+{
+ uint32_t i;
+ size_t read = 0, to_read = 0;
+
+ /* Step to a maximum of leb_total - 1 to keep the
+ restrictions. */
+ for (i = 0; i < MIN(blks, u->leb_total-1); i++) {
+ /* Read in data */
+ to_read = MIN(u->leb_size,
+ (u->bytes_total - u->bytes_read));
+ read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (read != to_read) {
+ return -EIO;
+ }
+ u->bytes_read += read;
+ u->blks_written++;
+ }
+
+ return 0;
+}
+
+static void
+clear_buf(ubi_info_t u)
+{
+ memset(u->buf, 0xff, u->eb_size);
+}
+
+static void
+write_ec_hdr(ubi_info_t u)
+{
+ memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE);
+}
+
+static int
+fill_data_buffer_from_file(ubi_info_t u, size_t* read)
+{
+ size_t to_read = 0;
+
+ if (u-> fp_in == NULL)
+ return -EIO;
+
+ to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read));
+ *read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (*read != to_read) {
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->ptr_data, data_size);
+
+ u->v->data_size = cpu_to_be32(data_size);
+ u->v->data_crc = cpu_to_be32(crc);
+
+ if (action & BROKEN_DATA_CRC) {
+ u->v->data_crc =
+ cpu_to_be32(be32_to_cpu(u->v->data_crc) + 1);
+ }
+ if (action & BROKEN_DATA_SIZE) {
+ u->v->data_size =
+ cpu_to_be32(be32_to_cpu(u->v->data_size) + 1);
+ }
+}
+
+static void
+write_vid_hdr(ubi_info_t u, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->v, UBI_VID_HDR_SIZE_CRC);
+ /* Write VID header */
+ u->v->hdr_crc = cpu_to_be32(crc);
+ if (action & BROKEN_HDR_CRC) {
+ u->v->hdr_crc = cpu_to_be32(be32_to_cpu(u->v->hdr_crc) + 1);
+ }
+ memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE);
+}
+
+static int
+write_to_output_stream(ubi_info_t u)
+{
+ size_t written;
+
+ written = fwrite(u->buf, 1, u->eb_size, u->fp_out);
+ if (written != u->eb_size) {
+ return -EIO;
+ }
+ return 0;
+}
+
+int
+ubigen_write_leb(ubi_info_t u, ubigen_action_t action)
+{
+ int rc = 0;
+ size_t read = 0;
+
+ clear_buf(u);
+ write_ec_hdr(u);
+
+ rc = fill_data_buffer_from_file(u, &read);
+ if (rc != 0)
+ return rc;
+
+ if (u->v->vol_type == UBI_VID_STATIC) {
+ add_static_info(u, read, action);
+ }
+
+ u->v->lnum = cpu_to_be32(u->blks_written);
+
+ if (action & MARK_AS_UPDATE) {
+ u->v->copy_flag = (u->v->copy_flag)++;
+ }
+
+ write_vid_hdr(u, action);
+ rc = write_to_output_stream(u);
+ if (rc != 0)
+ return rc;
+
+ /* Update current handle */
+ u->bytes_read += read;
+ u->blks_written++;
+ return 0;
+}
+
+int
+ubigen_write_complete(ubi_info_t u)
+{
+ size_t i;
+ int rc = 0;
+
+ for (i = 0; i < u->leb_total; i++) {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ if (rc != 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+ubigen_write_broken_update(ubi_info_t u, uint32_t blk)
+{
+ int rc = 0;
+
+ rc = skip_blks(u, blk);
+ if (rc != 0)
+ return rc;
+
+ rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC);
+ if (rc != 0)
+ return rc;
+
+
+ return 0;
+}
+
+void
+dump_info(ubi_info_t u ubi_unused)
+{
+#ifdef DEBUG
+ int err = 0;
+ if (!u) {
+ fprintf(stderr, "<empty>");
+ return;
+ }
+ if (!u->ec) {
+ fprintf(stderr, "<ec-empty>");
+ err = 1;
+ }
+ if (!u->v) {
+ fprintf(stderr, "<v-empty>");
+ err = 1;
+ }
+ if (err) return;
+
+ fprintf(stderr, "ubi volume\n");
+ fprintf(stderr, "version : %8d\n", u->v->version);
+ fprintf(stderr, "vol_id : %8d\n", be32_to_cpu(u->v->vol_id));
+ fprintf(stderr, "vol_type : %8s\n",
+ u->v->vol_type == UBI_VID_STATIC ?
+ "static" : "dynamic");
+ fprintf(stderr, "used_ebs : %8d\n",
+ be32_to_cpu(u->v->used_ebs));
+ fprintf(stderr, "eb_size : 0x%08x\n", u->eb_size);
+ fprintf(stderr, "leb_size : 0x%08x\n", u->leb_size);
+ fprintf(stderr, "data_pad : 0x%08x\n",
+ be32_to_cpu(u->v->data_pad));
+ fprintf(stderr, "leb_total : %8d\n", u->leb_total);
+ fprintf(stderr, "header offs : 0x%08x\n",
+ be32_to_cpu(u->ec->vid_hdr_offset));
+ fprintf(stderr, "bytes_total : %8d\n", u->bytes_total);
+ fprintf(stderr, " + in MiB : %8.2f M\n",
+ ((float)(u->bytes_total)) / 1024 / 1024);
+ fprintf(stderr, "-------------------------------\n\n");
+#else
+ return;
+#endif
+}
+
+int
+ubigen_destroy(ubi_info_t *u)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ ubi_info_t tmp = *u;
+
+ if (tmp) {
+ if (tmp->v)
+ free(tmp->v);
+ if (tmp->ec)
+ free(tmp->ec);
+ if (tmp->buf)
+ free(tmp->buf);
+ free(tmp);
+ }
+ *u = NULL;
+ return 0;
+}
+
+void
+ubigen_init(void)
+{
+ init_crc32_table(crc32_table);
+}
+
+int
+ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag,
+ size_t data_size, FILE* fp_in, FILE* fp_out)
+{
+ int rc = 0;
+ ubi_info_t res = NULL;
+ uint32_t crc;
+ uint32_t data_offset;
+
+ if (alignment == 0) {
+ rc = EUBIGEN_INVALID_ALIGNMENT;
+ goto ubigen_create_err;
+ }
+ if ((fp_in == NULL) || (fp_out == NULL)) {
+ rc = -EINVAL;
+ goto ubigen_create_err;
+ }
+
+ res = (ubi_info_t) calloc(1, sizeof(struct ubi_info));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr));
+ if (res->v == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr));
+ if (res->ec == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* data which is needed in the general process */
+ vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET;
+ data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE;
+ res->bytes_total = data_size;
+ res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE;
+ res->data_pad = (res->eb_size - data_offset) % alignment;
+ res->leb_size = res->eb_size - data_offset - res->data_pad;
+ res->leb_total = byte_to_blk(data_size, res->leb_size);
+ res->alignment = alignment;
+
+ if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) {
+ rc = EUBIGEN_TOO_SMALL_EB;
+ goto ubigen_create_err;
+ }
+ res->fp_in = fp_in;
+ res->fp_out = fp_out;
+
+ /* vid hdr data which doesn't change */
+ res->v->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
+ res->v->version = version ? version : UBI_VERSION;
+ res->v->vol_type = vol_type;
+ res->v->vol_id = cpu_to_be32(vol_id);
+ res->v->compat = compat_flag;
+ res->v->data_pad = cpu_to_be32(res->data_pad);
+
+ /* static only: used_ebs */
+ if (res->v->vol_type == UBI_VID_STATIC) {
+ res->v->used_ebs = cpu_to_be32(byte_to_blk
+ (res->bytes_total,
+ res->leb_size));
+ }
+
+ /* ec hdr (fixed, doesn't change) */
+ res->ec->magic = cpu_to_be32(UBI_EC_HDR_MAGIC);
+ res->ec->version = version ? version : UBI_VERSION;
+ res->ec->ec = cpu_to_be64(ec);
+ res->ec->vid_hdr_offset = cpu_to_be32(vid_hdr_offset);
+
+ res->ec->data_offset = cpu_to_be32(data_offset);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec,
+ UBI_EC_HDR_SIZE_CRC);
+ res->ec->hdr_crc = cpu_to_be32(crc);
+
+ /* prepare a read buffer */
+ res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t));
+ if (res->buf == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* point to distinct regions within the buffer */
+ res->ptr_ec_hdr = res->buf;
+ res->ptr_vid_hdr = res->buf + be32_to_cpu(res->ec->vid_hdr_offset);
+ res->ptr_data = res->buf + be32_to_cpu(res->ec->vid_hdr_offset)
+ + UBI_VID_HDR_SIZE;
+
+ rc = validate_ubi_info(res);
+ if (rc != 0) {
+ fprintf(stderr, "Volume validation failed: %d\n", rc);
+ goto ubigen_create_err;
+ }
+
+ dump_info(res);
+ *u = res;
+ return rc;
+
+ ubigen_create_err:
+ if (res) {
+ if (res->v)
+ free(res->v);
+ if (res->ec)
+ free(res->ec);
+ if (res->buf)
+ free(res->buf);
+ free(res);
+ }
+ *u = NULL;
+ return rc;
+}
+
+int
+ubigen_get_leb_size(ubi_info_t u, size_t* size)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *size = u->leb_size;
+ return 0;
+}
+
+
+int
+ubigen_get_leb_total(ubi_info_t u, size_t* total)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *total = u->leb_total;
+ return 0;
+}
+
+int
+ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* vol_name, struct ubi_vtbl_record *lvol_rec)
+{
+ uint32_t crc;
+
+ if ((u == NULL) || (vol_name == NULL))
+ return -EINVAL;
+
+ memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE);
+
+ lvol_rec->reserved_pebs =
+ cpu_to_be32(byte_to_blk(reserved_bytes, u->leb_size));
+ lvol_rec->alignment = cpu_to_be32(u->alignment);
+ lvol_rec->data_pad = u->v->data_pad;
+ lvol_rec->vol_type = u->v->vol_type;
+
+ lvol_rec->name_len =
+ cpu_to_be16((uint16_t)strlen((const char*)vol_name));
+
+ memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ lvol_rec, UBI_VTBL_RECORD_SIZE_CRC);
+ lvol_rec->crc = cpu_to_be32(crc);
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubimirror.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubimirror.c
new file mode 100644
index 0000000..d06770e
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/libubimirror.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <memory.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include <libubi.h>
+#include "ubimirror.h"
+
+#define COMPARE_BUF_SIZE (128 * 1024)
+
+#define DEFAULT_DEV_PATTERN "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d"
+
+#define EBUF(fmt...) do { \
+ snprintf(err_buf, err_buf_size, fmt); \
+} while (0)
+
+enum {
+ compare_error = -1,
+ seek_error = -2,
+ write_error = -3,
+ read_error = -4,
+ update_error = -5,
+ ubi_error = -6,
+ open_error = -7,
+ close_error = -8,
+ compare_equal = 0,
+ compare_different = 1
+};
+
+/*
+ * Read len number of bytes from fd.
+ * Return 0 on EOF, -1 on error.
+ */
+static ssize_t fill_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+ ssize_t got, have = 0;
+
+ do {
+ got = read(fd, buf + have, len - have);
+ if (got == -1 && errno != EINTR)
+ return -1;
+ have += got;
+ } while (got > 0 && have < len);
+ return have;
+}
+
+/*
+ * Write len number of bytes to fd.
+ * Return bytes written (>= 0), -1 on error.
+ */
+static ssize_t flush_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+ ssize_t done, have = 0;
+
+ do {
+ done = write(fd, buf + have, len - have);
+ if (done == -1 && errno != EINTR)
+ return -1;
+ have += done;
+ } while (done > 0 && have < len);
+ return have;
+}
+
+/*
+ * Compare two files. Return 0, 1, or -1, depending on whether the
+ * files are equal, different, or an error occured.
+ * Return compare-different when target volume can not be read. Might be
+ * an interrupted volume update and then the target device returns -EIO but
+ * can be updated.
+ *
+ * fd_a is source
+ * fd_b is destination
+ */
+static int compare_files(int fd_a, int fd_b)
+{
+ unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE];
+ ssize_t len_a, len_b;
+ int rc;
+
+ for (;;) {
+ len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a));
+ if (len_a == -1) {
+ rc = compare_error;
+ break;
+ }
+ len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b));
+ if (len_b == -1) {
+ rc = compare_different;
+ break;
+ }
+ if (len_a != len_b) {
+ rc = compare_different;
+ break;
+ }
+ if (len_a == 0) { /* Size on both files equal and EOF */
+ rc = compare_equal;
+ break;
+ }
+ if (memcmp(buf_a, buf_b, len_a) != 0 ) {
+ rc = compare_different;
+ break;
+ }
+ }
+ /* Position both files at the beginning */
+ if (lseek(fd_a, 0, SEEK_SET) == -1 ||
+ lseek(fd_b, 0, SEEK_SET) == -1)
+ rc = seek_error;
+ return rc;
+}
+
+int vol_get_used_bytes(int vol_fd, unsigned long long *bytes)
+{
+ off_t res;
+
+ res = lseek(vol_fd, 0, SEEK_END);
+ if (res == (off_t)-1)
+ return -1;
+ *bytes = (unsigned long long) res;
+ res = lseek(vol_fd, 0, SEEK_SET);
+ return res == (off_t)-1 ? -1 : 0;
+}
+
+static int copy_files(libubi_t ulib, int fd_in, int fd_out)
+{
+ unsigned char buf_a[COMPARE_BUF_SIZE];
+ ssize_t len_a, len_b;
+ unsigned long long update_size, copied;
+
+ if (vol_get_used_bytes(fd_in, &update_size) == -1 ||
+ ubi_update_start(ulib, fd_out, update_size) == -1)
+ return update_error;
+ for (copied = 0; copied < update_size; copied += len_b ) {
+ len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a));
+ if (len_a == -1)
+ return read_error;
+ if (len_a == 0) /* Reach EOF */
+ return 0;
+ len_b = flush_buffer(fd_out, buf_a, len_a);
+ if (len_b != len_a)
+ return write_error;
+ }
+ return 0;
+}
+
+int ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ uint32_t src_id;
+ char path[PATH_MAX];
+ libubi_t ulib;
+ int fd_in = -1, i = 0, fd_out = -1;
+
+ if (ids_size == 0)
+ return 0;
+ else {
+ if ((seqnum < 0) || (seqnum > (ids_size - 1))) {
+ EBUF("volume id %d out of range", seqnum);
+ return EUBIMIRROR_NO_SRC;
+ }
+ src_id = ids[seqnum];
+ }
+
+ ulib = libubi_open();
+ if (ulib == NULL)
+ return ubi_error;
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, src_id);
+
+ fd_in = open(path, O_RDONLY);
+ if (fd_in == -1) {
+ EBUF("open error source volume %d", ids[i]);
+ rc = open_error;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ if (ids[i] == src_id) /* skip self-mirror */
+ continue;
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, ids[i]);
+
+ fd_out = open(path, O_RDWR);
+ if (fd_out < 0){
+ EBUF("open error destination volume %d", ids[i]);
+ rc = open_error;
+ goto err;
+ }
+ rc = compare_files(fd_in, fd_out);
+ if (rc < 0) {
+ EBUF("compare error volume %d and %d", src_id, ids[i]);
+ goto err;
+ } else if (rc == compare_different) {
+ rc = copy_files(ulib, fd_in, fd_out);
+ if (rc != 0) {
+ EBUF("mirror error volume %d to %d", src_id,
+ ids[i]);
+ goto err;
+ }
+ }
+ if ((rc = close(fd_out)) == -1) {
+ EBUF("close error volume %d", ids[i]);
+ rc = close_error;
+ goto err;
+ } else
+ fd_out = -1;
+ }
+err:
+ if (fd_out != -1)
+ close(fd_out);
+ if (fd_in != -1)
+ close(fd_in);
+ if (ulib != NULL)
+ libubi_close(ulib);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/list.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/list.c
new file mode 100644
index 0000000..6eb716b
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/list.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "list.h"
+
+list_t
+mk_empty(void)
+{
+ return (list_t) NULL;
+}
+
+int
+is_empty(list_t l)
+{
+ return l == NULL;
+}
+
+info_t
+head(list_t l)
+{
+ assert(!is_empty(l));
+ return l->info;
+}
+
+list_t
+tail(list_t l)
+{
+ assert(!is_empty(l));
+ return l->next;
+}
+
+list_t
+remove_head(list_t l)
+{
+ list_t res;
+ assert(!is_empty(l));
+
+ res = l->next;
+ free(l);
+ return res;
+}
+
+list_t
+cons(info_t e, list_t l)
+{
+ list_t res = malloc(sizeof(*l));
+ if (!res)
+ return NULL;
+ res->info = e;
+ res->next = l;
+
+ return res;
+}
+
+list_t
+prepend_elem(info_t e, list_t l)
+{
+ return cons(e,l);
+}
+
+list_t
+append_elem(info_t e, list_t l)
+{
+ if (is_empty(l)) {
+ return cons(e,l);
+ }
+ l->next = append_elem(e, l->next);
+
+ return l;
+}
+
+list_t
+insert_sorted(cmp_func_t cmp, info_t e, list_t l)
+{
+ if (is_empty(l))
+ return cons(e, l);
+
+ switch (cmp(e, l->info)) {
+ case -1:
+ case 0:
+ return l;
+ break;
+ case 1:
+ l->next = insert_sorted(cmp, e, l);
+ break;
+ default:
+ break;
+ }
+
+ /* never reached */
+ return NULL;
+}
+
+list_t
+remove_all(free_func_t free_func, list_t l)
+{
+ if (is_empty(l))
+ return l;
+ list_t lnext = l->next;
+
+ if (free_func && l->info) {
+ free_func(&(l->info));
+ }
+ free(l);
+
+ return remove_all(free_func, lnext);
+}
+
+
+info_t
+is_in(cmp_func_t cmp, info_t e, list_t l)
+{
+ return
+ (is_empty(l))
+ ? NULL
+ : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next);
+}
+
+
+void
+apply(process_func_t process_func, list_t l)
+{
+ list_t ptr;
+ void *i;
+ foreach(i, ptr, l) {
+ process_func(i);
+ }
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/list.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/list.h
new file mode 100644
index 0000000..e8452a2
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/list.h
@@ -0,0 +1,56 @@
+#ifndef __LIST_H__
+#define __LIST_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdint.h>
+
+#define foreach(elem, ptr, list) \
+ for (elem = list != NULL ? (typeof(elem)) head(list) \
+ : NULL, ptr = list; \
+ ptr != NULL; \
+ ptr = tail(ptr), \
+ elem = (typeof(elem)) ptr ? head(ptr) : NULL)
+
+typedef struct node* list_t;
+typedef void* info_t;
+typedef int (*free_func_t)(info_t*);
+typedef int (*cmp_func_t)(info_t, info_t);
+typedef void (*process_func_t)(info_t);
+
+struct node {
+ list_t next;
+ info_t info;
+};
+
+list_t mk_empty(void);
+int is_empty(list_t l);
+info_t is_in(cmp_func_t cmp, info_t e, list_t l);
+info_t head(list_t l);
+list_t tail(list_t l);
+list_t remove_head(list_t l);
+list_t cons(info_t e, list_t l);
+list_t prepend_elem(info_t e, list_t);
+list_t append_elem(info_t e, list_t);
+list_t remove_all(free_func_t free_func, list_t l);
+list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l);
+void apply(process_func_t process_func, list_t l);
+
+#endif /* __LIST_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/mkbootenv.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/mkbootenv.c
new file mode 100644
index 0000000..50fc8ac
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/mkbootenv.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Create boot-parameter/pdd data from an ASCII-text input file.
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-media.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+
+#define PROGRAM_VERSION "1.3"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "mkbootenv - processes bootenv text files and convertes "
+ "them into a binary format.\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright informatoin.\n"
+" -o, --output=<fname> Write the output data to <output> instead of\n"
+" stdout.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: mkbootenv [-c?V] [-o <output>] [--copyright] [--output=<output>]\n"
+" [--help] [--usage] [--version] [bootenv-txt-file]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ FILE* fp_in;
+ FILE* fp_out;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "co:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case 'o':
+ args->fp_out = fopen(optarg, "wb");
+ if ((args->fp_out) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for output\n", optarg);
+ exit(1);
+ }
+ break;
+ case '?': /* help */
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s for input\n",
+ argv[optind]);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ bootenv_t env;
+
+ myargs args = {
+ .fp_in = stdin,
+ .fp_out = stdout,
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+
+ rc = bootenv_create(&env);
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle.");
+ goto err;
+ }
+ rc = bootenv_read_txt(args.fp_in, env);
+ if (rc != 0) {
+ err_msg("Cannot read bootenv from input file.");
+ goto err;
+ }
+ rc = bootenv_write(args.fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to output file.");
+ goto err;
+ }
+
+ if (args.fp_in != stdin) {
+ fclose(args.fp_in);
+ }
+ if (args.fp_out != stdout) {
+ fclose(args.fp_out);
+ }
+
+err:
+ bootenv_destroy(&env);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/nand2bin.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nand2bin.c
new file mode 100644
index 0000000..93ba29f
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nand2bin.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to decompose NAND images and strip OOB off. Not yet finished ...
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ * 1.4 Fixed OOB output file
+ * 1.5 Added verbose output and option to set blocksize.
+ * Added split block mode for more convenient analysis.
+ * 1.6 Fixed ECC error detection and correction.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "nandecc.h"
+
+#define PROGRAM_VERSION "1.6"
+
+#define MAXPATH 1024
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+struct args {
+ const char *oob_file;
+ const char *output_file;
+ size_t pagesize;
+ size_t blocksize;
+ int split_blocks;
+ size_t in_len; /* size of input file */
+ int correct_ecc;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .output_file = "data.bin",
+ .oob_file = "oob.bin",
+ .pagesize = 2048,
+ .blocksize = 128 * 1024,
+ .in_len = 0,
+ .split_blocks = 0,
+ .correct_ecc = 0,
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "nand2bin - split data and OOB.\n";
+
+static const char *optionsstr =
+" -o, --output=<output> Data output file\n"
+" -O, --oob=<oob> OOB output file\n"
+" -p, --pagesize=<pagesize> NAND pagesize\n"
+" -b, --blocksize=<blocksize> NAND blocksize\n"
+" -s, --split-blocks generate binaries for each block\n"
+" -e, --correct-ecc Correct data according to ECC info\n"
+" -v, --verbose verbose output\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n";
+
+static const char *usage =
+"Usage: nand2bin [-?] [-o <output>] [-O <oob>] [-p <pagesize>]\n"
+" [--output=<output>] [--oob=<oob>] [--pagesize=<pagesize>] [--help]\n"
+" [--usage] input.mif\n";
+
+static int verbose = 0;
+
+static struct option long_options[] = {
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+ { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' },
+ { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * str_to_num - Convert string into number and cope with endings like
+ * k, K, kib, KiB for kilobyte
+ * m, M, mib, MiB for megabyte
+ */
+static uint32_t str_to_num(char *str)
+{
+ char *s = str;
+ ulong num = strtoul(s, &s, 0);
+
+ if (*s != '\0') {
+ if (strcmp(s, "KiB") == 0)
+ num *= 1024;
+ else if (strcmp(s, "MiB") == 0)
+ num *= 1024*1024;
+ else {
+ fprintf(stderr, "WARNING: Wrong number format "
+ "\"%s\", check your paramters!\n", str);
+ }
+ }
+ return num;
+}
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The argument list
+ * @param args Pointer to program args structure
+ *
+ * @return error
+ *
+ */
+static int parse_opt(int argc, char **argv, struct args *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "b:eo:O:p:sv?", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'p': /* --pagesize<pagesize> */
+ args->pagesize = str_to_num(optarg);
+ break;
+
+ case 'b': /* --blocksize<blocksize> */
+ args->blocksize = str_to_num(optarg);
+ break;
+
+ case 'v': /* --verbose */
+ verbose++;
+ break;
+
+ case 's': /* --split-blocks */
+ args->split_blocks = 1;
+ break;
+
+ case 'e': /* --correct-ecc */
+ args->correct_ecc = 1;
+ break;
+
+ case 'o': /* --output=<output.bin> */
+ args->output_file = optarg;
+ break;
+
+ case 'O': /* --oob=<oob.bin> */
+ args->oob_file = optarg;
+ break;
+
+ case '?': /* help */
+ printf("Usage: nand2bin [OPTION...] input.mif\n");
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->arg1 = argv[optind++];
+
+ return 0;
+}
+
+static int calc_oobsize(size_t pagesize)
+{
+ switch (pagesize) {
+ case 512: return 16;
+ case 2048: return 64;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+static inline void hexdump(FILE *fp, const uint8_t *buf, ssize_t size)
+{
+ int k;
+
+ for (k = 0; k < size; k++) {
+ fprintf(fp, "%02x ", buf[k]);
+ if ((k & 15) == 15)
+ fprintf(fp, "\n");
+ }
+}
+
+static int process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize)
+{
+ int eccpoi, oobsize;
+ size_t i;
+
+ switch (pagesize) {
+ case 2048: oobsize = 64; eccpoi = 64 / 2; break;
+ case 512: oobsize = 16; eccpoi = 16 / 2; break;
+ default:
+ fprintf(stderr, "Unsupported page size: %zd\n", pagesize);
+ return -EINVAL;
+ }
+ memset(oobbuf, 0xff, oobsize);
+
+ for (i = 0; i < pagesize; i += 256, eccpoi += 3) {
+ oobbuf[eccpoi++] = 0x0;
+ /* Calculate ECC */
+ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]);
+ }
+ return 0;
+}
+
+static int bad_marker_offs_in_oob(int pagesize)
+{
+ switch (pagesize) {
+ case 2048: return 0;
+ case 512: return 5;
+ }
+ return -EINVAL;
+}
+
+static int decompose_image(struct args *args, FILE *in_fp,
+ FILE *bin_fp, FILE *oob_fp)
+{
+ int read, rc, page = 0;
+ size_t oobsize = calc_oobsize(args->pagesize);
+ uint8_t *buf = malloc(args->pagesize);
+ uint8_t *oob = malloc(oobsize);
+ uint8_t *calc_oob = malloc(oobsize);
+ uint8_t *calc_buf = malloc(args->pagesize);
+ uint8_t *page_buf;
+ int pages_per_block = args->blocksize / args->pagesize;
+ int eccpoi = 0, eccpoi_start;
+ unsigned int i;
+ int badpos = bad_marker_offs_in_oob(args->pagesize);
+
+ switch (args->pagesize) {
+ case 2048: eccpoi_start = 64 / 2; break;
+ case 512: eccpoi_start = 16 / 2; break;
+ default: exit(EXIT_FAILURE);
+ }
+
+ if (!buf)
+ exit(EXIT_FAILURE);
+ if (!oob)
+ exit(EXIT_FAILURE);
+ if (!calc_oob)
+ exit(EXIT_FAILURE);
+ if (!calc_buf)
+ exit(EXIT_FAILURE);
+
+ while (!feof(in_fp)) {
+ /* read page by page */
+ read = fread(buf, 1, args->pagesize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ if (read != (ssize_t)args->pagesize)
+ break;
+
+ read = fread(oob, 1, oobsize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+
+ page_buf = buf; /* default is unmodified data */
+
+ if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) {
+ if (verbose)
+ printf("Block %d is bad\n",
+ page / pages_per_block);
+ goto write_data;
+ }
+ if (args->correct_ecc)
+ page_buf = calc_buf;
+
+ process_page(buf, calc_oob, args->pagesize);
+ memcpy(calc_buf, buf, args->pagesize);
+
+ /*
+ * Our oob format uses only the last 3 bytes out of 4.
+ * The first byte is 0x00 when the ECC is generated by
+ * our toolset and 0xff when generated by Linux. This
+ * is to be fixed when we want nand2bin work for other
+ * ECC layouts too.
+ */
+ for (i = 0, eccpoi = eccpoi_start; i < args->pagesize;
+ i += 256, eccpoi += 4)
+ oob[eccpoi] = calc_oob[eccpoi] = 0xff;
+
+ if (verbose && memcmp(oob, calc_oob, oobsize) != 0) {
+ printf("\nECC compare mismatch found at block %d page %d!\n",
+ page / pages_per_block, page % pages_per_block);
+
+ printf("Read out OOB Data:\n");
+ hexdump(stdout, oob, oobsize);
+
+ printf("Calculated OOB Data:\n");
+ hexdump(stdout, calc_oob, oobsize);
+ }
+
+ /* Do correction on subpage base */
+ for (i = 0, eccpoi = eccpoi_start; i < args->pagesize;
+ i += 256, eccpoi += 4) {
+ rc = nand_correct_data(calc_buf + i, &oob[eccpoi + 1],
+ &calc_oob[eccpoi + 1]);
+
+ if (rc == -1)
+ fprintf(stdout, "Uncorrectable ECC error at "
+ "block %d page %d/%d\n",
+ page / pages_per_block,
+ page % pages_per_block, i / 256);
+ else if (rc > 0)
+ fprintf(stdout, "Correctable ECC error at "
+ "block %d page %d/%d\n",
+ page / pages_per_block,
+ page % pages_per_block, i / 256);
+ }
+
+ write_data:
+ rc = fwrite(page_buf, 1, args->pagesize, bin_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(oob, 1, oobsize, oob_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+
+ page++;
+ }
+ free(calc_buf);
+ free(calc_oob);
+ free(oob);
+ free(buf);
+ return 0;
+}
+
+static int split_blocks(struct args *args, FILE *in_fp)
+{
+ uint8_t *buf;
+ size_t oobsize = calc_oobsize(args->pagesize);
+ int pages_per_block = args->blocksize / args->pagesize;
+ int block_len = pages_per_block * (args->pagesize + oobsize);
+ int blocks = args->in_len / block_len;
+ char bname[256] = { 0, };
+ int badpos = bad_marker_offs_in_oob(args->pagesize);
+ int bad_blocks = 0, i, bad_block = 0;
+ ssize_t rc;
+ FILE *b;
+
+ buf = malloc(block_len);
+ if (!buf) {
+ perror("Not enough memory");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < blocks; i++) {
+ rc = fread(buf, 1, block_len, in_fp);
+ if (rc != block_len) {
+ fprintf(stderr, "cannot read enough data!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* do block analysis */
+ bad_block = 0;
+ if ((buf[args->pagesize + badpos] != 0xff) ||
+ (buf[2 * args->pagesize + oobsize + badpos] != 0xff)) {
+ bad_blocks++;
+ bad_block = 1;
+ }
+ if ((verbose && bad_block) || (verbose > 1)) {
+ printf("-- (block %d oob of page 0 and 1)\n", i);
+ hexdump(stdout, buf + args->pagesize, oobsize);
+ printf("--\n");
+ hexdump(stdout, buf + 2 * args->pagesize +
+ oobsize, oobsize);
+ }
+
+ /* write complete block out */
+ snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i);
+ b = fopen(bname, "w+");
+ if (!b) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(buf, 1, block_len, b);
+ if (rc != block_len) {
+ fprintf(stderr, "could not write all data!\n");
+ exit(EXIT_FAILURE);
+ }
+ fclose(b);
+ }
+
+ free(buf);
+ if (bad_blocks || verbose)
+ fprintf(stderr, "%d blocks, %d bad blocks\n",
+ blocks, bad_blocks);
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *in, *bin = NULL, *oob = NULL;
+ struct stat file_info;
+
+ parse_opt(argc, argv, &myargs);
+
+ if (!myargs.arg1) {
+ fprintf(stderr, "Please specify input file!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(myargs.arg1, &file_info) != 0) {
+ perror("Cannot fetch file size from input file.\n");
+ exit(EXIT_FAILURE);
+ }
+ myargs.in_len = file_info.st_size;
+
+ in = fopen(myargs.arg1, "r");
+ if (!in) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+
+ if (myargs.split_blocks) {
+ split_blocks(&myargs, in);
+ goto out;
+ }
+
+ bin = fopen(myargs.output_file, "w+");
+ if (!bin) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ oob = fopen(myargs.oob_file, "w+");
+ if (!oob) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ decompose_image(&myargs, in, bin, oob);
+
+ out:
+ if (in) fclose(in);
+ if (bin) fclose(bin);
+ if (oob) fclose(oob);
+ exit(EXIT_SUCCESS);
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandcorr.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandcorr.c
new file mode 100644
index 0000000..caa07e2
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandcorr.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in
+ * a 256 bytes of data.
+ *
+ * Reimplement by Thomas Gleixner after staring long enough at the
+ * mess in drivers/mtd/nand/nandecc.c
+ *
+ */
+
+#include "nandecc.h"
+
+static int countbits(uint32_t byte)
+{
+ int res = 0;
+
+ for (;byte; byte >>= 1)
+ res += byte & 0x01;
+ return res;
+}
+
+/**
+ * @dat: data which should be corrected
+ * @read_ecc: ecc information read from flash
+ * @calc_ecc: calculated ecc information from the data
+ * @return: number of corrected bytes
+ * or -1 when no correction is possible
+ */
+int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc,
+ const uint8_t *calc_ecc)
+{
+ uint8_t s0, s1, s2;
+
+ /*
+ * Do error detection
+ *
+ * Be careful, the index magic is due to a pointer to a
+ * uint32_t.
+ */
+ s0 = calc_ecc[0] ^ read_ecc[0];
+ s1 = calc_ecc[1] ^ read_ecc[1];
+ s2 = calc_ecc[2] ^ read_ecc[2];
+
+ if ((s0 | s1 | s2) == 0)
+ return 0;
+
+ /* Check for a single bit error */
+ if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+ ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+ ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+ uint32_t byteoffs, bitnum;
+
+ byteoffs = (s1 << 0) & 0x80;
+ byteoffs |= (s1 << 1) & 0x40;
+ byteoffs |= (s1 << 2) & 0x20;
+ byteoffs |= (s1 << 3) & 0x10;
+
+ byteoffs |= (s0 >> 4) & 0x08;
+ byteoffs |= (s0 >> 3) & 0x04;
+ byteoffs |= (s0 >> 2) & 0x02;
+ byteoffs |= (s0 >> 1) & 0x01;
+
+ bitnum = (s2 >> 5) & 0x04;
+ bitnum |= (s2 >> 4) & 0x02;
+ bitnum |= (s2 >> 3) & 0x01;
+
+ dat[byteoffs] ^= (1 << bitnum);
+
+ return 1;
+ }
+
+ if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+ return 1;
+
+ return -1;
+}
+
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandecc.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandecc.c
new file mode 100644
index 0000000..71660ef
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandecc.c
@@ -0,0 +1,159 @@
+/*
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ * drivers/mtd/nand/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+ * This file 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 or (at your option) any
+ * later version.
+ *
+ * This file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include "nandecc.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+/**
+ * nand_trans_result - [GENERIC] create non-inverted ECC
+ * @reg2: line parity reg 2
+ * @reg3: line parity reg 3
+ * @ecc_code: ecc
+ *
+ * Creates non-inverted ECC code from line parity
+ */
+static void nand_trans_result(uint8_t reg2, uint8_t reg3,
+ uint8_t *ecc_code)
+{
+ uint8_t a, b, i, tmp1, tmp2;
+
+ /* Initialize variables */
+ a = b = 0x80;
+ tmp1 = tmp2 = 0;
+
+ /* Calculate first ECC byte */
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Calculate second ECC byte */
+ b = 0x80;
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Store two of the ECC bytes */
+ ecc_code[1] = tmp1;
+ ecc_code[0] = tmp2;
+}
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for
+ * 256 byte block
+ *
+ * @dat: raw data
+ * @ecc_code: buffer for ECC
+ */
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code)
+{
+ uint8_t idx, reg1, reg2, reg3;
+ int j;
+
+ /* Initialize variables */
+ reg1 = reg2 = reg3 = 0;
+ ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+
+ /* Build up column parity */
+ for(j = 0; j < 256; j++) {
+
+ /* Get CP0 - CP5 from table */
+ idx = nand_ecc_precalc_table[dat[j]];
+ reg1 ^= (idx & 0x3f);
+
+ /* All bit XOR = 1 ? */
+ if (idx & 0x40) {
+ reg3 ^= (uint8_t) j;
+ reg2 ^= ~((uint8_t) j);
+ }
+ }
+
+ /* Create non-inverted ECC code from line parity */
+ nand_trans_result(reg2, reg3, ecc_code);
+
+ /* Calculate final ECC code */
+ ecc_code[0] = ~ecc_code[0];
+ ecc_code[1] = ~ecc_code[1];
+ ecc_code[2] = ((~reg1) << 2) | 0x03;
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandecc.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandecc.h
new file mode 100644
index 0000000..bcf1982
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/nandecc.h
@@ -0,0 +1,29 @@
+#ifndef _NAND_ECC_H
+#define _NAND_ECC_H
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * NAND ecc functions
+ */
+
+#include <stdint.h>
+
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code);
+int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc,
+ const uint8_t *calc_ecc);
+
+#endif
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pddcustomize.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pddcustomize.c
new file mode 100644
index 0000000..1eb9b9a
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pddcustomize.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2008
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * PDD (platform description data) contains a set of system specific
+ * boot-parameters. Some of those parameters need to be handled
+ * special on updates, e.g. the MAC addresses. They must also be kept
+ * if the system is updated and one must be able to modify them when
+ * the system has booted the first time. This tool is intended to do
+ * PDD modification.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ * 1.5 Migrated to new libubi
+ * 1.6 Fixed broken volume update
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <mtd/ubi-media.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "libubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.6"
+
+#define DEFAULT_DEV_PATTERN "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d"
+
+typedef enum action_t {
+ ACT_NORMAL = 0,
+ ACT_LIST,
+ ACT_ARGP_ABORT,
+ ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do { \
+ args->action = ACT_ARGP_ABORT; \
+} while (0)
+
+#define ERR_ARGP do { \
+ args->action = ACT_ARGP_ERR; \
+} while (0)
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "pddcustomize - customize bootenv and pdd values.\n";
+
+static const char *optionsstr =
+" -b, --both Mirror updated PDD to redundand copy.\n"
+" -c, --copyright Print copyright information.\n"
+" -i, --input=<input> Binary input file. For debug purposes.\n"
+" -l, --list List card bootenv/pdd values.\n"
+" -o, --output=<output> Binary output file. For debug purposes.\n"
+" -s, --side=<seqnum> The side/seqnum to update.\n"
+" -x, --host use x86 platform for debugging.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pddcustomize [-bclx?V] [-i <input>] [-o <output>] [-s <seqnum>]\n"
+" [--both] [--copyright] [--input=<input>] [--list]\n"
+" [--output=<output>] [--side=<seqnum>] [--host] [--help] [--usage]\n"
+" [--version] [key=value] [...]\n";
+
+struct option long_options[] = {
+ { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' },
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' },
+ { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+typedef struct myargs {
+ action_t action;
+ const char* file_in;
+ const char* file_out;
+ int both;
+ int side;
+ int x86; /* X86 host, use files for testing */
+ bootenv_t env_in;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+
+ return i;
+}
+
+static int
+extract_pair(bootenv_t env, const char* str)
+{
+ int rc = 0;
+ char* key;
+ char* val;
+
+ key = strdup(str);
+ if (key == NULL)
+ return -ENOMEM;
+
+ val = strstr(key, "=");
+ if (val == NULL) {
+ err_msg("Wrong argument: %s\n"
+ "Expecting key=value pair.\n", str);
+ rc = -1;
+ goto err;
+ }
+
+ *val = '\0'; /* split strings */
+ val++;
+ rc = bootenv_set(env, key, val);
+
+err:
+ free(key);
+ return rc;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ int rc = 0;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "clbxs:i:o:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ err_msg("%s\n", copyright);
+ ABORT_ARGP;
+ break;
+ case 'l':
+ args->action = ACT_LIST;
+ break;
+ case 'b':
+ args->both = 1;
+ break;
+ case 'x':
+ args->x86 = 1;
+ break;
+ case 's':
+ args->side = get_update_side(optarg);
+ if (args->side < 0) {
+ err_msg("Unsupported seqnum: %d.\n"
+ "Supported seqnums are "
+ "'0' and '1'\n",
+ args->side, optarg);
+ ERR_ARGP;
+ }
+ break;
+ case 'i':
+ args->file_in = optarg;
+ break;
+ case 'o':
+ args->file_out = optarg;
+ break;
+ case '?': /* help */
+ err_msg("Usage: pddcustomize [OPTION...] "
+ "[key=value] [...]");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ rc = extract_pair(args->env_in, argv[optind++]);
+ if (rc != 0)
+ ERR_ARGP;
+ }
+
+ return 0;
+}
+
+static int
+list_bootenv(bootenv_t env)
+{
+ int rc = 0;
+ rc = bootenv_write_txt(stdout, env);
+ if (rc != 0) {
+ err_msg("Cannot list bootenv/pdd. rc: %d\n", rc);
+ goto err;
+ }
+err:
+ return rc;
+}
+
+static int
+process_key_value(bootenv_t env_in, bootenv_t env)
+{
+ int rc = 0;
+ size_t size, i;
+ const char* tmp;
+ const char** key_vec = NULL;
+
+ rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < size; i++) {
+ rc = bootenv_get(env_in, key_vec[i], &tmp);
+ if (rc != 0) {
+ err_msg("Cannot read value to input key: %s. rc: %d\n",
+ key_vec[i], rc);
+ goto err;
+ }
+ rc = bootenv_set(env, key_vec[i], tmp);
+ if (rc != 0) {
+ err_msg("Cannot set value key: %s. rc: %d\n",
+ key_vec[i], rc);
+ goto err;
+ }
+ }
+
+err:
+ if (key_vec != NULL)
+ free(key_vec);
+ return rc;
+}
+
+static int
+read_bootenv(const char* file, bootenv_t env)
+{
+ int rc = 0;
+ FILE* fp_in = NULL;
+
+ fp_in = fopen(file, "rb");
+ if (fp_in == NULL) {
+ err_msg("Cannot open file: %s\n", file);
+ return -EIO;
+ }
+
+ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ err_msg("Cannot read bootenv from file %s. rc: %d\n",
+ file, rc);
+ goto err;
+ }
+
+err:
+ fclose(fp_in);
+ return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+ libubi_t ulib;
+ int rc = 0;
+ char path[PATH_MAX];
+ FILE* fp_in = NULL;
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ err_msg("Cannot allocate ubi structure\n");
+ return -1;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fp_in = fopen(path, "r");
+ if (fp_in == NULL) {
+ err_msg("Cannot open volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ err_msg("Cannot read volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+err:
+ if (fp_in)
+ fclose(fp_in);
+ libubi_close(ulib);
+ return rc;
+}
+
+static int
+write_bootenv(const char* file, bootenv_t env)
+{
+ int rc = 0;
+ FILE* fp_out;
+
+ fp_out = fopen(file, "wb");
+ if (fp_out == NULL) {
+ err_msg("Cannot open file: %s\n", file);
+ return -EIO;
+ }
+
+ rc = bootenv_write(fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc);
+ goto err;
+ }
+
+err:
+ fclose(fp_out);
+ return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+ libubi_t ulib;
+ int rc = 0;
+ char path[PATH_MAX];
+ FILE* fp_out = NULL;
+ size_t nbytes;
+
+ rc = bootenv_size(env, &nbytes);
+ if (rc) {
+ err_msg("Cannot determine size of bootenv structure\n");
+ return rc;
+ }
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ err_msg("Cannot allocate ubi structure\n");
+ return rc;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fp_out = fopen(path, "r+");
+ if (fp_out == NULL) {
+ err_msg("Cannot fopen volume:%d number:%d\n", devno, id);
+ rc = -EBADF;
+ goto err;
+ }
+
+ rc = ubi_update_start(ulib, fileno(fp_out), nbytes);
+ if (rc != 0) {
+ err_msg("Cannot start update for %s\n", path);
+ goto err;
+ }
+
+ rc = bootenv_write(fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to volume %d number:%d\n",
+ devno, id);
+ goto err;
+ }
+err:
+ if( fp_out )
+ fclose(fp_out);
+ libubi_close(ulib);
+ return rc;
+}
+
+static int
+do_mirror(int volno)
+{
+ char errbuf[1024];
+ uint32_t ids[2];
+ int rc;
+ int src_volno_idx = 0;
+
+ ids[0] = EXAMPLE_BOOTENV_VOL_ID_1;
+ ids[1] = EXAMPLE_BOOTENV_VOL_ID_2;
+
+ if (volno == EXAMPLE_BOOTENV_VOL_ID_2)
+ src_volno_idx = 1;
+
+ rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf,
+ sizeof errbuf);
+ if( rc )
+ err_msg(errbuf);
+ return rc;
+}
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ bootenv_t env = NULL;
+ uint32_t boot_volno;
+ myargs args = {
+ .action = ACT_NORMAL,
+ .file_in = NULL,
+ .file_out = NULL,
+ .side = -1,
+ .x86 = 0,
+ .both = 0,
+ .env_in = NULL,
+
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ rc = bootenv_create(&env);
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle. rc: %d", rc);
+ goto err;
+ }
+
+ rc = bootenv_create(&(args.env_in));
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle. rc: %d", rc);
+ goto err;
+ }
+
+ parse_opt(argc, argv, &args);
+ if (args.action == ACT_ARGP_ERR) {
+ rc = -1;
+ goto err;
+ }
+ if (args.action == ACT_ARGP_ABORT) {
+ rc = 0;
+ goto out;
+ }
+
+ if ((args.side == 0) || (args.side == -1))
+ boot_volno = EXAMPLE_BOOTENV_VOL_ID_1;
+ else
+ boot_volno = EXAMPLE_BOOTENV_VOL_ID_2;
+
+ if( args.x86 )
+ rc = read_bootenv(args.file_in, env);
+ else
+ rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (args.action == ACT_LIST) {
+ rc = list_bootenv(env);
+ if (rc != 0) {
+ goto err;
+ }
+ goto out;
+ }
+
+ rc = process_key_value(args.env_in, env);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if( args.x86 )
+ rc = write_bootenv(args.file_in, env);
+ else
+ rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+ if (rc != 0)
+ goto err;
+
+ if( args.both ) /* No side specified, update both */
+ rc = do_mirror(boot_volno);
+
+ out:
+ err:
+ bootenv_destroy(&env);
+ bootenv_destroy(&(args.env_in));
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/peb.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/peb.c
new file mode 100644
index 0000000..08b770f
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/peb.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "peb.h"
+
+int
+peb_cmp(peb_t eb_1, peb_t eb_2)
+{
+ assert(eb_1);
+ assert(eb_2);
+
+ return eb_1->num == eb_2->num ? 0
+ : eb_1->num > eb_2->num ? 1 : -1;
+}
+
+int
+peb_new(uint32_t eb_num, uint32_t eb_size, peb_t *peb)
+{
+ int rc = 0;
+
+ peb_t res = (peb_t) malloc(sizeof(struct peb));
+ if (!res) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ res->num = eb_num;
+ res->size = eb_size;
+ res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t));
+ if (!res->data) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(res->data, 0xff, res->size);
+
+ *peb = res;
+ return 0;
+err:
+ if (res) {
+ if (res->data)
+ free(res->data);
+ free(res);
+ }
+ *peb = NULL;
+ return rc;
+}
+
+int
+peb_fill(peb_t peb, uint8_t* buf, size_t buf_size)
+{
+ if (!peb)
+ return -EINVAL;
+
+ if (buf_size > peb->size)
+ return -EINVAL;
+
+ memcpy(peb->data, buf, buf_size);
+ return 0;
+}
+
+int
+peb_write(FILE* fp_out, peb_t peb)
+{
+ size_t written = 0;
+
+ if (peb == NULL)
+ return -EINVAL;
+
+ written = fwrite(peb->data, 1, peb->size, fp_out);
+
+ if (written != peb->size)
+ return -EIO;
+
+ return 0;
+}
+
+int
+peb_free(peb_t* peb)
+{
+ peb_t tmp = *peb;
+ if (tmp) {
+ if (tmp->data)
+ free(tmp->data);
+ free(tmp);
+ }
+ *peb = NULL;
+
+ return 0;
+}
+
+void peb_dump(FILE* fp_out, peb_t peb)
+{
+ fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size);
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/peb.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/peb.h
new file mode 100644
index 0000000..246bce8
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/peb.h
@@ -0,0 +1,41 @@
+#ifndef __RAW_BLOCK_H__
+#define __RAW_BLOCK_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct peb *peb_t;
+struct peb {
+ uint32_t num; /* Physical eraseblock number
+ * in the RAW file. */
+ uint32_t size; /* Data Size (equals physical
+ * erase block size) */
+ uint8_t* data; /* Data buffer */
+};
+
+int peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb);
+int peb_free(peb_t* peb);
+int peb_cmp(peb_t peb_1, peb_t peb_2);
+int peb_write(FILE* fp_out, peb_t peb);
+void peb_dump(FILE* fp_out, peb_t peb);
+
+#endif /* __RAW_BLOCK_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi.c
new file mode 100644
index 0000000..fa835e2
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * @file pfi.c
+ *
+ * @author Oliver Lohmann
+ * Andreas Arnez
+ * Joern Engel
+ * Frank Haverkamp
+ *
+ * @brief libpfi holds all code to create and process pfi files.
+ *
+ * <oliloh@de.ibm.com> Wed Feb 8 11:38:22 CET 2006: Initial creation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "pfi.h"
+
+#define PFI_MAGIC "PFI!\n"
+#define PFI_DATA "DATA\n" /* The same size as PFI_MAGIC */
+#define PFI_MAGIC_LEN 5
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+enum key_id {
+ /* version 1 */
+ key_version, /* must be index position 0! */
+ key_mode,
+ key_size,
+ key_crc,
+ key_label,
+ key_flags,
+ key_ubi_ids,
+ key_ubi_size,
+ key_ubi_type,
+ key_ubi_names,
+ key_ubi_alignment,
+ key_raw_starts,
+ key_raw_total_size,
+ num_keys,
+};
+
+struct pfi_header {
+ char defined[num_keys]; /* reserve all possible keys even if
+ version does not require this. */
+ int mode_no; /* current mode no. -> can only increase */
+ union {
+ char *str;
+ uint32_t num;
+ } value[num_keys];
+};
+
+
+#define PFI_MANDATORY 0x0001
+#define PFI_STRING 0x0002
+#define PFI_LISTVALUE 0x0004 /* comma seperated list of nums */
+#define PFI_MANDATORY_UBI 0x0008
+#define PFI_MANDATORY_RAW 0x0010
+
+struct key_descriptor {
+ enum key_id id;
+ const char *name;
+ uint32_t flags;
+};
+
+static const struct key_descriptor key_desc_v1[] = {
+ { key_version, "version", PFI_MANDATORY },
+ { key_mode, "mode", PFI_MANDATORY | PFI_STRING },
+ { key_size, "size", PFI_MANDATORY },
+ { key_crc, "crc", PFI_MANDATORY },
+ { key_label, "label", PFI_MANDATORY | PFI_STRING },
+ { key_flags, "flags", PFI_MANDATORY },
+ { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI },
+ { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI },
+ { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING },
+ { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW },
+};
+
+static const struct key_descriptor *key_descriptors[] = {
+ NULL,
+ key_desc_v1, /* version 1 */
+};
+
+static const int key_descriptors_max[] = {
+ 0, /* version 0 */
+ sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static const char* modes[] = {"raw", "ubi"}; /* order isn't arbitrary! */
+
+/* latest version contains all possible keys */
+static const struct key_descriptor *key_desc = key_desc_v1;
+
+#define PFI_IS_UBI(mode) \
+ (((mode) != NULL) && (strcmp("ubi", (mode)) == 0))
+
+#define PFI_IS_RAW(mode) \
+ (((mode) != NULL) && (strcmp("raw", (mode)) == 0))
+
+/**
+ * @return <0 On Error.
+ * >=0 Mode no.
+ */
+static int
+get_mode_no(const char* mode)
+{
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(modes); i++)
+ if (strcmp(mode, modes[i]) == 0)
+ return i;
+ return -1;
+}
+
+static int
+find_key_by_name (const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_keys; i++) {
+ if (strcmp(name, key_desc[i].name) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int
+check_valid (pfi_header head)
+{
+ int i;
+ int max_keys;
+ uint32_t version;
+ const char *mode;
+ const struct key_descriptor *desc;
+ uint32_t to_check = PFI_MANDATORY;
+
+ /*
+ * For the validity check the list of possible keys depends on
+ * the version of the PFI file used.
+ */
+ version = head->value[key_version].num;
+ if (version > PFI_HDRVERSION)
+ return PFI_ENOHEADER;
+
+ max_keys = key_descriptors_max[version];
+ desc = key_descriptors[version];
+
+ if (!desc)
+ return PFI_ENOVERSION;
+
+ mode = head->value[key_mode].str;
+ if (PFI_IS_UBI(mode)) {
+ to_check |= PFI_MANDATORY_UBI;
+ }
+ else if (PFI_IS_RAW(mode)) {
+ to_check |= PFI_MANDATORY_RAW;
+ }
+ else { /* neither UBI nor RAW == ERR */
+ return PFI_EINSUFF;
+ }
+
+ for (i = 0; i < max_keys; i++) {
+ if ((desc[i].flags & to_check) && !head->defined[i]) {
+ fprintf(stderr, "libpfi: %s missing\n", desc[i].name);
+ return PFI_EINSUFF;
+ }
+ }
+
+ return 0;
+}
+
+int pfi_header_init (pfi_header *head)
+{
+ int i;
+ pfi_header self = (pfi_header) malloc(sizeof(*self));
+
+ *head = self;
+ if (self == NULL)
+ return PFI_ENOMEM;
+
+ /* initialize maximum number of possible keys */
+ for (i = 0; i < num_keys; i++) {
+ memset(self, 0, sizeof(*self));
+ self->defined[i] = 0;
+ }
+
+ return 0;
+}
+
+int pfi_header_destroy (pfi_header *head)
+{
+ int i;
+ pfi_header self = *head;
+
+ for (i = 0; i < num_keys; i++) {
+ if (self->defined[i] && (key_desc[i].flags & PFI_STRING) &&
+ self->value[i].str) {
+ free(self->value[i].str);
+ }
+ }
+ free(*head);
+ *head = NULL;
+ return 0;
+}
+
+int pfi_header_setnumber (pfi_header head,
+ const char *key, uint32_t value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING)
+ return PFI_EBADTYPE;
+
+ head->value[key_id].num = value;
+ head->defined[key_id] = 1;
+ return 0;
+}
+
+int pfi_header_setvalue (pfi_header head,
+ const char *key, const char *value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (value == NULL)
+ return PFI_EINSUFF;
+
+ if ((key_id < 0) || (key_id >= num_keys))
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING) {
+ /*
+ * The value is a string. Copy to a newly allocated
+ * buffer. Delete the old value, if already set.
+ */
+ size_t len = strlen(value) + 1;
+ char *old_str = NULL;
+ char *str;
+
+ old_str = head->value[key_id].str;
+ if (old_str != NULL)
+ free(old_str);
+
+ str = head->value[key_id].str = (char *) malloc(len);
+ if (str == NULL)
+ return PFI_ENOMEM;
+
+ strcpy(str, value);
+ } else {
+ int len;
+ int ret;
+ /* FIXME: here we assume that the value is always
+ given in hex and starts with '0x'. */
+ ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len);
+ if (ret < 1 || value[len] != '\0')
+ return PFI_EBADTYPE;
+ }
+ head->defined[key_id] = 1;
+ return 0;
+}
+
+int pfi_header_getnumber (pfi_header head,
+ const char *key, uint32_t *value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING)
+ return PFI_EBADTYPE;
+
+ if (!head->defined[key_id])
+ return PFI_EUNDEF;
+
+ *value = head->value[key_id].num;
+ return 0;
+}
+
+int pfi_header_getstring (pfi_header head,
+ const char *key, char *value, size_t size)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (!(key_desc[key_id].flags & PFI_STRING))
+ return PFI_EBADTYPE;
+
+ if (!head->defined[key_id])
+ return PFI_EUNDEF;
+
+ strncpy(value, head->value[key_id].str, size-1);
+ value[size-1] = '\0';
+ return 0;
+}
+
+int pfi_header_write (FILE *out, pfi_header head)
+{
+ int i;
+ int ret;
+
+ pfi_header_setnumber(head, "version", PFI_HDRVERSION);
+
+ if ((ret = check_valid(head)) != 0)
+ return ret;
+
+ /* OK. Now write the header. */
+
+ ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out);
+ if (ret < PFI_MAGIC_LEN)
+ return ret;
+
+
+ for (i = 0; i < num_keys; i++) {
+ if (!head->defined[i])
+ continue;
+
+ ret = fprintf(out, "%s=", key_desc[i].name);
+ if (ret < 0)
+ return PFI_EFILE;
+
+ if (key_desc[i].flags & PFI_STRING) {
+ ret = fprintf(out, "%s", head->value[i].str);
+ if (ret < 0)
+ return PFI_EFILE;
+ } else {
+ ret = fprintf(out, "0x%8x", head->value[i].num);
+ if (ret < 0)
+ return PFI_EFILE;
+
+ }
+ ret = fprintf(out, "\n");
+ if (ret < 0)
+ return PFI_EFILE;
+ }
+ ret = fprintf(out, "\n");
+ if (ret < 0)
+ return PFI_EFILE;
+
+ ret = fflush(out);
+ if (ret != 0)
+ return PFI_EFILE;
+
+ return 0;
+}
+
+int pfi_header_read (FILE *in, pfi_header head)
+{
+ char magic[PFI_MAGIC_LEN];
+ char mode[PFI_KEYWORD_LEN];
+ char buf[256];
+
+ if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in))
+ return PFI_EFILE;
+ if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0) {
+ if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) {
+ return PFI_DATA_START;
+ }
+ return PFI_ENOHEADER;
+ }
+
+ while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') {
+ char *value;
+ char *end;
+ value = strchr(buf, '=');
+ if (value == NULL)
+ return PFI_ENOHEADER;
+
+ *value = '\0';
+ value++;
+ end = strchr(value, '\n');
+ if (end)
+ *end = '\0';
+
+ if (pfi_header_setvalue(head, buf, value))
+ return PFI_ENOHEADER;
+ }
+
+ if (check_valid(head) != 0)
+ return PFI_ENOHEADER;
+
+ /* set current mode no. in head */
+ pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN);
+ if (head->mode_no > get_mode_no(mode)) {
+ return PFI_EMODE;
+ }
+ head->mode_no = get_mode_no(mode);
+ return 0;
+}
+
+int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__)))
+{
+ fprintf(out, "Sorry not implemented yet. Write mail to "
+ "Andreas Arnez and complain!\n");
+ return 0;
+}
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data)
+{
+ int rc;
+ pfi_header header;
+
+ rc = pfi_header_init (&header);
+ if (0 != rc)
+ return rc;
+ if (!func)
+ return PFI_EINVAL;
+
+ while ((0 == rc) && !feof(in)) {
+ /*
+ * Read header and check consistency of the fields.
+ */
+ rc = pfi_header_read( in, header );
+ if (0 != rc)
+ break;
+ if (func) {
+ rc = func(in, header, priv_data);
+ if (rc != 0)
+ break;
+ }
+ }
+
+ pfi_header_destroy(&header);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi.h
new file mode 100644
index 0000000..8c5cc07
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi.h
@@ -0,0 +1,244 @@
+#ifndef __pfi_h
+#define __pfi_h
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ * Andreas Arnez <arnez@de.ibm.com>
+ * Joern Engel <engeljoe@de.ibm.com>
+ * Frank Haverkamp <haverkam@de.ibm.com>
+ *
+ * @brief libpfi will hold all code to create and process pfi
+ * images. Definitions made in this file are equaly usable for the
+ * development host and the target system.
+ *
+ * @note This header additionally holds the official definitions for
+ * the pfi headers.
+ */
+
+#include <stdio.h> /* FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Definitions. */
+
+#define PFI_HDRVERSION 1 /* current header version */
+
+#define PFI_ENOVERSION 1 /* unknown version */
+#define PFI_ENOHEADER 2 /* not a pfi header */
+#define PFI_EINSUFF 3 /* insufficient information */
+#define PFI_EUNDEF 4 /* key not defined */
+#define PFI_ENOMEM 5 /* out of memory */
+#define PFI_EBADTYPE 6 /* bad data type */
+#define PFI_EFILE 7 /* file I/O error: see errno */
+#define PFI_EFILEINVAL 8 /* file format not valid */
+#define PFI_EINVAL 9 /* invalid parameter */
+#define PFI_ERANGE 10 /* invalid range */
+#define PFI_EMODE 11 /* expecting other mode in this header */
+#define PFI_DATA_START 12 /* data section starts */
+#define PFI_EMAX 13 /* should be always larger as the largest
+ error code */
+
+#define PFI_LABEL_LEN 64 /* This is the maximum length for a
+ PFI header label */
+#define PFI_KEYWORD_LEN 32 /* This is the maximum length for an
+ entry in the mode and type fields */
+
+#define PFI_UBI_MAX_VOLUMES 128
+#define PFI_UBI_VOL_NAME_LEN 127
+
+/**
+ * @brief The pfi header allows to set flags which influence the flashing
+ * behaviour.
+ */
+#define PFI_FLAG_PROTECTED 0x00000001
+
+
+/**
+ * @brief Handle to pfi header. Used in most of the functions associated
+ * with pfi file handling.
+ */
+typedef struct pfi_header *pfi_header;
+
+
+/**
+ * @brief Initialize a pfi header object.
+ *
+ * @param head Pointer to handle. This function allocates memory
+ * for this data structure.
+ * @return 0 on success, otherwise:
+ * PFI_ENOMEM : no memory available for the handle.
+ */
+int pfi_header_init (pfi_header *head);
+
+
+/**
+ * @brief Destroy a pfi header object.
+ *
+ * @param head handle. head is invalid after calling this function.
+ * @return 0 always.
+ */
+int pfi_header_destroy (pfi_header *head);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value string. Must be 0 terminated.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_ENOMEM : no memory available for the handle.
+ * PFI_EBADTYPE : value is not an hex string. This happens
+ * when the key stores an integer and the
+ * new value is not convertable e.g. not in
+ * 0xXXXXXXXX format.
+ */
+int pfi_header_setvalue (pfi_header head,
+ const char *key, const char *value);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object. Provide the
+ * value as a number.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value value to set.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : value is not a string. This happens
+ * when the key stores a string.
+ */
+int pfi_header_setnumber (pfi_header head,
+ const char *key, uint32_t value);
+
+
+/**
+ * @brief For a given key, return the numerical value stored in a
+ * pfi header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : stored value is not an integer but a string.
+ */
+int pfi_header_getnumber (pfi_header head,
+ const char *key, uint32_t *value);
+
+
+static inline uint32_t
+pfi_getnumber(pfi_header head, const char *key)
+{
+ uint32_t value;
+ pfi_header_getnumber(head, key, &value);
+ return value;
+}
+
+/**
+ * @brief For a given key, return the string value stored in a pfi
+ * header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value string. Memory must be allocated by the user.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : stored value is not a string but an integer.
+ */
+int pfi_header_getstring (pfi_header head,
+ const char *key, char *value, size_t size);
+
+
+/**
+ * @brief Write a pfi header object into a given file.
+ *
+ * @param out output stream.
+ * @param head handle.
+ * @return 0 on success, error values otherwise:
+ * PFI_EINSUFF : not all mandatory fields are filled.
+ * PFI_ENOHEADER : wrong header version or magic number.
+ * -E* : see <asm/errno.h>.
+ */
+int pfi_header_write (FILE *out, pfi_header head);
+
+
+/**
+ * @brief Read a pfi header object from a given file.
+ *
+ * @param in input stream.
+ * @param head handle.
+ * @return 0 on success, error values otherwise:
+ * PFI_ENOVERSION: unknown header version.
+ * PFI_EFILE : cannot read enough data.
+ * PFI_ENOHEADER : wrong header version or magic number.
+ * -E* : see <asm/errno.h>.
+ *
+ * If the header verification returned success the user can assume that
+ * all mandatory fields for a particular version are accessible. Checking
+ * the return code when calling the get-function for those keys is not
+ * required in those cases. For optional fields the checking must still be
+ * done.
+ */
+int pfi_header_read (FILE *in, pfi_header head);
+
+
+/**
+ * @brief Display a pfi header in human-readable form.
+ *
+ * @param out output stream.
+ * @param head handle.
+ * @return always 0.
+ *
+ * @note Prints out that it is not implemented and whom you should
+ * contact if you need it urgently!.
+ */
+int pfi_header_dump (FILE *out, pfi_header head);
+
+
+/*
+ * @brief Iterates over a stream of pfi files. The iterator function
+ * must advance the file pointer in FILE *in to the next pfi
+ * header. Function exists on feof(in).
+ *
+ * @param in input file descriptor, must be open and valid.
+ * @param func iterator function called when pfi header could be
+ * read and was validated. The function must return 0 on
+ * success.
+ * @return See pfi_header_init and pfi_header_read.
+ * PFI_EINVAL : func is not valid
+ * 0 ok.
+ */
+typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data);
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __pfi_h */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi2bin.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi2bin.c
new file mode 100644
index 0000000..34ecc92
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfi2bin.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Convert a PFI file (partial flash image) into a plain binary file.
+ * This tool can be used to prepare the data to be burned into flash
+ * chips in a manufacturing step where the flashes are written before
+ * being soldered onto the hardware. For NAND images another step is
+ * required to add the right OOB data to the binary image.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <ubigen.h>
+#include <mtd/ubi-media.h>
+#include <mtd_swab.h>
+
+#include "config.h"
+#include "list.h"
+#include "error.h"
+#include "reader.h"
+#include "peb.h"
+#include "crc32.h"
+
+#define PROGRAM_VERSION "1.4"
+
+#define MAX_FNAME 255
+#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */
+#define ERR_BUF_SIZE 1024
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+static char err_buf[ERR_BUF_SIZE];
+
+/*
+ * Data used to buffer raw blocks which have to be
+ * located at a specific point inside the generated RAW file
+ */
+
+typedef enum action_t {
+ ACT_NOTHING = 0x00000000,
+ ACT_RAW = 0x00000001,
+} action_t;
+
+static const char copyright [] __attribute__((unused)) =
+ "(c) Copyright IBM Corp 2006\n";
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "pfi2bin - a tool to convert PFI files into binary images.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+" -c, --copyright\n"
+" -v, --verbose Print more information.\n"
+"\n"
+" Input:\n"
+" -j, --platform=pdd-file PDD information which contains the card settings.\n"
+"\n"
+" Output:\n"
+" -o, --output=filename Outputfile, default: stdout.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n"
+" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n"
+" [--usage] [--version] pfifile\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct io {
+ FILE* fp_pdd; /* a FilePointer to the PDD data */
+ FILE* fp_pfi; /* a FilePointer to the PFI input stream */
+ FILE* fp_out; /* a FilePointer to the output stream */
+} *io_t;
+
+typedef struct myargs {
+ /* common settings */
+ action_t action;
+ int verbose;
+ const char *f_in_pfi;
+ const char *f_in_pdd;
+ const char *f_out;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ /* common settings */
+ case 'v': /* --verbose=<level> */
+ args->verbose = 1;
+ break;
+
+ case 'c': /* --copyright */
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+
+ case 'j': /* --platform */
+ args->f_in_pdd = optarg;
+ break;
+
+ case 'o': /* --output */
+ args->f_out = optarg;
+ break;
+
+ case '?': /* help */
+ printf("pfi2bin [OPTION...] pfifile\n");
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->f_in_pfi = argv[optind++];
+
+ return 0;
+}
+
+
+static size_t
+byte_to_blk(size_t byte, size_t blk_size)
+{
+ return (byte % blk_size) == 0
+ ? byte / blk_size
+ : byte / blk_size + 1;
+}
+
+
+
+
+/**
+ * @precondition IO: File stream points to first byte of RAW data.
+ * @postcondition IO: File stream points to first byte of next
+ * or EOF.
+ */
+static int
+memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs,
+ io_t io)
+{
+ int rc = 0;
+ uint32_t i;
+
+ size_t read, to_read, eb_num;
+ size_t bytes_left;
+ list_t pebs = *raw_pebs;
+ peb_t peb = NULL;
+
+ long old_file_pos = ftell(io->fp_pfi);
+ for (i = 0; i < pfi_raw->starts_size; i++) {
+ bytes_left = pfi_raw->data_size;
+ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+
+ eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size);
+ while (bytes_left) {
+ to_read = MIN(bytes_left, pdd->eb_size);
+ rc = peb_new(eb_num++, pdd->eb_size, &peb);
+ if (rc != 0)
+ goto err;
+ read = fread(peb->data, 1, to_read, io->fp_pfi);
+ if (read != to_read) {
+ rc = -EIO;
+ goto err;
+ }
+ pebs = append_elem(peb, pebs);
+ bytes_left -= read;
+ }
+
+ }
+ *raw_pebs = pebs;
+ return 0;
+err:
+ pebs = remove_all((free_func_t)&peb_free, pebs);
+ return rc;
+}
+
+static int
+convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs,
+ struct ubi_vtbl_record *vol_tab,
+ size_t *ebs_written, io_t io)
+{
+ int rc = 0;
+ uint32_t i, j;
+ peb_t raw_peb;
+ peb_t cmp_peb;
+ ubi_info_t u;
+ size_t leb_total = 0;
+ uint8_t vol_type;
+
+ switch (ubi->type) {
+ case pfi_ubi_static:
+ vol_type = UBI_VID_STATIC; break;
+ case pfi_ubi_dynamic:
+ vol_type = UBI_VID_DYNAMIC; break;
+ default:
+ vol_type = UBI_VID_DYNAMIC;
+ }
+
+ rc = peb_new(0, 0, &cmp_peb);
+ if (rc != 0)
+ goto err;
+
+ long old_file_pos = ftell(io->fp_pfi);
+ for (i = 0; i < ubi->ids_size; i++) {
+ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+ rc = ubigen_create(&u, ubi->ids[i], vol_type,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ ubi->alignment, UBI_VERSION,
+ pdd->vid_hdr_offset, 0, ubi->data_size,
+ io->fp_pfi, io->fp_out);
+ if (rc != 0)
+ goto err;
+
+ rc = ubigen_get_leb_total(u, &leb_total);
+ if (rc != 0)
+ goto err;
+
+ j = 0;
+ while(j < leb_total) {
+ cmp_peb->num = *ebs_written;
+ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+ raw_pebs);
+ if (raw_peb) {
+ rc = peb_write(io->fp_out, raw_peb);
+ }
+ else {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ j++;
+ }
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+ /* memorize volume table entry */
+ rc = ubigen_set_lvol_rec(u, ubi->size,
+ ubi->names[i],
+ (void*) &vol_tab[ubi->ids[i]]);
+ if (rc != 0)
+ goto err;
+ ubigen_destroy(&u);
+ }
+
+ peb_free(&cmp_peb);
+ return 0;
+
+err:
+ peb_free(&cmp_peb);
+ ubigen_destroy(&u);
+ return rc;
+}
+
+
+static FILE*
+my_fmemopen (void *buf, size_t size, const char *opentype)
+{
+ FILE* f;
+ size_t ret;
+
+ assert(strcmp(opentype, "r") == 0);
+
+ f = tmpfile();
+ ret = fwrite(buf, 1, size, f);
+ rewind(f);
+
+ return f;
+}
+
+/**
+ * @brief Builds a UBI volume table from a volume entry list.
+ * @return 0 On success.
+ * else Error.
+ */
+static int
+write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs,
+ struct ubi_vtbl_record *vol_tab, size_t vol_tab_size,
+ size_t *ebs_written, io_t io)
+{
+ int rc = 0;
+ ubi_info_t u;
+ peb_t raw_peb;
+ peb_t cmp_peb;
+ size_t leb_size, leb_total, j = 0;
+ uint8_t *ptr = NULL;
+ FILE* fp_leb = NULL;
+ int vt_slots;
+ size_t vol_tab_size_limit;
+
+ rc = peb_new(0, 0, &cmp_peb);
+ if (rc != 0)
+ goto err;
+
+ /* @FIXME: Artem creates one volume with 2 LEBs.
+ * IMO 2 volumes would be more convenient. In order
+ * to get 2 reserved LEBs from ubigen, I have to
+ * introduce this stupid mechanism. Until no final
+ * decision of the VTAB structure is made... Good enough.
+ */
+ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ 1, UBI_VERSION,
+ pdd->vid_hdr_offset, UBI_COMPAT_REJECT,
+ vol_tab_size, stdin, io->fp_out);
+ /* @FIXME stdin for fp_in is a hack */
+ if (rc != 0)
+ goto err;
+ rc = ubigen_get_leb_size(u, &leb_size);
+ if (rc != 0)
+ goto err;
+ ubigen_destroy(&u);
+
+ /*
+ * The number of supported volumes is restricted by the eraseblock size
+ * and by the UBI_MAX_VOLUMES constant.
+ */
+ vt_slots = leb_size / UBI_VTBL_RECORD_SIZE;
+ if (vt_slots > UBI_MAX_VOLUMES)
+ vt_slots = UBI_MAX_VOLUMES;
+ vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE;
+
+ ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t));
+ if (ptr == NULL)
+ goto err;
+
+ memset(ptr, 0xff, leb_size);
+ memcpy(ptr, vol_tab, vol_tab_size_limit);
+ fp_leb = my_fmemopen(ptr, leb_size, "r");
+
+ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ 1, UBI_VERSION, pdd->vid_hdr_offset,
+ UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS,
+ fp_leb, io->fp_out);
+ if (rc != 0)
+ goto err;
+ rc = ubigen_get_leb_total(u, &leb_total);
+ if (rc != 0)
+ goto err;
+
+ long old_file_pos = ftell(fp_leb);
+ while(j < leb_total) {
+ rc = fseek(fp_leb, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+
+ cmp_peb->num = *ebs_written;
+ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+ raw_pebs);
+ if (raw_peb) {
+ rc = peb_write(io->fp_out, raw_peb);
+ }
+ else {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ j++;
+ }
+
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+
+err:
+ free(ptr);
+ peb_free(&cmp_peb);
+ ubigen_destroy(&u);
+ fclose(fp_leb);
+ return rc;
+}
+
+static int
+write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written,
+ FILE* fp_out)
+{
+ int rc = 0;
+ uint32_t j, delta;
+ list_t ptr;
+ peb_t empty_eb, peb;
+
+ /* create an empty 0xff EB (for padding) */
+ rc = peb_new(0, pdd->eb_size, &empty_eb);
+
+ foreach(peb, ptr, raw_blocks) {
+ if (peb->num < *ebs_written) {
+ continue; /* omit blocks which
+ are already passed */
+ }
+
+ if (peb->num < *ebs_written) {
+ err_msg("eb_num: %d\n", peb->num);
+ err_msg("Bug: This should never happen. %d %s",
+ __LINE__, __FILE__);
+ goto err;
+ }
+
+ delta = peb->num - *ebs_written;
+ if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) {
+ err_msg("RAW block outside of flash_size.");
+ goto err;
+ }
+ for (j = 0; j < delta; j++) {
+ rc = peb_write(fp_out, empty_eb);
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+ rc = peb_write(fp_out, peb);
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+
+err:
+ peb_free(&empty_eb);
+ return rc;
+}
+
+static int
+init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size)
+{
+ uint32_t crc;
+ size_t i;
+ struct ubi_vtbl_record* res = NULL;
+
+ *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
+
+ res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size);
+ if (vol_tab == NULL) {
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < UBI_MAX_VOLUMES; i++) {
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ &(res[i]), UBI_VTBL_RECORD_SIZE_CRC);
+ res[i].crc = cpu_to_be32(crc);
+ }
+
+ *vol_tab = res;
+ return 0;
+}
+
+static int
+create_raw(io_t io)
+{
+ int rc = 0;
+ size_t ebs_written = 0; /* eraseblocks written already... */
+ size_t vol_tab_size;
+ list_t ptr;
+
+ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+ list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */
+
+ struct ubi_vtbl_record *vol_tab = NULL;
+ pdd_data_t pdd = NULL;
+
+ rc = init_vol_tab (&vol_tab, &vol_tab_size);
+ if (rc != 0) {
+ err_msg("Cannot initialize volume table.");
+ goto err;
+ }
+
+ rc = read_pdd_data(io->fp_pdd, &pdd,
+ err_buf, ERR_BUF_SIZE);
+ if (rc != 0) {
+ err_msg("Cannot read necessary pdd_data: %s rc: %d",
+ err_buf, rc);
+ goto err;
+ }
+
+ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi,
+ err_buf, ERR_BUF_SIZE);
+ if (rc != 0) {
+ err_msg("Cannot read pfi header: %s rc: %d",
+ err_buf, rc);
+ goto err;
+ }
+
+ pfi_raw_t pfi_raw;
+ foreach(pfi_raw, ptr, pfi_raws) {
+ rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs,
+ io);
+ if (rc != 0) {
+ err_msg("Cannot create raw_block in mem. rc: %d\n",
+ rc);
+ goto err;
+ }
+ }
+
+ pfi_ubi_t pfi_ubi;
+ foreach(pfi_ubi, ptr, pfi_ubis) {
+ rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs,
+ vol_tab, &ebs_written, io);
+ if (rc != 0) {
+ err_msg("Cannot convert UBI volume. rc: %d\n", rc);
+ goto err;
+ }
+ }
+
+ rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size,
+ &ebs_written, io);
+ if (rc != 0) {
+ err_msg("Cannot write UBI volume table. rc: %d\n", rc);
+ goto err;
+ }
+
+ rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out);
+ if (rc != 0)
+ goto err;
+
+ if (io->fp_out != stdout)
+ info_msg("Physical eraseblocks written: %8d\n", ebs_written);
+err:
+ free(vol_tab);
+ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+ raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs);
+ free_pdd_data(&pdd);
+ return rc;
+}
+
+
+/* ------------------------------------------------------------------------- */
+static void
+open_io_handle(myargs *args, io_t io)
+{
+ /* set PDD input */
+ io->fp_pdd = fopen(args->f_in_pdd, "r");
+ if (io->fp_pdd == NULL) {
+ err_sys("Cannot open: %s", args->f_in_pdd);
+ }
+
+ /* set PFI input */
+ io->fp_pfi = fopen(args->f_in_pfi, "r");
+ if (io->fp_pfi == NULL) {
+ err_sys("Cannot open PFI input file: %s", args->f_in_pfi);
+ }
+
+ /* set output prefix */
+ if (strcmp(args->f_out,"") == 0)
+ io->fp_out = stdout;
+ else {
+ io->fp_out = fopen(args->f_out, "wb");
+ if (io->fp_out == NULL) {
+ err_sys("Cannot open output file: %s", args->f_out);
+ }
+ }
+}
+
+static void
+close_io_handle(io_t io)
+{
+ if (fclose(io->fp_pdd) != 0) {
+ err_sys("Cannot close PDD file.");
+ }
+ if (fclose(io->fp_pfi) != 0) {
+ err_sys("Cannot close PFI file.");
+ }
+ if (io->fp_out != stdout) {
+ if (fclose(io->fp_out) != 0) {
+ err_sys("Cannot close output file.");
+ }
+ }
+
+ io->fp_pdd = NULL;
+ io->fp_pfi = NULL;
+ io->fp_out = NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int rc = 0;
+
+ ubigen_init();
+ init_crc32_table(crc32_table);
+
+ struct io io = {NULL, NULL, NULL};
+ myargs args = {
+ .action = ACT_RAW,
+ .verbose = 0,
+
+ .f_in_pfi = "",
+ .f_in_pdd = "",
+ .f_out = "",
+
+ /* arguments */
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ /* parse arguments */
+ parse_opt(argc, argv, &args);
+
+ if (strcmp(args.f_in_pfi, "") == 0) {
+ err_quit("No PFI input file specified!");
+ }
+
+ if (strcmp(args.f_in_pdd, "") == 0) {
+ err_quit("No PDD input file specified!");
+ }
+
+ open_io_handle(&args, &io);
+
+ info_msg("[ Creating RAW...");
+ rc = create_raw(&io);
+ if (rc != 0) {
+ err_msg("Creating RAW failed.");
+ goto err;
+ }
+
+err:
+ close_io_handle(&io);
+ if (rc != 0) {
+ remove(args.f_out);
+ }
+
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash.c
new file mode 100644
index 0000000..754fe33
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ * Frank Haverkamp
+ *
+ * Process a PFI (partial flash image) and write the data to the
+ * specified UBI volumes. This tool is intended to be used for system
+ * update using PFI files.
+ *
+ * 1.1 fixed output to stderr and stdout in logfile mode.
+ * 1.2 updated.
+ * 1.3 removed argp parsing to be able to use uClib.
+ * 1.4 Minor cleanups.
+ * 1.5 Forgot to delete raw block before updating it.
+ * 1.6 Migrated to new libubi.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pfiflash.h>
+#undef DEBUG
+#include "error.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.6"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "pfiflash - a tool for updating a controller with PFI files.\n";
+
+static const char *optionsstr =
+" Standard options:\n"
+" -c, --copyright Print copyright information.\n"
+" -l, --logfile=<file> Write a logfile to <file>.\n"
+" -v, --verbose Be verbose during program execution.\n"
+"\n"
+" Process options:\n"
+" -C, --complete Execute a complete system update. Updates both\n"
+" sides.\n"
+" -p, --pdd-update=<type> Specify the pdd-update algorithm. <type> is either\n"
+" 'keep', 'merge' or 'overwrite'.\n"
+" -r, --raw-flash=<dev> Flash the raw data. Use the specified mtd device.\n"
+" -s, --side=<seqnum> Select the side which shall be updated.\n"
+" -x, --compare Only compare on-flash and pfi data, print info if\n"
+" an update is neccessary and return appropriate\n"
+" error code.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pfiflash [-cvC?V] [-l <file>] [-p <type>] [-r <dev>] [-s <seqnum>]\n"
+" [--copyright] [--logfile=<file>] [--verbose] [--complete]\n"
+" [--pdd-update=<type>] [--raw-flash=<dev>] [--side=<seqnum>]\n"
+" [--compare] [--help] [--usage] [--version] [pfifile]\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' },
+ { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ int verbose;
+ const char *logfile;
+ const char *raw_dev;
+
+ pdd_handling_t pdd_handling;
+ int seqnum;
+ int compare;
+ int complete;
+
+ FILE* fp_in;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static pdd_handling_t
+get_pdd_handling(const char* str)
+{
+ if (strcmp(str, "keep") == 0) {
+ return PDD_KEEP;
+ }
+ if (strcmp(str, "merge") == 0) {
+ return PDD_MERGE;
+ }
+ if (strcmp(str, "overwrite") == 0) {
+ return PDD_OVERWRITE;
+ }
+
+ return -1;
+}
+
+static int
+get_update_seqnum(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+
+ return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cl:vCp:r:s:x?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ /* standard options */
+ case 'c':
+ err_msg("%s\n", copyright);
+ exit(0);
+ break;
+ case 'v':
+ args->verbose = 1;
+ break;
+ case 'l':
+ args->logfile = optarg;
+ break;
+ /* process options */
+ case 'C':
+ args->complete = 1;
+ break;
+ case 'p':
+ args->pdd_handling = get_pdd_handling(optarg);
+ if ((int)args->pdd_handling < 0) {
+ err_quit("Unknown PDD handling: %s.\n"
+ "Please use either "
+ "'keep', 'merge' or"
+ "'overwrite'.\n'");
+ }
+ break;
+ case 's':
+ args->seqnum = get_update_seqnum(optarg);
+ if (args->seqnum < 0) {
+ err_quit("Unsupported side: %s.\n"
+ "Supported sides are '0' "
+ "and '1'\n", optarg);
+ }
+ break;
+ case 'x':
+ args->compare = 1;
+ break;
+ case 'r':
+ args->raw_dev = optarg;
+ break;
+ case '?': /* help */
+ err_msg("Usage: pfiflash [OPTION...] [pfifile]");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "r");
+ if ((args->fp_in) == NULL) {
+ err_sys("Cannot open PFI file %s for input",
+ argv[optind]);
+ }
+ }
+
+ return 0;
+}
+
+int main (int argc, char** argv)
+{
+ int rc = 0;
+ char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE];
+ memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE);
+
+ myargs args = {
+ .verbose = 0,
+ .seqnum = -1,
+ .compare = 0,
+ .complete = 0,
+ .logfile = NULL, /* "/tmp/pfiflash.log", */
+ .pdd_handling = PDD_KEEP,
+ .fp_in = stdin,
+ .raw_dev = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+ error_initlog(args.logfile);
+
+ if (!args.fp_in) {
+ rc = -1;
+ snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE,
+ "No PFI input file specified!\n");
+ goto err;
+ }
+
+ rc = pfiflash_with_options(args.fp_in, args.complete, args.seqnum,
+ args.compare, args.pdd_handling, args.raw_dev, err_buf,
+ PFIFLASH_MAX_ERR_BUF_SIZE);
+ if (rc < 0) {
+ goto err_fp;
+ }
+
+ err_fp:
+ if (args.fp_in != stdin)
+ fclose(args.fp_in);
+ err:
+ if (rc != 0)
+ err_msg("pfiflash: %s\nrc: %d\n", err_buf, rc);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash.h
new file mode 100644
index 0000000..039705d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash.h
@@ -0,0 +1,76 @@
+#ifndef __PFIFLASH_H__
+#define __PFIFLASH_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ *
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ *
+ * @brief The pfiflash library offers an interface for using the
+ * pfiflash * utility.
+ */
+
+#include <stdio.h> /* FILE */
+
+#define PFIFLASH_MAX_ERR_BUF_SIZE 1024
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum pdd_handling_t
+{
+ PDD_KEEP = 0,
+ PDD_MERGE,
+ PDD_OVERWRITE,
+ PDD_HANDLING_NUM, /* always the last item */
+} pdd_handling_t; /**< Possible PDD handle algorithms. */
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete [0|1] Do a complete system update.
+ * @param seqnum Index in a redundant group.
+ * @param compare [0|1] Compare contents.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param rawdev Device to use for raw flashing
+ * @param err_buf An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
+ pdd_handling_t pdd_handling, const char* rawdev,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete [0|1] Do a complete system update.
+ * @param seqnum Index in a redundant group.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param err_buf An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+ char *err_buf, size_t err_buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PFIFLASH_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash_error.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash_error.h
new file mode 100644
index 0000000..0f27f4a
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/pfiflash_error.h
@@ -0,0 +1,75 @@
+#ifndef __PFIFLASH_ERROR_H__
+#define __PFIFLASH_ERROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Author: Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <arnez@de.ibm.com>
+ */
+
+enum pfiflash_err {
+ PFIFLASH_ERR_EOF = 1,
+ PFIFLASH_ERR_FIO,
+ PFIFLASH_ERR_UBI_OPEN,
+ PFIFLASH_ERR_UBI_CLOSE,
+ PFIFLASH_ERR_UBI_MKVOL,
+ PFIFLASH_ERR_UBI_RMVOL,
+ PFIFLASH_ERR_UBI_VOL_UPDATE,
+ PFIFLASH_ERR_UBI_VOL_FOPEN,
+ PFIFLASH_ERR_UBI_UNKNOWN,
+ PFIFLASH_ERR_UBI_VID_OOB,
+ PFIFLASH_ERR_BOOTENV_CREATE,
+ PFIFLASH_ERR_BOOTENV_READ,
+ PFIFLASH_ERR_BOOTENV_SIZE,
+ PFIFLASH_ERR_BOOTENV_WRITE,
+ PFIFLASH_ERR_PDD_UNKNOWN,
+ PFIFLASH_ERR_MTD_OPEN,
+ PFIFLASH_ERR_MTD_CLOSE,
+ PFIFLASH_ERR_CRC_CHECK,
+ PFIFLASH_ERR_MTD_ERASE,
+ PFIFLASH_ERR_COMPARE,
+ PFIFLASH_CMP_DIFF
+};
+
+const char *const PFIFLASH_ERRSTR[] = {
+ "",
+ "unexpected EOF",
+ "file I/O error",
+ "couldn't open UBI",
+ "couldn't close UBI",
+ "couldn't make UBI volume %d",
+ "couldn't remove UBI volume %d",
+ "couldn't update UBI volume %d",
+ "couldn't open UBI volume %d",
+ "unknown UBI operation",
+ "PFI data contains out of bounds UBI id %d",
+ "couldn't create bootenv%s",
+ "couldn't read bootenv",
+ "couldn't resize bootenv",
+ "couldn't write bootenv on ubi%d_%d",
+ "unknown PDD handling algorithm",
+ "couldn't open MTD device %s",
+ "couldn't close MTD device %s",
+ "CRC check failed: given=0x%08x, calculated=0x%08x",
+ "couldn't erase raw mtd region",
+ "couldn't compare volumes",
+ "on-flash data differ from pfi data, update is neccessary"
+};
+
+#endif /* __PFIFLASH_ERROR_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/reader.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/reader.c
new file mode 100644
index 0000000..0ea8c6d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/reader.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Read in PFI (partial flash image) data and store it into internal
+ * data structures for further processing. Take also care about
+ * special handling if the data contains PDD (platform description
+ * data/boot-parameters).
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "bootenv.h"
+#include "reader.h"
+
+#define __unused __attribute__((unused))
+
+/* @FIXME hard coded offsets right now - get them from Artem? */
+#define NAND2048_DEFAULT_VID_HDR_OFF 1984
+#define NAND512_DEFAULT_VID_HDR_OFF 448
+#define NOR_DEFAULT_VID_HDR_OFF 64
+
+#define EBUF_PFI(fmt...) \
+ do { int i = snprintf(err_buf, err_buf_size, "%s\n", label); \
+ snprintf(err_buf + i, err_buf_size - i, fmt); \
+ } while (0)
+
+#define EBUF(fmt...) \
+ do { snprintf(err_buf, err_buf_size, fmt); } while (0)
+
+
+int
+read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ bootenv_t pdd = NULL;
+ pdd_data_t res = NULL;
+ const char* value;
+
+ res = (pdd_data_t) malloc(sizeof(struct pdd_data));
+ if (!res) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ rc = bootenv_create(&pdd);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_read_txt(fp_pdd, pdd);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_get(pdd, "flash_type", &value);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (strcmp(value, "NAND") == 0) {
+
+ rc = bootenv_get_num(pdd, "flash_page_size",
+ &(res->flash_page_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_page_size' from pdd.");
+ goto err;
+ }
+ res->flash_type = NAND_FLASH;
+
+ switch (res->flash_page_size) {
+ case 512:
+ res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF;
+ break;
+ case 2048:
+ res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF;
+ break;
+ default:
+ EBUF("Unsupported 'flash_page_size' %d.",
+ res->flash_page_size);
+ goto err;
+ }
+ }
+ else if (strcmp(value, "NOR") == 0){
+ res->flash_type = NOR_FLASH;
+ res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF;
+ }
+ else {
+ snprintf(err_buf, err_buf_size,
+ "Unkown flash type: %s", value);
+ goto err;
+ }
+
+ rc = bootenv_get_num(pdd, "flash_eraseblock_size",
+ &(res->eb_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_eraseblock_size' from pdd.");
+ goto err;
+ }
+
+ rc = bootenv_get_num(pdd, "flash_size",
+ &(res->flash_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_size' from pdd.");
+ goto err;
+ }
+
+ goto out;
+ err:
+ if (res) {
+ free(res);
+ res = NULL;
+ }
+ out:
+ bootenv_destroy(&pdd);
+ *pdd_data = res;
+ return rc;
+}
+
+int
+read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw,
+ const char* label, char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ char tmp_str[PFI_KEYWORD_LEN];
+ bootenv_list_t raw_start_list = NULL;
+ pfi_raw_t res;
+ size_t size;
+
+ res = (pfi_raw_t) malloc(sizeof(struct pfi_raw));
+ if (!res)
+ return -ENOMEM;
+
+ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'crc' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "raw_starts",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'raw_starts' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_create(&raw_start_list);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = bootenv_list_import(raw_start_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+
+ rc = bootenv_list_to_num_vector(raw_start_list,
+ &size, &(res->starts));
+ res->starts_size = size;
+
+ if (rc != 0) {
+ EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+ goto err;
+ }
+
+ goto out;
+
+ err:
+ if (res) {
+ free(res);
+ res = NULL;
+ }
+ out:
+ bootenv_list_destroy(&raw_start_list);
+ *pfi_raw = res;
+ return rc;
+}
+
+int
+read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi,
+ const char *label, char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ const char** tmp_names = NULL;
+ char tmp_str[PFI_KEYWORD_LEN];
+ bootenv_list_t ubi_id_list = NULL;
+ bootenv_list_t ubi_name_list = NULL;
+ pfi_ubi_t res;
+ uint32_t i;
+ size_t size;
+
+ res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi));
+ if (!res)
+ return -ENOMEM;
+
+ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'crc' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_ids' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_create(&ubi_id_list);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_list_create(&ubi_name_list);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = bootenv_list_import(ubi_id_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+
+ rc = bootenv_list_to_num_vector(ubi_id_list, &size,
+ &(res->ids));
+ res->ids_size = size;
+ if (rc != 0) {
+ EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+ goto err;
+ }
+
+ if (res->ids_size == 0) {
+ rc = -1;
+ EBUF_PFI("Sanity check failed: No ubi_ids specified.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_type",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_type' from PFI.");
+ goto err;
+ }
+ if (strcmp(tmp_str, "static") == 0)
+ res->type = pfi_ubi_static;
+ else if (strcmp(tmp_str, "dynamic") == 0)
+ res->type = pfi_ubi_dynamic;
+ else {
+ EBUF_PFI("Unknown ubi_type in PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_alignment' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_names",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_names' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_import(ubi_name_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+ rc = bootenv_list_to_vector(ubi_name_list, &size,
+ &(tmp_names));
+ res->names_size = size;
+ if (rc != 0) {
+ EBUF_PFI("Cannot create string array: %s", tmp_str);
+ goto err;
+ }
+
+ if (res->names_size != res->ids_size) {
+ EBUF_PFI("Sanity check failed: ubi_ids list does not match "
+ "sizeof ubi_names list.");
+ rc = -1;
+ }
+
+ /* copy tmp_names to own structure */
+ res->names = (char**) calloc(1, res->names_size * sizeof (char*));
+ if (res->names == NULL)
+ goto err;
+
+ for (i = 0; i < res->names_size; i++) {
+ res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char));
+ if (res->names[i] == NULL)
+ goto err;
+ strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1);
+ }
+
+ goto out;
+
+ err:
+ if (res) {
+ if (res->names) {
+ for (i = 0; i < res->names_size; i++) {
+ if (res->names[i]) {
+ free(res->names[i]);
+ }
+ }
+ free(res->names);
+ }
+ if (res->ids) {
+ free(res->ids);
+ }
+ free(res);
+ res = NULL;
+ }
+
+ out:
+ bootenv_list_destroy(&ubi_id_list);
+ bootenv_list_destroy(&ubi_name_list);
+ if (tmp_names != NULL)
+ free(tmp_names);
+ *pfi_ubi = res;
+ return rc;
+}
+
+
+int
+free_pdd_data(pdd_data_t* pdd_data)
+{
+ if (*pdd_data) {
+ free(*pdd_data);
+ }
+ *pdd_data = NULL;
+
+ return 0;
+}
+
+int
+free_pfi_raw(pfi_raw_t* pfi_raw)
+{
+ pfi_raw_t tmp = *pfi_raw;
+ if (tmp) {
+ if (tmp->starts)
+ free(tmp->starts);
+ free(tmp);
+ }
+ *pfi_raw = NULL;
+
+ return 0;
+}
+
+int
+free_pfi_ubi(pfi_ubi_t* pfi_ubi)
+{
+ size_t i;
+ pfi_ubi_t tmp = *pfi_ubi;
+ if (tmp) {
+ if (tmp->ids)
+ free(tmp->ids);
+ if (tmp->names) {
+ for (i = 0; i < tmp->names_size; i++) {
+ if (tmp->names[i]) {
+ free(tmp->names[i]);
+ }
+ }
+ free(tmp->names);
+ }
+ free(tmp);
+ }
+ *pfi_ubi = NULL;
+
+ return 0;
+}
+
+
+int
+read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ char mode[PFI_KEYWORD_LEN];
+ char label[PFI_LABEL_LEN];
+
+ *pfi_raws = mk_empty(); pfi_raw_t raw = NULL;
+ *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL;
+ pfi_header pfi_header = NULL;
+
+ /* read all headers from PFI and store them in lists */
+ rc = pfi_header_init(&pfi_header);
+ if (rc != 0) {
+ EBUF("Cannot initialize pfi header.");
+ goto err;
+ }
+ while ((rc == 0) && !feof(fp_pfi)) {
+ rc = pfi_header_read(fp_pfi, pfi_header);
+ if (rc != 0) {
+ if (rc == PFI_DATA_START) {
+ rc = 0;
+ break; /* data section starts,
+ all headers read */
+ }
+ else {
+ goto err;
+ }
+ }
+ rc = pfi_header_getstring(pfi_header, "label", label,
+ PFI_LABEL_LEN);
+ if (rc != 0) {
+ EBUF("Cannot read 'label' from PFI.");
+ goto err;
+ }
+ rc = pfi_header_getstring(pfi_header, "mode", mode,
+ PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF("Cannot read 'mode' from PFI.");
+ goto err;
+ }
+ if (strcmp(mode, "ubi") == 0) {
+ rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ goto err;
+ }
+ *pfi_ubis = append_elem(ubi, *pfi_ubis);
+ }
+ else if (strcmp(mode, "raw") == 0) {
+ rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ goto err;
+ }
+ *pfi_raws = append_elem(raw, *pfi_raws);
+ }
+ else {
+ EBUF("Recvieved unknown mode from PFI: %s", mode);
+ goto err;
+ }
+ }
+ goto out;
+
+ err:
+ *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws);
+ *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis);
+ out:
+ pfi_header_destroy(&pfi_header);
+ return rc;
+
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/reader.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/reader.h
new file mode 100644
index 0000000..715e464
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/reader.h
@@ -0,0 +1,87 @@
+#ifndef __READER_H__
+#define __READER_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Read Platform Description Data (PDD).
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "pfi.h"
+#include "bootenv.h"
+#include "list.h"
+
+typedef enum flash_type_t {
+ NAND_FLASH = 0,
+ NOR_FLASH,
+} flash_type_t;
+
+typedef struct pdd_data *pdd_data_t;
+typedef struct pfi_raw *pfi_raw_t;
+typedef struct pfi_ubi *pfi_ubi_t;
+
+struct pdd_data {
+ uint32_t flash_size;
+ uint32_t flash_page_size;
+ uint32_t eb_size;
+ uint32_t vid_hdr_offset;
+ flash_type_t flash_type;
+};
+
+struct pfi_raw {
+ uint32_t data_size;
+ uint32_t *starts;
+ uint32_t starts_size;
+ uint32_t crc;
+};
+
+struct pfi_ubi {
+ uint32_t data_size;
+ uint32_t alignment;
+ uint32_t *ids;
+ uint32_t ids_size;
+ char **names;
+ uint32_t names_size;
+ uint32_t size;
+ enum { pfi_ubi_dynamic, pfi_ubi_static } type;
+ int curr_seqnum; /* specifies the seqnum taken in an update,
+ default: 0 (used by pfiflash, ubimirror) */
+ uint32_t crc;
+};
+
+int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data,
+ char *err_buf, size_t err_buf_size);
+int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t *pfi_raw,
+ const char *label, char *err_buf, size_t err_buf_size);
+int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t *pfi_ubi,
+ const char *label, char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Reads all pfi headers into list structures, separated by
+ * RAW and UBI sections.
+ */
+int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+ char* err_buf, size_t err_buf_size);
+int free_pdd_data(pdd_data_t *pdd_data);
+int free_pfi_raw(pfi_raw_t *raw_pfi);
+int free_pfi_ubi(pfi_ubi_t *pfi_ubi);
+
+#endif /* __READER_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubigen.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubigen.c
new file mode 100644
index 0000000..bb55fa9
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubigen.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Tool to add UBI headers to binary images.
+ *
+ * 1.0 Initial version
+ * 1.1 Different CRC32 start value
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <mtd/ubi-media.h>
+
+#include "ubigen.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.3"
+
+typedef enum action_t {
+ ACT_NORMAL = 0x00000001,
+ ACT_BROKEN_UPDATE = 0x00000002,
+} action_t;
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubigen - a tool for adding UBI information to a binary input file.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+" -c, --copyright Print copyright information.\n"
+" -d, --debug\n"
+" -v, --verbose Print more progress information.\n"
+"\n"
+" UBI Settings:\n"
+" -A, --alignment=<num> Set the alignment size to <num> (default 1).\n"
+" Values can be specified as bytes, 'ki' or 'Mi'.\n"
+" -B, --blocksize=<num> Set the eraseblock size to <num> (default 128\n"
+" KiB).\n"
+" Values can be specified as bytes, 'ki' or 'Mi'.\n"
+" -E, --erasecount=<num> Set the erase count to <num> (default 0)\n"
+" -I, --id=<num> The UBI volume id.\n"
+" -O, --offset=<num> Offset from start of an erase block to the UBI\n"
+" volume header.\n"
+" -T, --type=<num> The UBI volume type:\n"
+" 1 = dynamic, 2 = static\n"
+" -X, --setver=<num> Set UBI version number to <num> (default 1)\n"
+"\n"
+" Input/Output:\n"
+" -i, --infile=<filename> Read input from file.\n"
+" -o, --outfile=<filename> Write output to file (default is stdout).\n"
+"\n"
+" Special options:\n"
+" -U, --broken-update=<leb> Create an ubi image which simulates a broken\n"
+" update.\n"
+" <leb> specifies the logical eraseblock number to\n"
+" update.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubigen [-cdv?V] [-A <num>] [-B <num>] [-E <num>] [-I <num>]\n"
+" [-O <num>] [-T <num>] [-X <num>] [-i <filename>] [-o <filename>]\n"
+" [-U <leb>] [--copyright] [--debug] [--verbose] [--alignment=<num>]\n"
+" [--blocksize=<num>] [--erasecount=<num>] [--id=<num>]\n"
+" [--offset=<num>] [--type=<num>] [--setver=<num>]\n"
+" [--infile=<filename>] [--outfile=<filename>]\n"
+" [--broken-update=<leb>] [--help] [--usage] [--version]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' },
+ { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' },
+ { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' },
+ { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' },
+ { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' },
+ { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' },
+ { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+#define CHECK_ENDP(option, endp) do { \
+ if (*endp) { \
+ fprintf(stderr, \
+ "Parse error option \'%s\'. " \
+ "No correct numeric value.\n" \
+ , option); \
+ exit(EXIT_FAILURE); \
+ } \
+} while(0)
+
+typedef struct myargs {
+ /* common settings */
+ action_t action;
+ int verbose;
+
+ int32_t id;
+ uint8_t type;
+ uint32_t eb_size;
+ uint64_t ec;
+ uint8_t version;
+ uint32_t hdr_offset;
+ uint32_t update_block;
+ uint32_t alignment;
+
+ FILE* fp_in;
+ FILE* fp_out;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = strtoul(cp, endp, base);
+
+ switch (**endp) {
+ case 'G':
+ result *= 1024;
+ case 'M':
+ result *= 1024;
+ case 'k':
+ case 'K':
+ result *= 1024;
+ /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+ if ((*endp)[1] == 'i')
+ (*endp) += 2;
+ }
+ return result;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ int err = 0;
+ char* endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case 'o': /* output */
+ args->fp_out = fopen(optarg, "wb");
+ if ((args->fp_out) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for output\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'i': /* input */
+ args->fp_in = fopen(optarg, "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for input\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'v': /* verbose */
+ args->verbose = 1;
+ break;
+
+ case 'B': /* eb_size */
+ args->eb_size =
+ (uint32_t)ustrtoul(optarg, &endp, 0);
+ CHECK_ENDP("B", endp);
+ break;
+ case 'E': /* erasecount */
+ args->ec = (uint64_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("E", endp);
+ break;
+ case 'I': /* id */
+ args->id = (uint16_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("I", endp);
+ break;
+ case 'T': /* type */
+ args->type =
+ (uint16_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("T", endp);
+ break;
+ case 'X': /* versionnr */
+ args->version =
+ (uint8_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("X", endp);
+ break;
+ case 'O': /* offset for volume hdr */
+ args->hdr_offset =
+ (uint32_t) strtoul(optarg, &endp, 0);
+ CHECK_ENDP("O", endp);
+ break;
+
+ case 'U': /* broken update */
+ args->action = ACT_BROKEN_UPDATE;
+ args->update_block =
+ (uint32_t) strtoul(optarg, &endp, 0);
+ CHECK_ENDP("U", endp);
+ break;
+
+ case '?': /* help */
+ fprintf(stderr, "Usage: ubigen [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ if (!args->fp_in) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s for "
+ "input\n", argv[optind]);
+ exit(1);
+ }
+ }
+ }
+ if (args->id < 0) {
+ err = 1;
+ fprintf(stderr,
+ "Please specify an UBI Volume ID.\n");
+ }
+ if (args->type == 0) {
+ err = 1;
+ fprintf(stderr,
+ "Please specify an UBI Volume type.\n");
+ }
+ if (err) {
+ fprintf(stderr, "%s", usage);
+ exit(1);
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int rc = 0;
+ ubi_info_t u;
+ struct stat file_info;
+ off_t input_len = 0; /* only used in static volumes */
+
+ myargs args = {
+ .action = ACT_NORMAL,
+ .verbose = 0,
+
+ .id = -1,
+ .type = 0,
+ .eb_size = 0,
+ .update_block = 0,
+ .ec = 0,
+ .version = 0,
+ .hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE),
+ .alignment = 1,
+
+ .fp_in = NULL,
+ .fp_out = stdout,
+ /* arguments */
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ ubigen_init(); /* Init CRC32 table in ubigen */
+
+ /* parse arguments */
+ parse_opt(argc, argv, &args);
+
+ if (fstat(fileno(args.fp_in), &file_info) != 0) {
+ fprintf(stderr, "Cannot fetch file size "
+ "from input file.\n");
+ }
+ input_len = file_info.st_size;
+
+ rc = ubigen_create(&u, (uint32_t)args.id, args.type,
+ args.eb_size, args.ec, args.alignment,
+ args.version, args.hdr_offset, 0 ,input_len,
+ args.fp_in, args.fp_out);
+
+ if (rc != 0) {
+ fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!args.fp_in || !args.fp_out) {
+ fprintf(stderr, "Input/Output error.\n");
+ exit(EXIT_FAILURE);
+
+ }
+
+ if (args.action & ACT_NORMAL) {
+ rc = ubigen_write_complete(u);
+ }
+ else if (args.action & ACT_BROKEN_UPDATE) {
+ rc = ubigen_write_broken_update(u, args.update_block);
+ }
+ if (rc != 0) {
+ fprintf(stderr, "Error converting input data.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubigen_destroy(&u);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubigen.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubigen.h
new file mode 100644
index 0000000..8d29120
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubigen.h
@@ -0,0 +1,149 @@
+#ifndef __UBIGEN_H__
+#define __UBIGEN_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to update UBI volumes.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <mtd/ubi-media.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_BLOCKSIZE (128 * 1024)
+#define DEFAULT_PAGESIZE (2*1024)
+
+#define EUBIGEN_INVALID_TYPE 1
+#define EUBIGEN_INVALID_HDR_OFFSET 2
+#define EUBIGEN_INVALID_ALIGNMENT 3
+#define EUBIGEN_TOO_SMALL_EB 4
+#define EUBIGEN_MAX_ERROR 5
+
+
+typedef enum action {
+ NO_ERROR = 0x00000000,
+ BROKEN_HDR_CRC = 0x00000001,
+ BROKEN_DATA_CRC = 0x00000002,
+ BROKEN_DATA_SIZE = 0x00000004,
+ BROKEN_OMIT_BLK = 0x00000008,
+ MARK_AS_UPDATE = 0x00000010,
+} ubigen_action_t;
+
+typedef struct ubi_info *ubi_info_t;
+
+/**
+ * @brief Initialize the internal CRC32 table.
+ * @note Necessary because of the used crc32 function in UBI.
+ * A usage of CRC32, from e.g. zlib will fail.
+ */
+void ubigen_init(void);
+
+/**
+ * @brief Create an ubigen handle.
+ * @param ...
+ * @return 0 On sucess.
+ * else Error.
+ * @note This parameterlist is ugly. But we have to use
+ * two big structs and meta information internally,
+ * filling them would be even uglier.
+ */
+int ubigen_create(ubi_info_t *u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset,
+ uint8_t compat_flag, size_t data_size,
+ FILE* fp_in, FILE* fp_out);
+
+/**
+ * @brief Destroy an ubigen handle.
+ * @param u Handle to free.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_destroy(ubi_info_t *u);
+
+/**
+ * @brief Get number of total logical EBs, necessary for the
+ * complete storage of data in the handle.
+ * @param u The handle.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_get_leb_total(ubi_info_t u, size_t* total);
+
+/**
+ * @brief Get the size in bytes of one logical EB in the handle.
+ * @param u The handle.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_get_leb_size(ubi_info_t u, size_t* size);
+
+
+/**
+ * @brief Write a logical EB (fits exactly into 1 physical EB).
+ * @param u Handle which holds all necessary data.
+ * @param action Additional operations which shall be applied on this
+ * logical eraseblock. Mostly injecting artifical errors.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_leb(ubi_info_t u, ubigen_action_t action);
+
+/**
+ * @brief Write a complete array of logical eraseblocks at once.
+ * @param u Handle which holds all necessary data.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_complete(ubi_info_t u);
+
+/**
+ * @brief Write a single block which is extracted from the
+ * binary input data.
+ * @param u Handle which holds all necessary data.
+ * @param blk Logical eraseblock which shall hold a inc. copy entry
+ * and a bad data crc.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_broken_update(ubi_info_t u, uint32_t blk);
+
+/**
+ * @brief Use the current ubi_info data and some additional data
+ * to set an UBI volume table entry from it.
+ * @param u Handle which holds some of the necessary data.
+ * @param res_bytes Number of reserved bytes which is stored in the volume
+ * table entry.
+ * @param name A string which shall be used as a volume label.
+ * @param lvol_r A pointer to a volume table entry.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* name, struct ubi_vtbl_record *lvol_rec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UBIGEN_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubimirror.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubimirror.c
new file mode 100644
index 0000000..da05a8b
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubimirror.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ * 1.4 Migrated to new libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-media.h>
+
+#include "config.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.4"
+
+typedef enum action_t {
+ ACT_NORMAL = 0,
+ ACT_ARGP_ABORT,
+ ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do { \
+ args->action = ACT_ARGP_ABORT; \
+} while (0)
+
+#define ERR_ARGP do { \
+ args->action = ACT_ARGP_ERR; \
+} while (0)
+
+#define VOL_ARGS_MAX 2
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubimirror - mirrors ubi volumes.\n";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright information.\n"
+" -s, --side=<seqnum> Use the side <seqnum> as source.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubimirror [-c?V] [-s <seqnum>] [--copyright] [--side=<seqnum>]\n"
+" [--help] [--usage] [--version] <source> <destination>\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "(C) IBM Coorporation 2007";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ action_t action;
+ int side;
+ int vol_no; /* index of current volume */
+ /* @FIXME replace by bootenv_list, makes live easier */
+ /* @FIXME remove the constraint of two entries in the array */
+ const char* vol[VOL_ARGS_MAX]; /* comma separated list of src/dst
+ volumes */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+ return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cs:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ err_msg("%s", copyright);
+ ABORT_ARGP;
+ break;
+ case 's':
+ args->side = get_update_side(optarg);
+ if (args->side < 0) {
+ err_msg("Unsupported seqnum: %s.\n"
+ "Supported seqnums are '0' "
+ "and '1'\n", optarg);
+ ERR_ARGP;
+ }
+ break;
+ case '?': /* help */
+ err_msg("Usage: ubimirror [OPTION...] "
+ "<source> <destination>\n");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+ }
+ }
+
+ while (optind < argc) {
+ /* only two entries allowed */
+ if (args->vol_no >= VOL_ARGS_MAX) {
+ err_msg("%s", usage);
+ ERR_ARGP;
+ }
+ args->vol[(args->vol_no)++] = argv[optind++];
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ unsigned int ids[VOL_ARGS_MAX];
+ char err_buf[1024];
+
+ myargs args = {
+ .action = ACT_NORMAL,
+ .side = -1,
+ .vol_no = 0,
+ .vol = {"", ""},
+ .options = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+ if (args.action == ACT_ARGP_ERR) {
+ rc = 127;
+ goto err;
+ }
+ if (args.action == ACT_ARGP_ABORT) {
+ rc = 126;
+ goto out;
+ }
+ if (args.vol_no < VOL_ARGS_MAX) {
+ fprintf(stderr, "missing volume number for %s\n",
+ args.vol_no == 0 ? "source and target" : "target");
+ rc = 125;
+ goto out;
+ }
+ for( rc = 0; rc < args.vol_no; ++rc){
+ char *endp;
+ ids[rc] = strtoul(args.vol[rc], &endp, 0);
+ if( *endp != '\0' ){
+ fprintf(stderr, "invalid volume number %s\n",
+ args.vol[rc]);
+ rc = 125;
+ goto out;
+ }
+ }
+ rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no,
+ err_buf, sizeof(err_buf));
+ if( rc ){
+ err_buf[sizeof err_buf - 1] = '\0';
+ fprintf(stderr, "%s", err_buf);
+ if( rc < 0 )
+ rc = -rc;
+ }
+ out:
+ err:
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubimirror.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubimirror.h
new file mode 100644
index 0000000..d7ae2ad
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/ubimirror.h
@@ -0,0 +1,66 @@
+#ifndef __UBIMIRROR_H__
+#define __UBIMIRROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * An utility to mirror UBI volumes.
+ */
+
+#include <stdint.h>
+
+/**
+ * @def EUBIMIRROR_SRC_EQ_DST
+ * @brief Given source volume is also in the set of destination volumes.
+ */
+#define EUBIMIRROR_SRC_EQ_DST 20
+
+/**
+ * @def EUBIMIRROR_NO_SRC
+ * @brief The given source volume does not exist.
+ */
+#define EUBIMIRROR_NO_SRC 21
+
+/**
+ * @def EUBIMIRROR_NO_DST
+ * @brief One of the given destination volumes does not exist.
+ */
+#define EUBIMIRROR_NO_DST 22
+
+/**
+ * @brief Mirrors UBI devices from a source device (specified by seqnum)
+ * to n target devices.
+ * @param devno Device number used by the UBI operations.
+ * @param seqnum An index into ids (defines the src_id).
+ * @param ids An array of ids.
+ * @param ids_size The number of entries in the ids array.
+ * @param err_buf A buffer to store verbose error messages.
+ * @param err_buf_size The size of the error buffer.
+ *
+ * @note A seqnum of value < 0 defaults to a seqnum of 0.
+ * @note A seqnum exceeding the range of ids_size defaults to 0.
+ * @note An empty ids list results in a empty stmt.
+ * @pre The UBI volume which shall be used as source volume exists.
+ * @pre The UBI volumes which are defined as destination volumes exist.
+ * @post The content of the UBI volume which was defined as source volume
+ * equals the content of the volumes which were defined as destination.
+ */
+int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size,
+ char *err_buf, size_t err_buf_size);
+
+#endif /* __UBIMIRROR_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi.c
new file mode 100644
index 0000000..0f72b18
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi.c
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ * Frank Haverkamp, haver@vnet.ibm.com
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups.
+ * 1.4 Meanwhile Drake had done a lot of changes, syncing those.
+ * 1.5 Bugfixes, simplifications
+ */
+
+/*
+ * unubi reads an image file containing blocks of UBI headers and data
+ * (such as produced from nand2bin) and rebuilds the volumes within. The
+ * default operation (when no flags are given) is to rebuild all valid
+ * volumes found in the image. unubi can also read straight from the
+ * onboard MTD device (ex. /dev/mtdblock/NAND).
+ */
+
+/* TODO: consideration for dynamic vs. static volumes */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <mtd/ubi-media.h>
+#include <mtd_swab.h>
+
+#include "crc32.h"
+#include "unubi_analyze.h"
+
+#define EXEC "unubi"
+#define CONTACT "haver@vnet.ibm.com"
+#define VERSION "1.5"
+
+static char doc[] = "\nVersion: " VERSION "\n";
+static int debug = 0;
+
+static const char *optionsstr =
+"Extract volumes and/or analysis information from an UBI data file.\n"
+"When no parameters are flagged or given, the default operation is\n"
+"to rebuild all valid complete UBI volumes found within the image.\n"
+"\n"
+" OPERATIONS\n"
+" -a, --analyze Analyze image and create gnuplot graphs\n"
+" -i, --info-table Extract volume information tables\n"
+" -r, --rebuild=<volume-id> Extract and rebuild volume\n"
+"\n"
+" OPTIONS\n"
+" -b, --blocksize=<block-size> Specify size of eraseblocks in image in bytes\n"
+" (default 128KiB)\n"
+" -d, --dir=<output-dir> Specify output directory\n"
+" -D, --debug Enable debug output\n"
+" -s, --headersize=<header-size> Specify size reserved for metadata in eraseblock\n"
+ " in bytes (default 2048 Byte)\n"
+ /* the -s option might be insufficient when using different vid
+ offset than what we used when writing this tool ... Better would
+ probably be --vid-hdr-offset or alike */
+"\n"
+" ADVANCED\n"
+" -e, --eb-split Generate individual eraseblock images (all\n"
+" eraseblocks)\n"
+" -v, --vol-split Generate individual eraseblock images (valid\n"
+" eraseblocks only)\n"
+" -V, --vol-split! Raw split by eraseblock (valid eraseblocks only)\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" --version Print program version\n"
+"\n";
+
+static const char *usage =
+"Usage: unubi [-aievV?] [-r <volume-id>] [-b <block-size>] [-d <output-dir>]\n"
+" [-s <header-size>] [--analyze] [--info-table]\n"
+" [--rebuild=<volume-id>] [--blocksize=<block-size>]\n"
+" [--dir=<output-dir>] [--headersize=<header-size>] [--eb-split]\n"
+" [--vol-split] [--vol-split!] [--help] [--usage] [--version]\n"
+" image-file\n";
+
+#define ERR_MSG(fmt...) \
+ fprintf(stderr, EXEC ": " fmt)
+
+#define SPLIT_DATA 1
+#define SPLIT_RAW 2
+
+#define DIR_FMT "unubi_%s"
+#define KIB 1024
+#define MIB (KIB * KIB)
+#define MAXPATH KIB
+
+/* filenames */
+#define FN_INVAL "%s/eb%04u%s" /* invalid eraseblock */
+#define FN_NSURE "%s/eb%04u_%03u_%03u_%03x%s" /* unsure eraseblock */
+#define FN_VALID "%s/eb%04u_%03u_%03u_%03x%s" /* valid eraseblock */
+#define FN_VOLSP "%s/vol%03u_%03u_%03u_%04zu" /* split volume */
+#define FN_VOLWH "%s/volume%03u" /* whole volume */
+#define FN_VITBL "%s/vol_info_table%zu" /* vol info table */
+
+static uint32_t crc32_table[256];
+
+/* struct args:
+ * bsize int, blocksize of image blocks
+ * hsize int, eraseblock header size
+ * analyze flag, when non-zero produce analysis
+ * eb_split flag, when non-zero output eb####
+ * note: SPLIT_DATA vs. SPLIT_RAW
+ * vol_split flag, when non-zero output vol###_####
+ * note: SPLIT_DATA vs. SPLIT_RAW
+ * odir_path string, directory to place volumes in
+ * img_path string, file to read as ubi image
+ * vols int array of size UBI_MAX_VOLUMES, where a 1 can be
+ * written for each --rebuild flag in the index specified
+ * then the array can be counted and collapsed using
+ * count_set() and collapse()
+ */
+struct args {
+ int analyze;
+ int itable;
+ uint32_t *vols;
+
+ size_t vid_hdr_offset;
+ size_t data_offset;
+ size_t bsize; /* FIXME replace by vid_hdr/data offs? */
+ size_t hsize;
+
+ char *odir_path;
+ int eb_split;
+ int vol_split;
+ char *img_path;
+
+ char **options;
+};
+
+struct option long_options[] = {
+ { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' },
+ { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+ { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' },
+ { NULL, 0, NULL, 0}
+};
+
+/**
+ * parses out a numerical value from a string of numbers followed by:
+ * k, K, kib, KiB for kibibyte
+ * m, M, mib, MiB for mebibyte
+ **/
+static uint32_t
+str_to_num(char *str)
+{
+ char *s;
+ ulong num;
+
+ s = str;
+ num = strtoul(s, &s, 0);
+
+ if (*s != '\0') {
+ if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) ||
+ (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0))
+ num *= KIB;
+ else if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) ||
+ (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0))
+ num *= MIB;
+ else
+ ERR_MSG("couldn't parse '%s', assuming %lu\n",
+ s, num);
+ }
+ return num;
+}
+
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ uint32_t i;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "ab:s:d:Deir:vV?J",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'a': /* --analyze */
+ args->analyze = 1;
+ break;
+ case 'b': /* --block-size=<block-size> */
+ args->bsize = str_to_num(optarg);
+ break;
+ case 's': /* --header-size=<header-size> */
+ args->hsize = str_to_num(optarg);
+ break;
+ case 'd': /* --dir=<output-dir> */
+ args->odir_path = optarg;
+ break;
+ case 'D': /* --debug */
+ /* I wanted to use -v but that was already
+ used ... */
+ debug = 1;
+ break;
+ case 'e': /* --eb-split */
+ args->eb_split = SPLIT_RAW;
+ break;
+ case 'i': /* --info-table */
+ args->itable = 1;
+ break;
+ case 'r': /* --rebuild=<volume-id> */
+ i = str_to_num(optarg);
+ if (i < UBI_MAX_VOLUMES)
+ args->vols[str_to_num(optarg)] = 1;
+ else {
+ ERR_MSG("volume-id out of bounds\n");
+ return -1;
+ }
+ break;
+ case 'v': /* --vol-split */
+ if (args->vol_split != SPLIT_RAW)
+ args->vol_split = SPLIT_DATA;
+ break;
+ case 'V': /* --vol-split! */
+ args->vol_split = SPLIT_RAW;
+ break;
+ case '?': /* help */
+ fprintf(stderr, "Usage: unubi [OPTION...] "
+ "image-file\n%s%s\nReport bugs to %s\n",
+ doc, optionsstr, CONTACT);
+ exit(0);
+ break;
+ case 'J':
+ fprintf(stderr, "%s\n", VERSION);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ /* FIXME I suppose hsize should be replaced! */
+ args->vid_hdr_offset = args->hsize - UBI_VID_HDR_SIZE;
+ args->data_offset = args->hsize;
+
+ if (optind < argc)
+ args->img_path = argv[optind++];
+ return 0;
+}
+
+
+/**
+ * counts the number of indicies which are flagged in full_array;
+ * full_array is an array of flags (1/0);
+ **/
+static size_t
+count_set(uint32_t *full_array, size_t full_len)
+{
+ size_t count, i;
+
+ if (full_array == NULL)
+ return 0;
+
+ for (i = 0, count = 0; i < full_len; i++)
+ if (full_array[i] != 0)
+ count++;
+
+ return count;
+}
+
+
+/**
+ * generates coll_array from full_array;
+ * full_array is an array of flags (1/0);
+ * coll_array is an array of the indicies in full_array which are flagged (1);
+ **/
+static size_t
+collapse(uint32_t *full_array, size_t full_len,
+ uint32_t *coll_array, size_t coll_len)
+{
+ size_t i, j;
+
+ if ((full_array == NULL) || (coll_array == NULL))
+ return 0;
+
+ for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++)
+ if (full_array[i] != 0) {
+ coll_array[j] = i;
+ j++;
+ }
+
+ return j;
+}
+
+/**
+ * data_crc: save the FILE* position, calculate the crc over a span,
+ * reset the position
+ * returns non-zero when EOF encountered
+ **/
+static int
+data_crc(FILE* fpin, size_t length, uint32_t *ret_crc)
+{
+ int rc;
+ size_t i;
+ char buf[length];
+ uint32_t crc;
+ fpos_t start;
+
+ rc = fgetpos(fpin, &start);
+ if (rc < 0)
+ return -1;
+
+ for (i = 0; i < length; i++) {
+ int c = fgetc(fpin);
+ if (c == EOF) {
+ ERR_MSG("unexpected EOF\n");
+ return -1;
+ }
+ buf[i] = (char)c;
+ }
+
+ rc = fsetpos(fpin, &start);
+ if (rc < 0)
+ return -1;
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length);
+ *ret_crc = crc;
+ return 0;
+}
+
+
+/**
+ * reads data of size len from fpin and writes it to path
+ **/
+static int
+extract_data(FILE* fpin, size_t len, const char *path)
+{
+ int rc;
+ size_t i;
+ FILE* fpout;
+
+ rc = 0;
+ fpout = NULL;
+
+ fpout = fopen(path, "wb");
+ if (fpout == NULL) {
+ ERR_MSG("couldn't open file for writing: %s\n", path);
+ rc = -1;
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ int c = fgetc(fpin);
+ if (c == EOF) {
+ ERR_MSG("unexpected EOF while writing: %s\n", path);
+ rc = -2;
+ goto err;
+ }
+ c = fputc(c, fpout);
+ if (c == EOF) {
+ ERR_MSG("couldn't write: %s\n", path);
+ rc = -3;
+ goto err;
+ }
+ }
+
+ err:
+ if (fpout != NULL)
+ fclose(fpout);
+ return rc;
+}
+
+
+/**
+ * extract volume information table from block. saves and reloads fpin
+ * position
+ * returns -1 when a fpos set or get fails, otherwise <= -2 on other
+ * failure and 0 on success
+ **/
+static int
+extract_itable(FILE *fpin, struct eb_info *cur, size_t bsize, size_t num,
+ const char *path)
+{
+ char filename[MAXPATH + 1];
+ int rc;
+ size_t i, max;
+ fpos_t temp;
+ FILE* fpout = NULL;
+ struct ubi_vtbl_record rec;
+
+ if (fpin == NULL || cur == NULL || path == NULL)
+ return -2;
+
+ /* remember position */
+ rc = fgetpos(fpin, &temp);
+ if (rc < 0)
+ return -1;
+
+ /* jump to top of eraseblock, skip to data section */
+ fsetpos(fpin, &cur->eb_top);
+ if (rc < 0)
+ return -1;
+ fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR);
+
+ /* prepare output file */
+ if (be32_to_cpu(cur->vid.vol_id) != UBI_LAYOUT_VOLUME_ID)
+ return -2;
+ memset(filename, 0, MAXPATH + 1);
+ snprintf(filename, MAXPATH, FN_VITBL, path, num);
+ fpout = fopen(filename, "w");
+ if (fpout == NULL)
+ return -2;
+
+ /* loop through entries */
+ fprintf(fpout,
+ "index\trpebs\talign\ttype\tcrc\t\tname\n");
+ max = bsize - be32_to_cpu(cur->ec.data_offset);
+ for (i = 0; i < (max / sizeof(rec)); i++) {
+ int blank = 1;
+ char *ptr, *base;
+ char name[UBI_VOL_NAME_MAX + 1];
+ const char *type = "unknown\0";
+ uint32_t crc;
+
+ /* read record */
+ rc = fread(&rec, 1, sizeof(rec), fpin);
+ if (rc == 0)
+ break;
+ if (rc != sizeof(rec)) {
+ ERR_MSG("reading volume information "
+ "table record failed\n");
+ rc = -3;
+ goto exit;
+ }
+
+ /* check crc */
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &rec,
+ UBI_VTBL_RECORD_SIZE_CRC);
+ if (crc != be32_to_cpu(rec.crc))
+ continue;
+
+ /* check for empty */
+ base = (char *)&rec;
+ ptr = base;
+ while (blank &&
+ ((unsigned)(ptr - base) < UBI_VTBL_RECORD_SIZE_CRC)) {
+ if (*ptr != 0)
+ blank = 0;
+ ptr++;
+ }
+
+ if (blank)
+ continue;
+
+ /* prep type string */
+ if (rec.vol_type == UBI_VID_DYNAMIC)
+ type = "dynamic\0";
+ else if (rec.vol_type == UBI_VID_STATIC)
+ type = "static\0";
+
+ /* prep name string */
+ rec.name[be16_to_cpu(rec.name_len)] = '\0';
+ sprintf(name, "%s", rec.name);
+
+ /* print record line to fpout */
+ fprintf(fpout, "%zu\t%u\t%u\t%s\t0x%08x\t%s\n",
+ i,
+ be32_to_cpu(rec.reserved_pebs),
+ be32_to_cpu(rec.alignment),
+ type,
+ be32_to_cpu(rec.crc),
+ name);
+ }
+
+ exit:
+ /* reset position */
+ if (fsetpos(fpin, &temp) < 0)
+ rc = -1;
+
+ if (fpout != NULL)
+ fclose(fpout);
+
+ return rc;
+}
+
+
+/**
+ * using eb chain, tries to rebuild the data of volume at vol_id, or for all
+ * the known volumes, if vol_id is NULL;
+ **/
+static int
+rebuild_volume(FILE * fpin, uint32_t *vol_id, struct eb_info **head,
+ const char *path, size_t block_size, size_t header_size)
+{
+ char filename[MAXPATH];
+ int rc;
+ uint32_t vol, num, data_size;
+ FILE* fpout;
+ struct eb_info *cur;
+
+ rc = 0;
+
+ if ((fpin == NULL) || (head == NULL) || (*head == NULL))
+ return 0;
+
+ /* when vol_id is null, then do all */
+ if (vol_id == NULL) {
+ cur = *head;
+ vol = be32_to_cpu(cur->vid.vol_id);
+ } else {
+ vol = *vol_id;
+ eb_chain_position(head, vol, NULL, &cur);
+ if (cur == NULL) {
+ if (debug)
+ ERR_MSG("no valid volume %d was found\n", vol);
+ return -1;
+ }
+ }
+
+ num = 0;
+ snprintf(filename, MAXPATH, FN_VOLWH, path, vol);
+ fpout = fopen(filename, "wb");
+ if (fpout == NULL) {
+ ERR_MSG("couldn't open file for writing: %s\n", filename);
+ return -1;
+ }
+
+ while (cur != NULL) {
+ size_t i;
+
+ if (be32_to_cpu(cur->vid.vol_id) != vol) {
+ /* close out file */
+ fclose(fpout);
+
+ /* only stay around if that was the only volume */
+ if (vol_id != NULL)
+ goto out;
+
+ /* begin with next */
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = 0;
+ snprintf(filename, MAXPATH, FN_VOLWH, path, vol);
+ fpout = fopen(filename, "wb");
+ if (fpout == NULL) {
+ ERR_MSG("couldn't open file for writing: %s\n",
+ filename);
+ return -1;
+ }
+ }
+
+ while (num < be32_to_cpu(cur->vid.lnum)) {
+ /* FIXME haver: I hope an empty block is
+ written out so that the binary has no holes
+ ... */
+ if (debug)
+ ERR_MSG("missing valid block %d for volume %d\n",
+ num, vol);
+ num++;
+ }
+
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc < 0)
+ goto out;
+ fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR);
+
+ if (cur->vid.vol_type == UBI_VID_DYNAMIC)
+ /* FIXME It might be that alignment has influence */
+ data_size = block_size - header_size;
+ else
+ data_size = be32_to_cpu(cur->vid.data_size);
+
+ for (i = 0; i < data_size; i++) {
+ int c = fgetc(fpin);
+ if (c == EOF) {
+ ERR_MSG("unexpected EOF while writing: %s\n",
+ filename);
+ rc = -2;
+ goto out;
+ }
+ c = fputc(c, fpout);
+ if (c == EOF) {
+ ERR_MSG("couldn't write: %s\n", filename);
+ rc = -3;
+ goto out;
+ }
+ }
+
+ cur = cur->next;
+ num++;
+ }
+
+ out:
+ if (vol_id == NULL)
+ fclose(fpout);
+ return rc;
+}
+
+
+/**
+ * traverses FILE* trying to load complete, valid and accurate header data
+ * into the eb chain;
+ **/
+static int
+unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a)
+{
+ char filename[MAXPATH + 1];
+ char reason[MAXPATH + 1];
+ int rc;
+ size_t i, count, itable_num;
+ /* relations:
+ * cur ~ head
+ * next ~ first */
+ struct eb_info *head, *cur, *first, *next;
+ struct eb_info **next_ptr;
+
+ rc = 0;
+ count = 0;
+ itable_num = 0;
+ head = NULL;
+ first = NULL;
+ next = NULL;
+ cur = malloc(sizeof(*cur));
+ if (cur == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(cur, 0, sizeof(*cur));
+
+ fgetpos(fpin, &(cur->eb_top));
+ while (1) {
+ const char *raw_path;
+ uint32_t crc;
+
+ cur->phys_addr = ftell(fpin);
+ cur->phys_block = cur->phys_addr / a->bsize;
+ cur->data_crc_ok = 0;
+ cur->ec_crc_ok = 0;
+ cur->vid_crc_ok = 0;
+
+ memset(filename, 0, MAXPATH + 1);
+ memset(reason, 0, MAXPATH + 1);
+
+ /* in case of an incomplete ec header */
+ raw_path = FN_INVAL;
+
+ /* read erasecounter header */
+ rc = fread(&cur->ec, 1, sizeof(cur->ec), fpin);
+ if (rc == 0)
+ goto out; /* EOF */
+ if (rc != sizeof(cur->ec)) {
+ ERR_MSG("reading ec-hdr failed\n");
+ rc = -1;
+ goto err;
+ }
+
+ /* check erasecounter header magic */
+ if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) {
+ snprintf(reason, MAXPATH, ".invalid.ec_magic");
+ goto invalid;
+ }
+
+ /* check erasecounter header crc */
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->ec),
+ UBI_EC_HDR_SIZE_CRC);
+ if (be32_to_cpu(cur->ec.hdr_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc");
+ goto invalid;
+ }
+
+ /* read volume id header */
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+ fseek(fpin, be32_to_cpu(cur->ec.vid_hdr_offset), SEEK_CUR);
+ rc = fread(&cur->vid, 1, sizeof(cur->vid), fpin);
+ if (rc == 0)
+ goto out; /* EOF */
+ if (rc != sizeof(cur->vid)) {
+ ERR_MSG("reading vid-hdr failed\n");
+ rc = -1;
+ goto err;
+ }
+
+ /* if the magic number is 0xFFFFFFFF, then it's very likely
+ * that the volume is empty */
+ if (be32_to_cpu(cur->vid.magic) == 0xffffffff) {
+ snprintf(reason, MAXPATH, ".empty");
+ goto invalid;
+ }
+
+ /* vol_id should be in bounds */
+ if ((be32_to_cpu(cur->vid.vol_id) >= UBI_MAX_VOLUMES) &&
+ (be32_to_cpu(cur->vid.vol_id) <
+ UBI_INTERNAL_VOL_START)) {
+ snprintf(reason, MAXPATH, ".invalid");
+ goto invalid;
+ } else
+ raw_path = FN_NSURE;
+
+ /* check volume id header magic */
+ if (be32_to_cpu(cur->vid.magic) != UBI_VID_HDR_MAGIC) {
+ snprintf(reason, MAXPATH, ".invalid.vid_magic");
+ goto invalid;
+ }
+ cur->ec_crc_ok = 1;
+
+ /* check volume id header crc */
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->vid),
+ UBI_VID_HDR_SIZE_CRC);
+ if (be32_to_cpu(cur->vid.hdr_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc");
+ goto invalid;
+ }
+ cur->vid_crc_ok = 1;
+
+ /* check data crc, but only for a static volume */
+ if (cur->vid.vol_type == UBI_VID_STATIC) {
+ rc = data_crc(fpin, be32_to_cpu(cur->vid.data_size),
+ &crc);
+ if (rc < 0)
+ goto err;
+ if (be32_to_cpu(cur->vid.data_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.data_crc");
+ goto invalid;
+ }
+ cur->data_crc_ok = 1;
+ }
+
+ /* enlist this vol, it's valid */
+ raw_path = FN_VALID;
+ cur->linear = count;
+ rc = eb_chain_insert(&head, cur);
+ if (rc < 0) {
+ if (rc == -ENOMEM) {
+ ERR_MSG("out of memory\n");
+ goto err;
+ }
+ ERR_MSG("unknown and unexpected error, please contact "
+ CONTACT "\n");
+ goto err;
+ }
+
+ /* extract info-table */
+ if (a->itable &&
+ (be32_to_cpu(cur->vid.vol_id) == UBI_LAYOUT_VOLUME_ID)) {
+ extract_itable(fpin, cur, a->bsize,
+ itable_num, a->odir_path);
+ itable_num++;
+ }
+
+ /* split volumes */
+ if (a->vol_split) {
+ size_t size = 0;
+
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+
+ /*
+ * FIXME For dynamic UBI volumes we must write
+ * the maximum available data. The
+ * vid.data_size field is not used in this
+ * case. The dynamic volume user is
+ * responsible for the content.
+ */
+ if (a->vol_split == SPLIT_DATA) {
+ /* Write only data section */
+ if (cur->vid.vol_type == UBI_VID_DYNAMIC) {
+ /* FIXME Formular is not
+ always right ... */
+ size = a->bsize - a->hsize;
+ } else
+ size = be32_to_cpu(cur->vid.data_size);
+
+ fseek(fpin,
+ be32_to_cpu(cur->ec.data_offset),
+ SEEK_CUR);
+ }
+ else if (a->vol_split == SPLIT_RAW)
+ /* write entire eraseblock */
+ size = a->bsize;
+
+ snprintf(filename, MAXPATH, FN_VOLSP,
+ a->odir_path,
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ be32_to_cpu(cur->vid.leb_ver), count);
+ rc = extract_data(fpin, size, filename);
+ if (rc < 0)
+ goto err;
+ }
+
+ invalid:
+ /* split eraseblocks */
+ if (a->eb_split) {
+ /* jump to top of block */
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+
+ if (strcmp(raw_path, FN_INVAL) == 0)
+ snprintf(filename, MAXPATH, raw_path,
+ a->odir_path, count, reason);
+ else
+ snprintf(filename, MAXPATH, raw_path,
+ a->odir_path,
+ count,
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ be32_to_cpu(cur->vid.leb_ver),
+ reason);
+
+ rc = extract_data(fpin, a->bsize, filename);
+ if (rc < 0)
+ goto err;
+ }
+
+ /* append to simple linked list */
+ if (first == NULL)
+ next_ptr = &first;
+ else
+ next_ptr = &next->next;
+
+ *next_ptr = malloc(sizeof(**next_ptr));
+ if (*next_ptr == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(*next_ptr, 0, sizeof(**next_ptr));
+
+ next = *next_ptr;
+ memcpy(next, cur, sizeof(*next));
+ next->next = NULL;
+
+ count++;
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+ fseek(fpin, a->bsize, SEEK_CUR);
+ memset(cur, 0, sizeof(*cur));
+
+ fgetpos(fpin, &(cur->eb_top));
+ }
+
+ out:
+ for (i = 0; i < vc; i++) {
+ rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path,
+ a->bsize, a->hsize);
+ if (rc < 0)
+ goto err;
+ }
+
+ /* if there were no volumes specified, rebuild them all,
+ * UNLESS eb_ or vol_ split or analyze was specified */
+ if ((vc == 0) && (!a->eb_split) && (!a->vol_split) &&
+ (!a->analyze) && (!a->itable)) {
+ rc = rebuild_volume(fpin, NULL, &head, a->odir_path, a->bsize,
+ a->hsize);
+ if (rc < 0)
+ goto err;
+ }
+
+ err:
+ free(cur);
+
+ if (a->analyze) {
+ char fname[PATH_MAX + 1];
+ FILE *fp;
+
+ unubi_analyze(&head, first, a->odir_path);
+
+ /* prepare output files */
+ memset(fname, 0, PATH_MAX + 1);
+ snprintf(fname, PATH_MAX, "%s/%s", a->odir_path, FN_EH_STAT);
+ fp = fopen(fname, "w");
+ if (fp != NULL) {
+ eb_chain_print(fp, head);
+ fclose(fp);
+ }
+ }
+ eb_chain_destroy(&head);
+ eb_chain_destroy(&first);
+
+ return rc;
+}
+
+
+/**
+ * handles command line arguments, then calls unubi_volumes
+ **/
+int
+main(int argc, char *argv[])
+{
+ int rc, free_a_odir;
+ size_t vols_len;
+ uint32_t *vols;
+ FILE* fpin;
+ struct args a;
+
+ rc = 0;
+ free_a_odir = 0;
+ vols_len = 0;
+ vols = NULL;
+ fpin = NULL;
+ init_crc32_table(crc32_table);
+
+ /* setup struct args a */
+ memset(&a, 0, sizeof(a));
+ a.bsize = 128 * KIB;
+ a.hsize = 2 * KIB;
+ a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES);
+ if (a.vols == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES);
+
+ /* parse args and check for validity */
+ parse_opt(argc, argv, &a);
+ if (a.img_path == NULL) {
+ ERR_MSG("no image file specified\n");
+ rc = EINVAL;
+ goto err;
+ }
+ else if (a.odir_path == NULL) {
+ char *ptr;
+ int len;
+
+ ptr = strrchr(a.img_path, '/');
+ if (ptr == NULL)
+ ptr = a.img_path;
+ else
+ ptr++;
+
+ len = strlen(DIR_FMT) + strlen(ptr);
+ free_a_odir = 1;
+ a.odir_path = malloc(sizeof(*a.odir_path) * len);
+ if (a.odir_path == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ snprintf(a.odir_path, len, DIR_FMT, ptr);
+ }
+
+ fpin = fopen(a.img_path, "rb");
+ if (fpin == NULL) {
+ ERR_MSG("couldn't open file for reading: "
+ "%s\n", a.img_path);
+ rc = EINVAL;
+ goto err;
+ }
+
+ rc = mkdir(a.odir_path, 0777);
+ if ((rc < 0) && (errno != EEXIST)) {
+ ERR_MSG("couldn't create ouput directory: "
+ "%s\n", a.odir_path);
+ rc = -rc;
+ goto err;
+ }
+
+ /* fill in vols array */
+ vols_len = count_set(a.vols, UBI_MAX_VOLUMES);
+ if (vols_len > 0) {
+ vols = malloc(sizeof(*vols) * vols_len);
+ if (vols == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len);
+ }
+
+ /* unubi volumes */
+ rc = unubi_volumes(fpin, vols, vols_len, &a);
+ if (rc < 0) {
+ /* ERR_MSG("error encountered while working on image file: "
+ "%s\n", a.img_path); */
+ rc = -rc;
+ goto err;
+ }
+
+ err:
+ free(a.vols);
+ if (free_a_odir != 0)
+ free(a.odir_path);
+ if (fpin != NULL)
+ fclose(fpin);
+ if (vols_len > 0)
+ free(vols);
+ return rc;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi_analyze.c b/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi_analyze.c
new file mode 100644
index 0000000..ceaa85f
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi_analyze.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ *
+ * unubi uses the following functions to generate analysis output based on
+ * the header information in a raw-UBI image
+ */
+
+/*
+ * TODO: use OOB data to check for eraseblock validity in NAND images
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <mtd_swab.h>
+
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define EC_X_INT 50
+
+/**
+ * intcmp - function needed by qsort to order integers
+ **/
+int intcmp(const void *a, const void *b)
+{
+ int A = *(int *)a;
+ int B = *(int *)b;
+ return A - B;
+}
+
+int longcmp(const void *a, const void *b)
+{
+ long long A = *(long long *)a;
+ long long B = *(long long *)b;
+ return A - B;
+}
+
+
+/**
+ * unubi_analyze_group_index - finds the normalized index in an array
+ * item: look for this item in the array
+ * array: array to search through
+ * size: length of the array
+ * array should be sorted for this algorithm to perform properly;
+ * if the item is not found returns -1, otherwise return value is the
+ * index in the array (note this contricts the array size to 2^32-1);
+ **/
+int
+norm_index(uint32_t item, uint32_t *array, size_t length)
+{
+ size_t i, index;
+
+ for (index = 0, i = 0; i < length; i++) {
+ if ((i != 0) && (array[i] != array[i - 1]))
+ index++;
+
+ if (item == array[i])
+ return index;
+ }
+
+ return -1;
+}
+
+
+/**
+ * unubi_analyze_ec_hdr - generate data table and plot script
+ * first: head of simple linked list
+ * path: folder to write into
+ * generates a data file containing the eraseblock index in the image
+ * and the erase counter found in its ec header;
+ * if the crc check fails, the line is commented out in the data file;
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_ec_hdr(struct eb_info *first, const char *path)
+{
+ char filename[PATH_MAX + 1];
+ size_t count, eraseblocks;
+ uint32_t crc, crc32_table[256];
+ uint64_t *erase_counts;
+ FILE* fpdata;
+ FILE* fpplot;
+ struct eb_info *cur;
+
+ if (first == NULL)
+ return -1;
+
+ /* crc check still needed for `first' linked list */
+ init_crc32_table(crc32_table);
+
+ /* prepare output files */
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA);
+ fpdata = fopen(filename, "w");
+ if (fpdata == NULL)
+ return -1;
+
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT);
+ fpplot = fopen(filename, "w");
+ if (fpplot == NULL) {
+ fclose(fpdata);
+ return -1;
+ }
+
+ /* make executable */
+ chmod(filename, 0755);
+
+ /* first run: count elements */
+ count = 0;
+ cur = first;
+ while (cur != NULL) {
+ cur = cur->next;
+ count++;
+ }
+ eraseblocks = count;
+
+ erase_counts = malloc(eraseblocks * sizeof(*erase_counts));
+ if (!erase_counts) {
+ perror("out of memory");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts));
+
+ /* second run: populate array to sort */
+ count = 0;
+ cur = first;
+ while (cur != NULL) {
+ erase_counts[count] = be64_to_cpu(cur->ec.ec);
+ cur = cur->next;
+ count++;
+ }
+ qsort(erase_counts, eraseblocks, sizeof(*erase_counts),
+ (void *)longcmp);
+
+ /* third run: generate data file */
+ count = 0;
+ cur = first;
+ fprintf(fpdata, "# eraseblock_no actual_erase_count "
+ "sorted_erase_count\n");
+ while (cur != NULL) {
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec,
+ UBI_EC_HDR_SIZE_CRC);
+
+ if ((be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) ||
+ (crc != be32_to_cpu(cur->ec.hdr_crc)))
+ fprintf(fpdata, "# ");
+
+ fprintf(fpdata, "%zu %llu %llu", count,
+ (unsigned long long)be64_to_cpu(cur->ec.ec),
+ (unsigned long long)erase_counts[count]);
+
+ if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC)
+ fprintf(fpdata, " ## bad magic: %08x",
+ be32_to_cpu(cur->ec.magic));
+
+ if (crc != be32_to_cpu(cur->ec.hdr_crc))
+ fprintf(fpdata, " ## CRC mismatch: given=%08x, "
+ "calc=%08x", be32_to_cpu(cur->ec.hdr_crc),
+ crc);
+
+ fprintf(fpdata, "\n");
+
+ cur = cur->next;
+ count++;
+ }
+ fclose(fpdata);
+
+ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+ fprintf(fpplot, "set xlabel \"eraseblock\"\n");
+
+ /* fourth run: generate plot file xtics */
+ count = 0;
+ cur = first;
+ fprintf(fpplot, "set xtics (");
+ while (cur != NULL) {
+ if ((count % EC_X_INT) == 0) {
+ if (count > 0)
+ fprintf(fpplot, ", ");
+ fprintf(fpplot, "%zd", count);
+ }
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ fprintf(fpplot, "set ylabel \"erase count\"\n");
+ fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1);
+ fprintf(fpplot, "# set yrange [-1:%llu]\n",
+ (unsigned long long)erase_counts[eraseblocks - 1] + 1);
+ fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n",
+ FN_EH_DATA, FN_EH_DATA);
+ fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n",
+ FN_EH_DATA, FN_EH_DATA);
+ fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ fclose(fpplot);
+
+ return 0;
+}
+
+
+/**
+ * unubi_analyze_vid_hdr - generate data table and plot script
+ * head: head of complex linked list (eb_chain)
+ * path: folder to write into
+ * generates a data file containing the volume id, logical number, leb version,
+ * and data size from the vid header;
+ * all eraseblocks listed in the eb_chain are valid (checked in unubi);
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_vid_hdr(struct eb_info **head, const char *path)
+{
+ char filename[PATH_MAX + 1];
+ int rc, y1, y2;
+ size_t count, step, breadth;
+ uint32_t *leb_versions, *data_sizes;
+ FILE* fpdata;
+ FILE* fpplot;
+ struct eb_info *cur;
+
+ if (head == NULL || *head == NULL)
+ return -1;
+
+ rc = 0;
+ fpdata = NULL;
+ fpplot = NULL;
+ data_sizes = NULL;
+ leb_versions = NULL;
+
+ /* prepare output files */
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA);
+ fpdata = fopen(filename, "w");
+ if (fpdata == NULL) {
+ rc = -1;
+ goto exit;
+ }
+
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT);
+ fpplot = fopen(filename, "w");
+ if (fpplot == NULL) {
+ rc = -1;
+ goto exit;
+ }
+
+ /* make executable */
+ chmod(filename, 0755);
+
+ /* first run: count elements */
+ count = 0;
+ cur = *head;
+ while (cur != NULL) {
+ cur = cur->next;
+ count++;
+ }
+ breadth = count;
+
+ leb_versions = malloc(breadth * sizeof(uint32_t));
+ if (leb_versions == NULL) {
+ rc = -1;
+ goto exit;
+ }
+ memset(leb_versions, 0, breadth * sizeof(uint32_t));
+
+ data_sizes = malloc(breadth * sizeof(uint32_t));
+ if (data_sizes == NULL) {
+ rc = -1;
+ goto exit;
+ }
+ memset(data_sizes, 0, breadth * sizeof(*data_sizes));
+
+ /* second run: populate arrays to sort */
+ count = 0;
+ cur = *head;
+ while (cur != NULL) {
+ leb_versions[count] = be32_to_cpu(cur->vid.leb_ver);
+ data_sizes[count] = be32_to_cpu(cur->vid.data_size);
+ cur = cur->next;
+ count++;
+ }
+ qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp);
+ qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp);
+
+ /* third run: generate data file */
+ count = 0;
+ cur = *head;
+ fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver "
+ "y2_axis data_size\n");
+ while (cur != NULL) {
+ y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions,
+ breadth);
+ y2 = norm_index(be32_to_cpu(cur->vid.data_size), data_sizes,
+ breadth);
+
+ if ((y1 == -1) || (y2 == -1)) {
+ rc = -1;
+ goto exit;
+ }
+
+ fprintf(fpdata, "%zu %u %u %u %u %u %u\n",
+ count,
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ y1,
+ be32_to_cpu(cur->vid.leb_ver),
+ y2,
+ be32_to_cpu(cur->vid.data_size));
+ cur = cur->next;
+ count++;
+ }
+
+ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+ fprintf(fpplot, "set xlabel \"volume\"\n");
+
+ /* fourth run: generate plot file xtics */
+ count = 0;
+ step = 0;
+ cur = *head;
+ fprintf(fpplot, "set xtics (");
+ while (cur != NULL) {
+ if (count > 0)
+ fprintf(fpplot, ", ");
+ if (step != be32_to_cpu(cur->vid.vol_id)) {
+ step = be32_to_cpu(cur->vid.vol_id);
+ fprintf(fpplot, "\"%zd\" %zd 0", step, count);
+ }
+ else
+ fprintf(fpplot, "\"%d\" %zd 1",
+ be32_to_cpu(cur->vid.lnum), count);
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+ fprintf(fpplot, "set nox2tics\n");
+
+ /* fifth run: generate plot file ytics */
+ count = 0;
+ cur = *head;
+ fprintf(fpplot, "set ylabel \"leb version\"\n");
+ fprintf(fpplot, "set ytics (");
+ while (cur->next != NULL) {
+ y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions,
+ breadth);
+
+ if (y1 == -1) {
+ rc = -1;
+ goto exit;
+ }
+
+ if (count > 0)
+ fprintf(fpplot, ", ");
+
+ fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.leb_ver),
+ y1);
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ /* sixth run: generate plot file y2tics */
+ count = 0;
+ cur = *head;
+ fprintf(fpplot, "set y2label \"data size\"\n");
+ fprintf(fpplot, "set y2tics (");
+ while (cur != NULL) {
+ y2 = norm_index(be32_to_cpu(cur->vid.data_size),
+ data_sizes, breadth);
+
+ if (y2 == -1) {
+ rc = -1;
+ goto exit;
+ }
+
+ if (count > 0)
+ fprintf(fpplot, ", ");
+
+ fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.data_size),
+ y2);
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth);
+ y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth);
+ fprintf(fpplot, "set xrange [-1:%zu]\n", count + 1);
+ fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1);
+ fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1);
+ fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" "
+ "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA);
+ fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" "
+ "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA);
+ fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ exit:
+ if (fpdata != NULL)
+ fclose(fpdata);
+ if (fpplot != NULL)
+ fclose(fpplot);
+ if (data_sizes != NULL)
+ free(data_sizes);
+ if (leb_versions != NULL)
+ free(leb_versions);
+
+ return rc;
+}
+
+
+/**
+ * unubi_analyze - run all analyses
+ * head: eb_chain head
+ * first: simple linked list of eraseblock headers (use .next)
+ * path: directory (without trailing slash) to output to
+ * returns 0 upon successful completion, or -1 otherwise
+ **/
+int
+unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path)
+{
+ int ec_rc, vid_rc;
+
+ if (path == NULL)
+ return -1;
+
+ ec_rc = unubi_analyze_ec_hdr(first, path);
+ vid_rc = unubi_analyze_vid_hdr(head, path);
+ if (ec_rc < 0 || vid_rc < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi_analyze.h b/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi_analyze.h
new file mode 100644
index 0000000..c478f53
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/src/unubi_analyze.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __UNUBI_ANALYZE_H__
+#define __UNUBI_ANALYZE_H__
+
+/*
+ * Author: Drake Dowsett
+ * Contact: Andreas Arnez (arnez@de.ibm.com)
+ *
+ * Eraseblock Chain
+ *
+ * A linked list structure to order eraseblocks by volume and logical number
+ * and to update by version number. Doesn't contain actual eraseblock data
+ * but rather the erasecounter and volume id headers as well as a position
+ * indicator.
+ *
+ * Diagram Example:
+ *
+ * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL
+ * | | | | | |
+ * NULL [V1.1v1] [V1.2v0] [V2.0v1] NULL [V2.2v0]
+ * | | | |
+ * [V1.1v0] NULL [V2.0v0] NULL
+ * | |
+ * NULL NULL
+ *
+ * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A,
+ * lnum B and leb_ver C
+ * -> represents the `next' pointer
+ * | represents the `older' pointer
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mtd/ubi-media.h>
+
+#define FN_EH_STAT "analysis_blocks.txt"
+#define FN_EH_DATA "analysis_ec_hdr.data"
+#define FN_EH_PLOT "analysis_ec_hdr.plot"
+#define FN_VH_DATA "analysis_vid_hdr.data"
+#define FN_VH_PLOT "analysis_vid_hdr.plot"
+
+struct eb_info {
+ struct ubi_ec_hdr ec;
+ struct ubi_vid_hdr vid;
+
+ fpos_t eb_top;
+ uint32_t linear;
+ int ec_crc_ok;
+ int vid_crc_ok;
+ int data_crc_ok;
+ uint32_t phys_addr;
+ int phys_block;
+
+ struct eb_info *next;
+ struct eb_info *older;
+};
+
+int eb_chain_insert(struct eb_info **head, struct eb_info *item);
+
+int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+ struct eb_info **pos);
+
+int eb_chain_print(FILE *stream, struct eb_info *head);
+
+int eb_chain_destroy(struct eb_info **head);
+
+int unubi_analyze(struct eb_info **head, struct eb_info *first,
+ const char *path);
+
+#endif /* __UNUBI_ANALYZE_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/old-utils/testcases.txt b/mtd-utils-1.3.1/ubi-utils/old-utils/testcases.txt
new file mode 100644
index 0000000..dcc1c35
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/old-utils/testcases.txt
@@ -0,0 +1,9 @@
+1. Start some large update, but write there byte-by-byte
+
+2. start update for N bytes, write N-x bytes, then write y bytes, y>x.
+ You have to see that at the last write only x bytes were written,
+ but y-x bytes were not. we may vary x a bit. good number would be
+ 1, 128, 128Ki-128...
+
+3. Try to start update for x bytes, write x bytes, then try to write more.
+ Check that it is impossible to write more.
diff --git a/mtd-utils-1.3.1/ubi-utils/src/common.c b/mtd-utils-1.3.1/ubi-utils/src/common.c
new file mode 100644
index 0000000..da5156d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/common.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2007, 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file contains various common stuff used by UBI utilities.
+ *
+ * Authors: Artem Bityutskiy
+ * Adrian Hunter
+ */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "common.h"
+
+/**
+ * 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;
+}
+
+/**
+ * ubiutils_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.
+ */
+long long ubiutils_get_bytes(const char *str)
+{
+ char *endp;
+ long long bytes = strtoull(str, &endp, 0);
+
+ if (endp == str || bytes < 0) {
+ fprintf(stderr, "incorrect amount of bytes: \"%s\"\n", str);
+ return -1;
+ }
+
+ if (*endp != '\0') {
+ int mult = get_multiplier(endp);
+
+ if (mult == -1) {
+ fprintf(stderr, "bad size specifier: \"%s\" - "
+ "should be 'KiB', 'MiB' or 'GiB'\n", endp);
+ return -1;
+ }
+ bytes *= mult;
+ }
+
+ return bytes;
+}
+
+/**
+ * ubiutils_print_bytes - print bytes.
+ * @bytes: variable to print
+ * @bracket: whether brackets have to be put or not
+ *
+ * This is a helper function which prints amount of bytes in a human-readable
+ * form, i.e., it prints the exact amount of bytes following by the approximate
+ * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes
+ * is.
+ */
+void ubiutils_print_bytes(long long bytes, int bracket)
+{
+ const char *p;
+
+ if (bracket)
+ p = " (";
+ else
+ p = ", ";
+
+ printf("%lld bytes", bytes);
+
+ if (bytes > 1024 * 1024 * 1024)
+ printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024));
+ else if (bytes > 1024 * 1024)
+ printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024));
+ else if (bytes > 1024 && bytes != 0)
+ printf("%s%.1f KiB", p, (double)bytes / 1024);
+ else
+ return;
+
+ if (bracket)
+ printf(")");
+}
+
+/**
+ * ubiutils_print_text - print text and fold it.
+ * @stream: file stream to print to
+ * @text: text to print
+ * @width: maximum allowed text width
+ *
+ * Print text and fold it so that each line would not have more then @width
+ * characters.
+ */
+void ubiutils_print_text(FILE *stream, const char *text, int width)
+{
+ int pos, bpos = 0;
+ const char *p;
+ char line[1024];
+
+ if (width > 1023) {
+ fprintf(stream, "%s\n", text);
+ return;
+ }
+ p = text;
+ pos = 0;
+ while (p[pos]) {
+ while (!isspace(p[pos])) {
+ line[pos] = p[pos];
+ if (!p[pos])
+ break;
+ ++pos;
+ if (pos == width) {
+ line[pos] = '\0';
+ fprintf(stream, "%s\n", line);
+ p += pos;
+ pos = 0;
+ }
+ }
+ while (pos < width) {
+ line[pos] = p[pos];
+ if (!p[pos]) {
+ bpos = pos;
+ break;
+ }
+ if (isspace(p[pos]))
+ bpos = pos;
+ ++pos;
+ }
+ line[bpos] = '\0';
+ fprintf(stream, "%s\n", line);
+ p += bpos;
+ pos = 0;
+ while (p[pos] && isspace(p[pos]))
+ ++p;
+ }
+}
+
+/**
+ * ubiutils_srand - randomly seed the standard pseudo-random generator.
+ *
+ * This helper function seeds the standard libc pseudo-random generator with a
+ * more or less random value to make sure the 'rand()' call does not return the
+ * same sequence every time UBI utilities run. Returns zero in case of success
+ * and a %-1 in case of error.
+ */
+int ubiutils_srand(void)
+{
+ struct timeval tv;
+ struct timezone tz;
+ unsigned int seed;
+
+ /*
+ * Just assume that a combination of the PID + current time is a
+ * reasonably random number.
+ */
+ if (gettimeofday(&tv, &tz))
+ return -1;
+
+ seed = (unsigned int)tv.tv_sec;
+ seed += (unsigned int)tv.tv_usec;
+ seed *= getpid();
+ seed %= RAND_MAX;
+ srand(seed);
+ return 0;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/common.h b/mtd-utils-1.3.1/ubi-utils/src/common.h
new file mode 100644
index 0000000..d14cb48
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/common.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) Artem Bityutskiy, 2007, 2008
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __UBI_UTILS_COMMON_H__
+#define __UBI_UTILS_COMMON_H__
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MIN(a ,b) ((a) < (b) ? (a) : (b))
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/* Verbose messages */
+#define verbose(verbose, fmt, ...) do { \
+ if (verbose) \
+ printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \
+} while(0)
+
+/* Normal messages */
+#define normsg(fmt, ...) do { \
+ printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \
+} while(0)
+#define normsg_cont(fmt, ...) do { \
+ printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \
+} while(0)
+#define normsg_cont(fmt, ...) do { \
+ printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \
+} while(0)
+
+/* Error messages */
+#define errmsg(fmt, ...) ({ \
+ fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \
+ -1; \
+})
+
+/* System error messages */
+#define sys_errmsg(fmt, ...) ({ \
+ int _err = errno; \
+ size_t _i; \
+ fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \
+ for (_i = 0; _i < sizeof(PROGRAM_NAME) + 1; _i++) \
+ fprintf(stderr, " "); \
+ fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \
+ -1; \
+})
+
+/* Warnings */
+#define warnmsg(fmt, ...) do { \
+ fprintf(stderr, PROGRAM_NAME ": warning!: " fmt "\n", ##__VA_ARGS__); \
+} while(0)
+
+static inline int is_power_of_2(unsigned long long n)
+{
+ return (n != 0 && ((n & (n - 1)) == 0));
+}
+
+long long ubiutils_get_bytes(const char *str);
+void ubiutils_print_bytes(long long bytes, int bracket);
+void ubiutils_print_text(FILE *stream, const char *txt, int len);
+int ubiutils_srand(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__UBI_UTILS_COMMON_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/src/crc32.c b/mtd-utils-1.3.1/ubi-utils/src/crc32.c
new file mode 100644
index 0000000..6b1e50c
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/crc32.c
@@ -0,0 +1,95 @@
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
diff --git a/mtd-utils-1.3.1/ubi-utils/src/crc32.h b/mtd-utils-1.3.1/ubi-utils/src/crc32.h
new file mode 100644
index 0000000..c001fcf
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/crc32.h
@@ -0,0 +1,17 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Returns a 32-bit CRC of the contents of the buffer */
+static inline uint32_t crc32(uint32_t val, const void *ss, int len)
+{
+ const unsigned char *s = ss;
+ while (--len >= 0)
+ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+ return val;
+}
+
+#endif
diff --git a/mtd-utils-1.3.1/ubi-utils/src/dictionary.c b/mtd-utils-1.3.1/ubi-utils/src/dictionary.c
new file mode 100644
index 0000000..b7c9ebf
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/dictionary.c
@@ -0,0 +1,405 @@
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.c
+ @author N. Devillard
+ @date Sep 2007
+ @version $Revision: 1.27 $
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*
+ $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $
+ $Revision: 1.27 $
+*/
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ 1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ 128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, int size)
+{
+ void * newptr ;
+
+ newptr = calloc(2*size, 1);
+ if (newptr==NULL) {
+ return NULL ;
+ }
+ memcpy(newptr, ptr, size);
+ free(ptr);
+ return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(char * s)
+{
+ char * t ;
+ if (!s)
+ return NULL ;
+ t = malloc(strlen(s)+1) ;
+ if (t) {
+ strcpy(t,s);
+ }
+ return t ;
+}
+
+/*---------------------------------------------------------------------------
+ Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(char * key)
+{
+ int len ;
+ unsigned hash ;
+ int i ;
+
+ len = strlen(key);
+ for (hash=0, i=0 ; i<len ; i++) {
+ hash += (unsigned)key[i] ;
+ hash += (hash<<10);
+ hash ^= (hash>>6) ;
+ }
+ hash += (hash <<3);
+ hash ^= (hash >>11);
+ hash += (hash <<15);
+ return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size)
+{
+ dictionary * d ;
+
+ /* If no size was specified, allocate space for DICTMINSZ */
+ if (size<DICTMINSZ) size=DICTMINSZ ;
+
+ if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
+ return NULL;
+ }
+ d->size = size ;
+ d->val = (char **)calloc(size, sizeof(char*));
+ d->key = (char **)calloc(size, sizeof(char*));
+ d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
+ return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+ int i ;
+
+ if (d==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]!=NULL)
+ free(d->key[i]);
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ }
+ free(d->val);
+ free(d->key);
+ free(d->hash);
+ free(d);
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, char * key, char * def)
+{
+ unsigned hash ;
+ int i ;
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ return d->val[i] ;
+ }
+ }
+ }
+ return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, char * key, char * val)
+{
+ int i ;
+ unsigned hash ;
+
+ if (d==NULL || key==NULL) return -1 ;
+
+ /* Compute hash for this key */
+ hash = dictionary_hash(key) ;
+ /* Find if value is already in dictionary */
+ if (d->n>0) {
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (hash==d->hash[i]) { /* Same hash value */
+ if (!strcmp(key, d->key[i])) { /* Same key */
+ /* Found a value: modify and return */
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ /* Value has been modified: return */
+ return 0 ;
+ }
+ }
+ }
+ }
+ /* Add a new value */
+ /* See if dictionary needs to grow */
+ if (d->n==d->size) {
+
+ /* Reached maximum size: reallocate dictionary */
+ d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ;
+ d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ;
+ d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
+ if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
+ /* Cannot grow dictionary */
+ return -1 ;
+ }
+ /* Double size */
+ d->size *= 2 ;
+ }
+
+ /* Insert key in the first empty slot */
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL) {
+ /* Add key here */
+ break ;
+ }
+ }
+ /* Copy key */
+ d->key[i] = xstrdup(key);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ d->hash[i] = hash;
+ d->n ++ ;
+ return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, char * key)
+{
+ unsigned hash ;
+ int i ;
+
+ if (key == NULL) {
+ return;
+ }
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ /* Found key */
+ break ;
+ }
+ }
+ }
+ if (i>=d->size)
+ /* Key not found */
+ return ;
+
+ free(d->key[i]);
+ d->key[i] = NULL ;
+ if (d->val[i]!=NULL) {
+ free(d->val[i]);
+ d->val[i] = NULL ;
+ }
+ d->hash[i] = 0 ;
+ d->n -- ;
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+ int i ;
+
+ if (d==NULL || out==NULL) return ;
+ if (d->n<1) {
+ fprintf(out, "empty dictionary\n");
+ return ;
+ }
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]) {
+ fprintf(out, "%20s\t[%s]\n",
+ d->key[i],
+ d->val[i] ? d->val[i] : "UNDEF");
+ }
+ }
+ return ;
+}
+
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+ dictionary * d ;
+ char * val ;
+ int i ;
+ char cval[90] ;
+
+ /* Allocate dictionary */
+ printf("allocating...\n");
+ d = dictionary_new(0);
+
+ /* Set values in dictionary */
+ printf("setting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ dictionary_set(d, cval, "salut");
+ }
+ printf("getting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ val = dictionary_get(d, cval, DICT_INVALID_KEY);
+ if (val==DICT_INVALID_KEY) {
+ printf("cannot get value for key [%s]\n", cval);
+ }
+ }
+ printf("unsetting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ dictionary_unset(d, cval);
+ }
+ if (d->n != 0) {
+ printf("error deleting values\n");
+ }
+ printf("deallocating...\n");
+ dictionary_del(d);
+ return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/mtd-utils-1.3.1/ubi-utils/src/dictionary.h b/mtd-utils-1.3.1/ubi-utils/src/dictionary.h
new file mode 100644
index 0000000..c7d1790
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/dictionary.h
@@ -0,0 +1,174 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.h
+ @author N. Devillard
+ @date Sep 2007
+ @version $Revision: 1.12 $
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*
+ $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $
+ $Author: ndevilla $
+ $Date: 2007-11-23 21:37:00 $
+ $Revision: 1.12 $
+*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*---------------------------------------------------------------------------
+ New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dictionary object
+
+ This object contains a list of string/string associations. Each
+ association is identified by a unique string key. Looking up values
+ in the dictionary is speeded up by the use of a (hopefully collision-free)
+ hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+ int n ; /** Number of entries in dictionary */
+ int size ; /** Storage size */
+ char ** val ; /** List of string values */
+ char ** key ; /** List of string keys */
+ unsigned * hash ; /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+ Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, char * key, char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
+
+#endif
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libiniparser.c b/mtd-utils-1.3.1/ubi-utils/src/libiniparser.c
new file mode 100644
index 0000000..3bea51e
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libiniparser.c
@@ -0,0 +1,646 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.c
+ @author N. Devillard
+ @date Sep 2007
+ @version 3.0
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*
+ $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $
+ $Revision: 2.18 $
+ $Date: 2008-01-03 18:35:39 $
+*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include <libiniparser.h>
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ (1024)
+#define INI_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+ LINE_UNPROCESSED,
+ LINE_ERROR,
+ LINE_EMPTY,
+ LINE_COMMENT,
+ LINE_SECTION,
+ LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Convert a string to lowercase.
+ @param s String to convert.
+ @return ptr to statically allocated string.
+
+ This function returns a pointer to a statically allocated string
+ containing a lowercased version of the input string. Do not free
+ or modify the returned string! Since the returned string is statically
+ allocated, it will be modified at each function call (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strlwc(const char * s)
+{
+ static char l[ASCIILINESZ+1];
+ int i ;
+
+ if (s==NULL) return NULL ;
+ memset(l, 0, ASCIILINESZ+1);
+ i=0 ;
+ while (s[i] && i<ASCIILINESZ) {
+ l[i] = (char)tolower((int)s[i]);
+ i++ ;
+ }
+ l[ASCIILINESZ]=(char)0;
+ return l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Remove blanks at the beginning and the end of a string.
+ @param s String to parse.
+ @return ptr to statically allocated string.
+
+ This function returns a pointer to a statically allocated string,
+ which is identical to the input string, except that all blank
+ characters at the end and the beg. of the string have been removed.
+ Do not free or modify the returned string! Since the returned string
+ is statically allocated, it will be modified at each function call
+ (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strstrip(char * s)
+{
+ static char l[ASCIILINESZ+1];
+ char * last ;
+
+ if (s==NULL) return NULL ;
+
+ while (isspace((int)*s) && *s) s++;
+ memset(l, 0, ASCIILINESZ+1);
+ strcpy(l, s);
+ last = l + strlen(l);
+ while (last > l) {
+ if (!isspace((int)*(last-1)))
+ break ;
+ last -- ;
+ }
+ *last = (char)0;
+ return (char*)l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+ int i ;
+ int nsec ;
+
+ if (d==NULL) return -1 ;
+ nsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ nsec ++ ;
+ }
+ }
+ return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+ int i ;
+ int foundsec ;
+
+ if (d==NULL || n<0) return NULL ;
+ foundsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ foundsec++ ;
+ if (foundsec>n)
+ break ;
+ }
+ }
+ if (foundsec<=n) {
+ return NULL ;
+ }
+ return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f)
+{
+ int i ;
+
+ if (d==NULL || f==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (d->val[i]!=NULL) {
+ fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+ } else {
+ fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+ }
+ }
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(dictionary * d, FILE * f)
+{
+ int i, j ;
+ char keym[ASCIILINESZ+1];
+ int nsec ;
+ char * secname ;
+ int seclen ;
+
+ if (d==NULL || f==NULL) return ;
+
+ nsec = iniparser_getnsec(d);
+ if (nsec<1) {
+ /* No section in file: dump all keys as they are */
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+ }
+ return ;
+ }
+ for (i=0 ; i<nsec ; i++) {
+ secname = iniparser_getsecname(d, i) ;
+ seclen = (int)strlen(secname);
+ fprintf(f, "\n[%s]\n", secname);
+ sprintf(keym, "%s:", secname);
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ fprintf(f,
+ "%-30s = %s\n",
+ d->key[j]+seclen+1,
+ d->val[j] ? d->val[j] : "");
+ }
+ }
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+ char * lc_key ;
+ char * sval ;
+
+ if (d==NULL || key==NULL)
+ return def ;
+
+ lc_key = strlwc(key);
+ sval = dictionary_get(d, lc_key, def);
+ return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ "42" -> 42
+ "042" -> 34 (octal -> decimal)
+ "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==INI_INVALID_KEY) return notfound ;
+ return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, char * key, double notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==INI_INVALID_KEY) return notfound ;
+ return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+ char * c ;
+ int ret ;
+
+ c = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (c==INI_INVALID_KEY) return notfound ;
+ if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+ ret = 1 ;
+ } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+ ret = 0 ;
+ } else {
+ ret = notfound ;
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+ dictionary * ini,
+ char * entry
+)
+{
+ int found=0 ;
+ if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+ found = 1 ;
+ }
+ return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, char * entry, char * val)
+{
+ return dictionary_set(ini, strlwc(entry), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, char * entry)
+{
+ dictionary_unset(ini, strlwc(entry));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Load a single line from an INI file
+ @param input_line Input line, may be concatenated multi-line input
+ @param section Output space to store section
+ @param key Output space to store key
+ @param value Output space to store value
+ @return line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+ char * input_line,
+ char * section,
+ char * key,
+ char * value)
+{
+ line_status sta ;
+ char line[ASCIILINESZ+1];
+ int len ;
+
+ strcpy(line, strstrip(input_line));
+ len = (int)strlen(line);
+
+ sta = LINE_UNPROCESSED ;
+ if (len<1) {
+ /* Empty line */
+ sta = LINE_EMPTY ;
+ } else if (line[0]=='#') {
+ /* Comment line */
+ sta = LINE_COMMENT ;
+ } else if (line[0]=='[' && line[len-1]==']') {
+ /* Section name */
+ sscanf(line, "[%[^]]", section);
+ strcpy(section, strstrip(section));
+ strcpy(section, strlwc(section));
+ sta = LINE_SECTION ;
+ } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+ || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
+ || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
+ /* Usual key=value, with or without comments */
+ strcpy(key, strstrip(key));
+ strcpy(key, strlwc(key));
+ strcpy(value, strstrip(value));
+ /*
+ * sscanf cannot handle '' or "" as empty values
+ * this is done here
+ */
+ if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+ value[0]=0 ;
+ }
+ sta = LINE_VALUE ;
+ } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+ || sscanf(line, "%[^=] %[=]", key, value) == 2) {
+ /*
+ * Special cases:
+ * key=
+ * key=;
+ * key=#
+ */
+ strcpy(key, strstrip(key));
+ strcpy(key, strlwc(key));
+ value[0]=0 ;
+ sta = LINE_VALUE ;
+ } else {
+ /* Generate syntax error */
+ sta = LINE_ERROR ;
+ }
+ return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+ FILE * in ;
+
+ char line [ASCIILINESZ+1] ;
+ char section [ASCIILINESZ+1] ;
+ char key [ASCIILINESZ+1] ;
+ char tmp [ASCIILINESZ+1] ;
+ char val [ASCIILINESZ+1] ;
+
+ int last=0 ;
+ int len ;
+ int lineno=0 ;
+ int errs=0;
+
+ dictionary * dict ;
+
+ if ((in=fopen(ininame, "r"))==NULL) {
+ fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+ return NULL ;
+ }
+
+ dict = dictionary_new(0) ;
+ if (!dict) {
+ fclose(in);
+ return NULL ;
+ }
+
+ memset(line, 0, ASCIILINESZ);
+ memset(section, 0, ASCIILINESZ);
+ memset(key, 0, ASCIILINESZ);
+ memset(val, 0, ASCIILINESZ);
+ last=0 ;
+
+ while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+ lineno++ ;
+ len = (int)strlen(line)-1;
+ /* Safety check against buffer overflows */
+ if (line[len]!='\n') {
+ fprintf(stderr,
+ "iniparser: input line too long in %s (%d)\n",
+ ininame,
+ lineno);
+ dictionary_del(dict);
+ fclose(in);
+ return NULL ;
+ }
+ /* Get rid of \n and spaces at end of line */
+ while ((len>=0) &&
+ ((line[len]=='\n') || (isspace(line[len])))) {
+ line[len]=0 ;
+ len-- ;
+ }
+ /* Detect multi-line */
+ if (line[len]=='\\') {
+ /* Multi-line value */
+ last=len ;
+ continue ;
+ } else {
+ last=0 ;
+ }
+ switch (iniparser_line(line, section, key, val)) {
+ case LINE_EMPTY:
+ case LINE_COMMENT:
+ break ;
+
+ case LINE_SECTION:
+ errs = dictionary_set(dict, section, NULL);
+ break ;
+
+ case LINE_VALUE:
+ sprintf(tmp, "%s:%s", section, key);
+ errs = dictionary_set(dict, tmp, val) ;
+ break ;
+
+ case LINE_ERROR:
+ fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+ ininame,
+ lineno);
+ fprintf(stderr, "-> %s\n", line);
+ errs++ ;
+ break;
+
+ default:
+ break ;
+ }
+ memset(line, 0, ASCIILINESZ);
+ last=0;
+ if (errs<0) {
+ fprintf(stderr, "iniparser: memory allocation failure\n");
+ break ;
+ }
+ }
+ if (errs) {
+ dictionary_del(dict);
+ dict = NULL ;
+ }
+ fclose(in);
+ return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+ dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libmtd.c b/mtd-utils-1.3.1/ubi-utils/src/libmtd.c
new file mode 100644
index 0000000..249fa23
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libmtd.c
@@ -0,0 +1,1055 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * MTD library.
+ */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+#include <libmtd.h>
+#include "libmtd_int.h"
+#include "common.h"
+
+/**
+ * mkpath - compose full path from 2 given components.
+ * @path: the first component
+ * @name: the second component
+ *
+ * This function returns the resulting path in case of success and %NULL in
+ * case of failure.
+ */
+static char *mkpath(const char *path, const char *name)
+{
+ char *n;
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ n = malloc(len1 + len2 + 2);
+ if (!n) {
+ sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2);
+ return NULL;
+ }
+
+ memcpy(n, path, len1);
+ if (n[len1 - 1] != '/')
+ n[len1++] = '/';
+
+ memcpy(n + len1, name, len2 + 1);
+ return n;
+}
+
+/**
+ * read_data - read data from a file.
+ * @file: the file to read from
+ * @buf: the buffer to read to
+ * @buf_len: buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure. Note, if the file contains more then @buf_len bytes of
+ * date, this function fails with %EINVAL error code.
+ */
+static int read_data(const char *file, void *buf, int buf_len)
+{
+ int fd, rd, tmp, tmp1;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+
+ if (rd == buf_len) {
+ errmsg("contents of \"%s\" is too long", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ ((char *)buf)[rd] = '\0';
+
+ /* Make sure all data is read */
+ tmp1 = read(fd, &tmp, 1);
+ if (tmp1 == 1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+ if (tmp1) {
+ errmsg("file \"%s\" contains too much data (> %d bytes)",
+ file, buf_len);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (close(fd)) {
+ sys_errmsg("close failed on \"%s\"", file);
+ return -1;
+ }
+
+ return rd;
+
+out_error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * read_major - read major and minor numbers from a file.
+ * @file: name of the file to read from
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns % in case of success, and %-1 in case of failure.
+ */
+static int read_major(const char *file, int *major, int *minor)
+{
+ int ret;
+ char buf[50];
+
+ ret = read_data(file, buf, 50);
+ if (ret < 0)
+ return ret;
+
+ ret = sscanf(buf, "%d:%d\n", major, minor);
+ if (ret != 2) {
+ errno = EINVAL;
+ return errmsg("\"%s\" does not have major:minor format", file);
+ }
+
+ if (*major < 0 || *minor < 0) {
+ errno = EINVAL;
+ return errmsg("bad major:minor %d:%d in \"%s\"",
+ *major, *minor, file);
+ }
+
+ return 0;
+}
+
+/**
+ * dev_get_major - get major and minor numbers of an MTD device.
+ * @lib: libmtd descriptor
+ * @dev_num: MTD device number
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns zero in case of success and %-1 in case of failure.
+ */
+static int dev_get_major(struct libmtd *lib, int dev_num, int *major, int *minor)
+{
+ char file[strlen(lib->mtd_dev) + 50];
+
+ sprintf(file, lib->mtd_dev, dev_num);
+ return read_major(file, major, minor);
+}
+
+/**
+ * dev_read_data - read data from an MTD device's sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: MTD device number
+ * @buf: buffer to read to
+ * @buf_len: buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len)
+{
+ char file[strlen(patt) + 100];
+
+ sprintf(file, patt, dev_num);
+ return read_data(file, buf, buf_len);
+}
+
+/**
+ * read_hex_ll - read a hex 'long long' value from a file.
+ * @file: the file to read from
+ * @value: the result is stored here
+ *
+ * This function reads file @file and interprets its contents as hexadecimal
+ * 'long long' integer. If this is not true, it fails with %EINVAL error code.
+ * Returns %0 in case of success and %-1 in case of failure.
+ */
+static int read_hex_ll(const char *file, long long *value)
+{
+ int fd, rd;
+ char buf[50];
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, sizeof(buf));
+ if (rd == -1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+ if (rd == sizeof(buf)) {
+ errmsg("contents of \"%s\" is too long", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+ buf[rd] = '\0';
+
+ if (sscanf(buf, "%llx\n", value) != 1) {
+ errmsg("cannot read integer from \"%s\"\n", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (*value < 0) {
+ errmsg("negative value %lld in \"%s\"", *value, file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (close(fd))
+ return sys_errmsg("close failed on \"%s\"", file);
+
+ return 0;
+
+out_error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * read_pos_ll - read a positive 'long long' value from a file.
+ * @file: the file to read from
+ * @value: the result is stored here
+ *
+ * This function reads file @file and interprets its contents as a positive
+ * 'long long' integer. If this is not true, it fails with %EINVAL error code.
+ * Returns %0 in case of success and %-1 in case of failure.
+ */
+static int read_pos_ll(const char *file, long long *value)
+{
+ int fd, rd;
+ char buf[50];
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, 50);
+ if (rd == -1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+ if (rd == 50) {
+ errmsg("contents of \"%s\" is too long", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (sscanf(buf, "%lld\n", value) != 1) {
+ errmsg("cannot read integer from \"%s\"\n", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (*value < 0) {
+ errmsg("negative value %lld in \"%s\"", *value, file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (close(fd))
+ return sys_errmsg("close failed on \"%s\"", file);
+
+ return 0;
+
+out_error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * read_hex_int - read an 'int' value from a file.
+ * @file: the file to read from
+ * @value: the result is stored here
+ *
+ * This function is the same as 'read_pos_ll()', but it reads an 'int'
+ * value, not 'long long'.
+ */
+static int read_hex_int(const char *file, int *value)
+{
+ long long res;
+
+ if (read_hex_ll(file, &res))
+ return -1;
+
+ /* Make sure the value has correct range */
+ if (res > INT_MAX || res < INT_MIN) {
+ errmsg("value %lld read from file \"%s\" is out of range",
+ res, file);
+ errno = EINVAL;
+ return -1;
+ }
+
+ *value = res;
+ return 0;
+}
+
+/**
+ * read_pos_int - read a positive 'int' value from a file.
+ * @file: the file to read from
+ * @value: the result is stored here
+ *
+ * This function is the same as 'read_pos_ll()', but it reads an 'int'
+ * value, not 'long long'.
+ */
+static int read_pos_int(const char *file, int *value)
+{
+ long long res;
+
+ if (read_pos_ll(file, &res))
+ return -1;
+
+ /* Make sure the value is not too big */
+ if (res > INT_MAX) {
+ errmsg("value %lld read from file \"%s\" is out of range",
+ res, file);
+ errno = EINVAL;
+ return -1;
+ }
+
+ *value = res;
+ return 0;
+}
+
+/**
+ * dev_read_hex_int - read an hex 'int' value from an MTD device sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: MTD device number
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_hex_int(const char *patt, int dev_num, int *value)
+{
+ char file[strlen(patt) + 50];
+
+ sprintf(file, patt, dev_num);
+ return read_hex_int(file, value);
+}
+
+/**
+ * dev_read_pos_int - read a positive 'int' value from an MTD device sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: MTD device number
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_pos_int(const char *patt, int dev_num, int *value)
+{
+ char file[strlen(patt) + 50];
+
+ sprintf(file, patt, dev_num);
+ return read_pos_int(file, value);
+}
+
+/**
+ * dev_read_pos_ll - read a positive 'long long' value from an MTD device sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: MTD device number
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_pos_ll(const char *patt, int dev_num, long long *value)
+{
+ char file[strlen(patt) + 50];
+
+ sprintf(file, patt, dev_num);
+ return read_pos_ll(file, value);
+}
+
+/**
+ * type_str2int - convert MTD device type to integer.
+ * @str: MTD device type string to convert
+ *
+ * This function converts MTD device type string @str, read from sysfs, into an
+ * integer.
+ */
+static int type_str2int(const char *str)
+{
+ if (!strcmp(str, "nand"))
+ return MTD_NANDFLASH;
+ if (!strcmp(str, "nor"))
+ return MTD_NORFLASH;
+ if (!strcmp(str, "rom"))
+ return MTD_ROM;
+ if (!strcmp(str, "absent"))
+ return MTD_ABSENT;
+ if (!strcmp(str, "dataflash"))
+ return MTD_DATAFLASH;
+ if (!strcmp(str, "ram"))
+ return MTD_RAM;
+ if (!strcmp(str, "ubi"))
+ return MTD_UBIVOLUME;
+ return -1;
+}
+
+/**
+ * dev_node2num - find UBI device number by its character device node.
+ * @lib: MTD library descriptor
+ * @node: name of the MTD device node
+ * @dev_num: MTD device number is returned here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_node2num(struct libmtd *lib, const char *node, int *dev_num)
+{
+ struct stat st;
+ int i, major, minor;
+ struct mtd_info info;
+
+ if (stat(node, &st))
+ return sys_errmsg("cannot get information about \"%s\"", node);
+
+ if (!S_ISCHR(st.st_mode)) {
+ errmsg("\"%s\" is not a character device", node);
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (mtd_get_info((libmtd_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+
+ ret = dev_get_major(lib, i, &major1, &minor1);
+ if (ret) {
+ if (errno == ENOENT)
+ continue;
+ if (!errno)
+ break;
+ return -1;
+ }
+
+ if (major1 == major && minor1 == minor) {
+ errno = 0;
+ *dev_num = i;
+ return 0;
+ }
+ }
+
+ errno = ENODEV;
+ return -1;
+}
+
+/**
+ * sysfs_is_supported - check whether the MTD sub-system supports MTD.
+ * @lib: MTD library descriptor
+ *
+ * The Linux kernel MTD subsystem gained MTD support starting from kernel
+ * 2.6.30 and libmtd tries to use sysfs interface if possible, because the NAND
+ * sub-page size is available there (and not available at all in pre-sysfs
+ * kernels).
+ *
+ * Very old kernels did not have "/sys/class/mtd" directory. Not very old
+ * kernels (e.g., 2.6.29) did have "/sys/class/mtd/mtdX" directories, by there
+ * were no files there, e.g., the "name" file was not present. So all we can do
+ * is to check for a "/sys/class/mtd/mtdX/name" file. But this is not a
+ * reliable check, because if this is a new system with no MTD devices - we'll
+ * treat it as a pre-sysfs system.
+ */
+static int sysfs_is_supported(struct libmtd *lib)
+{
+ int fd, num = -1;
+ DIR *sysfs_mtd;
+ char file[strlen(lib->mtd_name) + 10];
+
+ sysfs_mtd = opendir(lib->sysfs_mtd);
+ if (!sysfs_mtd) {
+ if (errno == ENOENT) {
+ errno = 0;
+ return 0;
+ }
+ return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd);
+ }
+
+ /*
+ * First of all find an "mtdX" directory. This is needed because there
+ * may be, for example, mtd1 but no mtd0.
+ */
+ while (1) {
+ int ret, dev_num;
+ char tmp_buf[256];
+ struct dirent *dirent;
+
+ dirent = readdir(sysfs_mtd);
+ if (!dirent)
+ break;
+
+ if (strlen(dirent->d_name) >= 255) {
+ errmsg("invalid entry in %s: \"%s\"",
+ lib->sysfs_mtd, dirent->d_name);
+ errno = EINVAL;
+ closedir(sysfs_mtd);
+ return -1;
+ }
+
+ ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s",
+ &dev_num, tmp_buf);
+ if (ret == 1) {
+ num = dev_num;
+ break;
+ }
+ }
+
+ if (closedir(sysfs_mtd))
+ return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd);
+
+ if (num == -1)
+ /* No mtd device, treat this as pre-sysfs system */
+ return 0;
+
+ sprintf(file, lib->mtd_name, num);
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return 0;
+
+ if (close(fd)) {
+ sys_errmsg("close failed on \"%s\"", file);
+ return -1;
+ }
+
+ return 1;
+}
+
+libmtd_t libmtd_open(void)
+{
+ struct libmtd *lib;
+
+ lib = calloc(1, sizeof(struct libmtd));
+ if (!lib)
+ return NULL;
+
+ lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD);
+ if (!lib->sysfs_mtd)
+ goto out_error;
+
+ lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT);
+ if (!lib->mtd)
+ goto out_error;
+
+ lib->mtd_name = mkpath(lib->mtd, MTD_NAME);
+ if (!lib->mtd_name)
+ goto out_error;
+
+ if (!sysfs_is_supported(lib)) {
+ free(lib->mtd);
+ free(lib->sysfs_mtd);
+ free(lib->mtd_name);
+ lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL;
+ return lib;
+ }
+
+ lib->mtd_dev = mkpath(lib->mtd, MTD_DEV);
+ if (!lib->mtd_dev)
+ goto out_error;
+
+ lib->mtd_type = mkpath(lib->mtd, MTD_TYPE);
+ if (!lib->mtd_type)
+ goto out_error;
+
+ lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE);
+ if (!lib->mtd_eb_size)
+ goto out_error;
+
+ lib->mtd_size = mkpath(lib->mtd, MTD_SIZE);
+ if (!lib->mtd_size)
+ goto out_error;
+
+ lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE);
+ if (!lib->mtd_min_io_size)
+ goto out_error;
+
+ lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE);
+ if (!lib->mtd_subpage_size)
+ goto out_error;
+
+ lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE);
+ if (!lib->mtd_oob_size)
+ goto out_error;
+
+ lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT);
+ if (!lib->mtd_region_cnt)
+ goto out_error;
+
+ lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS);
+ if (!lib->mtd_flags)
+ goto out_error;
+
+ lib->sysfs_supported = 1;
+ return lib;
+
+out_error:
+ libmtd_close((libmtd_t)lib);
+ return NULL;
+}
+
+void libmtd_close(libmtd_t desc)
+{
+ struct libmtd *lib = (struct libmtd *)desc;
+
+ free(lib->mtd_flags);
+ free(lib->mtd_region_cnt);
+ free(lib->mtd_oob_size);
+ free(lib->mtd_subpage_size);
+ free(lib->mtd_min_io_size);
+ free(lib->mtd_size);
+ free(lib->mtd_eb_size);
+ free(lib->mtd_type);
+ free(lib->mtd_dev);
+ free(lib->mtd_name);
+ free(lib->mtd);
+ free(lib->sysfs_mtd);
+ free(lib);
+}
+
+int mtd_get_info(libmtd_t desc, struct mtd_info *info)
+{
+ DIR *sysfs_mtd;
+ struct dirent *dirent;
+ struct libmtd *lib = (struct libmtd *)desc;
+
+ memset(info, 0, sizeof(struct mtd_info));
+
+ if (!lib->sysfs_supported)
+ return legacy_mtd_get_info(info);
+
+ info->sysfs_supported = 1;
+
+ /*
+ * We have to scan the MTD sysfs directory to identify how many MTD
+ * devices are present.
+ */
+ sysfs_mtd = opendir(lib->sysfs_mtd);
+ if (!sysfs_mtd) {
+ if (errno == ENOENT) {
+ errno = ENODEV;
+ return -1;
+ }
+ return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd);
+ }
+
+ info->lowest_dev_num = INT_MAX;
+ while (1) {
+ int dev_num, ret;
+ char tmp_buf[256];
+
+ errno = 0;
+ dirent = readdir(sysfs_mtd);
+ if (!dirent)
+ break;
+
+ if (strlen(dirent->d_name) >= 255) {
+ errmsg("invalid entry in %s: \"%s\"",
+ lib->sysfs_mtd, dirent->d_name);
+ errno = EINVAL;
+ goto out_close;
+ }
+
+ ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s",
+ &dev_num, tmp_buf);
+ if (ret == 1) {
+ info->dev_count += 1;
+ if (dev_num > info->highest_dev_num)
+ info->highest_dev_num = dev_num;
+ if (dev_num < info->lowest_dev_num)
+ info->lowest_dev_num = dev_num;
+ }
+ }
+
+ if (!dirent && errno) {
+ sys_errmsg("readdir failed on \"%s\"", lib->sysfs_mtd);
+ goto out_close;
+ }
+
+ if (closedir(sysfs_mtd))
+ return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd);
+
+ if (info->lowest_dev_num == INT_MAX)
+ info->lowest_dev_num = 0;
+
+ return 0;
+
+out_close:
+ closedir(sysfs_mtd);
+ return -1;
+}
+
+int mtd_get_dev_info1(libmtd_t desc, int dev_num, struct mtd_dev_info *mtd)
+{
+ int ret;
+ struct stat st;
+ struct libmtd *lib = (struct libmtd *)desc;
+
+ memset(mtd, 0, sizeof(struct mtd_dev_info));
+ mtd->dev_num = dev_num;
+
+ if (!lib->sysfs_supported)
+ return legacy_get_dev_info1(dev_num, mtd);
+ else {
+ char file[strlen(lib->mtd) + 10];
+
+ sprintf(file, lib->mtd, dev_num);
+ if (stat(file, &st)) {
+ if (errno == ENOENT)
+ errno = ENODEV;
+ return -1;
+ }
+ }
+
+ if (dev_get_major(lib, dev_num, &mtd->major, &mtd->minor))
+ return -1;
+
+ ret = dev_read_data(lib->mtd_name, dev_num, &mtd->name,
+ MTD_NAME_MAX + 1);
+ if (ret < 0)
+ return -1;
+ ((char *)mtd->name)[ret - 1] = '\0';
+
+ ret = dev_read_data(lib->mtd_type, dev_num, &mtd->type_str,
+ MTD_TYPE_MAX + 1);
+ if (ret < 0)
+ return -1;
+ ((char *)mtd->type_str)[ret - 1] = '\0';
+
+ if (dev_read_pos_int(lib->mtd_eb_size, dev_num, &mtd->eb_size))
+ return -1;
+ if (dev_read_pos_ll(lib->mtd_size, dev_num, &mtd->size))
+ return -1;
+ if (dev_read_pos_int(lib->mtd_min_io_size, dev_num, &mtd->min_io_size))
+ return -1;
+ if (dev_read_pos_int(lib->mtd_subpage_size, dev_num, &mtd->subpage_size))
+ return -1;
+ if (dev_read_pos_int(lib->mtd_oob_size, dev_num, &mtd->oob_size))
+ return -1;
+ if (dev_read_pos_int(lib->mtd_region_cnt, dev_num, &mtd->region_cnt))
+ return -1;
+ if (dev_read_hex_int(lib->mtd_flags, dev_num, &ret))
+ return -1;
+ mtd->writable = !!(ret & MTD_WRITEABLE);
+
+ mtd->eb_cnt = mtd->size / mtd->eb_size;
+ mtd->type = type_str2int(mtd->type_str);
+ mtd->bb_allowed = !!(mtd->type == MTD_NANDFLASH);
+
+ return 0;
+}
+
+int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd)
+{
+ int dev_num;
+ struct libmtd *lib = (struct libmtd *)desc;
+
+ if (!lib->sysfs_supported)
+ return legacy_get_dev_info(node, mtd);
+
+ if (dev_node2num(lib, node, &dev_num))
+ return -1;
+
+ return mtd_get_dev_info1(desc, dev_num, mtd);
+}
+
+int mtd_erase(const struct mtd_dev_info *mtd, int fd, int eb)
+{
+ struct erase_info_user ei;
+
+ ei.start = eb * mtd->eb_size;;
+ ei.length = mtd->eb_size;
+ return ioctl(fd, MEMERASE, &ei);
+}
+
+/* Patterns to write to a physical eraseblock when torturing it */
+static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
+
+/**
+ * check_pattern - check if buffer contains only a certain byte pattern.
+ * @buf: buffer to check
+ * @patt: the pattern to check
+ * @size: buffer size in bytes
+ *
+ * This function returns %1 in there are only @patt bytes in @buf, and %0 if
+ * something else was also found.
+ */
+static int check_pattern(const void *buf, uint8_t patt, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (((const uint8_t *)buf)[i] != patt)
+ return 0;
+ return 1;
+}
+
+int mtd_torture(const struct mtd_dev_info *mtd, int fd, int eb)
+{
+ int err, i, patt_count;
+ void *buf;
+
+ normsg("run torture test for PEB %d", eb);
+ patt_count = ARRAY_SIZE(patterns);
+
+ buf = malloc(mtd->eb_size);
+ if (!buf) {
+ errmsg("cannot allocate %d bytes of memory", mtd->eb_size);
+ return -1;
+ }
+
+ for (i = 0; i < patt_count; i++) {
+ err = mtd_erase(mtd, fd, eb);
+ if (err)
+ goto out;
+
+ /* Make sure the PEB contains only 0xFF bytes */
+ err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size);
+ if (err)
+ goto out;
+
+ err = check_pattern(buf, 0xFF, mtd->eb_size);
+ if (err == 0) {
+ errmsg("erased PEB %d, but a non-0xFF byte found", eb);
+ errno = EIO;
+ goto out;
+ }
+
+ /* Write a pattern and check it */
+ memset(buf, patterns[i], mtd->eb_size);
+ err = mtd_write(mtd, fd, eb, 0, buf, mtd->eb_size);
+ if (err)
+ goto out;
+
+ memset(buf, ~patterns[i], mtd->eb_size);
+ err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size);
+ if (err)
+ goto out;
+
+ err = check_pattern(buf, patterns[i], mtd->eb_size);
+ if (err == 0) {
+ errmsg("pattern %x checking failed for PEB %d",
+ patterns[i], eb);
+ errno = EIO;
+ goto out;
+ }
+ }
+
+ err = 0;
+ normsg("PEB %d passed torture test, do not mark it a bad", eb);
+
+out:
+ free(buf);
+ return -1;
+}
+
+int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb)
+{
+ int ret;
+ loff_t seek;
+
+ if (eb < 0 || eb >= mtd->eb_cnt) {
+ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks",
+ eb, mtd->dev_num, mtd->eb_cnt);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!mtd->bb_allowed)
+ return 0;
+
+ seek = (loff_t)eb * mtd->eb_size;
+ ret = ioctl(fd, MEMGETBADBLOCK, &seek);
+ if (ret == -1)
+ return sys_errmsg("MEMGETBADBLOCK ioctl failed for "
+ "eraseblock %d (mtd%d)", eb, mtd->dev_num);
+ return ret;
+}
+
+int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb)
+{
+ int ret;
+ loff_t seek;
+
+ if (!mtd->bb_allowed) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (eb < 0 || eb >= mtd->eb_cnt) {
+ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks",
+ eb, mtd->dev_num, mtd->eb_cnt);
+ errno = EINVAL;
+ return -1;
+ }
+
+ seek = (loff_t)eb * mtd->eb_size;
+ ret = ioctl(fd, MEMSETBADBLOCK, &seek);
+ if (ret == -1)
+ return sys_errmsg("MEMSETBADBLOCK ioctl failed for "
+ "eraseblock %d (mtd%d)", eb, mtd->dev_num);
+ return 0;
+}
+
+int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
+ void *buf, int len)
+{
+ int ret, rd = 0;
+ off_t seek;
+
+ if (eb < 0 || eb >= mtd->eb_cnt) {
+ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks",
+ eb, mtd->dev_num, mtd->eb_cnt);
+ errno = EINVAL;
+ return -1;
+ }
+ if (offs < 0 || offs + len > mtd->eb_size) {
+ errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d",
+ offs, len, mtd->dev_num, mtd->eb_size);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Seek to the beginning of the eraseblock */
+ seek = (off_t)eb * mtd->eb_size + offs;
+ if (lseek(fd, seek, SEEK_SET) != seek)
+ return sys_errmsg("cannot seek mtd%d to offset %llu",
+ mtd->dev_num, (unsigned long long)seek);
+
+ while (rd < len) {
+ ret = read(fd, buf, len);
+ if (ret < 0)
+ return sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)",
+ len, mtd->dev_num, eb, offs);
+ rd += ret;
+ }
+
+ return 0;
+}
+
+int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
+ void *buf, int len)
+{
+ int ret;
+ off_t seek;
+
+ if (eb < 0 || eb >= mtd->eb_cnt) {
+ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks",
+ eb, mtd->dev_num, mtd->eb_cnt);
+ errno = EINVAL;
+ return -1;
+ }
+ if (offs < 0 || offs + len > mtd->eb_size) {
+ errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d",
+ offs, len, mtd->dev_num, mtd->eb_size);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (offs % mtd->subpage_size) {
+ errmsg("write offset %d is not aligned to mtd%d min. I/O size %d",
+ offs, mtd->dev_num, mtd->subpage_size);
+ errno = EINVAL;
+ return -1;
+ }
+ if (len % mtd->subpage_size) {
+ errmsg("write length %d is not aligned to mtd%d min. I/O size %d",
+ len, mtd->dev_num, mtd->subpage_size);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Seek to the beginning of the eraseblock */
+ seek = (off_t)eb * mtd->eb_size + offs;
+ if (lseek(fd, seek, SEEK_SET) != seek)
+ return sys_errmsg("cannot seek mtd%d to offset %llu",
+ mtd->dev_num, (unsigned long long)seek);
+
+ ret = write(fd, buf, len);
+ if (ret != len)
+ return sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)",
+ len, mtd->dev_num, eb, offs);
+
+ return 0;
+}
+
+int mtd_probe_node(libmtd_t desc, const char *node)
+{
+ struct stat st;
+ struct mtd_info info;
+ int i, major, minor;
+ struct libmtd *lib = (struct libmtd *)desc;
+
+ if (stat(node, &st))
+ return sys_errmsg("cannot get information about \"%s\"", node);
+
+ if (!S_ISCHR(st.st_mode)) {
+ errmsg("\"%s\" is not a character device", node);
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (mtd_get_info((libmtd_t *)lib, &info))
+ return -1;
+
+ if (!lib->sysfs_supported)
+ return 0;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+
+ ret = dev_get_major(lib, i, &major1, &minor1);
+ if (ret) {
+ if (errno == ENOENT)
+ continue;
+ if (!errno)
+ break;
+ return -1;
+ }
+
+ if (major1 == major && minor1 == minor)
+ return 1;
+ }
+
+ errno = 0;
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libmtd_int.h b/mtd-utils-1.3.1/ubi-utils/src/libmtd_int.h
new file mode 100644
index 0000000..7de4b42
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libmtd_int.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * MTD library.
+ */
+
+#ifndef __LIBMTD_INT_H__
+#define __LIBMTD_INT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PROGRAM_NAME "libmtd"
+
+#define SYSFS_MTD "class/mtd"
+#define MTD_NAME_PATT "mtd%d"
+#define MTD_DEV "dev"
+#define MTD_NAME "name"
+#define MTD_TYPE "type"
+#define MTD_EB_SIZE "erasesize"
+#define MTD_SIZE "size"
+#define MTD_MIN_IO_SIZE "writesize"
+#define MTD_SUBPAGE_SIZE "subpagesize"
+#define MTD_OOB_SIZE "oobsize"
+#define MTD_REGION_CNT "numeraseregions"
+#define MTD_FLAGS "flags"
+
+/**
+ * libmtd - MTD library description data structure.
+ * @sysfs_mtd: MTD directory in sysfs
+ * @mtd: MTD device sysfs directory pattern
+ * @mtd_dev: MTD device major/minor numbers file pattern
+ * @mtd_name: MTD device name file pattern
+ * @mtd_type: MTD device type file pattern
+ * @mtd_eb_size: MTD device eraseblock size file pattern
+ * @mtd_size: MTD device size file pattern
+ * @mtd_min_io_size: minimum I/O unit size file pattern
+ * @mtd_subpage_size: sub-page size file pattern
+ * @mtd_oob_size: MTD device OOB size file pattern
+ * @mtd_region_cnt: count of additional erase regions file pattern
+ * @mtd_flags: MTD device flags file pattern
+ * @sysfs_supported: non-zero if sysfs is supported by MTD
+ */
+struct libmtd
+{
+ char *sysfs_mtd;
+ char *mtd;
+ char *mtd_dev;
+ char *mtd_name;
+ char *mtd_type;
+ char *mtd_eb_size;
+ char *mtd_size;
+ char *mtd_min_io_size;
+ char *mtd_subpage_size;
+ char *mtd_oob_size;
+ char *mtd_region_cnt;
+ char *mtd_flags;
+ unsigned int sysfs_supported:1;
+};
+
+int legacy_libmtd_open(void);
+int legacy_mtd_get_info(struct mtd_info *info);
+int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd);
+int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBMTD_INT_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libmtd_legacy.c b/mtd-utils-1.3.1/ubi-utils/src/libmtd_legacy.c
new file mode 100644
index 0000000..b477a4f
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libmtd_legacy.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * This file is part of the MTD library. Implements pre-2.6.30 kernels support,
+ * where MTD did not have sysfs interface. The main limitation of the old
+ * kernels was that the sub-page size was not exported to user-space, so it was
+ * not possible to get sub-page size.
+ */
+
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+#include <libmtd.h>
+#include "libmtd_int.h"
+#include "common.h"
+
+#define MTD_PROC_FILE "/proc/mtd"
+#define MTD_DEV_PATT "/dev/mtd%d"
+#define MTD_DEV_MAJOR 90
+
+#define PROC_MTD_FIRST "dev: size erasesize name\n"
+#define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1)
+#define PROC_MTD_MAX_LEN 4096
+#define PROC_MTD_PATT "mtd%d: %llx %x"
+
+/**
+ * struct proc_parse_info - /proc/mtd parsing information.
+ * @dev_num: MTD device number
+ * @size: device size
+ * @eb_size: eraseblock size
+ * @name: device name
+ * @buf: contents of /proc/mtd
+ * @data_size: how much data was read into @buf
+ * @pos: next string in @buf to parse
+ */
+struct proc_parse_info
+{
+ int dev_num;
+ long long size;
+ char name[MTD_NAME_MAX + 1];
+ int eb_size;
+ char *buf;
+ int data_size;
+ char *next;
+};
+
+static int proc_parse_start(struct proc_parse_info *pi)
+{
+ int fd, ret;
+
+ fd = open(MTD_PROC_FILE, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ pi->buf = malloc(PROC_MTD_MAX_LEN);
+ if (!pi->buf) {
+ sys_errmsg("cannot allocate %d bytes of memory",
+ PROC_MTD_MAX_LEN);
+ goto out_close;
+ }
+
+ ret = read(fd, pi->buf, PROC_MTD_MAX_LEN);
+ if (ret == -1) {
+ sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE);
+ goto out_free;
+ }
+
+ if (ret < PROC_MTD_FIRST_LEN ||
+ memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) {
+ errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE,
+ PROC_MTD_FIRST);
+ goto out_free;
+ }
+
+ pi->data_size = ret;
+ pi->next = pi->buf + PROC_MTD_FIRST_LEN;
+
+ close(fd);
+ return 0;
+
+out_free:
+ free(pi->buf);
+out_close:
+ close(fd);
+ return -1;
+}
+
+static int proc_parse_next(struct proc_parse_info *pi)
+{
+ int ret, len, pos = pi->next - pi->buf;
+ char *p, *p1;
+
+ if (pos >= pi->data_size) {
+ free(pi->buf);
+ return 0;
+ }
+
+ ret = sscanf(pi->next, PROC_MTD_PATT, &pi->dev_num, &pi->size,
+ &pi->eb_size);
+ if (ret != 3)
+ return errmsg("\"%s\" pattern not found", PROC_MTD_PATT);
+
+ p = memchr(pi->next, '\"', pi->data_size - pos);
+ if (!p)
+ return errmsg("opening \" not fount");
+ p += 1;
+ pos = p - pi->buf;
+ if (pos >= pi->data_size)
+ return errmsg("opening \" not fount");
+
+ p1 = memchr(p, '\"', pi->data_size - pos);
+ if (!p1)
+ return errmsg("closing \" not fount");
+ pos = p1 - pi->buf;
+ if (pos >= pi->data_size)
+ return errmsg("closing \" not fount");
+
+ len = p1 - p;
+ if (len > MTD_NAME_MAX)
+ return errmsg("too long mtd%d device name", pi->dev_num);
+
+ memcpy(pi->name, p, len);
+ pi->name[len] = '\0';
+
+ if (p1[1] != '\n')
+ return errmsg("opening \"\n\" not fount");
+ pi->next = p1 + 2;
+ return 1;
+}
+
+/**
+ * legacy_libmtd_open - legacy version of 'libmtd_open()'.
+ *
+ * This function is just checks that MTD is present in the system. Returns
+ * zero in case of success and %-1 in case of failure. In case of failure,
+ * errno contains zero if MTD is not present in the system, or contains the
+ * error code if a real error happened. This is similar to the 'libmtd_open()'
+ * return conventions.
+ */
+int legacy_libmtd_open(void)
+{
+ int fd;
+
+ fd = open(MTD_PROC_FILE, O_RDONLY);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ errno = 0;
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+/**
+ * legacy_mtd_get_info - legacy version of 'mtd_get_info()'.
+ * @info: the MTD device information is returned here
+ *
+ * This function is similar to 'mtd_get_info()' and has the same conventions.
+ */
+int legacy_mtd_get_info(struct mtd_info *info)
+{
+ int ret;
+ struct proc_parse_info pi;
+
+ ret = proc_parse_start(&pi);
+ if (ret)
+ return -1;
+
+ info->lowest_dev_num = INT_MAX;
+ while (proc_parse_next(&pi)) {
+ info->dev_count += 1;
+ if (pi.dev_num > info->highest_dev_num)
+ info->highest_dev_num = pi.dev_num;
+ if (pi.dev_num < info->lowest_dev_num)
+ info->lowest_dev_num = pi.dev_num;
+ }
+
+ return 0;
+}
+
+/**
+ * legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'.
+ * @node: name of the MTD device node
+ * @mtd: the MTD device information is returned here
+ *
+ * This function is similar to 'mtd_get_dev_info()' and has the same
+ * conventions.
+ */
+int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd)
+{
+ struct stat st;
+ struct mtd_info_user ui;
+ int fd, ret;
+ loff_t offs = 0;
+ struct proc_parse_info pi;
+
+ if (stat(node, &st)) {
+ sys_errmsg("cannot open \"%s\"", node);
+ if (errno == ENOENT)
+ normsg("MTD subsystem is old and does not support "
+ "sysfs, so MTD character device nodes have "
+ "to exist");
+ }
+
+ if (!S_ISCHR(st.st_mode)) {
+ errno = EINVAL;
+ return errmsg("\"%s\" is not a character device", node);
+ }
+
+ memset(mtd, '\0', sizeof(struct mtd_dev_info));
+ mtd->major = major(st.st_rdev);
+ mtd->minor = minor(st.st_rdev);
+
+ if (mtd->major != MTD_DEV_MAJOR) {
+ errno = EINVAL;
+ return errmsg("\"%s\" has major number %d, MTD devices have "
+ "major %d", node, mtd->major, MTD_DEV_MAJOR);
+ }
+
+ mtd->dev_num = mtd->minor / 2;
+
+ fd = open(node, O_RDWR);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", node);
+
+ if (ioctl(fd, MEMGETINFO, &ui)) {
+ sys_errmsg("MEMGETINFO ioctl request failed");
+ goto out_close;
+ }
+
+ ret = ioctl(fd, MEMGETBADBLOCK, &offs);
+ if (ret == -1) {
+ if (errno != EOPNOTSUPP) {
+ sys_errmsg("MEMGETBADBLOCK ioctl failed");
+ goto out_close;
+ }
+ errno = 0;
+ mtd->bb_allowed = 0;
+ } else
+ mtd->bb_allowed = 1;
+
+ mtd->type = ui.type;
+ mtd->size = ui.size;
+ mtd->eb_size = ui.erasesize;
+ mtd->min_io_size = ui.writesize;
+
+ if (mtd->min_io_size <= 0) {
+ errmsg("mtd%d (%s) has insane min. I/O unit size %d",
+ mtd->dev_num, node, mtd->min_io_size);
+ goto out_close;
+ }
+ if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) {
+ errmsg("mtd%d (%s) has insane eraseblock size %d",
+ mtd->dev_num, node, mtd->eb_size);
+ goto out_close;
+ }
+ if (mtd->size <= 0 || mtd->size < mtd->eb_size) {
+ errmsg("mtd%d (%s) has insane size %lld",
+ mtd->dev_num, node, mtd->size);
+ goto out_close;
+ }
+ mtd->eb_cnt = mtd->size / mtd->eb_size;
+
+ switch(mtd->type) {
+ case MTD_ABSENT:
+ errmsg("mtd%d (%s) is removable and is not present",
+ mtd->dev_num, node);
+ goto out_close;
+ case MTD_RAM:
+ strcpy((char *)mtd->type_str, "ram");
+ break;
+ case MTD_ROM:
+ strcpy((char *)mtd->type_str, "rom");
+ break;
+ case MTD_NORFLASH:
+ strcpy((char *)mtd->type_str, "nor");
+ break;
+ case MTD_NANDFLASH:
+ strcpy((char *)mtd->type_str, "nand");
+ break;
+ case MTD_DATAFLASH:
+ strcpy((char *)mtd->type_str, "dataflash");
+ break;
+ case MTD_UBIVOLUME:
+ strcpy((char *)mtd->type_str, "ubi");
+ break;
+ default:
+ goto out_close;
+ }
+
+ if (ui.flags & MTD_WRITEABLE)
+ mtd->writable = 1;
+ mtd->subpage_size = mtd->min_io_size;
+
+ close(fd);
+
+ /*
+ * Unfortunately, the device name is not available via ioctl, and
+ * we have to parse /proc/mtd to get it.
+ */
+ ret = proc_parse_start(&pi);
+ if (ret)
+ return -1;
+
+ while (proc_parse_next(&pi)) {
+ if (pi.dev_num == mtd->dev_num) {
+ strcpy((char *)mtd->name, pi.name);
+ return 0;
+ }
+ }
+
+ errmsg("mtd%d not found in \"%s\"", mtd->dev_num, MTD_PROC_FILE);
+ errno = ENOENT;
+ return -1;
+
+out_close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'.
+ * @node: name of the MTD device node
+ * @mtd: the MTD device information is returned here
+ *
+ * This function is similar to 'mtd_get_dev_info1()' and has the same
+ * conventions.
+ */
+int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd)
+{
+ char node[sizeof(MTD_DEV_PATT) + 20];
+
+ sprintf(node, MTD_DEV_PATT, dev_num);
+ return legacy_get_dev_info(node, mtd);
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libscan.c b/mtd-utils-1.3.1/ubi-utils/src/libscan.c
new file mode 100644
index 0000000..25b5658
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libscan.c
@@ -0,0 +1,226 @@
+/*
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI scanning library.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <mtd_swab.h>
+#include <mtd/ubi-media.h>
+#include <mtd/mtd-user.h>
+#include <libmtd.h>
+#include <libscan.h>
+#include "common.h"
+#include "crc32.h"
+
+#define PROGRAM_NAME "libscan"
+
+static int all_ff(const void *buf, int len)
+{
+ int i;
+ const uint8_t *p = buf;
+
+ for (i = 0; i < len; i++)
+ if (p[i] != 0xFF)
+ return 0;
+ return 1;
+}
+
+int ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info,
+ int verbose)
+{
+ int eb, v = (verbose == 2), pr = (verbose == 1);
+ struct ubi_scan_info *si;
+ unsigned long long sum = 0;
+
+ si = calloc(1, sizeof(struct ubi_scan_info));
+ if (!si)
+ return sys_errmsg("cannot allocate %zd bytes of memory",
+ sizeof(struct ubi_scan_info));
+
+ si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t));
+ if (!si->ec) {
+ sys_errmsg("cannot allocate %zd bytes of memory",
+ sizeof(struct ubi_scan_info));
+ goto out_si;
+ }
+
+ si->vid_hdr_offs = si->data_offs = -1;
+
+ verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt);
+ for (eb = 0; eb < mtd->eb_cnt; eb++) {
+ int ret;
+ uint32_t crc;
+ struct ubi_ec_hdr hdr;
+ unsigned long long ec;
+
+ if (v) {
+ normsg_cont("scanning eraseblock %d", eb);
+ fflush(stdout);
+ }
+ if (pr) {
+ printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete ",
+ eb, (long long)(eb + 1) * 100 / mtd->eb_cnt);
+ fflush(stdout);
+ }
+
+ ret = mtd_is_bad(mtd, fd, eb);
+ if (ret == -1)
+ goto out_ec;
+ if (ret) {
+ si->bad_cnt += 1;
+ si->ec[eb] = EB_BAD;
+ if (v)
+ printf(": bad\n");
+ continue;
+ }
+
+ ret = mtd_read(mtd, fd, eb, 0, &hdr, sizeof(struct ubi_ec_hdr));
+ if (ret < 0)
+ goto out_ec;
+
+ /* Check the EC header */
+ if (be32_to_cpu(hdr.magic) != UBI_EC_HDR_MAGIC) {
+ if (all_ff(&hdr, sizeof(struct ubi_ec_hdr))) {
+ si->empty_cnt += 1;
+ si->ec[eb] = EB_EMPTY;
+ if (v)
+ printf(": empty\n");
+ } else {
+ si->alien_cnt += 1;
+ si->ec[eb] = EB_ALIEN;
+ if (v)
+ printf(": alien\n");
+ }
+ continue;
+ }
+
+ crc = crc32(UBI_CRC32_INIT, &hdr, UBI_EC_HDR_SIZE_CRC);
+ if (be32_to_cpu(hdr.hdr_crc) != crc) {
+ si->corrupted_cnt += 1;
+ si->ec[eb] = EB_CORRUPTED;
+ if (v)
+ printf(": bad CRC %#08x, should be %#08x\n",
+ crc, be32_to_cpu(hdr.hdr_crc));
+ continue;
+ }
+
+ ec = be64_to_cpu(hdr.ec);
+ if (ec > EC_MAX) {
+ if (pr)
+ printf("\n");
+ errmsg("erase counter in EB %d is %llu, while this "
+ "program expects them to be less than %u",
+ eb, ec, EC_MAX);
+ goto out_ec;
+ }
+
+ if (si->vid_hdr_offs == -1) {
+ si->vid_hdr_offs = be32_to_cpu(hdr.vid_hdr_offset);
+ si->data_offs = be32_to_cpu(hdr.data_offset);
+ if (si->data_offs % mtd->min_io_size) {
+ if (pr)
+ printf("\n");
+ if (v)
+ printf(": corrupted because of the below\n");
+ warnmsg("bad data offset %d at eraseblock %d (n"
+ "of multiple of min. I/O unit size %d)",
+ si->data_offs, eb, mtd->min_io_size);
+ warnmsg("treat eraseblock %d as corrupted", eb);
+ si->corrupted_cnt += 1;
+ si->ec[eb] = EB_CORRUPTED;
+ continue;
+
+ }
+ } else {
+ if ((int)be32_to_cpu(hdr.vid_hdr_offset) != si->vid_hdr_offs) {
+ if (pr)
+ printf("\n");
+ if (v)
+ printf(": corrupted because of the below\n");
+ warnmsg("inconsistent VID header offset: was "
+ "%d, but is %d in eraseblock %d",
+ si->vid_hdr_offs,
+ be32_to_cpu(hdr.vid_hdr_offset), eb);
+ warnmsg("treat eraseblock %d as corrupted", eb);
+ si->corrupted_cnt += 1;
+ si->ec[eb] = EB_CORRUPTED;
+ continue;
+ }
+ if ((int)be32_to_cpu(hdr.data_offset) != si->data_offs) {
+ if (pr)
+ printf("\n");
+ if (v)
+ printf(": corrupted because of the below\n");
+ warnmsg("inconsistent data offset: was %d, but"
+ " is %d in eraseblock %d",
+ si->data_offs,
+ be32_to_cpu(hdr.data_offset), eb);
+ warnmsg("treat eraseblock %d as corrupted", eb);
+ si->corrupted_cnt += 1;
+ si->ec[eb] = EB_CORRUPTED;
+ continue;
+ }
+ }
+
+ si->ok_cnt += 1;
+ si->ec[eb] = ec;
+ if (v)
+ printf(": OK, erase counter %u\n", si->ec[eb]);
+ }
+
+ if (si->ok_cnt != 0) {
+ /* Calculate mean erase counter */
+ for (eb = 0; eb < mtd->eb_cnt; eb++) {
+ if (si->ec[eb] > EC_MAX)
+ continue;
+ sum += si->ec[eb];
+ }
+ si->mean_ec = sum / si->ok_cnt;
+ }
+
+ si->good_cnt = mtd->eb_cnt - si->bad_cnt;
+ verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d "
+ "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt,
+ si->empty_cnt, si->alien_cnt, si->bad_cnt);
+
+ *info = si;
+ if (pr)
+ printf("\n");
+ return 0;
+
+out_ec:
+ free(si->ec);
+out_si:
+ free(si);
+ *info = NULL;
+ return -1;
+}
+
+void ubi_scan_free(struct ubi_scan_info *si)
+{
+ free(si->ec);
+ free(si);
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libubi.c b/mtd-utils-1.3.1/ubi-utils/src/libubi.c
new file mode 100644
index 0000000..fd97774
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libubi.c
@@ -0,0 +1,1270 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <libubi.h>
+#include "libubi_int.h"
+#include "common.h"
+
+#define PROGRAM_NAME "libubi"
+
+/**
+ * mkpath - compose full path from 2 given components.
+ * @path: the first component
+ * @name: the second component
+ *
+ * This function returns the resulting path in case of success and %NULL in
+ * case of failure.
+ */
+static char *mkpath(const char *path, const char *name)
+{
+ char *n;
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ n = malloc(len1 + len2 + 2);
+ if (!n) {
+ sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2);
+ return NULL;
+ }
+
+ memcpy(n, path, len1);
+ if (n[len1 - 1] != '/')
+ n[len1++] = '/';
+
+ memcpy(n + len1, name, len2 + 1);
+ return n;
+}
+
+/**
+ * read_positive_ll - read a positive 'long long' value from a file.
+ * @file: the file to read from
+ * @value: the result is stored here
+ *
+ * This function reads file @file and interprets its contents as a positive
+ * 'long long' integer. If this is not true, it fails with %EINVAL error code.
+ * Returns %0 in case of success and %-1 in case of failure.
+ */
+static int read_positive_ll(const char *file, long long *value)
+{
+ int fd, rd;
+ char buf[50];
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, sizeof(buf));
+ if (rd == -1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+ if (rd == sizeof(buf)) {
+ errmsg("contents of \"%s\" is too long", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+ buf[rd] = '\0';
+
+ if (sscanf(buf, "%lld\n", value) != 1) {
+ errmsg("cannot read integer from \"%s\"\n", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (*value < 0) {
+ errmsg("negative value %lld in \"%s\"", *value, file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (close(fd))
+ return sys_errmsg("close failed on \"%s\"", file);
+
+ return 0;
+
+out_error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * read_positive_int - read a positive 'int' value from a file.
+ * @file: the file to read from
+ * @value: the result is stored here
+ *
+ * This function is the same as 'read_positive_ll()', but it reads an 'int'
+ * value, not 'long long'.
+ */
+static int read_positive_int(const char *file, int *value)
+{
+ long long res;
+
+ if (read_positive_ll(file, &res))
+ return -1;
+
+ /* Make sure the value is not too big */
+ if (res > INT_MAX) {
+ errmsg("value %lld read from file \"%s\" is out of range",
+ res, file);
+ errno = EINVAL;
+ return -1;
+ }
+
+ *value = res;
+ return 0;
+}
+
+/**
+ * read_data - read data from a file.
+ * @file: the file to read from
+ * @buf: the buffer to read to
+ * @buf_len: buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure. Note, if the file contains more then @buf_len bytes of
+ * date, this function fails with %EINVAL error code.
+ */
+static int read_data(const char *file, void *buf, int buf_len)
+{
+ int fd, rd, tmp, tmp1;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+
+ if (rd == buf_len) {
+ errmsg("contents of \"%s\" is too long", file);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ ((char *)buf)[rd] = '\0';
+
+ /* Make sure all data is read */
+ tmp1 = read(fd, &tmp, 1);
+ if (tmp1 == 1) {
+ sys_errmsg("cannot read \"%s\"", file);
+ goto out_error;
+ }
+ if (tmp1) {
+ errmsg("file \"%s\" contains too much data (> %d bytes)",
+ file, buf_len);
+ errno = EINVAL;
+ goto out_error;
+ }
+
+ if (close(fd)) {
+ sys_errmsg("close failed on \"%s\"", file);
+ return -1;
+ }
+
+ return rd;
+
+out_error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * read_major - read major and minor numbers from a file.
+ * @file: name of the file to read from
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns % in case of succes, and %-1 in case of failure.
+ */
+static int read_major(const char *file, int *major, int *minor)
+{
+ int ret;
+ char buf[50];
+
+ ret = read_data(file, buf, 50);
+ if (ret < 0)
+ return ret;
+
+ ret = sscanf(buf, "%d:%d\n", major, minor);
+ if (ret != 2) {
+ errno = EINVAL;
+ return errmsg("\"%s\" does not have major:minor format", file);
+ }
+
+ if (*major < 0 || *minor < 0) {
+ errno = EINVAL;
+ return errmsg("bad major:minor %d:%d in \"%s\"",
+ *major, *minor, file);
+ }
+
+ return 0;
+}
+
+/**
+ * dev_read_int - read a positive 'int' value from an UBI device sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_int(const char *patt, int dev_num, int *value)
+{
+ char file[strlen(patt) + 50];
+
+ sprintf(file, patt, dev_num);
+ return read_positive_int(file, value);
+}
+
+/**
+ * vol_read_int - read a positive 'int' value from an UBI volume sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value)
+{
+ char file[strlen(patt) + 100];
+
+ sprintf(file, patt, dev_num, vol_id);
+ return read_positive_int(file, value);
+}
+
+/**
+ * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_ll(const char *patt, int dev_num, long long *value)
+{
+ char file[strlen(patt) + 50];
+
+ sprintf(file, patt, dev_num);
+ return read_positive_ll(file, value);
+}
+
+/**
+ * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value)
+{
+ char file[strlen(patt) + 100];
+
+ sprintf(file, patt, dev_num, vol_id);
+ return read_positive_ll(file, value);
+}
+
+/**
+ * vol_read_data - read data from an UBI volume's sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @buf: buffer to read to
+ * @buf_len: buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len)
+{
+ char file[strlen(patt) + 100];
+
+ sprintf(file, patt, dev_num, vol_id);
+ return read_data(file, buf, buf_len);
+}
+
+/**
+ * dev_get_major - get major and minor numbers of an UBI device.
+ * @lib: libubi descriptor
+ * @dev_num: UBI device number
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns zero in case of succes and %-1 in case of failure.
+ */
+static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor)
+{
+ char file[strlen(lib->dev_dev) + 50];
+
+ sprintf(file, lib->dev_dev, dev_num);
+ return read_major(file, major, minor);
+}
+
+/**
+ * vol_get_major - get major and minor numbers of an UBI volume.
+ * @lib: libubi descriptor
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns zero in case of succes and %-1 in case of failure.
+ */
+static int vol_get_major(struct libubi *lib, int dev_num, int vol_id,
+ int *major, int *minor)
+{
+ char file[strlen(lib->vol_dev) + 100];
+
+ sprintf(file, lib->vol_dev, dev_num, vol_id);
+ return read_major(file, major, minor);
+}
+
+/**
+ * vol_node2nums - find UBI device number and volume ID by volume device node
+ * file.
+ * @lib: UBI library descriptor
+ * @node: UBI character device node name
+ * @dev_num: UBI device number is returned here
+ * @vol_id: volume ID is returned hers
+ *
+ * This function returns zero in case of succes and %-1 in case of failure.
+ */
+static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num,
+ int *vol_id)
+{
+ struct stat st;
+ struct ubi_info info;
+ int i, fd, major, minor;
+ char file[strlen(lib->ubi_vol) + 100];
+
+ if (stat(node, &st))
+ return sys_errmsg("cannot get information about \"%s\"",
+ node);
+
+ if (!S_ISCHR(st.st_mode)) {
+ errno = EINVAL;
+ return errmsg("\"%s\" is not a character device", node);
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (minor == 0) {
+ errno = EINVAL;
+ return errmsg("\"%s\" is not a volume character device", node);
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+
+ ret = dev_get_major(lib, i, &major1, &minor1);
+ if (ret) {
+ if (errno == ENOENT)
+ continue;
+ return -1;
+ }
+
+ if (major1 == major)
+ break;
+ }
+
+ if (i > info.highest_dev_num) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ /* Make sure this UBI volume exists */
+ sprintf(file, lib->ubi_vol, i, minor - 1);
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ *dev_num = i;
+ *vol_id = minor - 1;
+ errno = 0;
+ return 0;
+}
+
+/**
+ * dev_node2num - find UBI device number by its character device node.
+ * @lib: UBI library descriptor
+ * @node: UBI character device node name
+ * @dev_num: UBI device number is returned here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_node2num(struct libubi *lib, const char *node, int *dev_num)
+{
+ struct stat st;
+ struct ubi_info info;
+ int i, major, minor;
+
+ if (stat(node, &st))
+ return sys_errmsg("cannot get information about \"%s\"", node);
+
+ if (!S_ISCHR(st.st_mode)) {
+ errno = EINVAL;
+ return errmsg("\"%s\" is not a character device", node);
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (minor != 0) {
+ errno = EINVAL;
+ return errmsg("\"%s\" is not an UBI character device", node);
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+
+ ret = dev_get_major(lib, i, &major1, &minor1);
+ if (ret) {
+ if (errno == ENOENT)
+ continue;
+ return -1;
+ }
+
+ if (major1 == major) {
+ if (minor1 != 0) {
+ errmsg("UBI character device minor number is "
+ "%d, but must be 0", minor1);
+ errno = EINVAL;
+ return -1;
+ }
+ errno = 0;
+ *dev_num = i;
+ return 0;
+ }
+ }
+
+ errno = ENODEV;
+ return -1;
+}
+
+int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num)
+{
+ struct ubi_info info;
+ int i, ret, mtd_num1;
+ struct libubi *lib = desc;
+
+ if (ubi_get_info(desc, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1);
+ if (ret) {
+ if (errno == ENOENT)
+ continue;
+ return -1;
+ }
+
+ if (mtd_num1 == mtd_num) {
+ errno = 0;
+ *dev_num = i;
+ return 0;
+ }
+ }
+
+ errno = 0;
+ return -1;
+}
+
+libubi_t libubi_open(void)
+{
+ int fd, version;
+ struct libubi *lib;
+
+ lib = calloc(1, sizeof(struct libubi));
+ if (!lib)
+ return NULL;
+
+ lib->sysfs_ctrl = mkpath("/sys", SYSFS_CTRL);
+ if (!lib->sysfs_ctrl)
+ goto out_error;
+
+ lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV);
+ if (!lib->ctrl_dev)
+ goto out_error;
+
+ lib->sysfs_ubi = mkpath("/sys", SYSFS_UBI);
+ if (!lib->sysfs_ubi)
+ goto out_error;
+
+ /* Make sure UBI is present */
+ fd = open(lib->sysfs_ubi, O_RDONLY);
+ if (fd == -1) {
+ errno = 0;
+ goto out_error;
+ }
+
+ if (close(fd)) {
+ sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi);
+ goto out_error;
+ }
+
+ lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT);
+ if (!lib->ubi_dev)
+ goto out_error;
+
+ lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER);
+ if (!lib->ubi_version)
+ goto out_error;
+
+ lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV);
+ if (!lib->dev_dev)
+ goto out_error;
+
+ lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS);
+ if (!lib->dev_avail_ebs)
+ goto out_error;
+
+ lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS);
+ if (!lib->dev_total_ebs)
+ goto out_error;
+
+ lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT);
+ if (!lib->dev_bad_count)
+ goto out_error;
+
+ lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE);
+ if (!lib->dev_eb_size)
+ goto out_error;
+
+ lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC);
+ if (!lib->dev_max_ec)
+ goto out_error;
+
+ lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD);
+ if (!lib->dev_bad_rsvd)
+ goto out_error;
+
+ lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS);
+ if (!lib->dev_max_vols)
+ goto out_error;
+
+ lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE);
+ if (!lib->dev_min_io_size)
+ goto out_error;
+
+ lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM);
+ if (!lib->dev_mtd_num)
+ goto out_error;
+
+ lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT);
+ if (!lib->ubi_vol)
+ goto out_error;
+
+ lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE);
+ if (!lib->vol_type)
+ goto out_error;
+
+ lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV);
+ if (!lib->vol_dev)
+ goto out_error;
+
+ lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT);
+ if (!lib->vol_alignment)
+ goto out_error;
+
+ lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES);
+ if (!lib->vol_data_bytes)
+ goto out_error;
+
+ lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS);
+ if (!lib->vol_rsvd_ebs)
+ goto out_error;
+
+ lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE);
+ if (!lib->vol_eb_size)
+ goto out_error;
+
+ lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED);
+ if (!lib->vol_corrupted)
+ goto out_error;
+
+ lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME);
+ if (!lib->vol_name)
+ goto out_error;
+
+ if (read_positive_int(lib->ubi_version, &version))
+ goto out_error;
+ if (version != LIBUBI_UBI_VERSION) {
+ errmsg("this library was made for UBI version %d, but UBI "
+ "version %d is detected\n", LIBUBI_UBI_VERSION, version);
+ goto out_error;
+ }
+
+ return lib;
+
+out_error:
+ libubi_close((libubi_t)lib);
+ return NULL;
+}
+
+void libubi_close(libubi_t desc)
+{
+ struct libubi *lib = (struct libubi *)desc;
+
+ free(lib->vol_name);
+ free(lib->vol_corrupted);
+ free(lib->vol_eb_size);
+ free(lib->vol_rsvd_ebs);
+ free(lib->vol_data_bytes);
+ free(lib->vol_alignment);
+ free(lib->vol_dev);
+ free(lib->vol_type);
+ free(lib->ubi_vol);
+ free(lib->dev_mtd_num);
+ free(lib->dev_min_io_size);
+ free(lib->dev_max_vols);
+ free(lib->dev_bad_rsvd);
+ free(lib->dev_max_ec);
+ free(lib->dev_eb_size);
+ free(lib->dev_bad_count);
+ free(lib->dev_total_ebs);
+ free(lib->dev_avail_ebs);
+ free(lib->dev_dev);
+ free(lib->ubi_version);
+ free(lib->ubi_dev);
+ free(lib->sysfs_ubi);
+ free(lib->ctrl_dev);
+ free(lib->sysfs_ctrl);
+ free(lib);
+}
+
+int ubi_attach_mtd(libubi_t desc, const char *node,
+ struct ubi_attach_request *req)
+{
+ int fd, ret;
+ struct ubi_attach_req r;
+
+ memset(&r, 0, sizeof(struct ubi_attach_req));
+
+ desc = desc;
+ r.ubi_num = req->dev_num;
+ r.mtd_num = req->mtd_num;
+ r.vid_hdr_offset = req->vid_hdr_offset;
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", node);
+
+ ret = ioctl(fd, UBI_IOCATT, &r);
+ close(fd);
+ if (ret == -1)
+ return -1;
+
+ req->dev_num = r.ubi_num;
+
+#ifdef UDEV_SETTLE_HACK
+// if (system("udevsettle") == -1)
+// return -1;
+ usleep(100000);
+#endif
+
+ return ret;
+}
+
+int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num)
+{
+ int ret, ubi_dev;
+
+ ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev);
+ if (ret == -1) {
+ errno = ENODEV;
+ return ret;
+ }
+
+ return ubi_remove_dev(desc, node, ubi_dev);
+}
+
+int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev)
+{
+ int fd, ret;
+
+ desc = desc;
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", node);
+ ret = ioctl(fd, UBI_IOCDET, &ubi_dev);
+ if (ret == -1)
+ goto out_close;
+
+#ifdef UDEV_SETTLE_HACK
+// if (system("udevsettle") == -1)
+// return -1;
+ usleep(100000);
+#endif
+
+out_close:
+ close(fd);
+ return ret;
+}
+
+int ubi_probe_node(libubi_t desc, const char *node)
+{
+ struct stat st;
+ struct ubi_info info;
+ int i, fd, major, minor;
+ struct libubi *lib = (struct libubi *)desc;
+ char file[strlen(lib->ubi_vol) + 100];
+
+ if (stat(node, &st))
+ return sys_errmsg("cannot get information about \"%s\"", node);
+
+ if (!S_ISCHR(st.st_mode)) {
+ errmsg("\"%s\" is not a character device", node);
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(st.st_rdev);
+ minor = minor(st.st_rdev);
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+
+ ret = dev_get_major(lib, i, &major1, &minor1);
+ if (ret) {
+ if (errno == ENOENT)
+ continue;
+ if (!errno)
+ goto out_not_ubi;
+ return -1;
+ }
+
+ if (major1 == major)
+ break;
+ }
+
+ if (i > info.highest_dev_num)
+ goto out_not_ubi;
+
+ if (minor == 0)
+ return 1;
+
+ /* This is supposdely an UBI volume device node */
+ sprintf(file, lib->ubi_vol, i, minor - 1);
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ goto out_not_ubi;
+
+ return 2;
+
+out_not_ubi:
+ errmsg("\"%s\" has major:minor %d:%d, but this does not correspond to "
+ "any existing UBI device or volume", node, major, minor);
+ errno = ENODEV;
+ return -1;
+}
+
+int ubi_get_info(libubi_t desc, struct ubi_info *info)
+{
+ DIR *sysfs_ubi;
+ struct dirent *dirent;
+ struct libubi *lib = (struct libubi *)desc;
+
+ memset(info, 0, sizeof(struct ubi_info));
+
+ if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) {
+ /*
+ * Older UBI versions did not have control device, so we do not
+ * panic here for compatibility reasons. May be few years later
+ * we could return -1 here, but for now just set major:minor to
+ * -1.
+ */
+ info->ctrl_major = info->ctrl_minor = -1;
+ }
+
+ /*
+ * We have to scan the UBI sysfs directory to identify how many UBI
+ * devices are present.
+ */
+ sysfs_ubi = opendir(lib->sysfs_ubi);
+ if (!sysfs_ubi)
+ return -1;
+
+ info->lowest_dev_num = INT_MAX;
+ while (1) {
+ int dev_num, ret;
+ char tmp_buf[256];
+
+ errno = 0;
+ dirent = readdir(sysfs_ubi);
+ if (!dirent)
+ break;
+
+ if (strlen(dirent->d_name) >= 255) {
+ errmsg("invalid entry in %s: \"%s\"",
+ lib->sysfs_ubi, dirent->d_name);
+ errno = EINVAL;
+ goto out_close;
+ }
+
+ ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s",
+ &dev_num, tmp_buf);
+ if (ret == 1) {
+ info->dev_count += 1;
+ if (dev_num > info->highest_dev_num)
+ info->highest_dev_num = dev_num;
+ if (dev_num < info->lowest_dev_num)
+ info->lowest_dev_num = dev_num;
+ }
+ }
+
+ if (!dirent && errno) {
+ sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi);
+ goto out_close;
+ }
+
+ if (closedir(sysfs_ubi))
+ return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi);
+
+ if (info->lowest_dev_num == INT_MAX)
+ info->lowest_dev_num = 0;
+
+ if (read_positive_int(lib->ubi_version, &info->version))
+ return -1;
+
+ return 0;
+
+out_close:
+ closedir(sysfs_ubi);
+ return -1;
+}
+
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req)
+{
+ int fd, ret;
+ struct ubi_mkvol_req r;
+ size_t n;
+
+ memset(&r, 0, sizeof(struct ubi_mkvol_req));
+
+ desc = desc;
+ r.vol_id = req->vol_id;
+ r.alignment = req->alignment;
+ r.bytes = req->bytes;
+ r.vol_type = req->vol_type;
+
+ n = strlen(req->name);
+ if (n > UBI_MAX_VOLUME_NAME)
+ return -1;
+
+ strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1);
+ r.name_len = n;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", node);
+
+ ret = ioctl(fd, UBI_IOCMKVOL, &r);
+ if (ret == -1) {
+ close(fd);
+ return ret;
+ }
+
+ close(fd);
+ req->vol_id = r.vol_id;
+
+#ifdef UDEV_SETTLE_HACK
+// if (system("udevsettle") == -1)
+// return -1;
+ usleep(100000);
+#endif
+
+ return 0;
+}
+
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id)
+{
+ int fd, ret;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", node);
+
+ ret = ioctl(fd, UBI_IOCRMVOL, &vol_id);
+ if (ret == -1) {
+ close(fd);
+ return ret;
+ }
+
+ close(fd);
+
+#ifdef UDEV_SETTLE_HACK
+// if (system("udevsettle") == -1)
+// return -1;
+ usleep(100000);
+#endif
+
+ return 0;
+}
+
+int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol)
+{
+ int fd, ret;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, UBI_IOCRNVOL, rnvol);
+ if (ret == -1) {
+ close(fd);
+ return ret;
+ }
+
+ close(fd);
+
+#ifdef UDEV_SETTLE_HACK
+// if (system("udevsettle") == -1)
+// return -1;
+ usleep(100000);
+#endif
+
+ return 0;
+}
+
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes)
+{
+ int fd, ret;
+ struct ubi_rsvol_req req;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", node);
+
+ req.bytes = bytes;
+ req.vol_id = vol_id;
+
+ ret = ioctl(fd, UBI_IOCRSVOL, &req);
+ close(fd);
+ return ret;
+}
+
+int ubi_update_start(libubi_t desc, int fd, long long bytes)
+{
+ desc = desc;
+ if (ioctl(fd, UBI_IOCVOLUP, &bytes))
+ return -1;
+ return 0;
+}
+
+int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype)
+{
+ struct ubi_leb_change_req req;
+
+ desc = desc;
+ memset(&req, 0, sizeof(struct ubi_leb_change_req));
+ req.lnum = lnum;
+ req.bytes = bytes;
+ req.dtype = dtype;
+
+ if (ioctl(fd, UBI_IOCEBCH, &req))
+ return -1;
+ return 0;
+}
+
+/**
+ * dev_present - check whether an UBI device is present.
+ * @lib: libubi descriptor
+ * @dev_num: UBI device number to check
+ *
+ * This function returns %1 if UBI device is present and %0 if not.
+ */
+static int dev_present(struct libubi *lib, int dev_num)
+{
+ struct stat st;
+ char file[strlen(lib->ubi_dev) + 50];
+
+ sprintf(file, lib->ubi_dev, dev_num);
+ return !stat(file, &st);
+}
+
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
+{
+ DIR *sysfs_ubi;
+ struct dirent *dirent;
+ struct libubi *lib = (struct libubi *)desc;
+
+ memset(info, 0, sizeof(struct ubi_dev_info));
+ info->dev_num = dev_num;
+
+ if (!dev_present(lib, dev_num))
+ return -1;
+
+ sysfs_ubi = opendir(lib->sysfs_ubi);
+ if (!sysfs_ubi)
+ return -1;
+
+ info->lowest_vol_id = INT_MAX;
+
+ while (1) {
+ int vol_id, ret, devno;
+ char tmp_buf[256];
+
+ errno = 0;
+ dirent = readdir(sysfs_ubi);
+ if (!dirent)
+ break;
+
+ if (strlen(dirent->d_name) >= 255) {
+ errmsg("invalid entry in %s: \"%s\"",
+ lib->sysfs_ubi, dirent->d_name);
+ goto out_close;
+ }
+
+ ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf);
+ if (ret == 2 && devno == dev_num) {
+ info->vol_count += 1;
+ if (vol_id > info->highest_vol_id)
+ info->highest_vol_id = vol_id;
+ if (vol_id < info->lowest_vol_id)
+ info->lowest_vol_id = vol_id;
+ }
+ }
+
+ if (!dirent && errno) {
+ sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi);
+ goto out_close;
+ }
+
+ if (closedir(sysfs_ubi))
+ return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi);
+
+ if (info->lowest_vol_id == INT_MAX)
+ info->lowest_vol_id = 0;
+
+ if (dev_get_major(lib, dev_num, &info->major, &info->minor))
+ return -1;
+
+ if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs))
+ return -1;
+ if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs))
+ return -1;
+ if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count))
+ return -1;
+ if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size))
+ return -1;
+ if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd))
+ return -1;
+ if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec))
+ return -1;
+ if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count))
+ return -1;
+ if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size))
+ return -1;
+
+ info->avail_bytes = (long long)info->avail_lebs * info->leb_size;
+ info->total_bytes = (long long)info->total_lebs * info->leb_size;
+
+ return 0;
+
+out_close:
+ closedir(sysfs_ubi);
+ return -1;
+}
+
+int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info)
+{
+ int err, dev_num;
+ struct libubi *lib = (struct libubi *)desc;
+
+ err = ubi_probe_node(desc, node);
+ if (err != 1) {
+ if (err == 2)
+ errno = ENODEV;
+ return -1;
+ }
+
+ if (dev_node2num(lib, node, &dev_num))
+ return -1;
+
+ return ubi_get_dev_info1(desc, dev_num, info);
+}
+
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+ struct ubi_vol_info *info)
+{
+ int ret;
+ struct libubi *lib = (struct libubi *)desc;
+ char buf[50];
+
+ memset(info, 0, sizeof(struct ubi_vol_info));
+ info->dev_num = dev_num;
+ info->vol_id = vol_id;
+
+ if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor))
+ return -1;
+ if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor))
+ return -1;
+
+ ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50);
+ if (ret < 0)
+ return -1;
+
+ if (strncmp(buf, "static\n", ret) == 0)
+ info->type = UBI_STATIC_VOLUME;
+ else if (strncmp(buf, "dynamic\n", ret) == 0)
+ info->type = UBI_DYNAMIC_VOLUME;
+ else {
+ errmsg("bad value at \"%s\"", buf);
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = vol_read_int(lib->vol_alignment, dev_num, vol_id,
+ &info->alignment);
+ if (ret)
+ return -1;
+ ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id,
+ &info->data_bytes);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id,
+ &info->corrupted);
+ if (ret)
+ return -1;
+ info->rsvd_bytes = (long long)info->leb_size * info->rsvd_lebs;
+
+ ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name,
+ UBI_VOL_NAME_MAX + 2);
+ if (ret < 0)
+ return -1;
+
+ info->name[ret - 1] = '\0';
+ return 0;
+}
+
+int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info)
+{
+ int err, vol_id, dev_num;
+ struct libubi *lib = (struct libubi *)desc;
+
+ err = ubi_probe_node(desc, node);
+ if (err != 2) {
+ if (err == 1)
+ errno = ENODEV;
+ return -1;
+ }
+
+ if (vol_node2nums(lib, node, &dev_num, &vol_id))
+ return -1;
+
+ return ubi_get_vol_info1(desc, dev_num, vol_id, info);
+}
+
+int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name,
+ struct ubi_vol_info *info)
+{
+ int i, err;
+ unsigned int nlen = strlen(name);
+ struct ubi_dev_info dev_info;
+
+ if (nlen == 0) {
+ errmsg("bad \"name\" input parameter");
+ errno = EINVAL;
+ return -1;
+ }
+
+ err = ubi_get_dev_info1(desc, dev_num, &dev_info);
+ if (err)
+ return err;
+
+ for (i = dev_info.lowest_vol_id;
+ i <= dev_info.highest_vol_id; i++) {
+ err = ubi_get_vol_info1(desc, dev_num, i, info);
+ if (err == -1) {
+ if (errno == ENOENT)
+ continue;
+ return -1;
+ }
+
+ if (nlen == strlen(info->name) && !strcmp(name, info->name))
+ return 0;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+int ubi_set_property(int fd, uint8_t property, uint64_t value)
+{
+ struct ubi_set_prop_req r;
+
+ memset(&r, 0, sizeof(struct ubi_set_prop_req));
+ r.property = property;
+ r.value = value;
+
+ return ioctl(fd, UBI_IOCSETPROP, &r);
+}
+
+int ubi_leb_unmap(int fd, int lnum)
+{
+ return ioctl(fd, UBI_IOCEBUNMAP, &lnum);
+}
+
+int ubi_is_mapped(int fd, int lnum)
+{
+ return ioctl(fd, UBI_IOCEBISMAP, &lnum);
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libubi_int.h b/mtd-utils-1.3.1/ubi-utils/src/libubi_int.h
new file mode 100644
index 0000000..6d17d57
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libubi_int.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_INT_H__
+#define __LIBUBI_INT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The below are pre-define UBI file and directory names.
+ *
+ * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'.
+ * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is
+ * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y'
+ * directories to '/sys/class/ubi/'. For now libubi assumes old layout.
+ */
+
+#define SYSFS_UBI "class/ubi"
+#define SYSFS_CTRL "class/misc/ubi_ctrl/"
+
+#define CTRL_DEV "dev"
+
+#define UBI_VER "version"
+#define UBI_DEV_NAME_PATT "ubi%d"
+
+#define DEV_DEV "dev"
+#define DEV_AVAIL_EBS "avail_eraseblocks"
+#define DEV_TOTAL_EBS "total_eraseblocks"
+#define DEV_BAD_COUNT "bad_peb_count"
+#define DEV_EB_SIZE "eraseblock_size"
+#define DEV_MAX_EC "max_ec"
+#define DEV_MAX_RSVD "reserved_for_bad"
+#define DEV_MAX_VOLS "max_vol_count"
+#define DEV_MIN_IO_SIZE "min_io_size"
+#define DEV_MTD_NUM "mtd_num"
+
+#define UBI_VOL_NAME_PATT "ubi%d_%d"
+#define VOL_TYPE "type"
+#define VOL_DEV "dev"
+#define VOL_ALIGNMENT "alignment"
+#define VOL_DATA_BYTES "data_bytes"
+#define VOL_RSVD_EBS "reserved_ebs"
+#define VOL_EB_SIZE "usable_eb_size"
+#define VOL_CORRUPTED "corrupted"
+#define VOL_NAME "name"
+
+/**
+ * libubi - UBI library description data structure.
+ * @sysfs: sysfs file system path
+ * @sysfs_ctrl: UBI control device directory in sysfs
+ * @ctrl_dev: UBI control device major/minor numbers sysfs file
+ * @sysfs_ubi: UBI directory in sysfs
+ * @ubi_dev: UBI device sysfs directory pattern
+ * @ubi_version: UBI version file sysfs path
+ * @dev_dev: UBI device major/minor numbers file pattern
+ * @dev_avail_ebs: count of available eraseblocks sysfs path pattern
+ * @dev_total_ebs: total eraseblocks count sysfs path pattern
+ * @dev_bad_count: count of bad eraseblocks sysfs path pattern
+ * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern
+ * @dev_max_ec: maximum erase counter sysfs path pattern
+ * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks
+ * handling
+ * @dev_max_vols: maximum volumes number count sysfs path pattern
+ * @dev_min_io_size: minimum I/O unit size sysfs path pattern
+ * @ubi_vol: UBI volume sysfs directory pattern
+ * @vol_type: volume type sysfs path pattern
+ * @vol_dev: volume major/minor numbers file pattern
+ * @vol_alignment: volume alignment sysfs path pattern
+ * @vol_data_bytes: volume data size sysfs path pattern
+ * @vol_rsvd_ebs: volume reserved size sysfs path pattern
+ * @vol_eb_size: volume eraseblock size sysfs path pattern
+ * @vol_corrupted: volume corruption flag sysfs path pattern
+ * @vol_name: volume name sysfs path pattern
+ */
+struct libubi
+{
+ char *sysfs;
+ char *sysfs_ctrl;
+ char *ctrl_dev;
+ char *sysfs_ubi;
+ char *ubi_dev;
+ char *ubi_version;
+ char *dev_dev;
+ char *dev_avail_ebs;
+ char *dev_total_ebs;
+ char *dev_bad_count;
+ char *dev_eb_size;
+ char *dev_max_ec;
+ char *dev_bad_rsvd;
+ char *dev_max_vols;
+ char *dev_min_io_size;
+ char *dev_mtd_num;
+ char *ubi_vol;
+ char *vol_type;
+ char *vol_dev;
+ char *vol_alignment;
+ char *vol_data_bytes;
+ char *vol_rsvd_ebs;
+ char *vol_eb_size;
+ char *vol_corrupted;
+ char *vol_name;
+ char *vol_max_count;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_INT_H__ */
diff --git a/mtd-utils-1.3.1/ubi-utils/src/libubigen.c b/mtd-utils-1.3.1/ubi-utils/src/libubigen.c
new file mode 100644
index 0000000..db58cae
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/libubigen.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Generating UBI images.
+ *
+ * Authors: Oliver Lohmann
+ * Artem Bityutskiy
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <mtd/ubi-media.h>
+#include <mtd_swab.h>
+#include <libubigen.h>
+#include "crc32.h"
+#include "common.h"
+
+#define PROGRAM_NAME "libubigen"
+
+/**
+ * ubigen_info_init - initialize libubigen.
+ * @ui: libubigen information
+ * @peb_size: flash physical eraseblock size
+ * @min_io_size: flash minimum input/output unit size
+ * @subpage_size: flash sub-page, if present (has to be equivalent to
+ * @min_io_size if does not exist)
+ * @vid_hdr_offs: offset of the VID header
+ * @ubi_ver: UBI version
+ * @image_seq: UBI image sequence number
+ */
+void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
+ int subpage_size, int vid_hdr_offs, int ubi_ver,
+ uint32_t image_seq)
+{
+ if (!vid_hdr_offs) {
+ vid_hdr_offs = UBI_EC_HDR_SIZE + subpage_size - 1;
+ vid_hdr_offs /= subpage_size;
+ vid_hdr_offs *= subpage_size;
+ }
+
+ ui->peb_size = peb_size;
+ ui->min_io_size = min_io_size;
+ ui->vid_hdr_offs = vid_hdr_offs;
+ ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1;
+ ui->data_offs /= min_io_size;
+ ui->data_offs *= min_io_size;
+ ui->leb_size = peb_size - ui->data_offs;
+ ui->ubi_ver = ubi_ver;
+ ui->image_seq = image_seq;
+
+ ui->max_volumes = ui->leb_size / UBI_VTBL_RECORD_SIZE;
+ if (ui->max_volumes > UBI_MAX_VOLUMES)
+ ui->max_volumes = UBI_MAX_VOLUMES;
+ ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE;
+}
+
+/**
+ * ubigen_create_empty_vtbl - creates empty volume table.
+ *
+ * This function creates an empty volume table and returns a pointer to it in
+ * case of success and %NULL in case of failure. The returned object has to be
+ * freed with 'free()' call.
+ */
+struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui)
+{
+ struct ubi_vtbl_record *vtbl;
+ int i;
+
+ vtbl = calloc(1, ui->vtbl_size);
+ if (!vtbl) {
+ sys_errmsg("cannot allocate %d bytes of memory", ui->vtbl_size);
+ return NULL;
+ }
+
+ for (i = 0; i < ui->max_volumes; i++) {
+ uint32_t crc = crc32(UBI_CRC32_INIT, &vtbl[i],
+ UBI_VTBL_RECORD_SIZE_CRC);
+ vtbl[i].crc = cpu_to_be32(crc);
+ }
+
+ return vtbl;
+}
+
+/**
+ * ubigen_add_volume - add a volume to the volume table.
+ * @ui: libubigen information
+ * @vi: volume information
+ * @vtbl: volume table to add to
+ *
+ * This function adds volume described by input parameters to the volume table
+ * @vtbl.
+ */
+int ubigen_add_volume(const struct ubigen_info *ui,
+ const struct ubigen_vol_info *vi,
+ struct ubi_vtbl_record *vtbl)
+{
+ struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id];
+ uint32_t tmp;
+
+ if (vi->id >= ui->max_volumes)
+ return errmsg("too high volume id %d, max. volumes is %d",
+ vi->id, ui->max_volumes);
+
+ if (vi->alignment >= ui->leb_size)
+ return errmsg("too large alignment %d, max is %d (LEB size)",
+ vi->alignment, ui->leb_size);
+
+ memset(vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
+ tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size;
+ vtbl_rec->reserved_pebs = cpu_to_be32(tmp);
+ vtbl_rec->alignment = cpu_to_be32(vi->alignment);
+ vtbl_rec->vol_type = vi->type;
+ tmp = ui->leb_size % vi->alignment;
+ vtbl_rec->data_pad = cpu_to_be32(tmp);
+ vtbl_rec->flags = vi->flags;
+
+ memcpy(vtbl_rec->name, vi->name, vi->name_len);
+ vtbl_rec->name[vi->name_len] = '\0';
+ vtbl_rec->name_len = cpu_to_be16(vi->name_len);
+
+ tmp = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC);
+ vtbl_rec->crc = cpu_to_be32(tmp);
+ return 0;
+}
+
+/**
+ * ubigen_init_ec_hdr - initialize EC header.
+ * @ui: libubigen information
+ * @hdr: the EC header to initialize
+ * @ec: erase counter value
+ */
+void ubigen_init_ec_hdr(const struct ubigen_info *ui,
+ struct ubi_ec_hdr *hdr, long long ec)
+{
+ uint32_t crc;
+
+ memset(hdr, 0, sizeof(struct ubi_ec_hdr));
+
+ hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC);
+ hdr->version = ui->ubi_ver;
+ hdr->ec = cpu_to_be64(ec);
+ hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs);
+ hdr->data_offset = cpu_to_be32(ui->data_offs);
+ hdr->image_seq = cpu_to_be32(ui->image_seq);
+
+ crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr->hdr_crc = cpu_to_be32(crc);
+}
+
+/**
+ * init_vid_hdr - initialize VID header.
+ * @ui: libubigen information
+ * @vi: volume information
+ * @hdr: the VID header to initialize
+ * @lnum: logical eraseblock number
+ * @data: the contents of the LEB (static volumes only)
+ * @data_size: amount of data in this LEB (static volumes only)
+ *
+ * Note, @used_ebs, @data and @data_size are ignored in case of dynamic
+ * volumes.
+ */
+static void init_vid_hdr(const struct ubigen_info *ui,
+ const struct ubigen_vol_info *vi,
+ struct ubi_vid_hdr *hdr, int lnum,
+ const void *data, int data_size)
+{
+ uint32_t crc;
+
+ memset(hdr, 0, sizeof(struct ubi_vid_hdr));
+
+ hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
+ hdr->version = ui->ubi_ver;
+ hdr->vol_type = vi->type;
+ hdr->vol_id = cpu_to_be32(vi->id);
+ hdr->lnum = cpu_to_be32(lnum);
+ hdr->data_pad = cpu_to_be32(vi->data_pad);
+ hdr->compat = vi->compat;
+
+ if (vi->type == UBI_VID_STATIC) {
+ hdr->data_size = cpu_to_be32(data_size);
+ hdr->used_ebs = cpu_to_be32(vi->used_ebs);
+ crc = crc32(UBI_CRC32_INIT, data, data_size);
+ hdr->data_crc = cpu_to_be32(crc);
+ }
+
+ crc = crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC);
+ hdr->hdr_crc = cpu_to_be32(crc);
+}
+
+/**
+ * ubigen_write_volume - write UBI volume.
+ * @ui: libubigen information
+ * @vi: volume information
+ * @ec: erase coutner value to put to EC headers
+ * @bytes: volume size in bytes
+ * @in: input file descriptor (has to be properly seeked)
+ * @out: output file descriptor
+ *
+ * This function reads the contents of the volume from the input file @in and
+ * writes the UBI volume to the output file @out. Returns zero on success and
+ * %-1 on failure.
+ */
+int ubigen_write_volume(const struct ubigen_info *ui,
+ const struct ubigen_vol_info *vi, long long ec,
+ long long bytes, int in, int out)
+{
+ int len = vi->usable_leb_size, rd, lnum = 0;
+ char inbuf[ui->leb_size], outbuf[ui->peb_size];
+
+ if (vi->id >= ui->max_volumes)
+ return errmsg("too high volume id %d, max. volumes is %d",
+ vi->id, ui->max_volumes);
+
+ if (vi->alignment >= ui->leb_size)
+ return errmsg("too large alignment %d, max is %d (LEB size)",
+ vi->alignment, ui->leb_size);
+
+ memset(outbuf, 0xFF, ui->data_offs);
+ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec);
+
+ while (bytes) {
+ int l;
+ struct ubi_vid_hdr *vid_hdr;
+
+ if (bytes < len)
+ len = bytes;
+ bytes -= len;
+
+ l = len;
+ do {
+ rd = read(in, inbuf + len - l, l);
+ if (rd != l)
+ return sys_errmsg("cannot read %d bytes from the input file", l);
+
+ l -= rd;
+ } while (l);
+
+ vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]);
+ init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf, len);
+
+ memcpy(outbuf + ui->data_offs, inbuf, len);
+ memset(outbuf + ui->data_offs + len, 0xFF,
+ ui->peb_size - ui->data_offs - len);
+
+ if (write(out, outbuf, ui->peb_size) != ui->peb_size)
+ return sys_errmsg("cannot write %d bytes to the output file", ui->peb_size);
+
+ lnum += 1;
+ }
+
+ return 0;
+}
+
+/**
+ * ubigen_write_layout_vol - write UBI layout volume
+ * @ui: libubigen information
+ * @peb1: physical eraseblock number to write the first volume table copy
+ * @peb2: physical eraseblock number to write the second volume table copy
+ * @ec1: erase counter value for @peb1
+ * @ec2: erase counter value for @peb1
+ * @vtbl: volume table
+ * @fd: output file descriptor seeked to the proper position
+ *
+ * This function creates the UBI layout volume which contains 2 copies of the
+ * volume table. Returns zero in case of success and %-1 in case of failure.
+ */
+int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
+ long long ec1, long long ec2,
+ struct ubi_vtbl_record *vtbl, int fd)
+{
+ int ret;
+ struct ubigen_vol_info vi;
+ char *outbuf;
+ struct ubi_vid_hdr *vid_hdr;
+ off_t seek;
+
+ vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS;
+ vi.id = UBI_LAYOUT_VOLUME_ID;
+ vi.alignment = UBI_LAYOUT_VOLUME_ALIGN;
+ vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN;
+ vi.usable_leb_size = ui->leb_size - vi.data_pad;
+ vi.data_pad = ui->leb_size - vi.usable_leb_size;
+ vi.type = UBI_LAYOUT_VOLUME_TYPE;
+ vi.name = UBI_LAYOUT_VOLUME_NAME;
+ vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME);
+ vi.compat = UBI_LAYOUT_VOLUME_COMPAT;
+
+ outbuf = malloc(ui->peb_size);
+ if (!outbuf)
+ return sys_errmsg("failed to allocate %d bytes",
+ ui->peb_size);
+
+ memset(outbuf, 0xFF, ui->data_offs);
+ vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]);
+ memcpy(outbuf + ui->data_offs, vtbl, ui->vtbl_size);
+ memset(outbuf + ui->data_offs + ui->vtbl_size, 0xFF,
+ ui->peb_size - ui->data_offs - ui->vtbl_size);
+
+ seek = peb1 * ui->peb_size;
+ if (lseek(fd, seek, SEEK_SET) != seek) {
+ sys_errmsg("cannot seek output file");
+ goto out_free;
+ }
+
+ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1);
+ init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0);
+ ret = write(fd, outbuf, ui->peb_size);
+ if (ret != ui->peb_size) {
+ sys_errmsg("cannot write %d bytes", ui->peb_size);
+ goto out_free;
+ }
+
+ seek = peb2 * ui->peb_size;
+ if (lseek(fd, seek, SEEK_SET) != seek) {
+ sys_errmsg("cannot seek output file");
+ goto out_free;
+ }
+ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2);
+ init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0);
+ ret = write(fd, outbuf, ui->peb_size);
+ if (ret != ui->peb_size) {
+ sys_errmsg("cannot write %d bytes", ui->peb_size);
+ goto out_free;
+ }
+
+ free(outbuf);
+ return 0;
+
+out_free:
+ free(outbuf);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/mtdinfo.c b/mtd-utils-1.3.1/ubi-utils/src/mtdinfo.c
new file mode 100644
index 0000000..be8b1ff
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/mtdinfo.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2009 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
+ */
+
+/*
+ * An utility to get MTD information.
+ *
+ * Author: Artem Bityutskiy
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mtd/mtd-user.h>
+
+#include <libubigen.h>
+#include <libmtd.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.0"
+#define PROGRAM_NAME "mtdinfo"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int mtdn;
+ unsigned int all:1;
+ unsigned int ubinfo:1;
+ const char *node;
+};
+
+static struct args args = {
+ .mtdn = -1,
+ .ubinfo = 0,
+ .all = 0,
+ .node = NULL,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to print MTD information.";
+
+static const char *optionsstr =
+"-m, --mtdn=<MTD device number> MTD device number to get information about\n"
+"-u, --ubi-info print what would UBI layout be if it was put\n"
+" on this MTD device\n"
+"-a, --all print information about all MTD devices\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage 1: " PROGRAM_NAME " [-m <MTD device number>] [-u] [-h] [-V] [--mtdn <MTD device number>]\n"
+"\t\t[--ubi-info] [--help] [--version]\n"
+"Usage 2: " PROGRAM_NAME " <MTD device node file name> [-u] [-h] [-V] [--ubi-info] [--help]\n"
+"\t\t[--version]\n"
+"Example 1: " PROGRAM_NAME " - (no arguments) print general MTD information\n"
+"Example 2: " PROGRAM_NAME " -m 1 - print information about MTD device number 1\n"
+"Example 3: " PROGRAM_NAME " /dev/mtd0 - print information MTD device /dev/mtd0\n"
+"Example 4: " PROGRAM_NAME " /dev/mtd0 -u - print information MTD device /dev/mtd0\n"
+"\t\t\t\tand include UBI layout information\n"
+"Example 5: " PROGRAM_NAME " -a - print information about all MTD devices\n"
+"\t\t\tand include UBI layout information\n";
+
+static const struct option long_options[] = {
+ { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' },
+ { .name = "ubi-info", .has_arg = 0, .flag = NULL, .val = 'u' },
+ { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "am:uhV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'a':
+ args.all = 1;
+ break;
+
+ case 'u':
+ args.ubinfo = 1;
+ break;
+
+ case 'm':
+ args.mtdn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.mtdn < 0)
+ return errmsg("bad MTD device number: \"%s\"", optarg);
+
+ break;
+
+ case 'h':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc - 1)
+ args.node = argv[optind];
+ else if (optind < argc)
+ return errmsg("more then one MTD device specified (use -h for help)");
+
+ if (args.all && (args.node || args.mtdn != -1)) {
+ args.mtdn = -1;
+ args.node = NULL;
+ }
+
+ return 0;
+}
+
+static int translate_dev(libmtd_t libmtd, const char *node)
+{
+ int err;
+ struct mtd_dev_info mtd;
+
+ err = mtd_get_dev_info(libmtd, node, &mtd);
+ if (err) {
+ if (errno == ENODEV)
+ return errmsg("\"%s\" does not correspond to any "
+ "existing MTD device", node);
+ return sys_errmsg("cannot get information about MTD "
+ "device \"%s\"", node);
+ }
+
+ args.mtdn = mtd.dev_num;
+ return 0;
+}
+
+static int print_dev_info(libmtd_t libmtd, const struct mtd_info *mtd_info, int mtdn)
+{
+ int err;
+ struct mtd_dev_info mtd;
+ struct ubigen_info ui;
+
+ err = mtd_get_dev_info1(libmtd, mtdn, &mtd);
+ if (err) {
+ if (errno == ENODEV)
+ return errmsg("mtd%d does not correspond to any "
+ "existing MTD device", mtdn);
+ return sys_errmsg("cannot get information about MTD device %d",
+ mtdn);
+ }
+
+ printf("mtd%d\n", mtd.dev_num);
+ printf("Name: %s\n", mtd.name);
+ printf("Type: %s\n", mtd.type_str);
+ printf("Eraseblock size: ");
+ ubiutils_print_bytes(mtd.eb_size, 0);
+ printf("\n");
+ printf("Amount of eraseblocks: %d (", mtd.eb_cnt);
+ ubiutils_print_bytes(mtd.size, 0);
+ printf(")\n");
+ printf("Minimum input/output unit size: %d %s\n",
+ mtd.min_io_size, mtd.min_io_size > 1 ? "bytes" : "byte");
+ if (mtd_info->sysfs_supported)
+ printf("Sub-page size: %d %s\n",
+ mtd.subpage_size,
+ mtd.subpage_size > 1 ? "bytes" : "byte");
+ else if (mtd.type == MTD_NANDFLASH)
+ printf("Sub-page size: unknown\n");
+
+ if (mtd.oob_size > 0)
+ printf("OOB size: %d bytes\n",
+ mtd.oob_size);
+ if (mtd.region_cnt > 0)
+ printf("Additional erase regions: %d\n", mtd.oob_size);
+ if (mtd_info->sysfs_supported)
+ printf("Character device major/minor: %d:%d\n",
+ mtd.major, mtd.minor);
+ printf("Bad blocks are allowed: %s\n",
+ mtd.bb_allowed ? "true" : "false");
+ printf("Device is writable: %s\n",
+ mtd.writable ? "true" : "false");
+
+ if (!args.ubinfo)
+ goto out;
+
+ if (!mtd_info->sysfs_supported) {
+ errmsg("cannot provide UBI info, becasue sub-page size is "
+ "not known");
+ goto out;
+ }
+
+ ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size,
+ 0, 1, 0);
+ printf("Default UBI VID header offset: %d\n", ui.vid_hdr_offs);
+ printf("Default UBI data offset: %d\n", ui.data_offs);
+ printf("Default UBI LEB size: ");
+ ubiutils_print_bytes(ui.leb_size, 0);
+ printf("\n");
+ printf("Maximum UBI volumes count: %d\n", ui.max_volumes);
+
+out:
+ printf("\n");
+ return 0;
+}
+
+static int print_general_info(libmtd_t libmtd, const struct mtd_info *mtd_info,
+ int all)
+{
+ int i, err, first = 1;
+ struct mtd_dev_info mtd;
+
+ printf("Count of MTD devices: %d\n", mtd_info->dev_count);
+ if (mtd_info->dev_count == 0)
+ return 0;
+
+ for (i = mtd_info->lowest_dev_num;
+ i <= mtd_info->highest_dev_num; i++) {
+ err = mtd_get_dev_info1(libmtd, i, &mtd);
+ if (err == -1) {
+ if (errno == ENODEV)
+ continue;
+ return sys_errmsg("libmtd failed get MTD device %d "
+ "information", i);
+ }
+
+ if (!first)
+ printf(", mtd%d", i);
+ else {
+ printf("Present MTD devices: mtd%d", i);
+ first = 0;
+ }
+ }
+ printf("\n");
+ printf("Sysfs interface supported: %s\n",
+ mtd_info->sysfs_supported ? "yes" : "no");
+
+ if (!all)
+ return 0;
+
+ first = 1;
+ printf("\n");
+
+ for (i = mtd_info->lowest_dev_num;
+ i <= mtd_info->highest_dev_num; i++) {
+ err = print_dev_info(libmtd, mtd_info, i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libmtd_t libmtd;
+ struct mtd_info mtd_info;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libmtd = libmtd_open();
+ if (libmtd == NULL) {
+ if (errno == 0)
+ return errmsg("MTD is not present in the system");
+ return sys_errmsg("cannot open libmtd");
+ }
+
+ err = mtd_get_info(libmtd, &mtd_info);
+ if (err) {
+ if (errno == ENODEV)
+ return errmsg("MTD is not present");
+ return sys_errmsg("cannot get MTD information");
+ }
+
+ if (args.node) {
+ /*
+ * A character device was specified, translate this to MTD
+ * device number.
+ */
+ err = translate_dev(libmtd, args.node);
+ if (err)
+ goto out_libmtd;
+ }
+
+ if (args.mtdn == -1)
+ err = print_general_info(libmtd, &mtd_info, args.all);
+ else
+ err = print_dev_info(libmtd, &mtd_info, args.mtdn);
+ if (err)
+ goto out_libmtd;
+
+ libmtd_close(libmtd);
+ return 0;
+
+out_libmtd:
+ libmtd_close(libmtd);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubiattach.c b/mtd-utils-1.3.1/ubi-utils/src/ubiattach.c
new file mode 100644
index 0000000..a935b42
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubiattach.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 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
+ */
+
+/*
+ * An utility to attach MTD devices to UBI.
+ *
+ * Author: Artem Bityutskiy
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.0"
+#define PROGRAM_NAME "ubiattach"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int devn;
+ int mtdn;
+ int vidoffs;
+ const char *node;
+};
+
+static struct args args = {
+ .devn = UBI_DEV_NUM_AUTO,
+ .mtdn = -1,
+ .vidoffs = 0,
+ .node = NULL,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to attach MTD device to UBI.";
+
+static const char *optionsstr =
+"-d, --devn=<UBI device number> the number to assign to the newly created UBI device\n"
+" (the number is assigned automatically if this is not\n"
+" specified\n"
+"-m, --mtdn=<MTD device number> MTD device number to attach\n"
+"-O, --vid-hdr-offset VID header offset (do not specify this unless you\n"
+" really know what you do and the optimal defaults will\n"
+" be used)\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI control device node file name> [-m <MTD device number>] [-d <UBI device number>]\n"
+"\t\t[--mtdn=<MTD device number>] [--devn <UBI device number>]\n"
+"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - attach MTD device 0 (mtd0) to UBI\n"
+"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI and\n"
+" and create UBI device number 3 (ubi3)";
+
+static const struct option long_options[] = {
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' },
+ { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "m:d:O:hV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'd':
+ args.devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.devn < 0)
+ return errmsg("bad UBI device number: \"%s\"", optarg);
+
+ break;
+
+ case 'm':
+ args.mtdn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.mtdn < 0)
+ return errmsg("bad MTD device number: \"%s\"", optarg);
+
+ break;
+
+ case 'O':
+ args.vidoffs = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.vidoffs <= 0)
+ return errmsg("bad VID header offset: \"%s\"", optarg);
+
+ break;
+
+ case 'h':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc)
+ return errmsg("UBI control device name was not specified (use -h for help)");
+ else if (optind != argc - 1)
+ return errmsg("more then one UBI control device specified (use -h for help)");
+
+ if (args.mtdn == -1)
+ return errmsg("MTD device number was not specified (use -h for help)");
+
+ args.node = argv[optind];
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_info ubi_info;
+ struct ubi_dev_info dev_info;
+ struct ubi_attach_request req;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ return sys_errmsg("cannot open libubi");
+ }
+
+ /*
+ * Make sure the kernel is fresh enough and this feature is supported.
+ */
+ err = ubi_get_info(libubi, &ubi_info);
+ if (err) {
+ sys_errmsg("cannot get UBI information");
+ goto out_libubi;
+ }
+
+ if (ubi_info.ctrl_major == -1) {
+ errmsg("MTD attach/detach feature is not supported by your kernel");
+ goto out_libubi;
+ }
+
+ req.dev_num = args.devn;
+ req.mtd_num = args.mtdn;
+ req.vid_hdr_offset = args.vidoffs;
+
+ err = ubi_attach_mtd(libubi, args.node, &req);
+ if (err) {
+ sys_errmsg("cannot attach mtd%d", args.mtdn);
+ goto out_libubi;
+ }
+
+ /* Print some information about the new UBI device */
+ err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info);
+ if (err) {
+ sys_errmsg("cannot get information about newly created UBI device");
+ goto out_libubi;
+ }
+
+ printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs);
+ ubiutils_print_bytes(dev_info.total_bytes, 0);
+ printf("), available %d LEBs (", dev_info.avail_lebs);
+ ubiutils_print_bytes(dev_info.avail_bytes, 0);
+ printf("), LEB size ");
+ ubiutils_print_bytes(dev_info.leb_size, 1);
+ printf("\n");
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubicpvol.c b/mtd-utils-1.3.1/ubi-utils/src/ubicpvol.c
new file mode 100644
index 0000000..50955a4
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubicpvol.c
@@ -0,0 +1,291 @@
+/*
+ * Based originally on the ubiupdatevol utility
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Google, Inc, 2014
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * A utility to replicate/copy UBI volumes.
+ *
+ * Authors: Frank Haverkamp
+ * Joshua W. Boyer
+ * Artem Bityutskiy
+ * Peter van Vugt
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.0"
+#define PROGRAM_NAME "ubicpvol"
+
+struct args {
+ const char *destNode;
+ const char *srcNode;
+};
+
+static struct args args;
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to replicate/copy UBI volumes.\n";
+
+static const char *optionsstr =
+"Options:\n"
+"-h, --help print help message\n"
+"-V, --version print program version\n";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " [OPTIONS] <UBI source volume node> <UBI destination volume node>\n\n"
+"Example: " PROGRAM_NAME " /dev/ubi0_1 /dev/ubi0_2 - copy UBI volume \"/dev/ubi0_1\" to \"/dev/ubi0_2\"\n";
+
+static const char *notes =
+"Note that both volumes must have the same LEB size and the data in the source\n"
+"volume must fit into the destination volume.\n";
+
+struct option long_options[] = {
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "h?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s\n", doc);
+ fprintf(stderr, "%s\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ fprintf(stderr, "%s\n", notes);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind != argc - 2)
+ return errmsg("specify source and destination UBI device names as first 2 "
+ "parameters (use -h for help)");
+
+ args.srcNode = argv[optind];
+ args.destNode = argv[optind + 1];
+
+ return 0;
+}
+
+static int ubi_write(int fd, const void *buf, int len)
+{
+ int ret;
+
+ while (len) {
+ ret = write(fd, buf, len);
+ if (ret <= 0) {
+ if (errno == EINTR) {
+ warnmsg("do not interrupt me!");
+ continue;
+ }
+ return errmsg("error %d: cannot write %d bytes to volume \"%s\"",
+ errno, len, args.destNode);
+ }
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return 0;
+}
+
+static int copy_volume(libubi_t libubi, struct ubi_vol_info *src_vol_info, struct ubi_vol_info *dest_vol_info)
+{
+ int err, dest_fd, src_fd;
+ long long bytes;
+ char *buf;
+ int ret = -1;
+
+ if (src_vol_info->leb_size != dest_vol_info->leb_size) {
+ return errmsg("source and destination volumes have different LEB sizes");
+ } else if (src_vol_info->data_bytes > dest_vol_info->rsvd_bytes) {
+ return errmsg("source volume \"%s\" too large for destination volume \"%s\" (%lld > %lld)", args.srcNode, args.destNode, src_vol_info->data_bytes, dest_vol_info->rsvd_bytes);
+ }
+
+ buf = malloc(dest_vol_info->leb_size);
+ if (!buf)
+ return errmsg("cannot allocate %d bytes of memory", dest_vol_info->leb_size);
+
+ bytes = src_vol_info->data_bytes;
+
+ src_fd = open(args.srcNode, O_RDONLY);
+ if (src_fd == -1) {
+ errmsg("cannot open source UBI volume \"%s\"", args.srcNode);
+ goto out_free;
+ }
+
+ dest_fd = open(args.destNode, O_RDWR);
+ if (dest_fd == -1) {
+ errmsg("cannot open destination UBI volume \"%s\"", args.destNode);
+ goto out_close1;
+ }
+
+ err = ubi_update_start(libubi, dest_fd, bytes);
+ if (err) {
+ errmsg("cannot start volume \"%s\" update", args.destNode);
+ goto out_close;
+ }
+
+ while (bytes) {
+ int ret, to_copy = dest_vol_info->leb_size;
+
+ if (to_copy > bytes)
+ to_copy = bytes;
+
+ ret = read(src_fd, buf, to_copy);
+ if (ret <= 0) {
+ if (errno == EINTR) {
+ warnmsg("do not interrupt me!");
+ continue;
+ } else {
+ errmsg("cannot read %d bytes from \"%s\"",
+ to_copy, args.srcNode);
+ goto out_close;
+ }
+ }
+
+ err = ubi_write(dest_fd, buf, ret);
+ if (err)
+ {
+ errmsg("cannot write %d bytes to \"%s\"", ret, args.destNode);
+ goto out_close;
+ }
+ bytes -= ret;
+
+ fprintf(stdout, "\r" PROGRAM_NAME ": copied %lli%%", (100 - (100 * bytes / src_vol_info->data_bytes)));
+ fflush(stdout);
+ }
+ fprintf(stdout, "\r" PROGRAM_NAME ": finished copying ubi%d:%s to ubi%d:%s\n", src_vol_info->dev_num, src_vol_info->name, dest_vol_info->dev_num, dest_vol_info->name);
+ ret = 0;
+
+out_close:
+ close(dest_fd);
+out_close1:
+ close(src_fd);
+out_free:
+ free(buf);
+ return ret;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_vol_info src_vol_info, dest_vol_info;
+ int ret = -1;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ else
+ return errmsg("cannot open libubi");
+ }
+
+ err = ubi_probe_node(libubi, args.srcNode);
+ if (err == 1) {
+ errmsg("Source node \"%s\" is a UBI device node, not a UBI volume node",
+ args.srcNode);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("Source node \"%s\" is not a UBI volume node", args.srcNode);
+ else
+ errmsg("error while probing source node \"%s\"", args.srcNode);
+ goto out_libubi;
+ }
+
+ err = ubi_probe_node(libubi, args.destNode);
+ if (err == 1) {
+ errmsg("Destination node \"%s\" is a UBI device node, not a UBI volume node",
+ args.destNode);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("Destination node \"%s\" is not a UBI volume node", args.destNode);
+ else
+ errmsg("error while probing destination node \"%s\"", args.destNode);
+ goto out_libubi;
+ }
+
+ err = ubi_get_vol_info(libubi, args.srcNode, &src_vol_info);
+ if (err) {
+ errmsg("cannot get information about source UBI volume \"%s\"",
+ args.srcNode);
+ goto out_libubi;
+ }
+
+ err = ubi_get_vol_info(libubi, args.destNode, &dest_vol_info);
+ if (err) {
+ errmsg("cannot get information about destination UBI volume \"%s\"",
+ args.destNode);
+ goto out_libubi;
+ }
+
+ if ((src_vol_info.dev_num == dest_vol_info.dev_num) && (src_vol_info.vol_id == dest_vol_info.vol_id)) {
+ errmsg("Source and destination nodes \"%s\" and \"%s\" point to same volume", args.srcNode, args.destNode);
+ goto out_libubi;
+ }
+
+ err = copy_volume(libubi, &src_vol_info, &dest_vol_info);
+ if (err)
+ {
+ errmsg("copy failed, err=%d", args.srcNode, args.destNode, err);
+ goto out_libubi;
+ }
+
+ ret = 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return ret;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubicrc32.c b/mtd-utils-1.3.1/ubi-utils/src/ubicrc32.c
new file mode 100644
index 0000000..d39af10
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubicrc32.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <mtd/ubi-media.h>
+
+#include "crc32.h"
+#include "common.h"
+
+#define BUFSIZE 4096
+
+#define PROGRAM_VERSION "1.0"
+#define PROGRAM_NAME "ubicrc32"
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)";
+
+static const char *optionsstr =
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <file to calculate CRC32 for> [-h] [--help]";
+
+static const struct option long_options[] = {
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "hV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'h':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err = 0;
+ uint32_t crc = UBI_CRC32_INIT;
+ char buf[BUFSIZE];
+ FILE *fp;
+
+ if (argc > 1) {
+ fp = fopen(argv[1], "r");
+ if (!fp)
+ return sys_errmsg("cannot open \"%s\"", argv[1]);
+ } else
+ fp = stdin;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return err;
+
+ while (!feof(fp)) {
+ size_t read;
+
+ read = fread(buf, 1, BUFSIZE, fp);
+ if (ferror(fp)) {
+ sys_errmsg("cannot read input file");
+ err = -1;
+ goto out_close;
+ }
+ crc = crc32(crc, buf, read);
+ }
+
+ printf("0x%08x\n", crc);
+
+out_close:
+ if (fp != stdin)
+ fclose(fp);
+ return err;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubidetach.c b/mtd-utils-1.3.1/ubi-utils/src/ubidetach.c
new file mode 100644
index 0000000..83584cd
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubidetach.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 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
+ */
+
+/*
+ * An utility to delete UBI devices (detach MTD devices from UBI).
+ *
+ * Author: Artem Bityutskiy
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.0"
+#define PROGRAM_NAME "ubidetach"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int devn;
+ int mtdn;
+ const char *node;
+};
+
+static struct args args = {
+ .devn = UBI_DEV_NUM_AUTO,
+ .mtdn = -1,
+ .node = NULL,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+" - a tool to remove UBI devices (detach MTD devices from UBI)";
+
+static const char *optionsstr =
+"-d, --devn=<UBI device number> UBI device number to delete\n"
+"-m, --mtdn=<MTD device number> or altrnatively, MTD device number to detach -\n"
+" this will delete corresponding UBI device\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME "<UBI control device node file name> [-d <UBI device number>] [-m <MTD device number>]\n"
+"\t\t[--devn <UBI device number>] [--mtdn=<MTD device number>]\n"
+"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -d 2 - delete UBI device 2 (ubi2)\n"
+"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - detach MTD device 0 (mtd0)";
+
+static const struct option long_options[] = {
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "m:d:hV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'd':
+ args.devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.devn < 0)
+ return errmsg("bad UBI device number: \"%s\"", optarg);
+
+ break;
+
+ case 'm':
+ args.mtdn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.mtdn < 0)
+ return errmsg("bad MTD device number: \"%s\"", optarg);
+
+ break;
+
+ case 'h':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc)
+ return errmsg("UBI control device name was not specified (use -h for help)");
+ else if (optind != argc - 1)
+ return errmsg("more then one UBI control device specified (use -h for help)");
+
+ if (args.mtdn == -1 && args.devn == -1)
+ return errmsg("neither MTD nor UBI devices were specified (use -h for help)");
+
+ if (args.mtdn != -1 && args.devn != -1)
+ return errmsg("specify either MTD or UBI device (use -h for help)");
+
+ args.node = argv[optind];
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_info ubi_info;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ return sys_errmsg("cannot open libubi");
+ }
+
+ /*
+ * Make sure the kernel is fresh enough and this feature is supported.
+ */
+ err = ubi_get_info(libubi, &ubi_info);
+ if (err) {
+ sys_errmsg("cannot get UBI information");
+ goto out_libubi;
+ }
+
+ if (ubi_info.ctrl_major == -1) {
+ errmsg("MTD detach/detach feature is not supported by your kernel");
+ goto out_libubi;
+ }
+
+ if (args.devn != -1) {
+ err = ubi_remove_dev(libubi, args.node, args.devn);
+ if (err) {
+ sys_errmsg("cannot remove ubi%d", args.devn);
+ goto out_libubi;
+ }
+ } else {
+ err = ubi_detach_mtd(libubi, args.node, args.mtdn);
+ if (err) {
+ sys_errmsg("cannot detach mtd%d", args.mtdn);
+ goto out_libubi;
+ }
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
+
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubiformat.c b/mtd-utils-1.3.1/ubi-utils/src/ubiformat.c
new file mode 100644
index 0000000..8ad5051
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubiformat.c
@@ -0,0 +1,949 @@
+/*
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * An utility to format MTD devices into UBI and flash UBI images.
+ *
+ * Author: Artem Bityutskiy
+ */
+
+/*
+ * Maximum amount of consequtive eraseblocks which are considered as normal by
+ * this utility. Otherwise it is assume that something is wrong with the flash
+ * or the driver, and eraseblocks are stopped being marked as bad.
+ */
+#define MAX_CONSECUTIVE_BAD_BLOCKS 4
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+#include <libubi.h>
+#include <libmtd.h>
+#include <libscan.h>
+#include <libubigen.h>
+#include <mtd_swab.h>
+#include "crc32.h"
+#include "common.h"
+
+#define PROGRAM_VERSION "1.5"
+#define PROGRAM_NAME "ubiformat"
+
+/* The variables below are set by command line arguments */
+struct args {
+ unsigned int yes:1;
+ unsigned int quiet:1;
+ unsigned int verbose:1;
+ unsigned int override_ec:1;
+ unsigned int novtbl:1;
+ unsigned int manual_subpage;
+ int subpage_size;
+ int vid_hdr_offs;
+ int ubi_ver;
+ uint32_t image_seq;
+ off_t image_sz;
+ long long ec;
+ const char *image;
+ const char *node;
+ int node_fd;
+};
+
+static struct args args =
+{
+ .ubi_ver = 1,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to format MTD devices and flash UBI images";
+
+static const char *optionsstr =
+"-s, --sub-page-size=<bytes> minimum input/output unit used for UBI\n"
+" headers, e.g. sub-page size in case of NAND\n"
+" flash (equivalent to the minimum input/output\n"
+" unit size by default)\n"
+"-O, --vid-hdr-offset=<offs> offset if the VID header from start of the\n"
+" physical eraseblock (default is the next\n"
+" minimum I/O unit or sub-page after the EC\n"
+" header)\n"
+"-n, --no-volume-table only erase all eraseblock and preserve erase\n"
+" counters, do not write empty volume table\n"
+"-f, --flash-image=<file> flash image file, or '-' for stdin\n"
+"-S, --image-size=<bytes> bytes in input, if not reading from file\n"
+"-e, --erase-counter=<value> use <value> as the erase counter value for all\n"
+" eraseblocks\n"
+"-x, --ubi-ver=<num> UBI version number to put to EC headers\n"
+" (default is 1)\n"
+"-Q, --image-seq=<num> 32-bit UBI image sequence number to use\n"
+" (by default a random number is picked)\n"
+"-y, --yes assume the answer is \"yes\" for all question\n"
+" this program would otherwise ask\n"
+"-q, --quiet suppress progress percentage information\n"
+"-v, --verbose be verbose\n"
+"-h, -?, --help print help message\n"
+"-V, --version print program version\n";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <MTD device node file name> [-s <bytes>] [-O <offs>] [-n]\n"
+"\t\t\t[-f <file>] [-S <bytes>] [-e <value>] [-x <num>] [-y] [-q] [-v] [-h] [-v]\n"
+"\t\t\t[--sub-page-size=<bytes>] [--vid-hdr-offset=<offs>] [--no-volume-table]\n"
+"\t\t\t[--flash-image=<file>] [--image-size=<bytes>] [--erase-counter=<value>]\n"
+"\t\t\t[--ubi-ver=<num>] [--yes] [--quiet] [--verbose] [--help] [--version]\n\n"
+"Example 1: " PROGRAM_NAME " /dev/mtd0 -y - format MTD device number 0 and do\n"
+" not ask questions.\n"
+"Example 2: " PROGRAM_NAME " /dev/mtd0 -q -e 0 - format MTD device number 0,\n"
+" be quiet and force erase counter value 0.";
+
+static const struct option long_options[] = {
+ { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "no-volume-table", .has_arg = 0, .flag = NULL, .val = 'n' },
+ { .name = "flash-image", .has_arg = 1, .flag = NULL, .val = 'f' },
+ { .name = "image-size", .has_arg = 1, .flag = NULL, .val = 'S' },
+ { .name = "yes", .has_arg = 0, .flag = NULL, .val = 'y' },
+ { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' },
+ { .name = "quiet", .has_arg = 0, .flag = NULL, .val = 'q' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ ubiutils_srand();
+ args.image_seq = rand();
+
+ while (1) {
+ int key;
+ char *endp;
+ unsigned long int image_seq;
+
+ key = getopt_long(argc, argv, "nh?Vyqve:x:s:O:f:S:", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 's':
+ args.subpage_size = ubiutils_get_bytes(optarg);
+ if (args.subpage_size <= 0)
+ return errmsg("bad sub-page size: \"%s\"", optarg);
+ if (!is_power_of_2(args.subpage_size))
+ return errmsg("sub-page size should be power of 2");
+ break;
+
+ case 'O':
+ args.vid_hdr_offs = strtoul(optarg, &endp, 0);
+ if (args.vid_hdr_offs <= 0 || *endp != '\0' || endp == optarg)
+ return errmsg("bad VID header offset: \"%s\"", optarg);
+ break;
+
+ case 'e':
+ args.ec = strtoull(optarg, &endp, 0);
+ if (args.ec < 0 || *endp != '\0' || endp == optarg)
+ return errmsg("bad erase counter value: \"%s\"", optarg);
+ if (args.ec >= EC_MAX)
+ return errmsg("too high erase %llu, counter, max is %u", args.ec, EC_MAX);
+ args.override_ec = 1;
+ break;
+
+ case 'f':
+ args.image = optarg;
+ break;
+
+ case 'S':
+ args.image_sz = ubiutils_get_bytes(optarg);
+ if (args.image_sz <= 0)
+ return errmsg("bad image-size: \"%s\"", optarg);
+ break;
+
+ case 'n':
+ args.novtbl = 1;
+ break;
+
+ case 'y':
+ args.yes = 1;
+ break;
+
+ case 'q':
+ args.quiet = 1;
+ break;
+
+ case 'x':
+ args.ubi_ver = strtoul(optarg, &endp, 0);
+ if (args.ubi_ver < 0 || *endp != '\0' || endp == optarg)
+ return errmsg("bad UBI version: \"%s\"", optarg);
+ break;
+
+ case 'Q':
+ image_seq = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || image_seq > 0xFFFFFFFF)
+ return errmsg("bad UBI image sequence number: \"%s\"", optarg);
+ args.image_seq = image_seq;
+ break;
+
+
+ case 'v':
+ args.verbose = 1;
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (args.quiet && args.verbose)
+ return errmsg("using \"-q\" and \"-v\" at the same time does not make sense");
+
+ if (optind == argc)
+ return errmsg("MTD device name was not specified (use -h for help)");
+ else if (optind != argc - 1)
+ return errmsg("more then one MTD device specified (use -h for help)");
+
+ if (args.image && args.novtbl)
+ return errmsg("-n cannot be used together with -f");
+
+
+ args.node = argv[optind];
+ return 0;
+}
+
+static int want_exit(void)
+{
+ char buf[4];
+
+ while (1) {
+ normsg_cont("continue? (yes/no) ");
+ if (scanf("%3s", buf) == EOF) {
+ sys_errmsg("scanf returned unexpected EOF, assume \"yes\"");
+ return 1;
+ }
+ if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1))
+ return 0;
+ if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1))
+ return 1;
+ }
+}
+
+static int answer_is_yes(void)
+{
+ char buf[4];
+
+ while (1) {
+ if (scanf("%3s", buf) == EOF) {
+ sys_errmsg("scanf returned unexpected EOF, assume \"no\"");
+ return 0;
+ }
+ if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1))
+ return 1;
+ if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1))
+ return 0;
+ }
+}
+
+static void print_bad_eraseblocks(const struct mtd_dev_info *mtd,
+ const struct ubi_scan_info *si)
+{
+ int first = 1, eb;
+
+ if (si->bad_cnt == 0)
+ return;
+
+ normsg_cont("%d bad eraseblocks found, numbers: ", si->bad_cnt);
+ for (eb = 0; eb < mtd->eb_cnt; eb++) {
+ if (si->ec[eb] != EB_BAD)
+ continue;
+ if (first) {
+ printf("%d", eb);
+ first = 0;
+ } else
+ printf(", %d", eb);
+ }
+ printf("\n");
+}
+
+static int change_ech(struct ubi_ec_hdr *hdr, uint32_t image_seq,
+ long long ec)
+{
+ uint32_t crc;
+
+ /* Check the EC header */
+ if (be32_to_cpu(hdr->magic) != UBI_EC_HDR_MAGIC)
+ return errmsg("bad UBI magic %#08x, should be %#08x",
+ be32_to_cpu(hdr->magic), UBI_EC_HDR_MAGIC);
+
+ crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC);
+ if (be32_to_cpu(hdr->hdr_crc) != crc)
+ return errmsg("bad CRC %#08x, should be %#08x\n",
+ crc, be32_to_cpu(hdr->hdr_crc));
+
+ hdr->image_seq = cpu_to_be32(image_seq);
+ hdr->ec = cpu_to_be64(ec);
+ crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC);
+ hdr->hdr_crc = cpu_to_be32(crc);
+
+ return 0;
+}
+
+static int drop_ffs(const struct mtd_dev_info *mtd, const void *buf, int len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0; i--)
+ if (((const uint8_t *)buf)[i] != 0xFF)
+ break;
+
+ /* The resulting length must be aligned to the minimum flash I/O size */
+ len = i + 1;
+ len = (len + mtd->min_io_size - 1) / mtd->min_io_size;
+ len *= mtd->min_io_size;
+ return len;
+}
+
+static int open_file(off_t *sz)
+{
+ int fd;
+
+ if (!strcmp(args.image, "-")) {
+ if (args.image_sz == 0)
+ return errmsg("must use '-S' with non-zero value when reading from stdin");
+
+ *sz = args.image_sz;
+ fd = dup(STDIN_FILENO);
+ if (fd < 0)
+ return sys_errmsg("failed to dup stdin");
+ } else {
+ struct stat st;
+
+ if (stat(args.image, &st))
+ return sys_errmsg("cannot open \"%s\"", args.image);
+
+ *sz = st.st_size;
+ fd = open(args.image, O_RDONLY);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", args.image);
+ }
+
+ return fd;
+}
+
+static int read_all(int fd, void *buf, size_t len)
+{
+ while (len > 0) {
+ ssize_t l = read(fd, buf, len);
+ if (l == 0)
+ return errmsg("eof reached; %zu bytes remaining", len);
+ else if (l > 0) {
+ buf += l;
+ len -= l;
+ } else if (errno == EINTR || errno == EAGAIN)
+ continue;
+ else
+ return sys_errmsg("reading failed; %zu bytes remaining", len);
+ }
+
+ return 0;
+}
+
+/*
+ * Returns %-1 if consecutive bad blocks exceeds the
+ * MAX_CONSECUTIVE_BAD_BLOCKS and returns %0 otherwise.
+ */
+static int consecutive_bad_check(int eb)
+{
+ static int consecutive_bad_blocks = 1;
+ static int prev_bb = -1;
+
+ if (prev_bb == -1)
+ prev_bb = eb;
+
+ if (eb == prev_bb + 1)
+ consecutive_bad_blocks += 1;
+ else
+ consecutive_bad_blocks = 1;
+
+ prev_bb = eb;
+
+ if (consecutive_bad_blocks >= MAX_CONSECUTIVE_BAD_BLOCKS) {
+ if (!args.quiet)
+ printf("\n");
+ return errmsg("consecutive bad blocks exceed limit: %d, bad flash?",
+ MAX_CONSECUTIVE_BAD_BLOCKS);
+ }
+
+ return 0;
+}
+
+/* TODO: we should actually torture the PEB before marking it as bad */
+static int mark_bad(const struct mtd_dev_info *mtd, struct ubi_scan_info *si, int eb)
+{
+ int err;
+
+ if (!args.yes) {
+ normsg_cont("mark it as bad? Continue (yes/no) ");
+ if (!answer_is_yes())
+ return -1;
+ }
+
+ if (!args.quiet)
+ normsg_cont("marking block %d bad", eb);
+
+ if (!args.quiet)
+ printf("\n");
+
+ if (!mtd->bb_allowed) {
+ if (!args.quiet)
+ printf("\n");
+ return errmsg("bad blocks not supported by this flash");
+ }
+
+ err = mtd_mark_bad(mtd, args.node_fd, eb);
+ if (err)
+ return err;
+
+ si->bad_cnt += 1;
+ si->ec[eb] = EB_BAD;
+
+ return consecutive_bad_check(eb);
+}
+
+static int flash_image(const struct mtd_dev_info *mtd, const struct ubigen_info *ui,
+ struct ubi_scan_info *si)
+{
+ int fd, img_ebs, eb, written_ebs = 0, divisor;
+ off_t st_size;
+
+ fd = open_file(&st_size);
+ if (fd < 0)
+ return fd;
+
+ img_ebs = st_size / mtd->eb_size;
+
+ if (img_ebs > si->good_cnt) {
+ sys_errmsg("file \"%s\" is too large (%lld bytes)",
+ args.image, (long long)st_size);
+ goto out_close;
+ }
+
+ if (st_size % mtd->eb_size) {
+ return sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of ""eraseblock size (%d bytes)",
+ args.image, (long long)st_size, mtd->eb_size);
+ goto out_close;
+ }
+
+ verbose(args.verbose, "will write %d eraseblocks", img_ebs);
+ divisor = img_ebs;
+ for (eb = 0; eb < mtd->eb_cnt; eb++) {
+ int err, new_len;
+ char buf[mtd->eb_size];
+ long long ec;
+
+ if (!args.quiet && !args.verbose) {
+ printf("\r" PROGRAM_NAME ": flashing eraseblock %d -- %2lld %% complete ",
+ eb, (long long)(eb + 1) * 100 / divisor);
+ fflush(stdout);
+ }
+
+ if (si->ec[eb] == EB_BAD) {
+ divisor += 1;
+ continue;
+ }
+
+ if (args.verbose) {
+ normsg_cont("eraseblock %d: erase", eb);
+ fflush(stdout);
+ }
+
+ err = mtd_erase(mtd, args.node_fd, eb);
+ if (err) {
+ if (!args.quiet)
+ printf("\n");
+ sys_errmsg("failed to erase eraseblock %d", eb);
+
+ if (errno != EIO)
+ goto out_close;
+
+ if (mark_bad(mtd, si, eb))
+ goto out_close;
+
+ continue;
+ }
+
+ err = read_all(fd, buf, mtd->eb_size);
+ if (err) {
+ sys_errmsg("failed to read eraseblock %d from \"%s\"",
+ written_ebs, args.image);
+ goto out_close;
+ }
+
+ if (args.override_ec)
+ ec = args.ec;
+ else if (si->ec[eb] <= EC_MAX)
+ ec = si->ec[eb] + 1;
+ else
+ ec = si->mean_ec;
+
+ if (args.verbose) {
+ printf(", change EC to %lld", ec);
+ fflush(stdout);
+ }
+
+ err = change_ech((struct ubi_ec_hdr *)buf, ui->image_seq, ec);
+ if (err) {
+ errmsg("bad EC header at eraseblock %d of \"%s\"",
+ written_ebs, args.image);
+ goto out_close;
+ }
+
+ if (args.verbose) {
+ printf(", write data\n");
+ fflush(stdout);
+ }
+
+ new_len = drop_ffs(mtd, buf, mtd->eb_size);
+
+ err = mtd_write(mtd, args.node_fd, eb, 0, buf, new_len);
+ if (err) {
+ sys_errmsg("cannot write eraseblock %d", eb);
+
+ if (errno != EIO)
+ goto out_close;
+
+ err = mtd_torture(mtd, args.node_fd, eb);
+ if (err) {
+ if (mark_bad(mtd, si, eb))
+ goto out_close;
+ }
+ continue;
+ }
+ if (++written_ebs >= img_ebs)
+ break;
+ }
+
+ if (!args.quiet && !args.verbose)
+ printf("\n");
+ close(fd);
+ return eb + 1;
+
+out_close:
+ close(fd);
+ return -1;
+}
+
+static int format(const struct mtd_dev_info *mtd, const struct ubigen_info *ui,
+ struct ubi_scan_info *si, int start_eb, int novtbl)
+{
+ int eb, err, write_size;
+ struct ubi_ec_hdr *hdr;
+ struct ubi_vtbl_record *vtbl;
+ int eb1 = -1, eb2 = -1;
+ long long ec1 = -1, ec2 = -1;
+
+ write_size = UBI_EC_HDR_SIZE + mtd->subpage_size - 1;
+ write_size /= mtd->subpage_size;
+ write_size *= mtd->subpage_size;
+ hdr = malloc(write_size);
+ if (!hdr)
+ return sys_errmsg("cannot allocate %d bytes of memory", write_size);
+ memset(hdr, 0xFF, write_size);
+
+ for (eb = start_eb; eb < mtd->eb_cnt; eb++) {
+ long long ec;
+
+ if (!args.quiet && !args.verbose) {
+ printf("\r" PROGRAM_NAME ": formatting eraseblock %d -- %2lld %% complete ",
+ eb, (long long)(eb + 1 - start_eb) * 100 / (mtd->eb_cnt - start_eb));
+ fflush(stdout);
+ }
+
+ if (si->ec[eb] == EB_BAD)
+ continue;
+
+ if (args.override_ec)
+ ec = args.ec;
+ else if (si->ec[eb] <= EC_MAX)
+ ec = si->ec[eb] + 1;
+ else
+ ec = si->mean_ec;
+ ubigen_init_ec_hdr(ui, hdr, ec);
+
+ if (args.verbose) {
+ normsg_cont("eraseblock %d: erase", eb);
+ fflush(stdout);
+ }
+
+ err = mtd_erase(mtd, args.node_fd, eb);
+ if (err) {
+ if (!args.quiet)
+ printf("\n");
+
+ sys_errmsg("failed to erase eraseblock %d", eb);
+ if (errno != EIO)
+ goto out_free;
+
+ if (mark_bad(mtd, si, eb))
+ goto out_free;
+ continue;
+ }
+
+ if ((eb1 == -1 || eb2 == -1) && !novtbl) {
+ if (eb1 == -1) {
+ eb1 = eb;
+ ec1 = ec;
+ } else if (eb2 == -1) {
+ eb2 = eb;
+ ec2 = ec;
+ }
+ if (args.verbose)
+ printf(", do not write EC, leave for vtbl\n");
+ continue;
+ }
+
+ if (args.verbose) {
+ printf(", write EC %lld\n", ec);
+ fflush(stdout);
+ }
+
+ err = mtd_write(mtd, args.node_fd, eb, 0, hdr, write_size);
+ if (err) {
+ if (!args.quiet && !args.verbose)
+ printf("\n");
+ sys_errmsg("cannot write EC header (%d bytes buffer) to eraseblock %d",
+ write_size, eb);
+
+ if (errno != EIO) {
+ if (!args.subpage_size != mtd->min_io_size)
+ normsg("may be sub-page size is "
+ "incorrect?");
+ goto out_free;
+ }
+
+ err = mtd_torture(mtd, args.node_fd, eb);
+ if (err) {
+ if (mark_bad(mtd, si, eb))
+ goto out_free;
+ }
+ continue;
+
+ }
+ }
+
+ if (!args.quiet && !args.verbose)
+ printf("\n");
+
+ if (!novtbl) {
+ if (eb1 == -1 || eb2 == -1) {
+ errmsg("no eraseblocks for volume table");
+ goto out_free;
+ }
+
+ verbose(args.verbose, "write volume table to eraseblocks %d and %d", eb1, eb2);
+ vtbl = ubigen_create_empty_vtbl(ui);
+ if (!vtbl)
+ goto out_free;
+
+ err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl,
+ args.node_fd);
+ free(vtbl);
+ if (err) {
+ errmsg("cannot write layout volume");
+ goto out_free;
+ }
+ }
+
+ free(hdr);
+ return 0;
+
+out_free:
+ free(hdr);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err, verbose;
+ libmtd_t libmtd;
+ struct mtd_info mtd_info;
+ struct mtd_dev_info mtd;
+ libubi_t libubi;
+ struct ubigen_info ui;
+ struct ubi_scan_info *si;
+
+ libmtd = libmtd_open();
+ if (!libmtd)
+ return errmsg("MTD subsystem is not present");
+
+ err = parse_opt(argc, argv);
+ if (err)
+ goto out_close_mtd;
+
+ err = mtd_get_info(libmtd, &mtd_info);
+ if (err) {
+ if (errno == ENODEV)
+ errmsg("MTD is not present");
+ sys_errmsg("cannot get MTD information");
+ goto out_close_mtd;
+ }
+
+ err = mtd_get_dev_info(libmtd, args.node, &mtd);
+ if (err) {
+ sys_errmsg("cannot get information about \"%s\"", args.node);
+ goto out_close_mtd;
+ }
+
+ if (!is_power_of_2(mtd.min_io_size)) {
+ errmsg("min. I/O size is %d, but should be power of 2",
+ mtd.min_io_size);
+ goto out_close;
+ }
+
+ if (!mtd_info.sysfs_supported) {
+ /*
+ * Linux kernels older than 2.6.30 did not support sysfs
+ * interface, and it is impossible to find out sub-page
+ * size in these kernels. This is why users should
+ * provide -s option.
+ */
+ if (args.subpage_size == 0) {
+ warnmsg("your MTD system is old and it is impossible "
+ "to detect sub-page size. Use -s to get rid "
+ "of this warning");
+ normsg("assume sub-page to be %d", mtd.subpage_size);
+ } else {
+ mtd.subpage_size = args.subpage_size;
+ args.manual_subpage = 1;
+ }
+ } else if (args.subpage_size && args.subpage_size != mtd.subpage_size) {
+ mtd.subpage_size = args.subpage_size;
+ args.manual_subpage = 1;
+ }
+
+ if (args.manual_subpage) {
+ /* Do some sanity check */
+ if (args.subpage_size > mtd.min_io_size) {
+ errmsg("sub-page cannot be larger than min. I/O unit");
+ goto out_close;
+ }
+
+ if (mtd.min_io_size % args.subpage_size) {
+ errmsg("min. I/O unit size should be multiple of "
+ "sub-page size");
+ goto out_close;
+ }
+ }
+
+ args.node_fd = open(args.node, O_RDWR);
+ if (args.node_fd == -1) {
+ sys_errmsg("cannot open \"%s\"", args.node);
+ goto out_close_mtd;
+ }
+
+ /* Validate VID header offset if it was specified */
+ if (args.vid_hdr_offs != 0) {
+ if (args.vid_hdr_offs % 8) {
+ errmsg("VID header offset has to be multiple of min. I/O unit size");
+ goto out_close;
+ }
+ if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) {
+ errmsg("bad VID header offset");
+ goto out_close;
+ }
+ }
+
+ if (!mtd.writable) {
+ errmsg("mtd%d (%s) is a read-only device", mtd.dev_num, args.node);
+ goto out_close;
+ }
+
+ /* Make sure this MTD device is not attached to UBI */
+ libubi = libubi_open();
+ if (libubi) {
+ int ubi_dev_num;
+
+ err = mtd_num2ubi_dev(libubi, mtd.dev_num, &ubi_dev_num);
+ libubi_close(libubi);
+ if (!err) {
+ errmsg("please, first detach mtd%d (%s) from ubi%d",
+ mtd.dev_num, args.node, ubi_dev_num);
+ goto out_close;
+ }
+ }
+
+ if (!args.quiet) {
+ normsg_cont("mtd%d (%s), size ", mtd.dev_num, mtd.type_str);
+ ubiutils_print_bytes(mtd.size, 1);
+ printf(", %d eraseblocks of ", mtd.eb_cnt);
+ ubiutils_print_bytes(mtd.eb_size, 1);
+ printf(", min. I/O size %d bytes\n", mtd.min_io_size);
+ }
+
+ if (args.quiet)
+ verbose = 0;
+ else if (args.verbose)
+ verbose = 2;
+ else
+ verbose = 1;
+ err = ubi_scan(&mtd, args.node_fd, &si, verbose);
+ if (err) {
+ errmsg("failed to scan mtd%d (%s)", mtd.dev_num, args.node);
+ goto out_close;
+ }
+
+ if (si->good_cnt == 0) {
+ errmsg("all %d eraseblocks are bad", si->bad_cnt);
+ goto out_free;
+ }
+
+ if (si->good_cnt < 2 && (!args.novtbl || args.image)) {
+ errmsg("too few non-bad eraseblocks (%d) on mtd%d",
+ si->good_cnt, mtd.dev_num);
+ goto out_free;
+ }
+
+ if (!args.quiet) {
+ if (si->ok_cnt)
+ normsg("%d eraseblocks have valid erase counter, mean value is %lld",
+ si->ok_cnt, si->mean_ec);
+ if (si->empty_cnt)
+ normsg("%d eraseblocks are supposedly empty", si->empty_cnt);
+ if (si->corrupted_cnt)
+ normsg("%d corrupted erase counters", si->corrupted_cnt);
+ print_bad_eraseblocks(&mtd, si);
+ }
+
+ if (si->alien_cnt) {
+ if (!args.yes || !args.quiet)
+ warnmsg("%d of %d eraseblocks contain non-ubifs data",
+ si->alien_cnt, si->good_cnt);
+ if (!args.yes && want_exit()) {
+ if (args.yes && !args.quiet)
+ printf("yes\n");
+ goto out_free;
+ }
+ }
+
+ if (!args.override_ec && si->empty_cnt < si->good_cnt) {
+ int percent = ((double)si->ok_cnt)/si->good_cnt * 100;
+
+ /*
+ * Make sure the majority of eraseblocks have valid
+ * erase counters.
+ */
+ if (percent < 50) {
+ if (!args.yes || !args.quiet)
+ warnmsg("only %d of %d eraseblocks have valid erase counter",
+ si->ok_cnt, si->good_cnt);
+ normsg("erase counter 0 will be used for all eraseblocks");
+ normsg("note, arbitrary erase counter value may be specified using -e option");
+ if (!args.yes && want_exit()) {
+ if (args.yes && !args.quiet)
+ printf("yes\n");
+ goto out_free;
+ }
+ args.ec = 0;
+ args.override_ec = 1;
+ } else if (percent < 95) {
+ if (!args.yes || !args.quiet)
+ warnmsg("only %d of %d eraseblocks have valid erase counter",
+ si->ok_cnt, si->good_cnt);
+ normsg("mean erase counter %lld will be used for the rest of eraseblock",
+ si->mean_ec);
+ if (!args.yes && want_exit()) {
+ if (args.yes && !args.quiet)
+ printf("yes\n");
+ goto out_free;
+ }
+ args.ec = si->mean_ec;
+ args.override_ec = 1;
+ }
+ }
+
+ if (!args.quiet && args.override_ec)
+ normsg("use erase counter %lld for all eraseblocks", args.ec);
+
+ ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size,
+ args.vid_hdr_offs, args.ubi_ver, args.image_seq);
+
+ if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) {
+ /*
+ * Hmm, what we read from flash and what we calculated using
+ * min. I/O unit size and sub-page size differs.
+ */
+ if (!args.yes || !args.quiet) {
+ warnmsg("VID header and data offsets on flash are %d and %d, "
+ "which is different to requested offsets %d and %d",
+ si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs,
+ ui.data_offs);
+ normsg_cont("use new offsets %d and %d? (yes/no) ",
+ ui.vid_hdr_offs, ui.data_offs);
+ }
+ if (args.yes || answer_is_yes()) {
+ if (args.yes && !args.quiet)
+ printf("yes\n");
+ } else
+ ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, 0,
+ si->vid_hdr_offs, args.ubi_ver,
+ args.image_seq);
+ normsg("use offsets %d and %d", ui.vid_hdr_offs, ui.data_offs);
+ }
+
+ if (args.image) {
+ err = flash_image(&mtd, &ui, si);
+ if (err < 0)
+ goto out_free;
+
+ err = format(&mtd, &ui, si, err, 1);
+ if (err)
+ goto out_free;
+ } else {
+ err = format(&mtd, &ui, si, 0, args.novtbl);
+ if (err)
+ goto out_free;
+ }
+
+ ubi_scan_free(si);
+ close(args.node_fd);
+ libmtd_close(libmtd);
+ return 0;
+
+out_free:
+ ubi_scan_free(si);
+out_close:
+ close(args.node_fd);
+out_close_mtd:
+ libmtd_close(libmtd);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubimkvol.c b/mtd-utils-1.3.1/ubi-utils/src/ubimkvol.c
new file mode 100644
index 0000000..c1b577d
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubimkvol.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * An utility to create UBI volumes.
+ *
+ * Authors: Artem Bityutskiy <dedekind@infradead.org>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.1"
+#define PROGRAM_NAME "ubimkvol"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int vol_id;
+ int vol_type;
+ long long bytes;
+ int lebs;
+ int alignment;
+ const char *name;
+ const char *node;
+ int maxavs;
+};
+
+static struct args args = {
+ .vol_type = UBI_DYNAMIC_VOLUME,
+ .bytes = -1,
+ .lebs = -1,
+ .alignment = 1,
+ .vol_id = UBI_VOL_NUM_AUTO,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to create UBI volumes.";
+
+static const char *optionsstr =
+"-a, --alignment=<alignment> volume alignment (default is 1)\n"
+"-n, --vol_id=<volume ID> UBI volume ID, if not specified, the volume ID\n"
+" will be assigned automatically\n"
+"-N, --name=<name> volume name\n"
+"-s, --size=<bytes> volume size volume size in bytes, kilobytes (KiB)\n"
+" or megabytes (MiB)\n"
+"-S, --lebs=<LEBs count> alternative way to give volume size in logical\n"
+" eraseblocks\n"
+"-m, --maxavsize set volume size to maximum available size\n"
+"-t, --type=<static|dynamic> volume type (dynamic, static), default is dynamic\n"
+"-h, -?, --help print help message\n"
+"-V, --version print program version";
+
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI device node file name> [-h] [-a <alignment>] [-n <volume ID>] [-N <name>]\n"
+"\t\t\t[-s <bytes>] [-S <LEBs>] [-t <static|dynamic>] [-V] [-m]\n"
+"\t\t\t[--alignment=<alignment>][--vol_id=<volume ID>] [--name=<name>]\n"
+"\t\t\t[--size=<bytes>] [--lebs=<LEBs>] [--type=<static|dynamic>] [--help]\n"
+"\t\t\t[--version] [--maxavsize]\n\n"
+"Example: " PROGRAM_NAME " /dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n"
+" named \"config_data\" on UBI device /dev/ubi0.";
+
+static const struct option long_options[] = {
+ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' },
+ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' },
+ { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { .name = "maxavsize", .has_arg = 0, .flag = NULL, .val = 'm' },
+ { NULL, 0, NULL, 0},
+};
+
+static int param_sanity_check(void)
+{
+ int len;
+
+ if (args.bytes == -1 && !args.maxavs && args.lebs == -1)
+ return errmsg("volume size was not specified (use -h for help)");
+
+ if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) ||
+ (args.lebs != -1 && (args.maxavs || args.bytes != -1)) ||
+ (args.maxavs && (args.bytes != -1 || args.lebs != -1)))
+ return errmsg("size specified with more then one option");
+
+ if (args.name == NULL)
+ return errmsg("volume name was not specified (use -h for help)");
+
+ len = strlen(args.name);
+ if (len > UBI_MAX_VOLUME_NAME)
+ return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME);
+
+ return 0;
+}
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "a:n:N:s:S:t:h?Vm", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 't':
+ if (!strcmp(optarg, "dynamic"))
+ args.vol_type = UBI_DYNAMIC_VOLUME;
+ else if (!strcmp(optarg, "static"))
+ args.vol_type = UBI_STATIC_VOLUME;
+ else
+ return errmsg("bad volume type: \"%s\"", optarg);
+ break;
+
+ case 's':
+ args.bytes = ubiutils_get_bytes(optarg);
+ if (args.bytes <= 0)
+ return errmsg("bad volume size: \"%s\"", optarg);
+ break;
+
+ case 'S':
+ args.lebs = strtoull(optarg, &endp, 0);
+ if (endp == optarg || args.lebs <= 0 || *endp != '\0')
+ return errmsg("bad LEB count: \"%s\"", optarg);
+ break;
+
+ case 'a':
+ args.alignment = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.alignment <= 0)
+ return errmsg("bad volume alignment: \"%s\"", optarg);
+ break;
+
+ case 'n':
+ args.vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.vol_id < 0)
+ return errmsg("bad volume ID: " "\"%s\"", optarg);
+ break;
+
+ case 'N':
+ args.name = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case 'm':
+ args.maxavs = 1;
+ break;
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc)
+ return errmsg("UBI device name was not specified (use -h for help)");
+ else if (optind != argc - 1)
+ return errmsg("more then one UBI device specified (use -h for help)");
+
+ args.node = argv[optind];
+
+ if (param_sanity_check())
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+ struct ubi_vol_info vol_info;
+ struct ubi_mkvol_request req;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return err;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ return sys_errmsg("cannot open libubi");
+ }
+
+ err = ubi_probe_node(libubi, args.node);
+ if (err == 2) {
+ errmsg("\"%s\" is an UBI volume node, not an UBI device node",
+ args.node);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("\"%s\" is not an UBI device node", args.node);
+ else
+ sys_errmsg("error while probing \"%s\"", args.node);
+ goto out_libubi;
+ }
+
+ err = ubi_get_dev_info(libubi, args.node, &dev_info);
+ if (err) {
+ sys_errmsg("cannot get information about UBI device \"%s\"",
+ args.node);
+ goto out_libubi;
+ }
+
+ if (dev_info.avail_bytes == 0) {
+ errmsg("UBI device does not have free logical eraseblocks");
+ goto out_libubi;
+ }
+
+ if (args.maxavs) {
+ args.bytes = dev_info.avail_bytes;
+ printf("Set volume size to %lld\n", args.bytes);
+ }
+
+ if (args.lebs != -1) {
+ args.bytes = dev_info.leb_size;
+ args.bytes -= dev_info.leb_size % args.alignment;
+ args.bytes *= args.lebs;
+ }
+
+ req.vol_id = args.vol_id;
+ req.alignment = args.alignment;
+ req.bytes = args.bytes;
+ req.vol_type = args.vol_type;
+ req.name = args.name;
+
+ err = ubi_mkvol(libubi, args.node, &req);
+ if (err < 0) {
+ sys_errmsg("cannot UBI create volume");
+ goto out_libubi;
+ }
+
+ args.vol_id = req.vol_id;
+
+ /* Print information about the created device */
+ err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info);
+ if (err) {
+ sys_errmsg("cannot get information about newly created UBI volume");
+ goto out_libubi;
+ }
+
+ printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs);
+ ubiutils_print_bytes(vol_info.rsvd_bytes, 0);
+ printf("), LEB size ");
+ ubiutils_print_bytes(vol_info.leb_size, 1);
+ printf(", %s, name \"%s\", alignment %d\n",
+ req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static",
+ vol_info.name, vol_info.alignment);
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubinfo.c b/mtd-utils-1.3.1/ubi-utils/src/ubinfo.c
new file mode 100644
index 0000000..8c5a1a9
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubinfo.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2007, 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
+ */
+
+/*
+ * An utility to get UBI information.
+ *
+ * Author: Artem Bityutskiy
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.1"
+#define PROGRAM_NAME "ubinfo"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int devn;
+ int vol_id;
+ int all;
+ const char *node;
+};
+
+static struct args args = {
+ .vol_id = -1,
+ .devn = -1,
+ .all = 0,
+ .node = NULL,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to print UBI information.";
+
+static const char *optionsstr =
+"-d, --devn=<UBI device number> UBI device number to get information about\n"
+"-n, --vol_id=<volume ID> ID of UBI volume to print information about\n"
+"-a, --all print information about all devices and volumes,\n"
+" or about all volumes if the UBI device was\n"
+" specified\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage 1: " PROGRAM_NAME " [-d <UBI device number>] [-n <volume ID>] [-a] [-h] [-V] [--vol_id=<volume ID>]\n"
+"\t\t[--devn <UBI device number>] [--all] [--help] [--version]\n"
+"Usage 2: " PROGRAM_NAME " <UBI device node file name> [-a] [-h] [-V] [--all] [--help] [--version]\n"
+"Usage 3: " PROGRAM_NAME " <UBI volume node file name> [-h] [-V] [--help] [--version]\n\n"
+"Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n"
+"Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n"
+"Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n"
+" device /dev/ubi0\n"
+"Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n"
+"Example 5: " PROGRAM_NAME " -a - print all information\n";
+
+static const struct option long_options[] = {
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "an:d:hV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'a':
+ args.all = 1;
+ break;
+
+ case 'n':
+ args.vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.vol_id < 0)
+ return errmsg("bad volume ID: " "\"%s\"", optarg);
+ break;
+
+ case 'd':
+ args.devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.devn < 0)
+ return errmsg("bad UBI device number: \"%s\"", optarg);
+
+ break;
+
+ case 'h':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc - 1)
+ args.node = argv[optind];
+ else if (optind < argc)
+ return errmsg("more then one UBI device specified (use -h for help)");
+
+ return 0;
+}
+
+static int translate_dev(libubi_t libubi, const char *node)
+{
+ int err;
+
+ err = ubi_probe_node(libubi, node);
+ if (err == -1) {
+ if (errno != ENODEV)
+ return sys_errmsg("error while probing \"%s\"", node);
+ return errmsg("\"%s\" does not correspond to any UBI device or volume", node);
+ }
+
+ if (err == 1) {
+ struct ubi_dev_info dev_info;
+
+ err = ubi_get_dev_info(libubi, node, &dev_info);
+ if (err)
+ return sys_errmsg("cannot get information about UBI device \"%s\"", node);
+
+ args.devn = dev_info.dev_num;
+ } else {
+ struct ubi_vol_info vol_info;
+
+ err = ubi_get_vol_info(libubi, node, &vol_info);
+ if (err)
+ return sys_errmsg("cannot get information about UBI volume \"%s\"", node);
+
+ if (args.vol_id != -1)
+ return errmsg("both volume character device node (\"%s\") and "
+ "volume ID (%d) are specify, use only one of them"
+ "(use -h for help)", node, args.vol_id);
+
+ args.devn = vol_info.dev_num;
+ args.vol_id = vol_info.vol_id;
+ }
+
+ return 0;
+}
+
+static int print_vol_info(libubi_t libubi, int dev_num, int vol_id)
+{
+ int err;
+ struct ubi_vol_info vol_info;
+
+ err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info);
+ if (err)
+ return sys_errmsg("cannot get information about UBI volume %d on ubi%d",
+ vol_id, dev_num);
+
+ printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num);
+ printf("Type: %s\n",
+ vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static");
+ printf("Alignment: %d\n", vol_info.alignment);
+
+ printf("Size: %d LEBs (", vol_info.rsvd_lebs);
+ ubiutils_print_bytes(vol_info.rsvd_bytes, 0);
+ printf(")\n");
+
+ if (vol_info.type == UBI_STATIC_VOLUME) {
+ printf("Data bytes: ");
+ ubiutils_print_bytes(vol_info.data_bytes, 1);
+ printf("\n");
+ }
+ printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK");
+ printf("Name: %s\n", vol_info.name);
+ printf("Character device major/minor: %d:%d\n",
+ vol_info.major, vol_info.minor);
+
+ return 0;
+}
+
+static int print_dev_info(libubi_t libubi, int dev_num, int all)
+{
+ int i, err, first = 1;
+ struct ubi_dev_info dev_info;
+ struct ubi_vol_info vol_info;
+
+ err = ubi_get_dev_info1(libubi, dev_num, &dev_info);
+ if (err)
+ return sys_errmsg("cannot get information about UBI device %d", dev_num);
+
+ printf("ubi%d\n", dev_info.dev_num);
+ printf("Volumes count: %d\n", dev_info.vol_count);
+ printf("Logical eraseblock size: ");
+ ubiutils_print_bytes(dev_info.leb_size, 0);
+ printf("\n");
+
+ printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs);
+ ubiutils_print_bytes(dev_info.total_bytes, 0);
+ printf(")\n");
+
+ printf("Amount of available logical eraseblocks: %d (", dev_info.avail_lebs);
+ ubiutils_print_bytes(dev_info.avail_bytes, 0);
+ printf(")\n");
+
+ printf("Maximum count of volumes %d\n", dev_info.max_vol_count);
+ printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count);
+ printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd);
+ printf("Current maximum erase counter value: %lld\n", dev_info.max_ec);
+ printf("Minimum input/output unit size: %d %s\n",
+ dev_info.min_io_size, dev_info.min_io_size > 1 ? "bytes" : "byte");
+ printf("Character device major/minor: %d:%d\n",
+ dev_info.major, dev_info.minor);
+
+ if (dev_info.vol_count == 0)
+ return 0;
+
+ printf("Present volumes: ");
+ for (i = dev_info.lowest_vol_id;
+ i <= dev_info.highest_vol_id; i++) {
+ err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
+ if (err == -1) {
+ if (errno == ENOENT)
+ continue;
+
+ return sys_errmsg("libubi failed to probe volume %d on ubi%d",
+ i, dev_info.dev_num);
+ }
+
+ if (!first)
+ printf(", %d", i);
+ else {
+ printf("%d", i);
+ first = 0;
+ }
+ }
+ printf("\n");
+
+ if (!all)
+ return 0;
+
+ first = 1;
+ printf("\n");
+
+ for (i = dev_info.lowest_vol_id;
+ i <= dev_info.highest_vol_id; i++) {
+ if(!first)
+ printf("-----------------------------------\n");
+ err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
+ if (err == -1) {
+ if (errno == ENOENT)
+ continue;
+
+ return sys_errmsg("libubi failed to probe volume %d on ubi%d",
+ i, dev_info.dev_num);
+ }
+ first = 0;
+
+ err = print_vol_info(libubi, dev_info.dev_num, i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int print_general_info(libubi_t libubi, int all)
+{
+ int i, err, first = 1;
+ struct ubi_info ubi_info;
+ struct ubi_dev_info dev_info;
+
+ err = ubi_get_info(libubi, &ubi_info);
+ if (err)
+ return sys_errmsg("cannot get UBI information");
+
+ printf("UBI version: %d\n", ubi_info.version);
+ printf("Count of UBI devices: %d\n", ubi_info.dev_count);
+ if (ubi_info.ctrl_major != -1)
+ printf("UBI control device major/minor: %d:%d\n",
+ ubi_info.ctrl_major, ubi_info.ctrl_minor);
+ else
+ printf("UBI control device is not supported by this kernel\n");
+
+ if (ubi_info.dev_count == 0)
+ return 0;
+
+ printf("Present UBI devices: ");
+ for (i = ubi_info.lowest_dev_num;
+ i <= ubi_info.highest_dev_num; i++) {
+ err = ubi_get_dev_info1(libubi, i, &dev_info);
+ if (err == -1) {
+ if (errno == ENOENT)
+ continue;
+
+ printf("\n");
+ return sys_errmsg("libubi failed to probe UBI device %d", i);
+ }
+
+ if (!first)
+ printf(", ubi%d", i);
+ else {
+ printf("ubi%d", i);
+ first = 0;
+ }
+ }
+ printf("\n");
+
+ if (!all)
+ return 0;
+
+ first = 1;
+ printf("\n");
+
+ for (i = ubi_info.lowest_dev_num;
+ i <= ubi_info.highest_dev_num; i++) {
+ if(!first)
+ printf("\n===================================\n\n");
+ first = 0;
+ err = print_dev_info(libubi, i, all);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ return sys_errmsg("cannot open libubi");
+ }
+
+ if (args.node) {
+ /*
+ * A character device was specified, translate this into UBI
+ * device number and volume ID.
+ */
+ err = translate_dev(libubi, args.node);
+ if (err)
+ goto out_libubi;
+ }
+
+ if (args.vol_id != -1 && args.devn == -1) {
+ errmsg("volume ID is specified, but UBI device number is not "
+ "(use -h for help)\n");
+ goto out_libubi;
+ }
+
+ if (args.devn != -1 && args.vol_id != -1) {
+ print_vol_info(libubi, args.devn, args.vol_id);
+ goto out;
+ }
+
+ if (args.devn == -1 && args.vol_id == -1)
+ err = print_general_info(libubi, args.all);
+ else if (args.devn != -1 && args.vol_id == -1)
+ err = print_dev_info(libubi, args.devn, args.all);
+
+ if (err)
+ goto out_libubi;
+
+out:
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubinize.c b/mtd-utils-1.3.1/ubi-utils/src/ubinize.c
new file mode 100644
index 0000000..ab5b0b8
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubinize.c
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Generate UBI images.
+ *
+ * Authors: Artem Bityutskiy
+ * Oliver Lohmann
+ */
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <mtd/ubi-media.h>
+#include <libubigen.h>
+#include <libiniparser.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.2"
+#define PROGRAM_NAME "ubinize"
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+" - a tool to generate UBI images. An UBI image may contain one or more UBI "
+"volumes which have to be defined in the input configuration ini-file. The "
+"ini file defines all the UBI volumes - their characteristics and the and the "
+"contents, but it does not define the characteristics of the flash the UBI "
+"image is generated for. Instead, the flash characteristics are defined via "
+"the command-line options. Note, if not sure about some of the command-line "
+"parameters, do not specify them and let the utility to use default values.";
+
+static const char *optionsstr =
+"-o, --output=<file name> output file name\n"
+"-p, --peb-size=<bytes> size of the physical eraseblock of the flash\n"
+" this UBI image is created for in bytes,\n"
+" kilobytes (KiB), or megabytes (MiB)\n"
+" (mandatory parameter)\n"
+"-m, --min-io-size=<bytes> minimum input/output unit size of the flash\n"
+" in bytes\n"
+"-s, --sub-page-size=<bytes> minimum input/output unit used for UBI\n"
+" headers, e.g. sub-page size in case of NAND\n"
+" flash (equivalent to the minimum input/output\n"
+" unit size by default)\n"
+"-O, --vid-hdr-offset=<num> offset if the VID header from start of the\n"
+" physical eraseblock (default is the next\n"
+" minimum I/O unit or sub-page after the EC\n"
+" header)\n"
+"-e, --erase-counter=<num> the erase counter value to put to EC headers\n"
+" (default is 0)\n"
+"-x, --ubi-ver=<num> UBI version number to put to EC headers\n"
+" (default is 1)\n"
+"-Q, --image-seq=<num> 32-bit UBI image sequence number to use\n"
+" (by default a random number is picked)\n"
+"-v, --verbose be verbose\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " [-o filename] [-p <bytes>] [-m <bytes>] [-s <bytes>] [-O <num>] [-e <num>]\n"
+"\t\t[-x <num>] [-Q <num>] [-v] [-h] [-V] [--output=<filename>] [--peb-size=<bytes>]\n"
+"\t\t[--min-io-size=<bytes>] [--sub-page-size=<bytes>] [--vid-hdr-offset=<num>]\n"
+"\t\t[--erase-counter=<num>] [--ubi-ver=<num>] [--image-seq=<num>] [--verbose] [--help]\n"
+"\t\t[--version] ini-file\n"
+"Example: " PROGRAM_NAME " -o ubi.img -p 16KiB -m 512 -s 256 cfg.ini - create UBI image\n"
+" 'ubi.img' as described by configuration file 'cfg.ini'";
+
+static const char *ini_doc = "INI-file format.\n"
+"The input configuration ini-file describes all the volumes which have to\n"
+"be included to the output UBI image. Each volume is described in its own\n"
+"section which may be named arbitrarily. The section consists on\n"
+"\"key=value\" pairs, for example:\n\n"
+"[jffs2-volume]\n"
+"mode=ubi\n"
+"image=../jffs2.img\n"
+"vol_id=1\n"
+"vol_size=30MiB\n"
+"vol_type=dynamic\n"
+"vol_name=jffs2_volume\n"
+"vol_flags=autoresize\n"
+"vol_alignment=1\n\n"
+"This example configuration file tells the utility to create an UBI image\n"
+"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n"
+"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n"
+"\"image=../jffs2.img\" line tells the utility to take the contents of the\n"
+"volume from the \"../jffs2.img\" file. The size of the image file has to be\n"
+"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n"
+"mandatory and just tells that the section describes an UBI volume - other\n"
+"section modes may be added in the future.\n"
+"Notes:\n"
+" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n"
+" gigabytes (GiB) or bytes (no modifier);\n"
+" * if \"vol_size\" key is absent, the volume size is assumed to be\n"
+" equivalent to the size of the image file (defined by \"image\" key);\n"
+" * if the \"image\" is absent, the volume is assumed to be empty;\n"
+" * volume alignment must not be greater than the logical eraseblock size;\n"
+" * one ini file may contain arbitrary number of sections, the utility will\n"
+" put all the volumes which are described by these section to the output\n"
+" UBI image file.";
+
+struct option long_options[] = {
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' },
+ { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' },
+ { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' },
+ { .name = "image-seq", .has_arg = 1, .flag = NULL, .val = 'Q' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+struct args {
+ const char *f_in;
+ const char *f_out;
+ int out_fd;
+ int peb_size;
+ int min_io_size;
+ int subpage_size;
+ int vid_hdr_offs;
+ int ec;
+ int ubi_ver;
+ uint32_t image_seq;
+ int verbose;
+ dictionary *dict;
+};
+
+static struct args args = {
+ .peb_size = -1,
+ .min_io_size = -1,
+ .subpage_size = -1,
+ .ubi_ver = 1,
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ ubiutils_srand();
+ args.image_seq = rand();
+
+ while (1) {
+ int key;
+ char *endp;
+ unsigned long int image_seq;
+
+ key = getopt_long(argc, argv, "o:p:m:s:O:e:x:Q:vhV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'o':
+ args.out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ if (args.out_fd == -1)
+ return sys_errmsg("cannot open file \"%s\"", optarg);
+ args.f_out = optarg;
+ break;
+
+ case 'p':
+ args.peb_size = ubiutils_get_bytes(optarg);
+ if (args.peb_size <= 0)
+ return errmsg("bad physical eraseblock size: \"%s\"", optarg);
+ break;
+
+ case 'm':
+ args.min_io_size = ubiutils_get_bytes(optarg);
+ if (args.min_io_size <= 0)
+ return errmsg("bad min. I/O unit size: \"%s\"", optarg);
+ if (!is_power_of_2(args.min_io_size))
+ return errmsg("min. I/O unit size should be power of 2");
+ break;
+
+ case 's':
+ args.subpage_size = ubiutils_get_bytes(optarg);
+ if (args.subpage_size <= 0)
+ return errmsg("bad sub-page size: \"%s\"", optarg);
+ if (!is_power_of_2(args.subpage_size))
+ return errmsg("sub-page size should be power of 2");
+ break;
+
+ case 'O':
+ args.vid_hdr_offs = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.vid_hdr_offs < 0)
+ return errmsg("bad VID header offset: \"%s\"", optarg);
+ break;
+
+ case 'e':
+ args.ec = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.ec < 0)
+ return errmsg("bad erase counter value: \"%s\"", optarg);
+ break;
+
+ case 'x':
+ args.ubi_ver = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.ubi_ver < 0)
+ return errmsg("bad UBI version: \"%s\"", optarg);
+ break;
+
+ case 'Q':
+ image_seq = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || image_seq > 0xFFFFFFFF)
+ return errmsg("bad UBI image sequence number: \"%s\"", optarg);
+ args.image_seq = image_seq;
+ break;
+
+ case 'v':
+ args.verbose = 1;
+ break;
+
+ case 'h':
+ ubiutils_print_text(stderr, doc, 80);
+ fprintf(stderr, "\n%s\n\n", ini_doc);
+ fprintf(stderr, "%s\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc)
+ return errmsg("input configuration file was not specified (use -h for help)");
+
+ if (optind != argc - 1)
+ return errmsg("more then one configuration file was specified (use -h for help)");
+
+ args.f_in = argv[optind];
+
+ if (args.peb_size < 0)
+ return errmsg("physical eraseblock size was not specified (use -h for help)");
+
+ if (args.peb_size > 1024*1024)
+ return errmsg("too high physical eraseblock size %d", args.peb_size);
+
+ if (args.min_io_size < 0)
+ return errmsg("min. I/O unit size was not specified (use -h for help)");
+
+ if (args.subpage_size < 0)
+ args.subpage_size = args.min_io_size;
+
+ if (args.subpage_size > args.min_io_size)
+ return errmsg("sub-page cannot be larger then min. I/O unit");
+
+ if (args.peb_size % args.min_io_size)
+ return errmsg("physical eraseblock should be multiple of min. I/O units");
+
+ if (args.min_io_size % args.subpage_size)
+ return errmsg("min. I/O unit size should be multiple of sub-page size");
+
+ if (!args.f_out)
+ return errmsg("output file was not specified (use -h for help)");
+
+ if (args.vid_hdr_offs) {
+ if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE >= args.peb_size)
+ return errmsg("bad VID header position");
+ if (args.vid_hdr_offs % 8)
+ return errmsg("VID header offset has to be multiple of min. I/O unit size");
+ }
+
+ return 0;
+}
+
+static int read_section(const struct ubigen_info *ui, const char *sname,
+ struct ubigen_vol_info *vi, const char **img,
+ struct stat *st)
+{
+ char buf[256];
+ const char *p;
+
+ *img = NULL;
+
+ if (strlen(sname) > 128)
+ return errmsg("too long section name \"%s\"", sname);
+
+ /* Make sure mode is UBI, otherwise ignore this section */
+ sprintf(buf, "%s:mode", sname);
+ p = iniparser_getstring(args.dict, buf, NULL);
+ if (!p) {
+ errmsg("\"mode\" key not found in section \"%s\"", sname);
+ errmsg("the \"mode\" key is mandatory and has to be "
+ "\"mode=ubi\" if the section describes an UBI volume");
+ return -1;
+ }
+
+ /* If mode is not UBI, skip this section */
+ if (strcmp(p, "ubi")) {
+ verbose(args.verbose, "skip non-ubi section \"%s\"", sname);
+ return 1;
+ }
+
+ verbose(args.verbose, "mode=ubi, keep parsing");
+
+ /* Fetch volume type */
+ sprintf(buf, "%s:vol_type", sname);
+ p = iniparser_getstring(args.dict, buf, NULL);
+ if (!p) {
+ normsg("volume type was not specified in "
+ "section \"%s\", assume \"dynamic\"\n", sname);
+ vi->type = UBI_VID_DYNAMIC;
+ } else {
+ if (!strcmp(p, "static"))
+ vi->type = UBI_VID_STATIC;
+ else if (!strcmp(p, "dynamic"))
+ vi->type = UBI_VID_DYNAMIC;
+ else
+ return errmsg("invalid volume type \"%s\" in section \"%s\"",
+ p, sname);
+ }
+
+ verbose(args.verbose, "volume type: %s",
+ vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static");
+
+ /* Fetch the name of the volume image file */
+ sprintf(buf, "%s:image", sname);
+ p = iniparser_getstring(args.dict, buf, NULL);
+ if (p) {
+ *img = p;
+ if (stat(p, st))
+ return sys_errmsg("cannot stat \"%s\" referred from section \"%s\"",
+ p, sname);
+ if (st->st_size == 0)
+ return errmsg("empty file \"%s\" referred from section \"%s\"",
+ p, sname);
+ } else if (vi->type == UBI_VID_STATIC)
+ return errmsg("image is not specified for static volume in section \"%s\"",
+ sname);
+
+ /* Fetch volume id */
+ sprintf(buf, "%s:vol_id", sname);
+ vi->id = iniparser_getint(args.dict, buf, -1);
+ if (vi->id == -1)
+ return errmsg("\"vol_id\" key not found in section \"%s\"", sname);
+ if (vi->id < 0)
+ return errmsg("negative volume ID %d in section \"%s\"",
+ vi->id, sname);
+ if (vi->id >= ui->max_volumes)
+ return errmsg("too high volume ID %d in section \"%s\", max. is %d",
+ vi->id, sname, ui->max_volumes);
+
+ verbose(args.verbose, "volume ID: %d", vi->id);
+
+ /* Fetch volume size */
+ sprintf(buf, "%s:vol_size", sname);
+ p = iniparser_getstring(args.dict, buf, NULL);
+ if (p) {
+ vi->bytes = ubiutils_get_bytes(p);
+ if (vi->bytes <= 0)
+ return errmsg("bad \"vol_size\" key value \"%s\" (section \"%s\")",
+ p, sname);
+
+ /* Make sure the image size is not larger than volume size */
+ if (*img && st->st_size > vi->bytes)
+ return errmsg("error in section \"%s\": size of the image file "
+ "\"%s\" is %lld, which is larger than volume size %lld",
+ sname, *img, (long long)st->st_size, vi->bytes);
+ verbose(args.verbose, "volume size: %lld bytes", vi->bytes);
+ } else {
+ struct stat st;
+
+ if (!*img)
+ return errmsg("neither image file (\"image=\") nor volume size "
+ "(\"vol_size=\") specified in section \"%s\"", sname);
+
+ if (stat(*img, &st))
+ return sys_errmsg("cannot stat \"%s\"", *img);
+
+ vi->bytes = st.st_size;
+
+ if (vi->bytes == 0)
+ return errmsg("file \"%s\" referred from section \"%s\" is empty",
+ *img, sname);
+
+ normsg_cont("volume size was not specified in section \"%s\", assume"
+ " minimum to fit image \"%s\"", sname, *img);
+ ubiutils_print_bytes(vi->bytes, 1);
+ printf("\n");
+ }
+
+ /* Fetch volume name */
+ sprintf(buf, "%s:vol_name", sname);
+ p = iniparser_getstring(args.dict, buf, NULL);
+ if (!p)
+ return errmsg("\"vol_name\" key not found in section \"%s\"", sname);
+
+ vi->name = p;
+ vi->name_len = strlen(p);
+ if (vi->name_len > UBI_VOL_NAME_MAX)
+ return errmsg("too long volume name in section \"%s\", max. is %d characters",
+ vi->name, UBI_VOL_NAME_MAX);
+
+ verbose(args.verbose, "volume name: %s", p);
+
+ /* Fetch volume alignment */
+ sprintf(buf, "%s:vol_alignment", sname);
+ vi->alignment = iniparser_getint(args.dict, buf, -1);
+ if (vi->alignment == -1)
+ vi->alignment = 1;
+ else if (vi->id < 0)
+ return errmsg("negative volume alignement %d in section \"%s\"",
+ vi->alignment, sname);
+
+ verbose(args.verbose, "volume alignment: %d", vi->alignment);
+
+ /* Fetch volume flags */
+ sprintf(buf, "%s:vol_flags", sname);
+ p = iniparser_getstring(args.dict, buf, NULL);
+ if (p) {
+ if (!strcmp(p, "autoresize")) {
+ verbose(args.verbose, "autoresize flags found");
+ vi->flags |= UBI_VTBL_AUTORESIZE_FLG;
+ } else {
+ return errmsg("unknown flags \"%s\" in section \"%s\"",
+ p, sname);
+ }
+ }
+
+ /* Initialize the rest of the volume information */
+ vi->data_pad = ui->leb_size % vi->alignment;
+ vi->usable_leb_size = ui->leb_size - vi->data_pad;
+ if (vi->type == UBI_VID_DYNAMIC)
+ vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size;
+ else
+ vi->used_ebs = (st->st_size + vi->usable_leb_size - 1) / vi->usable_leb_size;
+ vi->compat = 0;
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err = -1, sects, i, autoresize_was_already = 0;
+ struct ubigen_info ui;
+ struct ubi_vtbl_record *vtbl;
+ struct ubigen_vol_info *vi;
+ off_t seek;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ ubigen_info_init(&ui, args.peb_size, args.min_io_size,
+ args.subpage_size, args.vid_hdr_offs,
+ args.ubi_ver, args.image_seq);
+
+ verbose(args.verbose, "LEB size: %d", ui.leb_size);
+ verbose(args.verbose, "PEB size: %d", ui.peb_size);
+ verbose(args.verbose, "min. I/O size: %d", ui.min_io_size);
+ verbose(args.verbose, "sub-page size: %d", args.subpage_size);
+ verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs);
+ verbose(args.verbose, "data offset: %d", ui.data_offs);
+ verbose(args.verbose, "UBI image sequence number: %u", ui.image_seq);
+
+ vtbl = ubigen_create_empty_vtbl(&ui);
+ if (!vtbl)
+ goto out;
+
+ args.dict = iniparser_load(args.f_in);
+ if (!args.dict) {
+ errmsg("cannot load the input ini file \"%s\"", args.f_in);
+ goto out_vtbl;
+ }
+
+ verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in);
+
+ /* Each section describes one volume */
+ sects = iniparser_getnsec(args.dict);
+ if (sects == -1) {
+ errmsg("ini-file parsing error (iniparser_getnsec)");
+ goto out_dict;
+ }
+
+ verbose(args.verbose, "count of sections: %d", sects);
+ if (sects == 0) {
+ errmsg("no sections found the ini-file \"%s\"", args.f_in);
+ goto out_dict;
+ }
+
+ if (sects > ui.max_volumes) {
+ errmsg("too many sections (%d) in the ini-file \"%s\"",
+ sects, args.f_in);
+ normsg("each section corresponds to an UBI volume, maximum "
+ "count of volumes is %d", ui.max_volumes);
+ goto out_dict;
+ }
+
+ vi = calloc(sizeof(struct ubigen_vol_info), sects);
+ if (!vi) {
+ errmsg("cannot allocate memory");
+ goto out_dict;
+ }
+
+ /*
+ * Skip 2 PEBs at the beginning of the file for the volume table which
+ * will be written later.
+ */
+ seek = ui.peb_size * 2;
+ if (lseek(args.out_fd, seek, SEEK_SET) != seek) {
+ sys_errmsg("cannot seek file \"%s\"", args.f_out);
+ goto out_free;
+ }
+
+ for (i = 0; i < sects; i++) {
+ const char *sname = iniparser_getsecname(args.dict, i);
+ const char *img = NULL;
+ struct stat st;
+ int fd, j;
+
+ if (!sname) {
+ errmsg("ini-file parsing error (iniparser_getsecname)");
+ goto out_free;
+ }
+
+ if (args.verbose)
+ printf("\n");
+ verbose(args.verbose, "parsing section \"%s\"", sname);
+
+ err = read_section(&ui, sname, &vi[i], &img, &st);
+ if (err == -1)
+ goto out_free;
+
+ verbose(args.verbose, "adding volume %d", vi[i].id);
+
+ /*
+ * Make sure that volume ID and name is unique and that only
+ * one volume has auto-resize flag
+ */
+ for (j = 0; j < i; j++) {
+ if (vi[i].id == vi[j].id) {
+ errmsg("volume IDs must be unique, but ID %d "
+ "in section \"%s\" is not",
+ vi[i].id, sname);
+ goto out_free;
+ }
+
+ if (!strcmp(vi[i].name, vi[j].name)) {
+ errmsg("volume name must be unique, but name "
+ "\"%s\" in section \"%s\" is not",
+ vi[i].name, sname);
+ goto out_free;
+ }
+ }
+
+ if (vi[i].flags & UBI_VTBL_AUTORESIZE_FLG) {
+ if (autoresize_was_already)
+ return errmsg("only one volume is allowed "
+ "to have auto-resize flag");
+ autoresize_was_already = 1;
+ }
+
+ err = ubigen_add_volume(&ui, &vi[i], vtbl);
+ if (err) {
+ errmsg("cannot add volume for section \"%s\"", sname);
+ goto out_free;
+ }
+
+ if (img) {
+ fd = open(img, O_RDONLY);
+ if (fd == -1) {
+ sys_errmsg("cannot open \"%s\"", img);
+ goto out_free;
+ }
+
+ verbose(args.verbose, "writing volume %d", vi[i].id);
+ verbose(args.verbose, "image file: %s", img);
+
+ err = ubigen_write_volume(&ui, &vi[i], args.ec, st.st_size, fd, args.out_fd);
+ close(fd);
+ if (err) {
+ errmsg("cannot write volume for section \"%s\"", sname);
+ goto out_free;
+ }
+ }
+
+ if (args.verbose)
+ printf("\n");
+ }
+
+ verbose(args.verbose, "writing layout volume");
+
+ err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl, args.out_fd);
+ if (err) {
+ errmsg("cannot write layout volume");
+ goto out_free;
+ }
+
+ verbose(args.verbose, "done");
+
+ free(vi);
+ iniparser_freedict(args.dict);
+ free(vtbl);
+ close(args.out_fd);
+ return 0;
+
+out_free:
+ free(vi);
+out_dict:
+ iniparser_freedict(args.dict);
+out_vtbl:
+ free(vtbl);
+out:
+ close(args.out_fd);
+ remove(args.f_out);
+ return err;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubirename.c b/mtd-utils-1.3.1/ubi-utils/src/ubirename.c
new file mode 100644
index 0000000..00c53e4
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubirename.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 Logitech.
+ *
+ * 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
+ */
+
+/*
+ * An utility to get rename UBI volumes.
+ *
+ * Author: Richard Titmuss
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.0"
+#define PROGRAM_NAME "ubirename"
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI device node file name> [<old name> <new name>|...]\n\n"
+"Example: " PROGRAM_NAME "/dev/ubi0 A B C D - rename volume A to B, and C to D\n\n"
+"This utility allows re-naming several volumes in one go atomically.\n"
+"For example, if you have volumes A and B, then you may rename A into B\n"
+"and B into A at one go, and the operation will be atomic. This allows\n"
+"implementing atomic UBI volumes upgrades. E.g., if you have volume A\n"
+"and want to upgrade it atomically, you create a temporary volume B,\n"
+"put your new data to B, then rename A to B and B to A, and then you\n"
+"may remove old volume B.\n"
+"It is also allowed to re-name multiple volumes at a time, but 16 max.\n"
+"renames at once, which means you may specify up to 32 volume names.\n"
+"If you have volumes A and B, and re-name A to B, bud do not re-name\n"
+"B to something else in the same request, old volume B will be removed\n"
+"and A will be renamed into B.\n";
+
+static int get_vol_id(libubi_t libubi, struct ubi_dev_info *dev_info,
+ char *name)
+{
+ int err, i;
+ struct ubi_vol_info vol_info;
+
+ for (i=dev_info->lowest_vol_id; i<=dev_info->highest_vol_id; i++) {
+ err = ubi_get_vol_info1(libubi, dev_info->dev_num, i, &vol_info);
+ if (err == -1) {
+ if (errno == ENOENT)
+ continue;
+ return -1;
+ }
+
+ if (strcmp(name, vol_info.name) == 0)
+ return vol_info.vol_id;
+ }
+
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ int i, err;
+ int count = 0;
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+ struct ubi_rnvol_req rnvol;
+ const char *node;
+
+ if (argc < 3 || (argc & 1) == 1) {
+ errmsg("too few arguments");
+ fprintf(stderr, "%s\n", usage);
+ return -1;
+ }
+
+ if (argc > UBI_MAX_RNVOL + 2) {
+ errmsg("too many volumes to re-name, max. is %d",
+ UBI_MAX_RNVOL);
+ return -1;
+ }
+
+ node = argv[1];
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ return sys_errmsg("cannot open libubi");
+ }
+
+ err = ubi_probe_node(libubi, node);
+ if (err == 2) {
+ errmsg("\"%s\" is an UBI volume node, not an UBI device node",
+ node);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("\"%s\" is not an UBI device node", node);
+ else
+ sys_errmsg("error while probing \"%s\"", node);
+ goto out_libubi;
+ }
+
+ err = ubi_get_dev_info(libubi, node, &dev_info);
+ if (err == -1) {
+ sys_errmsg("cannot get information about UBI device \"%s\"", node);
+ goto out_libubi;
+ }
+
+ for (i = 2; i < argc; i += 2) {
+ err = get_vol_id(libubi, &dev_info, argv[i]);
+ if (err == -1) {
+ errmsg("\"%s\" volume not found", argv[i]);
+ goto out_libubi;
+ }
+
+ rnvol.ents[count].vol_id = err;
+ rnvol.ents[count].name_len = strlen(argv[i + 1]);
+ strcpy(rnvol.ents[count++].name, argv[i + 1]);
+ }
+
+ rnvol.count = count;
+
+ err = ubi_rnvols(libubi, node, &rnvol);
+ if (err == -1) {
+ sys_errmsg("cannot rename volumes");
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubirmvol.c b/mtd-utils-1.3.1/ubi-utils/src/ubirmvol.c
new file mode 100644
index 0000000..4fbe73a
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubirmvol.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * An utility to remove UBI volumes.
+ *
+ * Authors: Artem Bityutskiy <dedekind@infradead.org>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.1"
+#define PROGRAM_NAME "ubirmvol"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int vol_id;
+ const char *node;
+ const char *name;
+};
+
+static struct args args = {
+ .vol_id = -1,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to remove UBI volumes.";
+
+static const char *optionsstr =
+"-n, --vol_id=<volume id> volume ID to remove\n"
+"-N, --name=<volume name> volume name to remove\n"
+"-h, -?, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI device node file name> [-n <volume id>] [--vol_id=<volume id>]\n\n"
+" [-N <volume name>] [--name=<volume name>] [-h] [--help]\n\n"
+"Example: " PROGRAM_NAME "/dev/ubi0 -n 1 - remove UBI volume 1 from UBI device corresponding\n"
+" to /dev/ubi0\n"
+" " PROGRAM_NAME "/dev/ubi0 -N my_vol - remove UBI named \"my_vol\" from UBI device\n"
+" corresponding to /dev/ubi0";
+
+static const struct option long_options[] = {
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int param_sanity_check(void)
+{
+ if (args.vol_id == -1 && !args.name) {
+ errmsg("please, specify either volume ID or volume name");
+ return -1;
+ }
+
+ if (args.vol_id != -1 && args.name) {
+ errmsg("please, specify either volume ID or volume name, not both");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "n:N:h?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+
+ case 'n':
+ args.vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.vol_id < 0) {
+ errmsg("bad volume ID: " "\"%s\"", optarg);
+ return -1;
+ }
+ break;
+
+ case 'N':
+ args.name = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ errmsg("parameter is missing");
+ return -1;
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc) {
+ errmsg("UBI device name was not specified (use -h for help)");
+ return -1;
+ } else if (optind != argc - 1) {
+ errmsg("more then one UBI device specified (use -h for help)");
+ return -1;
+ }
+
+ args.node = argv[optind];
+
+ if (param_sanity_check())
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ return sys_errmsg("cannot open libubi");
+ }
+
+ err = ubi_probe_node(libubi, args.node);
+ if (err == 2) {
+ errmsg("\"%s\" is an UBI volume node, not an UBI device node",
+ args.node);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("\"%s\" is not an UBI device node", args.node);
+ else
+ sys_errmsg("error while probing \"%s\"", args.node);
+ goto out_libubi;
+ }
+
+ if (args.name) {
+ struct ubi_dev_info dev_info;
+ struct ubi_vol_info vol_info;
+
+ err = ubi_get_dev_info(libubi, args.node, &dev_info);
+ if (err) {
+ sys_errmsg("cannot get information about UBI device \"%s\"",
+ args.node);
+ goto out_libubi;
+ }
+
+ err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num,
+ args.name, &vol_info);
+ if (err) {
+ sys_errmsg("cannot find UBI volume \"%s\"", args.name);
+ goto out_libubi;
+ }
+
+ args.vol_id = vol_info.vol_id;
+ }
+
+ err = ubi_rmvol(libubi, args.node, args.vol_id);
+ if (err) {
+ sys_errmsg("cannot UBI remove volume");
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubirsvol.c b/mtd-utils-1.3.1/ubi-utils/src/ubirsvol.c
new file mode 100644
index 0000000..a32a956
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubirsvol.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * An utility to resize UBI volumes.
+ *
+ * Authors: Artem Bityutskiy <dedekind@infradead.org>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.1"
+#define PROGRAM_NAME "ubirsvol"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int vol_id;
+ const char *node;
+ const char *name;
+ long long bytes;
+ int lebs;
+};
+
+static struct args args = {
+ .vol_id = -1,
+ .bytes = -1,
+ .lebs = -1,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to resize UBI volumes.";
+
+static const char *optionsstr =
+"-n, --vol_id=<volume id> volume ID to resize\n"
+"-N, --name=<volume name> volume name to resize\n"
+"-s, --size=<bytes> volume size volume size in bytes, kilobytes (KiB)\n"
+" or megabytes (MiB)\n"
+"-S, --lebs=<LEBs count> alternative way to give volume size in logical\n"
+" eraseblocks\n"
+"-h, -?, --help print help message\n"
+"-V, --version print program version";
+
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI device node file name> [-n <volume id>] [--vol_id=<volume id>]\n\n"
+" [-N <volume name>] [--name=<volume name>] [-s <bytes>] [-S <LEBs>] [-h] [--help]\n\n"
+"Example: " PROGRAM_NAME " /dev/ubi0 -n 1 -s 1MiB resize UBI volume 1 to 1 MiB on\n"
+" UBI device corresponding to /dev/ubi0\n"
+" " PROGRAM_NAME " /dev/ubi0 -N my_vol -s 1MiB - resize UBI volume named \"my_vol\" to 1 MiB\n"
+" on UBI device corresponding to /dev/ubi0";
+
+static const struct option long_options[] = {
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' },
+ { NULL, 0, NULL, 0},
+};
+
+static int param_sanity_check(void)
+{
+ if (args.vol_id == -1 && !args.name) {
+ errmsg("please, specify either volume ID or volume name");
+ return -1;
+ }
+
+ if (args.vol_id != -1 && args.name) {
+ errmsg("please, specify either volume ID or volume name, not both");
+ return -1;
+ }
+
+ if (args.bytes == -1 && args.lebs == -1)
+ return errmsg("volume size was not specified (use -h for help)");
+
+ if (args.bytes != -1 && args.lebs != -1)
+ return errmsg("size specified with more then one option");
+
+ return 0;
+}
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "s:S:n:N:h?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 's':
+ args.bytes = ubiutils_get_bytes(optarg);
+ if (args.bytes <= 0)
+ return errmsg("bad volume size: \"%s\"", optarg);
+ break;
+
+ case 'S':
+ args.lebs = strtoull(optarg, &endp, 0);
+ if (endp == optarg || args.lebs <= 0 || *endp != '\0')
+ return errmsg("bad LEB count: \"%s\"", optarg);
+ break;
+
+ case 'n':
+ args.vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.vol_id < 0) {
+ errmsg("bad volume ID: " "\"%s\"", optarg);
+ return -1;
+ }
+ break;
+
+ case 'N':
+ args.name = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ errmsg("parameter is missing");
+ return -1;
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc) {
+ errmsg("UBI device name was not specified (use -h for help)");
+ return -1;
+ } else if (optind != argc - 1) {
+ errmsg("more then one UBI device specified (use -h for help)");
+ return -1;
+ }
+
+ args.node = argv[optind];
+
+ if (param_sanity_check())
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+ struct ubi_vol_info vol_info;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi)
+ return sys_errmsg("cannot open libubi");
+
+ err = ubi_probe_node(libubi, args.node);
+ if (err == 2) {
+ errmsg("\"%s\" is an UBI volume node, not an UBI device node",
+ args.node);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("\"%s\" is not an UBI device node", args.node);
+ else
+ sys_errmsg("error while probing \"%s\"", args.node);
+ }
+
+ err = ubi_get_dev_info(libubi, args.node, &dev_info);
+ if (err) {
+ sys_errmsg("cannot get information about UBI device \"%s\"",
+ args.node);
+ goto out_libubi;
+ }
+
+ if (args.name) {
+ err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num,
+ args.name, &vol_info);
+ if (err) {
+ sys_errmsg("cannot find UBI volume \"%s\"", args.name);
+ goto out_libubi;
+ }
+
+ args.vol_id = vol_info.vol_id;
+ } else {
+ err = ubi_get_vol_info1(libubi, dev_info.dev_num,
+ args.vol_id, &vol_info);
+ if (err) {
+ sys_errmsg("cannot find UBI volume ID %d", args.vol_id);
+ goto out_libubi;
+ }
+ }
+
+ if (args.lebs != -1)
+ args.bytes = vol_info.leb_size * args.lebs;
+
+ err = ubi_rsvol(libubi, args.node, args.vol_id, args.bytes);
+ if (err) {
+ sys_errmsg("cannot UBI resize volume");
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils-1.3.1/ubi-utils/src/ubiupdatevol.c b/mtd-utils-1.3.1/ubi-utils/src/ubiupdatevol.c
new file mode 100644
index 0000000..5c1cd32
--- /dev/null
+++ b/mtd-utils-1.3.1/ubi-utils/src/ubiupdatevol.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * An utility to update UBI volumes.
+ *
+ * Authors: Frank Haverkamp
+ * Joshua W. Boyer
+ * Artem Bityutskiy
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.2"
+#define PROGRAM_NAME "ubiupdatevol"
+
+struct args {
+ int truncate;
+ const char *node;
+ const char *img;
+ /* For deprecated -d and -B options handling */
+ char dev_name[256];
+ int size;
+ int use_stdin;
+};
+
+static struct args args;
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to write data to UBI volumes.";
+
+static const char *optionsstr =
+"-t, --truncate truncate volume (wipe it out)\n"
+"-s, --size=<bytes> bytes in input, if not reading from file\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI volume node file name> [-t] [-s <size>] [-h] [-V] [--truncate]\n"
+"\t\t\t[--size=<size>] [--help] [--version] <image file>\n\n"
+"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n"
+"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1";
+
+struct option long_options[] = {
+ { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { NULL, 0, NULL, 0}
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key;
+ char *endp;
+
+ key = getopt_long(argc, argv, "ts:h?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 't':
+ args.truncate = 1;
+ break;
+
+ case 's':
+ args.size = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg || args.size < 0)
+ return errmsg("bad size: " "\"%s\"", optarg);
+ break;
+
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s\n\n", doc);
+ fprintf(stderr, "%s\n\n", usage);
+ fprintf(stderr, "%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc)
+ return errmsg("UBI device name was not specified (use -h for help)");
+ else if (optind != argc - 2 && !args.truncate)
+ return errmsg("specify UBI device name and image file name as first 2 "
+ "parameters (use -h for help)");
+
+ args.node = argv[optind];
+ args.img = argv[optind + 1];
+
+ if (args.img && args.truncate)
+ return errmsg("You can't truncate and specify an image (use -h for help)");
+
+ if (args.img && !args.truncate) {
+ if (strcmp(args.img, "-") == 0)
+ args.use_stdin = 1;
+ if (args.use_stdin && !args.size)
+ return errmsg("file size must be specified if input is stdin");
+ }
+
+ return 0;
+}
+
+static int truncate_volume(libubi_t libubi)
+{
+ int err, fd;
+
+ fd = open(args.node, O_RDWR);
+ if (fd == -1)
+ return sys_errmsg("cannot open \"%s\"", args.node);
+
+ err = ubi_update_start(libubi, fd, 0);
+ if (err) {
+ sys_errmsg("cannot truncate volume \"%s\"", args.node);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int ubi_write(int fd, const void *buf, int len)
+{
+ int ret;
+
+ while (len) {
+ ret = write(fd, buf, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ warnmsg("do not interrupt me!");
+ continue;
+ }
+ return sys_errmsg("cannot write %d bytes to volume \"%s\"",
+ len, args.node);
+ }
+
+ if (ret == 0)
+ return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node);
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return 0;
+}
+
+static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info)
+{
+ int err, fd, ifd;
+ long long bytes;
+ char *buf;
+
+ buf = malloc(vol_info->leb_size);
+ if (!buf)
+ return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size);
+
+ if (!args.size) {
+ struct stat st;
+ err = stat(args.img, &st);
+ if (err < 0) {
+ errmsg("stat failed on \"%s\"", args.img);
+ goto out_free;
+ }
+
+ bytes = st.st_size;
+ } else
+ bytes = args.size;
+
+ if (bytes > vol_info->rsvd_bytes) {
+ errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)",
+ args.img, bytes, args.node, vol_info->rsvd_bytes);
+ goto out_free;
+ }
+
+ fd = open(args.node, O_RDWR);
+ if (fd == -1) {
+ sys_errmsg("cannot open UBI volume \"%s\"", args.node);
+ goto out_free;
+ }
+
+ if (args.use_stdin)
+ ifd = STDIN_FILENO;
+ else {
+ ifd = open(args.img, O_RDONLY);
+ if (ifd == -1) {
+ sys_errmsg("cannot open \"%s\"", args.img);
+ goto out_close1;
+ }
+ }
+
+ err = ubi_update_start(libubi, fd, bytes);
+ if (err) {
+ sys_errmsg("cannot start volume \"%s\" update", args.node);
+ goto out_close;
+ }
+
+ while (bytes) {
+ int ret, to_copy = vol_info->leb_size;
+
+ if (to_copy > bytes)
+ to_copy = bytes;
+
+ ret = read(ifd, buf, to_copy);
+ if (ret <= 0) {
+ if (errno == EINTR) {
+ warnmsg("do not interrupt me!");
+ continue;
+ } else {
+ sys_errmsg("cannot read %d bytes from \"%s\"",
+ to_copy, args.img);
+ goto out_close;
+ }
+ }
+
+ err = ubi_write(fd, buf, ret);
+ if (err)
+ goto out_close;
+ bytes -= ret;
+ }
+
+ close(ifd);
+ close(fd);
+ free(buf);
+ return 0;
+
+out_close:
+ close(ifd);
+out_close1:
+ close(fd);
+out_free:
+ free(buf);
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_vol_info vol_info;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+ return errmsg("UBI is not present in the system");
+ else
+ return errmsg("cannot open libubi");
+ }
+
+ err = ubi_probe_node(libubi, args.node);
+ if (err == 1) {
+ errmsg("\"%s\" is an UBI device node, not an UBI volume node",
+ args.node);
+ goto out_libubi;
+ } else if (err < 0) {
+ if (errno == ENODEV)
+ errmsg("\"%s\" is not an UBI volume node", args.node);
+ else
+ sys_errmsg("error while probing \"%s\"", args.node);
+ goto out_libubi;
+ }
+
+ err = ubi_get_vol_info(libubi, args.node, &vol_info);
+ if (err) {
+ sys_errmsg("cannot get information about UBI volume \"%s\"",
+ args.node);
+ goto out_libubi;
+ }
+
+ if (args.truncate)
+ err = truncate_volume(libubi);
+ else
+ err = update_volume(libubi, &vol_info);
+ if (err)
+ goto out_libubi;
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/mtd-utils.patches/mtd-utils-00.description b/mtd-utils.patches/mtd-utils-00.description
new file mode 100644
index 0000000..d447f87
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-00.description
@@ -0,0 +1,3 @@
+Fix bug in nandtest with upstream change (http://git.infradead.org/mtd-utils.git/commit/94fc0263667926cfad7647c1054b4473cc92ecee).
+
+Also picked up some whitespace and other trivial stuff.
\ No newline at end of file
diff --git a/mtd-utils.patches/mtd-utils-00.patch b/mtd-utils.patches/mtd-utils-00.patch
new file mode 100644
index 0000000..a62434a
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-00.patch
@@ -0,0 +1,129 @@
+diff -aruN a/nandtest.c b/nandtest.c
+--- a/nandtest.c 2011-10-17 11:17:50.972096998 -0700
++++ b/nandtest.c 2011-10-17 11:16:59.116096996 -0700
+@@ -1,3 +1,5 @@
++#define PROGRAM_NAME "nandtest"
++
+ #define _GNU_SOURCE
+ #include <ctype.h>
+ #include <errno.h>
+@@ -17,14 +19,15 @@
+
+ void usage(void)
+ {
+- fprintf(stderr, "usage: nandtest [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"
+- " -o, --offset Start offset on flash\n"
+- " -l, --length Length of flash to test\n"
+- " -k, --keep Restore existing contents after test\n");
++ fprintf(stderr, "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"
++ " -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(1);
+ }
+
+@@ -70,23 +73,23 @@
+ }
+ if (len < meminfo.erasesize) {
+ printf("\n");
+- fprintf(stderr, "Short write (%d bytes)\n", len);
++ fprintf(stderr, "Short write (%zd bytes)\n", len);
+ exit(1);
+ }
+
+ printf("\r%08x: reading...", (unsigned)ofs);
+ fflush(stdout);
+-
++
+ len = pread(fd, rbuf, meminfo.erasesize, ofs);
+ if (len < meminfo.erasesize) {
+ printf("\n");
+ if (len)
+- fprintf(stderr, "Short read (%d bytes)\n", len);
++ fprintf(stderr, "Short read (%zd bytes)\n", len);
+ else
+ perror("read");
+ exit(1);
+ }
+-
++
+ if (ioctl(fd, ECCGETSTATS, &newstats)) {
+ printf("\n");
+ perror("ECCGETSTATS");
+@@ -95,7 +98,9 @@
+ }
+
+ if (newstats.corrected > oldstats.corrected) {
+- printf("\nECC corrected at %08x\n", (unsigned) ofs);
++ 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) {
+@@ -181,7 +186,7 @@
+ case 'l':
+ length = strtol(optarg, NULL, 0);
+ break;
+-
++
+ }
+ }
+ if (argc - optind != 1)
+@@ -192,7 +197,7 @@
+ perror("open");
+ exit(1);
+ }
+-
++
+ if (ioctl(fd, MEMGETINFO, &meminfo)) {
+ perror("MEMGETINFO");
+ close(fd);
+@@ -203,20 +208,20 @@
+ length = meminfo.size;
+
+ if (offset % meminfo.erasesize) {
+- fprintf(stderr, "Offset %x not multiple of erase size %x\n",
++ 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",
++ 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",
++ fprintf(stderr, "Length %x + offset %x exceeds device size %x\n",
+ length, offset, meminfo.size);
+ exit(1);
+- }
++ }
+
+ wbuf = malloc(meminfo.erasesize * 3);
+ if (!wbuf) {
+@@ -259,11 +264,11 @@
+ printf("\r%08x: reading... ", (unsigned)test_ofs);
+ fflush(stdout);
+
+- len = pread(fd, rbuf, meminfo.erasesize, test_ofs);
++ len = pread(fd, kbuf, meminfo.erasesize, test_ofs);
+ if (len < meminfo.erasesize) {
+ printf("\n");
+ if (len)
+- fprintf(stderr, "Short read (%d bytes)\n", len);
++ fprintf(stderr, "Short read (%zd bytes)\n", len);
+ else
+ perror("read");
+ exit(1);
diff --git a/mtd-utils.patches/mtd-utils-50.description b/mtd-utils.patches/mtd-utils-50.description
new file mode 100644
index 0000000..36bc1c4
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-50.description
@@ -0,0 +1,2 @@
+This patch adds support for independently specifying both PREFIX and
+EXEC_PREFIX.
diff --git a/mtd-utils.patches/mtd-utils-50.patch b/mtd-utils.patches/mtd-utils-50.patch
new file mode 100644
index 0000000..2686389
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-50.patch
@@ -0,0 +1,16 @@
+diff -aruN a/Makefile b/Makefile
+--- a/Makefile 2010-01-15 09:12:24.000000000 -0800
++++ b/Makefile 2010-09-08 16:03:05.000000000 -0700
+@@ -1,6 +1,12 @@
+
+ # -*- sh -*-
+
++PREFIX=/usr
++EXEC_PREFIX=$(PREFIX)
++SBINDIR=$(EXEC_PREFIX)/sbin
++MANDIR=$(PREFIX)/man
++INCLUDEDIR=$(PREFIX)/include
++
+ CPPFLAGS += -I./include $(ZLIBCPPFLAGS) $(LZOCPPFLAGS)
+
+ ifeq ($(WITHOUT_XATTR), 1)
diff --git a/mtd-utils.patches/mtd-utils-51.description b/mtd-utils.patches/mtd-utils-51.description
new file mode 100644
index 0000000..300e283
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-51.description
@@ -0,0 +1,2 @@
+This patch allows the make caller to specify flags supplying the
+location of UUID headers and libraries.
diff --git a/mtd-utils.patches/mtd-utils-51.patch b/mtd-utils.patches/mtd-utils-51.patch
new file mode 100644
index 0000000..df60c76
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-51.patch
@@ -0,0 +1,19 @@
+diff -aruN a/mkfs.ubifs/Makefile b/mkfs.ubifs/Makefile
+--- a/mkfs.ubifs/Makefile 2010-01-15 09:12:24.000000000 -0800
++++ b/mkfs.ubifs/Makefile 2010-09-12 14:23:23.000000000 -0700
+@@ -1,13 +1,13 @@
+
+ CPPFLAGS += -I../include -I../ubi-utils/include
+-CPPFLAGS += $(ZLIBCPPFLAGS) $(LZOCPPFLAGS)
++CPPFLAGS += $(ZLIBCPPFLAGS) $(LZOCPPFLAGS) $(UUIDCPPFLAGS)
+
+ ALL_SOURCES=*.[ch] hashtable/*.[ch]
+
+ TARGETS = mkfs.ubifs
+
+ LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid -L$(BUILDDIR)/../ubi-utils/ -lubi
+-LDLIBS_mkfs.ubifs += $(ZLIBLDFLAGS) $(LZOLDFLAGS)
++LDLIBS_mkfs.ubifs += $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS)
+
+ include ../common.mk
+
diff --git a/mtd-utils.patches/mtd-utils-52.description b/mtd-utils.patches/mtd-utils-52.description
new file mode 100644
index 0000000..4697c02
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-52.description
@@ -0,0 +1 @@
+This patch suppresses building the old, deprecated and unused UBI utilities.
diff --git a/mtd-utils.patches/mtd-utils-52.patch b/mtd-utils.patches/mtd-utils-52.patch
new file mode 100644
index 0000000..f34c686
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-52.patch
@@ -0,0 +1,12 @@
+diff -aruN a/ubi-utils/Makefile b/ubi-utils/Makefile
+--- a/ubi-utils/Makefile 2010-01-15 09:12:24.000000000 -0800
++++ b/ubi-utils/Makefile 2011-08-02 13:55:18.420060547 -0700
+@@ -4,8 +4,6 @@
+
+ KERNELHDR := ../include
+
+-SUBDIRS = old-utils
+-
+ # CFLAGS += -Werror
+ CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR)
+
diff --git a/mtd-utils.patches/mtd-utils-53.description b/mtd-utils.patches/mtd-utils-53.description
new file mode 100644
index 0000000..387fcac
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-53.description
@@ -0,0 +1,4 @@
+Two related changes:
+1. Updates nandwrite to not attempt to write past the partition boundary when the last block is bad
+2. Adds an option to nanddump to skip empty pages
+This prevents a nanddump piped to nandwrite failing because the last block in a parition is bad, even when the tail of the data coming out is empty.
\ No newline at end of file
diff --git a/mtd-utils.patches/mtd-utils-53.patch b/mtd-utils.patches/mtd-utils-53.patch
new file mode 100644
index 0000000..3703fea
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-53.patch
@@ -0,0 +1,91 @@
+diff -aruN a/nanddump.c b/nanddump.c
+--- a/nanddump.c 2011-11-02 21:48:36.159532935 -0700
++++ b/nanddump.c 2011-11-04 12:20:36.000000000 -0700
+@@ -54,6 +54,7 @@
+ "-p --prettyprint Print nice (hexdump)\n"
+ "-q --quiet Don't display progress and status messages\n"
+ "-s addr --startaddress=addr Start address\n"
++"-e --omitempty Omit empty pages\n"
+ );
+ exit(EXIT_SUCCESS);
+ }
+@@ -83,6 +84,7 @@
+ static const char *dumpfile; // dump file name
+ static bool omitbad = false;
+ static bool quiet = false; // suppress diagnostic output
++static bool omitempty = false; // don't output empty pages
+
+ static void process_options (int argc, char * const argv[])
+ {
+@@ -90,7 +92,7 @@
+
+ for (;;) {
+ int option_index = 0;
+- static const char *short_options = "bs:f:il:opqn";
++ static const char *short_options = "bs:f:il:opqne";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+@@ -103,6 +105,7 @@
+ {"length", required_argument, 0, 'l'},
+ {"noecc", no_argument, 0, 'n'},
+ {"quiet", no_argument, 0, 'q'},
++ {"omitempty", no_argument, 0, 'e'},
+ {0, 0, 0, 0},
+ };
+
+@@ -153,6 +156,9 @@
+ case 'n':
+ noecc = true;
+ break;
++ case 'e':
++ omitempty = true;
++ break;
+ case '?':
+ error++;
+ break;
+@@ -192,6 +198,7 @@
+ struct nand_oobinfo old_oobinfo;
+ struct mtd_ecc_stats stat1, stat2;
+ bool eccstats = false;
++ bool isempty;
+
+ process_options(argc, argv);
+
+@@ -329,6 +336,23 @@
+ stat1 = stat2;
+ }
+
++ if (omitempty) {
++ isempty = true;
++ for (i = 0; i < bs; i++) {
++ if (readbuf[i] != 0xff) {
++ isempty = false;
++ break;
++ }
++ }
++
++ if (isempty) {
++ if (!quiet)
++ fprintf(stderr, "Skipping empty page at offset 0x%08lx\n", ofs);
++
++ continue;
++ }
++ }
++
+ /* Write out page data */
+ if (pretty_print) {
+ for (i = 0; i < bs; i += 16) {
+diff -aruN a/nandwrite.c b/nandwrite.c
+--- a/nandwrite.c 2011-11-02 21:48:36.159532935 -0700
++++ b/nandwrite.c 2011-11-03 15:54:26.949570997 -0700
+@@ -486,6 +486,9 @@
+
+ if (baderaseblock) {
+ mtdoffset = blockstart + meminfo.erasesize;
++ if (mtdoffset >= meminfo.size) {
++ goto closeall;
++ }
+ }
+ offs += meminfo.erasesize / blockalign ;
+ } while ( offs < blockstart + meminfo.erasesize );
diff --git a/mtd-utils.patches/mtd-utils-54.description b/mtd-utils.patches/mtd-utils-54.description
new file mode 100644
index 0000000..9948fbe
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-54.description
@@ -0,0 +1 @@
+This patch changes nandtest to restore page by page instead of block by block. This is necessary for BCH support since writing all 0xff's results in non-0xff values in the OOB area, and for blocks that jffs2 thinks are empty this is very bad.
\ No newline at end of file
diff --git a/mtd-utils.patches/mtd-utils-54.patch b/mtd-utils.patches/mtd-utils-54.patch
new file mode 100644
index 0000000..2bd685c
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-54.patch
@@ -0,0 +1,111 @@
+diff -aruN a/nandtest.c b/nandtest.c
+--- a/nandtest.c 2011-11-16 11:20:33.715549264 -0800
++++ b/nandtest.c 2011-11-16 11:20:46.135549260 -0800
+@@ -4,6 +4,7 @@
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
++#include <stdbool.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -37,6 +38,9 @@
+ int markbad=0;
+ int seed;
+
++/*
++ * Erase and write block by block, checking for errors
++ */
+ int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf)
+ {
+ struct erase_info_user er;
+@@ -126,6 +130,71 @@
+ return 0;
+ }
+
++/*
++ * Restore page by page, skipping empty writes (writing all 0xff can result in non-0xff
++ * OOB bytes in empty blocks, which will be a problem for the fs after the test is complete).
++ */
++int erase_and_restore(loff_t ofs, unsigned char *data)
++{
++ struct erase_info_user er;
++ ssize_t len;
++ int page;
++ int page_count = (meminfo.erasesize/meminfo.writesize);
++ int i;
++ bool isempty;
++ unsigned char *data_page = data;
++
++ printf("\r%08x: erasing... ", (unsigned)ofs);
++ fflush(stdout);
++
++ er.start = ofs;
++ er.length = meminfo.erasesize;
++
++ /* Erase the whole block */
++ 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);
++
++ /* Write back page by page */
++ for (page = 0; page < page_count; page++, data_page += meminfo.writesize, ofs += meminfo.writesize) {
++ isempty = true;
++ for (i = 0; i < meminfo.writesize; i++) {
++ if (data_page[i] != 0xff) {
++ isempty = false;
++ break;
++ }
++ }
++
++ if (isempty == false) {
++ len = pwrite(fd, data_page, meminfo.writesize, 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.writesize) {
++ printf("\n");
++ fprintf(stderr, "Short write (%zd bytes)\n", len);
++ exit(1);
++ }
++ }
++ }
++
++ return 0;
++}
+
+ /*
+ * Main program
+@@ -226,7 +295,7 @@
+ wbuf = malloc(meminfo.erasesize * 3);
+ if (!wbuf) {
+ fprintf(stderr, "Could not allocate %d bytes for buffer\n",
+- meminfo.erasesize * 2);
++ meminfo.erasesize * 3);
+ exit(1);
+ }
+ rbuf = wbuf + meminfo.erasesize;
+@@ -277,7 +346,7 @@
+ if (erase_and_write(test_ofs, wbuf, rbuf))
+ continue;
+ if (keep_contents)
+- erase_and_write(test_ofs, kbuf, rbuf);
++ erase_and_restore(test_ofs, kbuf);
+ }
+ printf("\nFinished pass %d successfully\n", pass+1);
+ }
diff --git a/mtd-utils.patches/mtd-utils-55.description b/mtd-utils.patches/mtd-utils-55.description
new file mode 100644
index 0000000..7bccbcb
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-55.description
@@ -0,0 +1 @@
+This patch adds the ubicpvol utility for replicating/copying ubi volumes. Previously, rsync() was used, which fails on read-only filesystems.
diff --git a/mtd-utils.patches/mtd-utils-55.patch b/mtd-utils.patches/mtd-utils-55.patch
new file mode 100644
index 0000000..77a7a30
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-55.patch
@@ -0,0 +1,323 @@
+diff -Naur a/ubi-utils/Makefile b/ubi-utils/Makefile
+--- a/ubi-utils/Makefile 2014-12-19 15:27:10.990583444 -0800
++++ b/ubi-utils/Makefile 2014-12-19 14:52:57.462133419 -0800
+@@ -8,7 +8,7 @@
+ CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR)
+
+ LIBS = libubi libmtd libubigen libiniparser libscan
+-TARGETS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
++TARGETS = ubicpvol ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
+ ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol
+
+ VPATH = src
+diff -Naur a/ubi-utils/src/ubicpvol.c b/ubi-utils/src/ubicpvol.c
+--- a/ubi-utils/src/ubicpvol.c 1969-12-31 16:00:00.000000000 -0800
++++ b/ubi-utils/src/ubicpvol.c 2014-12-19 15:24:59.765776184 -0800
+@@ -0,0 +1,291 @@
++/*
++ * Based originally on the ubiupdatevol utility
++ * Copyright (c) International Business Machines Corp., 2006
++ * Copyright (c) Google, Inc, 2014
++ *
++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++/*
++ * A utility to replicate/copy UBI volumes.
++ *
++ * Authors: Frank Haverkamp
++ * Joshua W. Boyer
++ * Artem Bityutskiy
++ * Peter van Vugt
++ */
++
++#include <fcntl.h>
++#include <stdio.h>
++#include <stdint.h>
++#include <getopt.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <libubi.h>
++#include "common.h"
++
++#define PROGRAM_VERSION "1.0"
++#define PROGRAM_NAME "ubicpvol"
++
++struct args {
++ const char *destNode;
++ const char *srcNode;
++};
++
++static struct args args;
++
++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
++ " - a tool to replicate/copy UBI volumes.\n";
++
++static const char *optionsstr =
++"Options:\n"
++"-h, --help print help message\n"
++"-V, --version print program version\n";
++
++static const char *usage =
++"Usage: " PROGRAM_NAME " [OPTIONS] <UBI source volume node> <UBI destination volume node>\n\n"
++"Example: " PROGRAM_NAME " /dev/ubi0_1 /dev/ubi0_2 - copy UBI volume \"/dev/ubi0_1\" to \"/dev/ubi0_2\"\n";
++
++static const char *notes =
++"Note that both volumes must have the same LEB size and the data in the source\n"
++"volume must fit into the destination volume.\n";
++
++struct option long_options[] = {
++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
++ { NULL, 0, NULL, 0}
++};
++
++static int parse_opt(int argc, char * const argv[])
++{
++ while (1) {
++ int key;
++
++ key = getopt_long(argc, argv, "h?V", long_options, NULL);
++ if (key == -1)
++ break;
++
++ switch (key) {
++ case 'h':
++ case '?':
++ fprintf(stderr, "%s\n", doc);
++ fprintf(stderr, "%s\n", usage);
++ fprintf(stderr, "%s\n", optionsstr);
++ fprintf(stderr, "%s\n", notes);
++ exit(EXIT_SUCCESS);
++
++ case 'V':
++ fprintf(stderr, "%s\n", PROGRAM_VERSION);
++ exit(EXIT_SUCCESS);
++
++ case ':':
++ return errmsg("parameter is missing");
++
++ default:
++ fprintf(stderr, "Use -h for help\n");
++ return -1;
++ }
++ }
++
++ if (optind != argc - 2)
++ return errmsg("specify source and destination UBI device names as first 2 "
++ "parameters (use -h for help)");
++
++ args.srcNode = argv[optind];
++ args.destNode = argv[optind + 1];
++
++ return 0;
++}
++
++static int ubi_write(int fd, const void *buf, int len)
++{
++ int ret;
++
++ while (len) {
++ ret = write(fd, buf, len);
++ if (ret <= 0) {
++ if (errno == EINTR) {
++ warnmsg("do not interrupt me!");
++ continue;
++ }
++ return errmsg("error %d: cannot write %d bytes to volume \"%s\"",
++ errno, len, args.destNode);
++ }
++
++ len -= ret;
++ buf += ret;
++ }
++
++ return 0;
++}
++
++static int copy_volume(libubi_t libubi, struct ubi_vol_info *src_vol_info, struct ubi_vol_info *dest_vol_info)
++{
++ int err, dest_fd, src_fd;
++ long long bytes;
++ char *buf;
++ int ret = -1;
++
++ if (src_vol_info->leb_size != dest_vol_info->leb_size) {
++ return errmsg("source and destination volumes have different LEB sizes");
++ } else if (src_vol_info->data_bytes > dest_vol_info->rsvd_bytes) {
++ return errmsg("source volume \"%s\" too large for destination volume \"%s\" (%lld > %lld)", args.srcNode, args.destNode, src_vol_info->data_bytes, dest_vol_info->rsvd_bytes);
++ }
++
++ buf = malloc(dest_vol_info->leb_size);
++ if (!buf)
++ return errmsg("cannot allocate %d bytes of memory", dest_vol_info->leb_size);
++
++ bytes = src_vol_info->data_bytes;
++
++ src_fd = open(args.srcNode, O_RDONLY);
++ if (src_fd == -1) {
++ errmsg("cannot open source UBI volume \"%s\"", args.srcNode);
++ goto out_free;
++ }
++
++ dest_fd = open(args.destNode, O_RDWR);
++ if (dest_fd == -1) {
++ errmsg("cannot open destination UBI volume \"%s\"", args.destNode);
++ goto out_close1;
++ }
++
++ err = ubi_update_start(libubi, dest_fd, bytes);
++ if (err) {
++ errmsg("cannot start volume \"%s\" update", args.destNode);
++ goto out_close;
++ }
++
++ while (bytes) {
++ int ret, to_copy = dest_vol_info->leb_size;
++
++ if (to_copy > bytes)
++ to_copy = bytes;
++
++ ret = read(src_fd, buf, to_copy);
++ if (ret <= 0) {
++ if (errno == EINTR) {
++ warnmsg("do not interrupt me!");
++ continue;
++ } else {
++ errmsg("cannot read %d bytes from \"%s\"",
++ to_copy, args.srcNode);
++ goto out_close;
++ }
++ }
++
++ err = ubi_write(dest_fd, buf, ret);
++ if (err)
++ {
++ errmsg("cannot write %d bytes to \"%s\"", ret, args.destNode);
++ goto out_close;
++ }
++ bytes -= ret;
++
++ fprintf(stdout, "\r" PROGRAM_NAME ": copied %lli%%", (100 - (100 * bytes / src_vol_info->data_bytes)));
++ fflush(stdout);
++ }
++ fprintf(stdout, "\r" PROGRAM_NAME ": finished copying ubi%d:%s to ubi%d:%s\n", src_vol_info->dev_num, src_vol_info->name, dest_vol_info->dev_num, dest_vol_info->name);
++ ret = 0;
++
++out_close:
++ close(dest_fd);
++out_close1:
++ close(src_fd);
++out_free:
++ free(buf);
++ return ret;
++}
++
++int main(int argc, char * const argv[])
++{
++ int err;
++ libubi_t libubi;
++ struct ubi_vol_info src_vol_info, dest_vol_info;
++ int ret = -1;
++
++ err = parse_opt(argc, argv);
++ if (err)
++ return -1;
++
++ libubi = libubi_open();
++ if (!libubi) {
++ if (errno == 0)
++ return errmsg("UBI is not present in the system");
++ else
++ return errmsg("cannot open libubi");
++ }
++
++ err = ubi_probe_node(libubi, args.srcNode);
++ if (err == 1) {
++ errmsg("Source node \"%s\" is a UBI device node, not a UBI volume node",
++ args.srcNode);
++ goto out_libubi;
++ } else if (err < 0) {
++ if (errno == ENODEV)
++ errmsg("Source node \"%s\" is not a UBI volume node", args.srcNode);
++ else
++ errmsg("error while probing source node \"%s\"", args.srcNode);
++ goto out_libubi;
++ }
++
++ err = ubi_probe_node(libubi, args.destNode);
++ if (err == 1) {
++ errmsg("Destination node \"%s\" is a UBI device node, not a UBI volume node",
++ args.destNode);
++ goto out_libubi;
++ } else if (err < 0) {
++ if (errno == ENODEV)
++ errmsg("Destination node \"%s\" is not a UBI volume node", args.destNode);
++ else
++ errmsg("error while probing destination node \"%s\"", args.destNode);
++ goto out_libubi;
++ }
++
++ err = ubi_get_vol_info(libubi, args.srcNode, &src_vol_info);
++ if (err) {
++ errmsg("cannot get information about source UBI volume \"%s\"",
++ args.srcNode);
++ goto out_libubi;
++ }
++
++ err = ubi_get_vol_info(libubi, args.destNode, &dest_vol_info);
++ if (err) {
++ errmsg("cannot get information about destination UBI volume \"%s\"",
++ args.destNode);
++ goto out_libubi;
++ }
++
++ if ((src_vol_info.dev_num == dest_vol_info.dev_num) && (src_vol_info.vol_id == dest_vol_info.vol_id)) {
++ errmsg("Source and destination nodes \"%s\" and \"%s\" point to same volume", args.srcNode, args.destNode);
++ goto out_libubi;
++ }
++
++ err = copy_volume(libubi, &src_vol_info, &dest_vol_info);
++ if (err)
++ {
++ errmsg("copy failed, err=%d", args.srcNode, args.destNode, err);
++ goto out_libubi;
++ }
++
++ ret = 0;
++
++out_libubi:
++ libubi_close(libubi);
++ return ret;
++}
+diff -Naur a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c
+--- a/ubi-utils/src/ubiupdatevol.c 2010-01-15 09:12:24.000000000 -0800
++++ b/ubi-utils/src/ubiupdatevol.c 2014-12-19 15:12:23.549163250 -0800
+@@ -283,10 +283,9 @@
+ libubi = libubi_open();
+ if (!libubi) {
+ if (errno == 0)
+- errmsg("UBI is not present in the system");
++ return errmsg("UBI is not present in the system");
+ else
+- sys_errmsg("cannot open libubi");
+- goto out_libubi;
++ return errmsg("cannot open libubi");
+ }
+
+ err = ubi_probe_node(libubi, args.node);
diff --git a/mtd-utils.patches/mtd-utils-56.description b/mtd-utils.patches/mtd-utils-56.description
new file mode 100644
index 0000000..e51010a
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-56.description
@@ -0,0 +1 @@
+This patch removes parallelism from common.mk. When parallelizing the build there was a dependency between mkfs.ubifs and ubi-utils, so run this library serially.
diff --git a/mtd-utils.patches/mtd-utils-56.patch b/mtd-utils.patches/mtd-utils-56.patch
new file mode 100644
index 0000000..7d4a4e3
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-56.patch
@@ -0,0 +1,9 @@
+diff -Naur a/common.mk b/common.mk
+--- a/common.mk 2015-02-19 14:37:51.171316542 -0800
++++ b/common.mk 2015-02-19 14:31:21.235607345 -0800
+@@ -1,3 +1,5 @@
++.NOTPARALLEL:
++
+ CC := $(CROSS)gcc
+ AR := $(CROSS)ar
+ RANLIB := $(CROSS)ranlib
diff --git a/mtd-utils.patches/mtd-utils-57.description b/mtd-utils.patches/mtd-utils-57.description
new file mode 100644
index 0000000..2c9d9a5
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-57.description
@@ -0,0 +1 @@
+Backport of upstream commit 96a5eeaf754c34bf684b6957ab8479d8557b3e95
diff --git a/mtd-utils.patches/mtd-utils-57.patch b/mtd-utils.patches/mtd-utils-57.patch
new file mode 100644
index 0000000..02a2b2e
--- /dev/null
+++ b/mtd-utils.patches/mtd-utils-57.patch
@@ -0,0 +1,124 @@
+diff -Naur mtd-utils-1.3.1.pristine/nanddump.c mtd-utils-1.3.1.N/nanddump.c
+--- mtd-utils-1.3.1.pristine/nanddump.c 2015-08-24 17:31:43.707872733 -0700
++++ mtd-utils-1.3.1.N/nanddump.c 2015-08-24 17:52:22.608267037 -0700
+@@ -178,20 +178,14 @@
+ }
+
+ /*
+- * Buffers for reading data from flash
+- */
+-static unsigned char readbuf[4096];
+-static unsigned char oobbuf[128];
+-
+-/*
+ * Main program
+ */
+ int main(int argc, char * const argv[])
+ {
+ unsigned long ofs, end_addr = 0;
+ unsigned long long blockstart = 1;
+- int ret, i, fd, ofd, bs, badblock = 0;
+- struct mtd_oob_buf oob = {0, 16, oobbuf};
++ int ret, i, fd, ofd = 0, bs, badblock = 0;
++ struct mtd_oob_buf oob;
+ mtd_info_t meminfo;
+ char pretty_buf[80];
+ int oobinfochanged = 0 ;
+@@ -199,6 +193,7 @@
+ struct mtd_ecc_stats stat1, stat2;
+ bool eccstats = false;
+ bool isempty;
++ unsigned char *readbuf = NULL, *oobbuf = NULL;
+
+ process_options(argc, argv);
+
+@@ -215,18 +210,17 @@
+ exit (EXIT_FAILURE);
+ }
+
+- /* Make sure device page sizes are valid */
+- if (!(meminfo.oobsize == 128 && meminfo.writesize == 4096) &&
+- !(meminfo.oobsize == 64 && meminfo.writesize == 2048) &&
+- !(meminfo.oobsize == 32 && meminfo.writesize == 1024) &&
+- !(meminfo.oobsize == 16 && meminfo.writesize == 512) &&
+- !(meminfo.oobsize == 8 && meminfo.writesize == 256)) {
+- fprintf(stderr, "Unknown flash (not normal NAND)\n");
+- close(fd);
+- exit(EXIT_FAILURE);
+- }
+- /* Read the real oob length */
++ /* Allocate buffers */
++ oobbuf = malloc(sizeof(oobbuf) * meminfo.oobsize);
++ readbuf = malloc(sizeof(readbuf) * meminfo.writesize);
++
++ if (oobbuf == NULL || readbuf == NULL)
++ goto closeall;
++
++ /* Fill in oob info */
++ oob.start = 0;
+ oob.length = meminfo.oobsize;
++ oob.ptr = oobbuf;
+
+ if (noecc) {
+ ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW);
+@@ -237,20 +231,17 @@
+ case ENOTTY:
+ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMGETOOBSEL");
+- close (fd);
+- exit (EXIT_FAILURE);
++ goto closeall;
+ }
+ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+- close (fd);
+- exit (EXIT_FAILURE);
++ goto closeall;
+ }
+ oobinfochanged = 1;
+ break;
+ default:
+ perror ("MTDFILEMODE");
+- close (fd);
+- exit (EXIT_FAILURE);
++ goto closeall;
+ }
+ }
+ } else {
+@@ -274,8 +265,7 @@
+ ofd = STDOUT_FILENO;
+ } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) {
+ perror (dumpfile);
+- close(fd);
+- exit(EXIT_FAILURE);
++ goto closeall;
+ }
+
+ /* Initialize start/end addresses and block size */
+@@ -420,14 +410,14 @@
+ if (oobinfochanged == 1) {
+ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+- close(fd);
+- close(ofd);
+- return EXIT_FAILURE;
++ goto closeall;
+ }
+ }
+- /* Close the output file and MTD device */
++ /* Close the output file and MTD device, free memory */
+ close(fd);
+ close(ofd);
++ free(oobbuf);
++ free(readbuf);
+
+ /* Exit happy */
+ return EXIT_SUCCESS;
+@@ -441,5 +431,7 @@
+ }
+ close(fd);
+ close(ofd);
++ free(oobbuf);
++ free(readbuf);
+ exit(EXIT_FAILURE);
+ }
diff --git a/mtd-utils.tar.bz2 b/mtd-utils.tar.bz2
new file mode 100644
index 0000000..85f2173
--- /dev/null
+++ b/mtd-utils.tar.bz2
Binary files differ
diff --git a/mtd-utils.url b/mtd-utils.url
new file mode 100644
index 0000000..1c6ba0b
--- /dev/null
+++ b/mtd-utils.url
@@ -0,0 +1 @@
+ftp://ftp.linux-mtd.infradead.org/pub/mtd-utils/mtd-utils-1.3.1.tar.bz2
diff --git a/mtd-utils.version b/mtd-utils.version
new file mode 100644
index 0000000..3a3cd8c
--- /dev/null
+++ b/mtd-utils.version
@@ -0,0 +1 @@
+1.3.1