mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-03-17 12:18:54 +02:00
purge out mtd-utils
This commit is contained in:
parent
0fd039053c
commit
c16fafb1df
@ -1,340 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
function mkftl () {
|
||||
mknod /dev/ftl$1 b 44 $2
|
||||
for a in `seq 1 15`; do
|
||||
mknod /dev/ftl$1$a b 44 `expr $2 + $a`
|
||||
done
|
||||
}
|
||||
function mknftl () {
|
||||
mknod /dev/nftl$1 b 93 $2
|
||||
for a in `seq 1 15`; do
|
||||
mknod /dev/nftl$1$a b 93 `expr $2 + $a`
|
||||
done
|
||||
}
|
||||
function mkrfd () {
|
||||
mknod /dev/rfd$1 b 256 $2
|
||||
for a in `seq 1 15`; do
|
||||
mknod /dev/rfd$1$a b 256 `expr $2 + $a`
|
||||
done
|
||||
}
|
||||
function mkinftl () {
|
||||
mknod /dev/inftl$1 b 96 $2
|
||||
for a in `seq 1 15`; do
|
||||
mknod /dev/inftl$1$a b 96 `expr $2 + $a`
|
||||
done
|
||||
}
|
||||
|
||||
M=0
|
||||
for C in a b c d e f g h i j k l m n o p; do
|
||||
mkftl $C $M
|
||||
mknftl $C $M
|
||||
mkrfd $C $M
|
||||
mkinftl $C $M
|
||||
let M=M+16
|
||||
done
|
||||
|
||||
for a in `seq 0 16` ; do
|
||||
mknod /dev/mtd$a c 90 `expr $a + $a`
|
||||
mknod /dev/mtdr$a c 90 `expr $a + $a + 1`
|
||||
mknod /dev/mtdblock$a b 31 $a
|
||||
done
|
||||
|
@ -1,102 +0,0 @@
|
||||
|
||||
# -*- sh -*-
|
||||
|
||||
OPTFLAGS := -O2 -Wall
|
||||
SBINDIR=/usr/sbin
|
||||
MANDIR=/usr/share/man
|
||||
INCLUDEDIR=/usr/include
|
||||
CROSS=mipsel-linux-
|
||||
CC := $(CROSS)gcc
|
||||
CFLAGS := -I./include $(OPTFLAGS)
|
||||
|
||||
ifeq ($(origin CROSS),undefined)
|
||||
BUILDDIR := .
|
||||
else
|
||||
# Remove the trailing slash to make the directory name
|
||||
BUILDDIR := .#$(CROSS:-=)
|
||||
endif
|
||||
|
||||
ifeq ($(WITHOUT_XATTR), 1)
|
||||
CFLAGS += -DWITHOUT_XATTR
|
||||
endif
|
||||
|
||||
#RAWTARGETS = 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
|
||||
|
||||
RAWTARGETS = flash_erase flash_eraseall nanddump nanddump_vfat \
|
||||
flash_info \
|
||||
flash_otp_info flash_otp_dump nandwrite nandwrite_mlc \
|
||||
nandtest \
|
||||
sumtool #jffs2reader
|
||||
|
||||
TARGETS = $(foreach target,$(RAWTARGETS),$(BUILDDIR)/$(target))
|
||||
|
||||
SYMLINKS =
|
||||
|
||||
%: %.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -g -o $@ $^
|
||||
|
||||
$(BUILDDIR)/%.o: %.c
|
||||
mkdir -p $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,$(BUILDDIR)/.$(<F).dep
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
all: $(TARGETS)
|
||||
make -C $(BUILDDIR)/ubi-utils
|
||||
|
||||
IGNORE=${wildcard $(BUILDDIR)/.*.c.dep}
|
||||
-include ${IGNORE}
|
||||
|
||||
clean:
|
||||
rm -f $(BUILDDIR)/*.o $(TARGETS) $(BUILDDIR)/.*.c.dep $(SYMLINKS)
|
||||
if [ "$(BUILDDIR)x" != ".x" ]; then rm -rf $(BUILDDIR); fi
|
||||
make -C $(BUILDDIR)/ubi-utils clean
|
||||
|
||||
$(SYMLINKS):
|
||||
ln -sf ../fs/jffs2/$@ $@
|
||||
|
||||
$(BUILDDIR)/mkfs.jffs2: $(BUILDDIR)/crc32.o \
|
||||
$(BUILDDIR)/compr_rtime.o \
|
||||
$(BUILDDIR)/mkfs.jffs2.o \
|
||||
$(BUILDDIR)/compr_zlib.o \
|
||||
$(BUILDDIR)/compr_lzo.o \
|
||||
$(BUILDDIR)/compr.o \
|
||||
$(BUILDDIR)/rbtree.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^ -lz -llzo2
|
||||
|
||||
$(BUILDDIR)/flash_eraseall: $(BUILDDIR)/crc32.o $(BUILDDIR)/flash_eraseall.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(BUILDDIR)/jffs2reader: $(BUILDDIR)/jffs2reader.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^ -lz
|
||||
|
||||
$(BUILDDIR)/jffs2dump: $(BUILDDIR)/jffs2dump.o $(BUILDDIR)/crc32.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(BUILDDIR)/sumtool: $(BUILDDIR)/sumtool.o $(BUILDDIR)/crc32.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(BUILDDIR)/serve_image: $(BUILDDIR)/serve_image.o $(BUILDDIR)/crc32.o $(BUILDDIR)/fec.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(BUILDDIR)/recv_image: $(BUILDDIR)/recv_image.o $(BUILDDIR)/crc32.o $(BUILDDIR)/fec.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
$(BUILDDIR)/fectest: $(BUILDDIR)/fectest.o $(BUILDDIR)/crc32.o $(BUILDDIR)/fec.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
|
||||
|
||||
install: ${TARGETS}
|
||||
mkdir -p ${DESTDIR}/${SBINDIR}
|
||||
install -m0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
|
||||
mkdir -p ${DESTDIR}/${MANDIR}/man1
|
||||
gzip -9c mkfs.jffs2.1 > ${DESTDIR}/${MANDIR}/man1/mkfs.jffs2.1.gz
|
||||
make -C $(BUILDDIR)/ubi-utils install
|
@ -1,538 +0,0 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory
|
||||
* in the jffs2 directory.
|
||||
*/
|
||||
|
||||
#include "compr.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/jffs2.h>
|
||||
|
||||
#define FAVOUR_LZO_PERCENT 80
|
||||
|
||||
extern int page_size;
|
||||
|
||||
/* LIST IMPLEMENTATION (from linux/list.h) */
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void __list_del(struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = (void *) 0;
|
||||
entry->prev = (void *) 0;
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
|
||||
/* Available compressors are on this list */
|
||||
static LIST_HEAD(jffs2_compressor_list);
|
||||
|
||||
/* Actual compression mode */
|
||||
static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
|
||||
|
||||
void jffs2_set_compression_mode(int mode)
|
||||
{
|
||||
jffs2_compression_mode = mode;
|
||||
}
|
||||
|
||||
int jffs2_get_compression_mode(void)
|
||||
{
|
||||
return jffs2_compression_mode;
|
||||
}
|
||||
|
||||
/* Statistics for blocks stored without compression */
|
||||
static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
|
||||
|
||||
/* Compression test stuffs */
|
||||
|
||||
static int jffs2_compression_check = 0;
|
||||
|
||||
static unsigned char *jffs2_compression_check_buf = NULL;
|
||||
|
||||
void jffs2_compression_check_set(int yesno)
|
||||
{
|
||||
jffs2_compression_check = yesno;
|
||||
}
|
||||
|
||||
int jffs2_compression_check_get(void)
|
||||
{
|
||||
return jffs2_compression_check;
|
||||
}
|
||||
|
||||
static int jffs2_error_cnt = 0;
|
||||
|
||||
int jffs2_compression_check_errorcnt_get(void)
|
||||
{
|
||||
return jffs2_error_cnt;
|
||||
}
|
||||
|
||||
#define JFFS2_BUFFER_FILL 0x55
|
||||
|
||||
/* Called before compression (if compression_check is setted) to prepare
|
||||
the buffer for buffer overflow test */
|
||||
static void jffs2_decompression_test_prepare(unsigned char *buf, int size)
|
||||
{
|
||||
memset(buf,JFFS2_BUFFER_FILL,size+1);
|
||||
}
|
||||
|
||||
/* Called after compression (if compression_check is setted) to test the result */
|
||||
static void jffs2_decompression_test(struct jffs2_compressor *compr,
|
||||
unsigned char *data_in, unsigned char *output_buf,
|
||||
uint32_t cdatalen, uint32_t datalen, uint32_t buf_size)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
/* buffer overflow test */
|
||||
for (i=buf_size;i>cdatalen;i--) {
|
||||
if (output_buf[i]!=JFFS2_BUFFER_FILL) {
|
||||
fprintf(stderr,"COMPR_ERROR: buffer overflow at %s. "
|
||||
"(bs=%d csize=%d b[%d]=%d)\n", compr->name,
|
||||
buf_size, cdatalen, i, (int)(output_buf[i]));
|
||||
jffs2_error_cnt++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* allocing temporary buffer for decompression */
|
||||
if (!jffs2_compression_check_buf) {
|
||||
jffs2_compression_check_buf = malloc(page_size);
|
||||
if (!jffs2_compression_check_buf) {
|
||||
fprintf(stderr,"No memory for buffer allocation. Compression check disabled.\n");
|
||||
jffs2_compression_check = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* decompressing */
|
||||
if (!compr->decompress) {
|
||||
fprintf(stderr,"JFFS2 compression check: there is no decompress function at %s.\n", compr->name);
|
||||
jffs2_error_cnt++;
|
||||
return;
|
||||
}
|
||||
if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen,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;
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in the
|
||||
* jffs2 directory.
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_COMPR_H__
|
||||
#define __JFFS2_COMPR_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "linux/jffs2.h"
|
||||
|
||||
#define CONFIG_JFFS2_ZLIB
|
||||
#define CONFIG_JFFS2_RTIME
|
||||
#define CONFIG_JFFS2_LZO
|
||||
|
||||
#define JFFS2_RUBINMIPS_PRIORITY 10
|
||||
#define JFFS2_DYNRUBIN_PRIORITY 20
|
||||
#define JFFS2_RTIME_PRIORITY 50
|
||||
#define JFFS2_ZLIB_PRIORITY 60
|
||||
#define JFFS2_LZO_PRIORITY 80
|
||||
|
||||
#define JFFS2_COMPR_MODE_NONE 0
|
||||
#define JFFS2_COMPR_MODE_PRIORITY 1
|
||||
#define JFFS2_COMPR_MODE_SIZE 2
|
||||
#define JFFS2_COMPR_MODE_FAVOURLZO 3
|
||||
|
||||
#define kmalloc(a,b) malloc(a)
|
||||
#define kfree(a) free(a)
|
||||
#ifndef GFP_KERNEL
|
||||
#define GFP_KERNEL 0
|
||||
#endif
|
||||
|
||||
#define vmalloc(a) malloc(a)
|
||||
#define vfree(a) free(a)
|
||||
|
||||
#define printk(...) fprintf(stderr,__VA_ARGS__)
|
||||
|
||||
#define KERN_EMERG
|
||||
#define KERN_ALERT
|
||||
#define KERN_CRIT
|
||||
#define KERN_ERR
|
||||
#define KERN_WARNING
|
||||
#define KERN_NOTICE
|
||||
#define KERN_INFO
|
||||
#define KERN_DEBUG
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
void jffs2_set_compression_mode(int mode);
|
||||
int jffs2_get_compression_mode(void);
|
||||
int jffs2_set_compression_mode_name(const char *mode_name);
|
||||
|
||||
int jffs2_enable_compressor_name(const char *name);
|
||||
int jffs2_disable_compressor_name(const char *name);
|
||||
|
||||
int jffs2_set_compressor_priority(const char *name, int priority);
|
||||
|
||||
struct jffs2_compressor {
|
||||
struct list_head list;
|
||||
int priority; /* used by prirority comr. mode */
|
||||
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__ */
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* JFFS2 LZO Compression Interface.
|
||||
*
|
||||
* Copyright (C) 2007 Nokia Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Richard Purdie <rpurdie@openedhand.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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)
|
||||
{
|
||||
uint32_t 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;
|
||||
uint32_t 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);
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001-2003 Red Hat, Inc.
|
||||
*
|
||||
* Created by Arjan van de Ven <arjanv@redhat.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* Very simple lz77-ish encoder.
|
||||
*
|
||||
* Theory of operation: Both encoder and decoder have a list of "last
|
||||
* occurrences" for every possible source-value; after sending the
|
||||
* first source-byte, the second byte indicated the "run" length of
|
||||
* matches
|
||||
*
|
||||
* The algorithm is intended to only send "whole bytes", no bit-messing.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "compr.h"
|
||||
|
||||
/* _compress returns the compressed size, -1 if bigger */
|
||||
static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
|
||||
uint32_t *sourcelen, uint32_t *dstlen, 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);
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2001 Red Hat, Inc.
|
||||
*
|
||||
* Created by David Woodhouse <dwmw2@cambridge.redhat.com>
|
||||
*
|
||||
* The original JFFS, from which the design for JFFS2 was derived,
|
||||
* was designed and implemented by Axis Communications AB.
|
||||
*
|
||||
* The contents of this file are subject to the Red Hat eCos Public
|
||||
* License Version 1.1 (the "Licence"); you may not use this file
|
||||
* except in compliance with the Licence. You may obtain a copy of
|
||||
* the Licence at http://www.redhat.com/
|
||||
*
|
||||
* Software distributed under the Licence is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the Licence for the specific language governing rights and
|
||||
* limitations under the Licence.
|
||||
*
|
||||
* The Original Code is JFFS2 - Journalling Flash File System, version 2
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU General Public License version 2 (the "GPL"), in
|
||||
* which case the provisions of the GPL are applicable instead of the
|
||||
* above. If you wish to allow the use of your version of this file
|
||||
* only under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the RHEPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice and
|
||||
* other provisions required by the GPL. If you do not delete the
|
||||
* provisions above, a recipient may use your version of this file
|
||||
* under either the RHEPL or the GPL.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
#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
|
@ -1,129 +0,0 @@
|
||||
# This is a sample device table file for use with mkfs.jffs2. You can
|
||||
# do all sorts of interesting things with a device table file. For
|
||||
# example, if you want to adjust the permissions on a particular file
|
||||
# you can just add an entry like:
|
||||
# /sbin/foobar f 2755 0 0 - - - - -
|
||||
# and (assuming the file /sbin/foobar exists) it will be made setuid
|
||||
# root (regardless of what its permissions are on the host filesystem.
|
||||
#
|
||||
# Device table entries take the form of:
|
||||
# <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
|
||||
# where name is the file name, type can be one of:
|
||||
# f A regular file
|
||||
# d Directory
|
||||
# c Character special device file
|
||||
# b Block special device file
|
||||
# p Fifo (named pipe)
|
||||
# uid is the user id for the target file, gid is the group id for the
|
||||
# target file. The rest of the entried apply only to device special
|
||||
# file.
|
||||
|
||||
# When building a target filesystem, it is desirable to not have to
|
||||
# become root and then run 'mknod' a thousand times. Using a device
|
||||
# table you can create device nodes and directories "on the fly".
|
||||
# Furthermore, you can use a single table entry to create a many device
|
||||
# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15]
|
||||
# I could just use the following two table entries:
|
||||
# /dev/hda b 640 0 0 3 0 0 0 -
|
||||
# /dev/hda b 640 0 0 3 1 1 1 15
|
||||
#
|
||||
# Have fun
|
||||
# -Erik Andersen <andersen@codepoet.org>
|
||||
#
|
||||
|
||||
#<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
|
||||
/dev d 755 0 0 - - - - -
|
||||
/dev/mem c 640 0 0 1 1 0 0 -
|
||||
/dev/kmem c 640 0 0 1 2 0 0 -
|
||||
/dev/null c 640 0 0 1 3 0 0 -
|
||||
/dev/zero c 640 0 0 1 5 0 0 -
|
||||
/dev/random c 640 0 0 1 8 0 0 -
|
||||
/dev/urandom c 640 0 0 1 9 0 0 -
|
||||
/dev/tty c 666 0 0 5 0 0 0 -
|
||||
/dev/tty c 666 0 0 4 0 0 1 6
|
||||
/dev/console c 640 0 0 5 1 0 0 -
|
||||
/dev/ram b 640 0 0 1 1 0 0 -
|
||||
/dev/ram b 640 0 0 1 0 0 1 4
|
||||
/dev/loop b 640 0 0 7 0 0 1 2
|
||||
/dev/ptmx c 666 0 0 5 2 0 0 -
|
||||
#/dev/ttyS c 640 0 0 4 64 0 1 4
|
||||
#/dev/psaux c 640 0 0 10 1 0 0 -
|
||||
#/dev/rtc c 640 0 0 10 135 0 0 -
|
||||
|
||||
# Adjust permissions on some normal files
|
||||
#/etc/shadow f 600 0 0 - - - - -
|
||||
#/bin/tinylogin f 4755 0 0 - - - - -
|
||||
|
||||
# User-mode Linux stuff
|
||||
/dev/ubda b 640 0 0 98 0 0 0 -
|
||||
/dev/ubda b 640 0 0 98 1 1 1 15
|
||||
|
||||
# IDE Devices
|
||||
/dev/hda b 640 0 0 3 0 0 0 -
|
||||
/dev/hda b 640 0 0 3 1 1 1 15
|
||||
/dev/hdb b 640 0 0 3 64 0 0 -
|
||||
/dev/hdb b 640 0 0 3 65 1 1 15
|
||||
#/dev/hdc b 640 0 0 22 0 0 0 -
|
||||
#/dev/hdc b 640 0 0 22 1 1 1 15
|
||||
#/dev/hdd b 640 0 0 22 64 0 0 -
|
||||
#/dev/hdd b 640 0 0 22 65 1 1 15
|
||||
#/dev/hde b 640 0 0 33 0 0 0 -
|
||||
#/dev/hde b 640 0 0 33 1 1 1 15
|
||||
#/dev/hdf b 640 0 0 33 64 0 0 -
|
||||
#/dev/hdf b 640 0 0 33 65 1 1 15
|
||||
#/dev/hdg b 640 0 0 34 0 0 0 -
|
||||
#/dev/hdg b 640 0 0 34 1 1 1 15
|
||||
#/dev/hdh b 640 0 0 34 64 0 0 -
|
||||
#/dev/hdh b 640 0 0 34 65 1 1 15
|
||||
|
||||
# SCSI Devices
|
||||
#/dev/sda b 640 0 0 8 0 0 0 -
|
||||
#/dev/sda b 640 0 0 8 1 1 1 15
|
||||
#/dev/sdb b 640 0 0 8 16 0 0 -
|
||||
#/dev/sdb b 640 0 0 8 17 1 1 15
|
||||
#/dev/sdc b 640 0 0 8 32 0 0 -
|
||||
#/dev/sdc b 640 0 0 8 33 1 1 15
|
||||
#/dev/sdd b 640 0 0 8 48 0 0 -
|
||||
#/dev/sdd b 640 0 0 8 49 1 1 15
|
||||
#/dev/sde b 640 0 0 8 64 0 0 -
|
||||
#/dev/sde b 640 0 0 8 65 1 1 15
|
||||
#/dev/sdf b 640 0 0 8 80 0 0 -
|
||||
#/dev/sdf b 640 0 0 8 81 1 1 15
|
||||
#/dev/sdg b 640 0 0 8 96 0 0 -
|
||||
#/dev/sdg b 640 0 0 8 97 1 1 15
|
||||
#/dev/sdh b 640 0 0 8 112 0 0 -
|
||||
#/dev/sdh b 640 0 0 8 113 1 1 15
|
||||
#/dev/sg c 640 0 0 21 0 0 1 15
|
||||
#/dev/scd b 640 0 0 11 0 0 1 15
|
||||
#/dev/st c 640 0 0 9 0 0 1 8
|
||||
#/dev/nst c 640 0 0 9 128 0 1 8
|
||||
#/dev/st c 640 0 0 9 32 1 1 4
|
||||
#/dev/st c 640 0 0 9 64 1 1 4
|
||||
#/dev/st c 640 0 0 9 96 1 1 4
|
||||
|
||||
# Floppy disk devices
|
||||
#/dev/fd b 640 0 0 2 0 0 1 2
|
||||
#/dev/fd0d360 b 640 0 0 2 4 0 0 -
|
||||
#/dev/fd1d360 b 640 0 0 2 5 0 0 -
|
||||
#/dev/fd0h1200 b 640 0 0 2 8 0 0 -
|
||||
#/dev/fd1h1200 b 640 0 0 2 9 0 0 -
|
||||
#/dev/fd0u1440 b 640 0 0 2 28 0 0 -
|
||||
#/dev/fd1u1440 b 640 0 0 2 29 0 0 -
|
||||
#/dev/fd0u2880 b 640 0 0 2 32 0 0 -
|
||||
#/dev/fd1u2880 b 640 0 0 2 33 0 0 -
|
||||
|
||||
# All the proprietary cdrom devices in the world
|
||||
#/dev/aztcd b 640 0 0 29 0 0 0 -
|
||||
#/dev/bpcd b 640 0 0 41 0 0 0 -
|
||||
#/dev/capi20 c 640 0 0 68 0 0 1 2
|
||||
#/dev/cdu31a b 640 0 0 15 0 0 0 -
|
||||
#/dev/cdu535 b 640 0 0 24 0 0 0 -
|
||||
#/dev/cm206cd b 640 0 0 32 0 0 0 -
|
||||
#/dev/sjcd b 640 0 0 18 0 0 0 -
|
||||
#/dev/sonycd b 640 0 0 15 0 0 0 -
|
||||
#/dev/gscd b 640 0 0 16 0 0 0 -
|
||||
#/dev/sbpcd b 640 0 0 25 0 0 0 -
|
||||
#/dev/sbpcd b 640 0 0 25 0 0 1 4
|
||||
#/dev/mcd b 640 0 0 23 0 0 0 -
|
||||
#/dev/optcd b 640 0 0 17 0 0 0 -
|
||||
|
@ -1,148 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,317 +0,0 @@
|
||||
/*
|
||||
* docfdisk.c: Modify INFTL partition tables
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
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.
|
||||
|
||||
---------------------------
|
||||
|
||||
---------------------------
|
@ -1,917 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* compatibility stuff
|
||||
*/
|
||||
#ifdef MSDOS /* but also for others, e.g. sun... */
|
||||
#define NEED_BCOPY
|
||||
#define bcmp(a,b,n) memcmp(a,b,n)
|
||||
#endif
|
||||
|
||||
#ifdef NEED_BCOPY
|
||||
#define bcopy(s, d, siz) memcpy((d), (s), (siz))
|
||||
#define bzero(d, siz) memset((d), '\0', (siz))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
bzero(id_row, 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 (bcmp(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
|
||||
*/
|
||||
bzero(retval->enc_matrix, 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)
|
||||
bcopy(src[index], fec, sz*sizeof(gf) ) ;
|
||||
else if (index < code->n) {
|
||||
p = &(code->enc_matrix[index*k] );
|
||||
bzero(fec, 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)
|
||||
bcopy(src + (index * sz), fec, sz*sizeof(gf) ) ;
|
||||
else if (index < code->n) {
|
||||
p = &(code->enc_matrix[index*k] );
|
||||
bzero(fec, 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) {
|
||||
bzero(p, k*sizeof(gf) );
|
||||
p[i] = 1 ;
|
||||
} else
|
||||
#endif
|
||||
if (index[i] < code->n )
|
||||
bcopy( &(code->enc_matrix[index[i]*k]), p, 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" );
|
||||
bzero(new_pkt[row], 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) {
|
||||
bcopy(new_pkt[row], 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 */
|
@ -1,92 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "mcast_image.h"
|
||||
#include "crc32.h"
|
||||
|
||||
#define ERASE_SIZE 131072
|
||||
//#define 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;
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* 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 0x%llx at offset 0x%llx",
|
||||
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 0x%llx at offset 0x%llx",
|
||||
erase.length, erase.start);
|
||||
fflush(stdout);
|
||||
|
||||
if(unlock != 0)
|
||||
{
|
||||
//Unlock the sector first.
|
||||
printf("\rPerforming Flash unlock at offset 0x%llx",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;
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
/* 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.1.1.1 $"
|
||||
|
||||
static const char *exe_name;
|
||||
static const char *mtd_device;
|
||||
static int quiet; /* true -- don't output progress */
|
||||
static int jffs2; // format for jffs2 usage
|
||||
|
||||
static void process_options (int argc, char *argv[]);
|
||||
static void display_help (void);
|
||||
static void display_version (void);
|
||||
static struct jffs2_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));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
|
||||
fprintf(stderr, "%s: %s: unable to get MTD device info\n", exe_name, mtd_device);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
erase.length = meminfo.erasesize;
|
||||
isNAND = meminfo.type == MTD_NANDFLASH ? 1 : 0;
|
||||
|
||||
if (jffs2) {
|
||||
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);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Check for autoplacement */
|
||||
if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
|
||||
/* Get the position of the free bytes */
|
||||
if (!oobinfo.oobfree[0][1]) {
|
||||
fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n");
|
||||
exit(1);
|
||||
}
|
||||
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) {
|
||||
unsigned long long offset = erase.start;
|
||||
int ret = ioctl(fd, MEMGETBADBLOCK, &offset);
|
||||
if (ret > 0) {
|
||||
if (!quiet)
|
||||
printf ("\nSkipping bad block at 0x%09llx\n", erase.start);
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
if (errno == EOPNOTSUPP) {
|
||||
bbtest = 0;
|
||||
if (isNAND) {
|
||||
fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
printf
|
||||
("\rErasing %d Kibyte @ %llx -- %2u %% complete.",
|
||||
meminfo.erasesize / 1024, erase.start,
|
||||
(unsigned long long)
|
||||
erase.start * 100 / meminfo.size);
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
if (ioctl(fd, MEMERASE, &erase) != 0) {
|
||||
fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* format for JFFS2 ? */
|
||||
if (!jffs2)
|
||||
continue;
|
||||
|
||||
/* write cleanmarker */
|
||||
if (isNAND) {
|
||||
struct mtd_oob_buf oob;
|
||||
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)
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void process_options (int argc, char *argv[])
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
exe_name = argv[0];
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "jq";
|
||||
static const struct option long_options[] = {
|
||||
{"help", no_argument, 0, 0},
|
||||
{"version", no_argument, 0, 0},
|
||||
{"jffs2", no_argument, 0, 'j'},
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"silent", no_argument, 0, 'q'},
|
||||
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int c = getopt_long(argc, argv, short_options,
|
||||
long_options, &option_index);
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
switch (option_index) {
|
||||
case 0:
|
||||
display_help();
|
||||
break;
|
||||
case 1:
|
||||
display_version();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
case 'j':
|
||||
jffs2 = 1;
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
fprintf(stderr, "%s: no MTD device specified\n", exe_name);
|
||||
error = 1;
|
||||
}
|
||||
if (error) {
|
||||
fprintf(stderr, "Try `%s --help' for more information.\n",
|
||||
exe_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mtd_device = argv[optind];
|
||||
}
|
||||
|
||||
|
||||
void display_help (void)
|
||||
{
|
||||
printf("Usage: %s [OPTION] MTD_DEVICE\n"
|
||||
"Erases all of the specified MTD device.\n"
|
||||
"\n"
|
||||
" -j, --jffs2 format the device for jffs2\n"
|
||||
" -q, --quiet don't display progress messages\n"
|
||||
" --silent same as --quiet\n"
|
||||
" --help display this help and exit\n"
|
||||
" --version output version information and exit\n",
|
||||
exe_name);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
void display_version (void)
|
||||
{
|
||||
printf(PROGRAM " " VERSION "\n"
|
||||
"\n"
|
||||
"Copyright (C) 2000 Arcom Control Systems Ltd\n"
|
||||
"\n"
|
||||
PROGRAM " comes with NO WARRANTY\n"
|
||||
"to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"You may redistribute copies of " PROGRAM "\n"
|
||||
"under the terms of the GNU General Public Licence.\n"
|
||||
"See the file `COPYING' for more information.\n");
|
||||
exit(0);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* FILE flash_lock.c
|
||||
*
|
||||
* This utility locks one or more sectors of flash device.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mtd/mtd-user.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
struct mtd_info_user mtdInfo;
|
||||
struct erase_info_user mtdLockInfo;
|
||||
int num_sectors;
|
||||
int ofs;
|
||||
|
||||
/*
|
||||
* Parse command line options
|
||||
*/
|
||||
if(argc != 4)
|
||||
{
|
||||
fprintf(stderr, "USAGE: %s <mtd device> <ofs in hex> <num of sectors in decimal or -1 for all sectors>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
else if(strncmp(argv[1], "/dev/mtd", 8) != 0)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if(fd < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not open mtd device: %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(ioctl(fd, MEMGETINFO, &mtdInfo))
|
||||
{
|
||||
fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
sscanf(argv[2], "%x",&ofs);
|
||||
sscanf(argv[3], "%d",&num_sectors);
|
||||
if(ofs > mtdInfo.size - mtdInfo.erasesize)
|
||||
{
|
||||
fprintf(stderr, "%x is beyond device size %x\n",ofs,(unsigned int)(mtdInfo.size - mtdInfo.erasesize));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (num_sectors == -1) {
|
||||
num_sectors = mtdInfo.size/mtdInfo.erasesize;
|
||||
}
|
||||
else {
|
||||
if(num_sectors > mtdInfo.size/mtdInfo.erasesize)
|
||||
{
|
||||
fprintf(stderr, "%d are too many sectors, device only has %d\n",num_sectors,(int)(mtdInfo.size/mtdInfo.erasesize));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
mtdLockInfo.start = ofs;
|
||||
mtdLockInfo.length = num_sectors * mtdInfo.erasesize;
|
||||
if(ioctl(fd, MEMLOCK, &mtdLockInfo))
|
||||
{
|
||||
fprintf(stderr, "Could not lock MTD device: %s\n", argv[1]);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* FILE flash_unlock.c
|
||||
*
|
||||
* This utility unlock all sectors of flash device.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mtd/mtd-user.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
struct mtd_info_user mtdInfo;
|
||||
struct erase_info_user mtdLockInfo;
|
||||
|
||||
/*
|
||||
* Parse command line options
|
||||
*/
|
||||
if(argc != 2)
|
||||
{
|
||||
fprintf(stderr, "USAGE: %s <mtd device>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
else if(strncmp(argv[1], "/dev/mtd", 8) != 0)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if(fd < 0)
|
||||
{
|
||||
fprintf(stderr, "Could not open mtd device: %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(ioctl(fd, MEMGETINFO, &mtdInfo))
|
||||
{
|
||||
fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mtdLockInfo.start = 0;
|
||||
mtdLockInfo.length = mtdInfo.size;
|
||||
if(ioctl(fd, MEMUNLOCK, &mtdLockInfo))
|
||||
{
|
||||
fprintf(stderr, "Could not unlock MTD device: %s\n", argv[1]);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,389 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2d3D, Inc.
|
||||
* Written by Abraham vd Merwe <abraham@2d3d.co.za>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Renamed to flashcp.c to avoid conflicts with fcp from fsh package
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of other contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
#include <getopt.h>
|
||||
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#define EXIT_FAILURE 1
|
||||
#define EXIT_SUCCESS 0
|
||||
|
||||
/* for debugging purposes only */
|
||||
#ifdef DEBUG
|
||||
#undef DEBUG
|
||||
#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); }
|
||||
#else
|
||||
#undef DEBUG
|
||||
#define DEBUG(fmt,args...)
|
||||
#endif
|
||||
|
||||
#define KB(x) ((x) / 1024)
|
||||
#define PERCENTAGE(x,total) (((x) * 100) / (total))
|
||||
|
||||
/* size of read/write buffer */
|
||||
#define BUFSIZE (10 * 1024)
|
||||
|
||||
/* cmd-line flags */
|
||||
#define FLAG_NONE 0x00
|
||||
#define FLAG_VERBOSE 0x01
|
||||
#define FLAG_HELP 0x02
|
||||
#define FLAG_FILENAME 0x04
|
||||
#define FLAG_DEVICE 0x08
|
||||
|
||||
/* error levels */
|
||||
#define LOG_NORMAL 1
|
||||
#define LOG_ERROR 2
|
||||
|
||||
static void log_printf (int level,const char *fmt, ...)
|
||||
{
|
||||
FILE *fp = level == LOG_NORMAL ? stdout : stderr;
|
||||
va_list ap;
|
||||
va_start (ap,fmt);
|
||||
vfprintf (fp,fmt,ap);
|
||||
va_end (ap);
|
||||
fflush (fp);
|
||||
}
|
||||
|
||||
static void showusage (const char *progname,bool error)
|
||||
{
|
||||
int level = error ? LOG_ERROR : LOG_NORMAL;
|
||||
|
||||
log_printf (level,
|
||||
"\n"
|
||||
"Flash Copy - Written by Abraham van der Merwe <abraham@2d3d.co.za>\n"
|
||||
"\n"
|
||||
"usage: %s [ -v | --verbose ] <filename> <device>\n"
|
||||
" %s -h | --help\n"
|
||||
"\n"
|
||||
" -h | --help Show this help message\n"
|
||||
" -v | --verbose Show progress reports\n"
|
||||
" <filename> File which you want to copy to flash\n"
|
||||
" <device> Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
|
||||
"\n",
|
||||
progname,progname);
|
||||
|
||||
exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static int safe_open (const char *pathname,int flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open (pathname,flags);
|
||||
if (fd < 0)
|
||||
{
|
||||
log_printf (LOG_ERROR,"While trying to open %s",pathname);
|
||||
if (flags & O_RDWR)
|
||||
log_printf (LOG_ERROR," for read/write access");
|
||||
else if (flags & O_RDONLY)
|
||||
log_printf (LOG_ERROR," for read access");
|
||||
else if (flags & O_WRONLY)
|
||||
log_printf (LOG_ERROR," for write access");
|
||||
log_printf (LOG_ERROR,": %m\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
result = read (fd,buf,count);
|
||||
if (count != result)
|
||||
{
|
||||
if (verbose) log_printf (LOG_NORMAL,"\n");
|
||||
if (result < 0)
|
||||
{
|
||||
log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void safe_rewind (int fd,const char *filename)
|
||||
{
|
||||
if (lseek (fd,0L,SEEK_SET) < 0)
|
||||
{
|
||||
log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static int dev_fd = -1,fil_fd = -1;
|
||||
|
||||
static void cleanup (void)
|
||||
{
|
||||
if (dev_fd > 0) close (dev_fd);
|
||||
if (fil_fd > 0) close (fil_fd);
|
||||
}
|
||||
|
||||
int main (int argc,char *argv[])
|
||||
{
|
||||
const char *progname,*filename = NULL,*device = NULL;
|
||||
int i,flags = FLAG_NONE;
|
||||
ssize_t result;
|
||||
size_t size,written;
|
||||
struct mtd_info_user mtd;
|
||||
struct erase_info_user erase;
|
||||
struct stat filestat;
|
||||
unsigned char src[BUFSIZE],dest[BUFSIZE];
|
||||
|
||||
(progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);
|
||||
|
||||
/*********************
|
||||
* parse cmd-line
|
||||
*****************/
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "hv";
|
||||
static const struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int c = getopt_long(argc, argv, short_options,
|
||||
long_options, &option_index);
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
flags |= FLAG_HELP;
|
||||
DEBUG("Got FLAG_HELP\n");
|
||||
break;
|
||||
case 'v':
|
||||
flags |= FLAG_VERBOSE;
|
||||
DEBUG("Got FLAG_VERBOSE\n");
|
||||
break;
|
||||
default:
|
||||
DEBUG("Unknown parameter: %s\n",argv[option_index]);
|
||||
showusage (progname,true);
|
||||
}
|
||||
}
|
||||
if (optind+2 == argc) {
|
||||
flags |= FLAG_FILENAME;
|
||||
filename = argv[optind];
|
||||
DEBUG("Got filename: %s\n",filename);
|
||||
|
||||
flags |= FLAG_DEVICE;
|
||||
device = argv[optind+1];
|
||||
DEBUG("Got device: %s\n",device);
|
||||
}
|
||||
|
||||
if (flags & FLAG_HELP || progname == NULL || device == NULL)
|
||||
showusage (progname,flags != FLAG_HELP);
|
||||
|
||||
atexit (cleanup);
|
||||
|
||||
/* get some info about the flash device */
|
||||
dev_fd = safe_open (device,O_SYNC | O_RDWR);
|
||||
if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
|
||||
{
|
||||
DEBUG("ioctl(): %m\n");
|
||||
log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* get some info about the file we want to copy */
|
||||
fil_fd = safe_open (filename,O_RDONLY);
|
||||
if (fstat (fil_fd,&filestat) < 0)
|
||||
{
|
||||
log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* does it fit into the device/partition? */
|
||||
if (filestat.st_size > mtd.size)
|
||||
{
|
||||
log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* erase enough blocks so that we can write the file *
|
||||
*****************************************************/
|
||||
|
||||
#warning "Check for smaller erase regions"
|
||||
|
||||
erase.start = 0;
|
||||
erase.length = filestat.st_size & ~(mtd.erasesize - 1);
|
||||
if (filestat.st_size % mtd.erasesize) erase.length += mtd.erasesize;
|
||||
if (flags & FLAG_VERBOSE)
|
||||
{
|
||||
/* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */
|
||||
int blocks = erase.length / mtd.erasesize;
|
||||
erase.length = mtd.erasesize;
|
||||
log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks);
|
||||
for (i = 1; i <= blocks; i++)
|
||||
{
|
||||
log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
|
||||
if (ioctl (dev_fd,MEMERASE,&erase) < 0)
|
||||
{
|
||||
log_printf (LOG_NORMAL,"\n");
|
||||
log_printf (LOG_ERROR,
|
||||
"While erasing blocks 0x%.8x-0x%.8x on %s: %m\n",
|
||||
(unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
erase.start += mtd.erasesize;
|
||||
}
|
||||
log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if not, erase the whole chunk in one shot */
|
||||
if (ioctl (dev_fd,MEMERASE,&erase) < 0)
|
||||
{
|
||||
log_printf (LOG_ERROR,
|
||||
"While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n",
|
||||
(unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size);
|
||||
|
||||
/**********************************
|
||||
* write the entire file to flash *
|
||||
**********************************/
|
||||
|
||||
if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size));
|
||||
size = filestat.st_size;
|
||||
i = BUFSIZE;
|
||||
written = 0;
|
||||
while (size)
|
||||
{
|
||||
if (size < BUFSIZE) i = size;
|
||||
if (flags & FLAG_VERBOSE)
|
||||
log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)",
|
||||
KB (written + i),
|
||||
KB (filestat.st_size),
|
||||
PERCENTAGE (written + i,filestat.st_size));
|
||||
|
||||
/* read from filename */
|
||||
safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
|
||||
|
||||
/* write to device */
|
||||
result = write (dev_fd,src,i);
|
||||
if (i != result)
|
||||
{
|
||||
if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
|
||||
if (result < 0)
|
||||
{
|
||||
log_printf (LOG_ERROR,
|
||||
"While writing data to 0x%.8x-0x%.8x on %s: %m\n",
|
||||
written,written + i,device);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
log_printf (LOG_ERROR,
|
||||
"Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n",
|
||||
written,written + i,device,written + result,filestat.st_size);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
written += i;
|
||||
size -= i;
|
||||
}
|
||||
if (flags & FLAG_VERBOSE)
|
||||
log_printf (LOG_NORMAL,
|
||||
"\rWriting data: %luk/%luk (100%%)\n",
|
||||
KB (filestat.st_size),
|
||||
KB (filestat.st_size));
|
||||
DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size);
|
||||
|
||||
/**********************************
|
||||
* verify that flash == file data *
|
||||
**********************************/
|
||||
|
||||
safe_rewind (fil_fd,filename);
|
||||
safe_rewind (dev_fd,device);
|
||||
size = filestat.st_size;
|
||||
i = BUFSIZE;
|
||||
written = 0;
|
||||
if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size));
|
||||
while (size)
|
||||
{
|
||||
if (size < BUFSIZE) i = size;
|
||||
if (flags & FLAG_VERBOSE)
|
||||
log_printf (LOG_NORMAL,
|
||||
"\rVerifying data: %dk/%luk (%lu%%)",
|
||||
KB (written + i),
|
||||
KB (filestat.st_size),
|
||||
PERCENTAGE (written + i,filestat.st_size));
|
||||
|
||||
/* read from filename */
|
||||
safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
|
||||
|
||||
/* read from device */
|
||||
safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE);
|
||||
|
||||
/* compare buffers */
|
||||
if (memcmp (src,dest,i))
|
||||
{
|
||||
log_printf (LOG_ERROR,
|
||||
"File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n",
|
||||
written,written + i);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
written += i;
|
||||
size -= i;
|
||||
}
|
||||
if (flags & FLAG_VERBOSE)
|
||||
log_printf (LOG_NORMAL,
|
||||
"\rVerifying data: %luk/%luk (100%%)\n",
|
||||
KB (filestat.st_size),
|
||||
KB (filestat.st_size));
|
||||
DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -1,232 +0,0 @@
|
||||
/* Ported to MTD system.
|
||||
* Based on:
|
||||
*/
|
||||
/*======================================================================
|
||||
|
||||
Utility to create an FTL partition in a memory region
|
||||
|
||||
ftl_check.c 1.10 1999/10/25 20:01:35
|
||||
|
||||
The contents of this file are subject to the Mozilla Public
|
||||
License Version 1.1 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a copy of
|
||||
the License at http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS
|
||||
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
rights and limitations under the License.
|
||||
|
||||
The initial developer of the original code is David A. Hinds
|
||||
<dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
|
||||
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
|
||||
Alternatively, the contents of this file may be used under the
|
||||
terms of the GNU Public License version 2 (the "GPL"), in which
|
||||
case the provisions of the GPL are applicable instead of the
|
||||
above. If you wish to allow the use of your version of this file
|
||||
only under the terms of the GPL and not to allow others to use
|
||||
your version of this file under the MPL, indicate your decision
|
||||
by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this
|
||||
file under either the MPL or the GPL.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
#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;
|
||||
}
|
@ -1,342 +0,0 @@
|
||||
/* Ported to MTD system.
|
||||
* Based on:
|
||||
*/
|
||||
/*======================================================================
|
||||
|
||||
Utility to create an FTL partition in a memory region
|
||||
|
||||
ftl_format.c 1.13 1999/10/25 20:01:35
|
||||
|
||||
The contents of this file are subject to the Mozilla Public
|
||||
License Version 1.1 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a copy of
|
||||
the License at http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS
|
||||
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
rights and limitations under the License.
|
||||
|
||||
The initial developer of the original code is David A. Hinds
|
||||
<dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
|
||||
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
|
||||
Alternatively, the contents of this file may be used under the
|
||||
terms of the GNU Public License version 2 (the "GPL"), in which
|
||||
case the provisions of the GPL are applicable instead of the
|
||||
above. If you wish to allow the use of your version of this file
|
||||
only under the terms of the GPL and not to allow others to use
|
||||
your version of this file under the MPL, indicate your decision
|
||||
by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this
|
||||
file under either the MPL or the GPL.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
#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;
|
||||
}
|
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* 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.1.1.1 2008-05-13 07:15:32 yrtan 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__ */
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* $Id: ftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan 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__ */
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* $Id: inftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan 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__ */
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* $Id: jffs2-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan 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__ */
|
@ -1,170 +0,0 @@
|
||||
/*
|
||||
* $Id: mtd-abi.h,v 1.1.1.1 2008-10-30 14:29:21 lhhuang Exp $
|
||||
*
|
||||
* Portions of MTD ABI definition which are shared by kernel and user space
|
||||
*/
|
||||
|
||||
#ifndef __MTD_ABI_H__
|
||||
#define __MTD_ABI_H__
|
||||
|
||||
#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
|
||||
separate files was to avoid #ifdef __KERNEL__ */
|
||||
#define __user
|
||||
#endif
|
||||
|
||||
typedef unsigned long long size_mtd_t;
|
||||
typedef unsigned long long loff_mtd_t;
|
||||
|
||||
struct erase_info_user {
|
||||
uint64_t start;
|
||||
uint64_t length;
|
||||
};
|
||||
|
||||
struct mtd_oob_buf {
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
unsigned char __user *ptr;
|
||||
};
|
||||
|
||||
struct mtd_page_buf {
|
||||
uint32_t start; //page start address
|
||||
uint32_t ooblength;
|
||||
uint32_t datlength;
|
||||
unsigned char __user *oobptr;
|
||||
unsigned char __user *datptr;
|
||||
};
|
||||
|
||||
#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;
|
||||
uint64_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 {
|
||||
uint64_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_mtd_t)
|
||||
#define MEMSETBADBLOCK _IOW('M', 12, loff_mtd_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)
|
||||
#define MEMWRITEPAGE _IOWR('M', 20, struct mtd_page_buf)
|
||||
|
||||
/*
|
||||
* 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[104]; /* more fields(13*8) are required for
|
||||
* 8-bit BCH ECC and 4KB pagesize nand, by Regen */
|
||||
};
|
||||
|
||||
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[128];
|
||||
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__ */
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* $Id: mtd-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan 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__ */
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* $Id: nftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan 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__ */
|
@ -1,372 +0,0 @@
|
||||
/*
|
||||
* 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. May be included by user-space.
|
||||
*/
|
||||
|
||||
#ifndef __UBI_HEADER_H__
|
||||
#define __UBI_HEADER_H__
|
||||
|
||||
#include <stdint.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 unit 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(uint32_t))
|
||||
#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(uint32_t))
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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 then 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.
|
||||
*/
|
||||
struct ubi_ec_hdr {
|
||||
uint32_t magic;
|
||||
uint8_t version;
|
||||
uint8_t padding1[3];
|
||||
uint64_t ec; /* Warning: the current limit is 31-bit anyway! */
|
||||
uint32_t vid_hdr_offset;
|
||||
uint32_t data_offset;
|
||||
uint8_t padding2[36];
|
||||
uint32_t 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
|
||||
* @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be
|
||||
* removed, kept only for not breaking older UBI users)
|
||||
* @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
|
||||
* @padding1: reserved for future, zeroes
|
||||
* @sqnum: sequence number
|
||||
* @padding2: 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 then 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.
|
||||
*
|
||||
* Note, there is an obsolete @leb_ver field which was used instead of @sqnum
|
||||
* in the past. But it is not used anymore and we keep it in order to be able
|
||||
* to deal with old UBI images. It will be removed at some point.
|
||||
*
|
||||
* 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 unit, then
|
||||
* the wear-leveling unit 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 {
|
||||
uint32_t magic;
|
||||
uint8_t version;
|
||||
uint8_t vol_type;
|
||||
uint8_t copy_flag;
|
||||
uint8_t compat;
|
||||
uint32_t vol_id;
|
||||
uint32_t lnum;
|
||||
uint32_t leb_ver; /* obsolete, to be removed, don't use */
|
||||
uint32_t data_size;
|
||||
uint32_t used_ebs;
|
||||
uint32_t data_pad;
|
||||
uint32_t data_crc;
|
||||
uint8_t padding1[4];
|
||||
uint64_t sqnum;
|
||||
uint8_t padding2[12];
|
||||
uint32_t 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(uint32_t))
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
uint32_t reserved_pebs;
|
||||
uint32_t alignment;
|
||||
uint32_t data_pad;
|
||||
uint8_t vol_type;
|
||||
uint8_t upd_marker;
|
||||
uint16_t name_len;
|
||||
uint8_t name[UBI_VOL_NAME_MAX+1];
|
||||
uint8_t flags;
|
||||
uint8_t padding[23];
|
||||
uint32_t crc;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif /* !__UBI_HEADER_H__ */
|
@ -1,284 +0,0 @@
|
||||
/*
|
||||
* 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__
|
||||
|
||||
#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
|
||||
separate files was to avoid #ifdef __KERNEL__ */
|
||||
#define __user
|
||||
#endif
|
||||
/*
|
||||
* 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 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);
|
||||
*
|
||||
* Atomic eraseblock change
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL
|
||||
* command of the corresponding UBI volume character device. A pointer to
|
||||
* &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is
|
||||
* expected to write the requested amount of bytes. This is similar to the
|
||||
* "volume update" IOCTL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
/* 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)
|
||||
/* An eraseblock erasure command, used for debugging, disabled by default */
|
||||
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
|
||||
/* An atomic eraseblock change command */
|
||||
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t)
|
||||
/* Start UBI leb read */
|
||||
#define UBI_IOCLEBREAD _IOWR(UBI_VOL_IOC_MAGIC, 3, struct ubi_leb)
|
||||
|
||||
/* Maximum MTD device name length supported by UBI */
|
||||
#define MAX_UBI_MTD_NAME_LEN 127
|
||||
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
uint8_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 then 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_leb_change_req - a data structure used in atomic logical
|
||||
* eraseblock 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;
|
||||
uint8_t dtype;
|
||||
uint8_t padding[7];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* struct ubi_leb - a data structure describe LEB.
|
||||
* @lnum: logical eraseblock number to dump
|
||||
* @lebbuf: LEB data buffer
|
||||
*/
|
||||
struct ubi_leb{
|
||||
unsigned int lnum;
|
||||
char __user *buf;
|
||||
};
|
||||
|
||||
#endif /* __UBI_USER_H__ */
|
@ -1,51 +0,0 @@
|
||||
#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
|
@ -1,359 +0,0 @@
|
||||
/*
|
||||
* Dump JFFS filesystem.
|
||||
* Useful when it buggers up.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,690 +0,0 @@
|
||||
/*
|
||||
* dumpjffs2.c
|
||||
*
|
||||
* Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Overview:
|
||||
* This utility dumps the contents of a binary JFFS2 image
|
||||
*
|
||||
*
|
||||
* Bug/ToDo:
|
||||
*/
|
||||
|
||||
#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.1.1.1 $"
|
||||
|
||||
#define PAD(x) (((x)+3)&~3)
|
||||
|
||||
/* For outputting a byte-swapped version of the input image. */
|
||||
#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)})
|
||||
#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)})
|
||||
|
||||
#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; })
|
||||
#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)})
|
||||
|
||||
// Global variables
|
||||
long imglen; // length of image
|
||||
char *data; // image data
|
||||
|
||||
void display_help (void)
|
||||
{
|
||||
printf("Usage: dumpjffs2 [OPTION] INPUTFILE\n"
|
||||
"Dumps the contents of a binary JFFS2 image.\n"
|
||||
"\n"
|
||||
" --help display this help and exit\n"
|
||||
" --version output version information and exit\n"
|
||||
"-b --bigendian image is big endian\n"
|
||||
"-l --littleendian image is little endian\n"
|
||||
"-c --content dump image contents\n"
|
||||
"-e fname --endianconvert=fname convert image endianness, output to file fname\n"
|
||||
"-r --recalccrc recalc name and data crc on endian conversion\n"
|
||||
"-d len --datsize=len size of data chunks, when oob data in binary image (NAND only)\n"
|
||||
"-o len --oobsize=len size of oob data chunk in binary image (NAND only)\n"
|
||||
"-v --verbose verbose output\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void display_version (void)
|
||||
{
|
||||
printf(PROGRAM " " VERSION "\n"
|
||||
"\n"
|
||||
"Copyright (C) 2003 Thomas Gleixner \n"
|
||||
"\n"
|
||||
PROGRAM " comes with NO WARRANTY\n"
|
||||
"to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"You may redistribute copies of " PROGRAM "\n"
|
||||
"under the terms of the GNU General Public Licence.\n"
|
||||
"See the file `COPYING' for more information.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Option variables
|
||||
|
||||
int verbose; // verbose output
|
||||
char *img; // filename of image
|
||||
int dumpcontent; // dump image content
|
||||
int target_endian = __BYTE_ORDER; // image endianess
|
||||
int convertendian; // convert endianness
|
||||
int recalccrc; // recalc name and data crc's on endian conversion
|
||||
char cnvfile[256]; // filename for conversion output
|
||||
int datsize; // Size of data chunks, when oob data is inside the binary image
|
||||
int oobsize; // Size of oob chunks, when oob data is inside the binary image
|
||||
|
||||
void process_options (int argc, char *argv[])
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "blce:rd:o:v";
|
||||
static const struct option long_options[] = {
|
||||
{"help", no_argument, 0, 0},
|
||||
{"version", no_argument, 0, 0},
|
||||
{"bigendian", no_argument, 0, 'b'},
|
||||
{"littleendian", no_argument, 0, 'l'},
|
||||
{"content", no_argument, 0, 'c'},
|
||||
{"endianconvert", required_argument, 0, 'e'},
|
||||
{"datsize", required_argument, 0, 'd'},
|
||||
{"oobsize", required_argument, 0, 'o'},
|
||||
{"recalccrc", required_argument, 0, 'r'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int c = getopt_long(argc, argv, short_options,
|
||||
long_options, &option_index);
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
switch (option_index) {
|
||||
case 0:
|
||||
display_help();
|
||||
break;
|
||||
case 1:
|
||||
display_version();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'b':
|
||||
target_endian = __BIG_ENDIAN;
|
||||
break;
|
||||
case 'l':
|
||||
target_endian = __LITTLE_ENDIAN;
|
||||
break;
|
||||
case 'c':
|
||||
dumpcontent = 1;
|
||||
break;
|
||||
case 'd':
|
||||
datsize = atoi(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
oobsize = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
convertendian = 1;
|
||||
strcpy (cnvfile, optarg);
|
||||
break;
|
||||
case 'r':
|
||||
recalccrc = 1;
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 1 || error)
|
||||
display_help ();
|
||||
|
||||
img = argv[optind];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dump image contents
|
||||
*/
|
||||
void do_dumpcontent (void)
|
||||
{
|
||||
char *p = data, *p_free_begin;
|
||||
union jffs2_node_union *node;
|
||||
int empty = 0, dirty = 0;
|
||||
char name[256];
|
||||
uint32_t crc;
|
||||
uint16_t type;
|
||||
int bitchbitmask = 0;
|
||||
int obsolete;
|
||||
|
||||
p_free_begin = NULL;
|
||||
while ( p < (data + imglen)) {
|
||||
node = (union jffs2_node_union*) p;
|
||||
|
||||
/* Skip empty space */
|
||||
if (!p_free_begin)
|
||||
p_free_begin = p;
|
||||
if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
|
||||
p += 4;
|
||||
empty += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p != p_free_begin)
|
||||
printf("Empty space found from 0x%08x to 0x%08x\n", p_free_begin-data, p-data);
|
||||
p_free_begin = NULL;
|
||||
|
||||
if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
|
||||
if (!bitchbitmask++)
|
||||
printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic));
|
||||
p += 4;
|
||||
dirty += 4;
|
||||
continue;
|
||||
}
|
||||
bitchbitmask = 0;
|
||||
|
||||
type = je16_to_cpu(node->u.nodetype);
|
||||
if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
|
||||
obsolete = 1;
|
||||
type |= JFFS2_NODE_ACCURATE;
|
||||
} else
|
||||
obsolete = 0;
|
||||
/* Set accurate for CRC check */
|
||||
node->u.nodetype = cpu_to_je16(type);
|
||||
|
||||
crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
|
||||
if (crc != je32_to_cpu (node->u.hdr_crc)) {
|
||||
printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc);
|
||||
p += 4;
|
||||
dirty += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(je16_to_cpu(node->u.nodetype)) {
|
||||
|
||||
case JFFS2_NODETYPE_INODE:
|
||||
printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
|
||||
obsolete ? "Obsolete" : "",
|
||||
p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
|
||||
je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
|
||||
je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));
|
||||
|
||||
crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8);
|
||||
if (crc != je32_to_cpu (node->i.node_crc)) {
|
||||
printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc);
|
||||
p += PAD(je32_to_cpu (node->i.totlen));
|
||||
dirty += PAD(je32_to_cpu (node->i.totlen));;
|
||||
continue;
|
||||
}
|
||||
|
||||
crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize));
|
||||
if (crc != je32_to_cpu(node->i.data_crc)) {
|
||||
printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc);
|
||||
p += PAD(je32_to_cpu (node->i.totlen));
|
||||
dirty += PAD(je32_to_cpu (node->i.totlen));;
|
||||
continue;
|
||||
}
|
||||
|
||||
p += PAD(je32_to_cpu (node->i.totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT:
|
||||
memcpy (name, node->d.name, node->d.nsize);
|
||||
name [node->d.nsize] = 0x0;
|
||||
printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n",
|
||||
obsolete ? "Obsolete" : "",
|
||||
p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
|
||||
je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
|
||||
node->d.nsize, name);
|
||||
|
||||
crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8);
|
||||
if (crc != je32_to_cpu (node->d.node_crc)) {
|
||||
printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc);
|
||||
p += PAD(je32_to_cpu (node->d.totlen));
|
||||
dirty += PAD(je32_to_cpu (node->d.totlen));;
|
||||
continue;
|
||||
}
|
||||
|
||||
crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize);
|
||||
if (crc != je32_to_cpu(node->d.name_crc)) {
|
||||
printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc);
|
||||
p += PAD(je32_to_cpu (node->d.totlen));
|
||||
dirty += PAD(je32_to_cpu (node->d.totlen));;
|
||||
continue;
|
||||
}
|
||||
|
||||
p += PAD(je32_to_cpu (node->d.totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_NODETYPE_SUMMARY: {
|
||||
|
||||
int i;
|
||||
struct jffs2_sum_marker * sm;
|
||||
|
||||
printf("%8s Inode Sum node at 0x%08x, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n",
|
||||
obsolete ? "Obsolete" : "",
|
||||
p - data,
|
||||
je32_to_cpu (node->s.totlen),
|
||||
je32_to_cpu (node->s.sum_num),
|
||||
je32_to_cpu (node->s.cln_mkr));
|
||||
|
||||
crc = crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8);
|
||||
if (crc != je32_to_cpu (node->s.node_crc)) {
|
||||
printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc);
|
||||
p += PAD(je32_to_cpu (node->s.totlen));
|
||||
dirty += PAD(je32_to_cpu (node->s.totlen));;
|
||||
continue;
|
||||
}
|
||||
|
||||
crc = crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary));
|
||||
if (crc != je32_to_cpu(node->s.sum_crc)) {
|
||||
printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc);
|
||||
p += PAD(je32_to_cpu (node->s.totlen));
|
||||
dirty += PAD(je32_to_cpu (node->s.totlen));;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
void *sp;
|
||||
sp = (p + sizeof(struct jffs2_raw_summary));
|
||||
|
||||
for(i=0; i<je32_to_cpu(node->s.sum_num); i++) {
|
||||
|
||||
switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE : {
|
||||
|
||||
struct jffs2_sum_inode_flash *spi;
|
||||
spi = sp;
|
||||
|
||||
printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n",
|
||||
"",
|
||||
je32_to_cpu (spi->inode),
|
||||
je32_to_cpu (spi->version),
|
||||
je32_to_cpu (spi->offset),
|
||||
je32_to_cpu (spi->totlen));
|
||||
|
||||
sp += JFFS2_SUMMARY_INODE_SIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT : {
|
||||
|
||||
char name[255];
|
||||
struct jffs2_sum_dirent_flash *spd;
|
||||
spd = sp;
|
||||
|
||||
memcpy(name,spd->name,spd->nsize);
|
||||
name [spd->nsize] = 0x0;
|
||||
|
||||
printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n",
|
||||
"",
|
||||
je32_to_cpu (spd->offset),
|
||||
je32_to_cpu (spd->totlen),
|
||||
je32_to_cpu (spd->pino),
|
||||
je32_to_cpu (spd->version),
|
||||
je32_to_cpu (spd->ino),
|
||||
spd->nsize,
|
||||
name);
|
||||
|
||||
sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize);
|
||||
break;
|
||||
}
|
||||
|
||||
default :
|
||||
printf("Unknown summary node!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker));
|
||||
|
||||
printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n",
|
||||
"",
|
||||
je32_to_cpu(sm->offset),
|
||||
je32_to_cpu(sm->magic),
|
||||
je32_to_cpu(node->s.padded));
|
||||
}
|
||||
|
||||
p += PAD(je32_to_cpu (node->s.totlen));
|
||||
break;
|
||||
}
|
||||
|
||||
case JFFS2_NODETYPE_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);
|
||||
}
|
@ -1,939 +0,0 @@
|
||||
/* vi: set sw=4 ts=4: */
|
||||
/*
|
||||
* jffs2reader v0.0.18 A jffs2 image reader
|
||||
*
|
||||
* Copyright (c) 2001 Jari Kirma <Jari.Kirma@hut.fi>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the author be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any
|
||||
* purpose, including commercial applications, and to alter it and
|
||||
* redistribute it freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must
|
||||
* not claim that you wrote the original software. If you use this
|
||||
* software in a product, an acknowledgment in the product
|
||||
* documentation would be appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must
|
||||
* not be misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source
|
||||
* distribution.
|
||||
*
|
||||
*
|
||||
*********
|
||||
* This code was altered September 2001
|
||||
* Changes are Copyright (c) Erik Andersen <andersen@codepoet.org>
|
||||
*
|
||||
* In compliance with (2) above, this is hereby marked as an altered
|
||||
* version of this software. It has been altered as follows:
|
||||
* *) Listing a directory now mimics the behavior of 'ls -l'
|
||||
* *) Support for recursive listing has been added
|
||||
* *) Without options, does a recursive 'ls' on the whole filesystem
|
||||
* *) option parsing now uses getopt()
|
||||
* *) Now uses printf, and error messages go to stderr.
|
||||
* *) The copyright notice has been cleaned up and reformatted
|
||||
* *) The code has been reformatted
|
||||
* *) Several twisty code paths have been fixed so I can understand them.
|
||||
* -Erik, 1 September 2001
|
||||
*
|
||||
* *) Made it show major/minor numbers for device nodes
|
||||
* *) Made it show symlink targets
|
||||
* -Erik, 13 September 2001
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
||||
- Add CRC checking code to places marked with XXX.
|
||||
- Add support for other node compression types.
|
||||
|
||||
- Test with real life images.
|
||||
- Maybe port into bootloader.
|
||||
*/
|
||||
|
||||
/*
|
||||
BUGS:
|
||||
|
||||
- Doesn't check CRC checksums.
|
||||
*/
|
||||
|
||||
|
||||
#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);
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
#!/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
|
@ -1,54 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define PKT_SIZE 2820
|
||||
|
||||
struct image_pkt_hdr {
|
||||
uint32_t resend;
|
||||
uint32_t totcrc;
|
||||
uint32_t nr_blocks;
|
||||
uint32_t blocksize;
|
||||
uint32_t block_crc;
|
||||
uint32_t block_nr;
|
||||
uint32_t pkt_sequence;
|
||||
uint16_t pkt_nr;
|
||||
uint16_t nr_pkts;
|
||||
uint32_t thislen;
|
||||
uint32_t thiscrc;
|
||||
};
|
||||
|
||||
struct image_pkt {
|
||||
struct image_pkt_hdr hdr;
|
||||
unsigned char data[PKT_SIZE];
|
||||
};
|
||||
|
||||
struct fec_parms;
|
||||
|
||||
/* k - number of actual data packets
|
||||
* n - total number of packets including data and redundant packets
|
||||
* (actual packet size isn't relevant here) */
|
||||
struct fec_parms *fec_new(int k, int n);
|
||||
void fec_free(struct fec_parms *p);
|
||||
|
||||
/* src - array of (n) pointers to data packets
|
||||
* fec - buffer for packet to be generated
|
||||
* index - index of packet to be generated (0 <= index < n)
|
||||
* sz - data packet size
|
||||
*
|
||||
* _linear version just takes a pointer to the raw data; no
|
||||
* mucking about with packet pointers.
|
||||
*/
|
||||
void fec_encode(struct fec_parms *code, unsigned char *src[],
|
||||
unsigned char *fec, int index, int sz);
|
||||
void fec_encode_linear(struct fec_parms *code, unsigned char *src,
|
||||
unsigned char *fec, int index, int sz);
|
||||
|
||||
/* data - array of (k) pointers to data packets, in arbitrary order (see i)
|
||||
* i - indices of (data) packets
|
||||
* sz - data packet size
|
||||
*
|
||||
* Will never fail as long as you give it (k) individual data packets.
|
||||
* Will re-order the (data) pointers but not the indices -- data packets
|
||||
* are ordered on return.
|
||||
*/
|
||||
int fec_decode(struct fec_parms *code, unsigned char *data[],
|
||||
int i[], int sz);
|
@ -1,259 +0,0 @@
|
||||
.TH MKFS.JFFS2 1
|
||||
.SH NAME
|
||||
mkfs.jffs2 \- Create a JFFS2 file system image from directory
|
||||
.SH SYNOPSIS
|
||||
.B mkfs.jffs2
|
||||
[
|
||||
.B -p,--pad[=SIZE]
|
||||
]
|
||||
[
|
||||
.B -r,-d,--root
|
||||
.I directory
|
||||
]
|
||||
[
|
||||
.B -s,--pagesize=SIZE
|
||||
]
|
||||
[
|
||||
.B -e,--eraseblock=SIZE
|
||||
]
|
||||
[
|
||||
.B -c,--cleanmarker=SIZE
|
||||
]
|
||||
[
|
||||
.B -n,--no-cleanmarkers
|
||||
]
|
||||
[
|
||||
.B -o,--output
|
||||
.I image.jffs2
|
||||
]
|
||||
[
|
||||
.B -l,--little-endian
|
||||
]
|
||||
[
|
||||
.B -b,--big-endian
|
||||
]
|
||||
[
|
||||
.B -D,--devtable=FILE
|
||||
]
|
||||
[
|
||||
.B -f,--faketime
|
||||
]
|
||||
[
|
||||
.B -q,--squash
|
||||
]
|
||||
[
|
||||
.B -U,--squash-uids
|
||||
]
|
||||
[
|
||||
.B -P,--squash-perms
|
||||
]
|
||||
[
|
||||
.B --with-xattr
|
||||
]
|
||||
[
|
||||
.B --with-selinux
|
||||
]
|
||||
[
|
||||
.B --with-posix-acl
|
||||
]
|
||||
[
|
||||
.B -m,--compression-mode=MODE
|
||||
]
|
||||
[
|
||||
.B -x,--disable-compressor=NAME
|
||||
]
|
||||
[
|
||||
.B -X,--enable-compressor=NAME
|
||||
]
|
||||
[
|
||||
.B -y,--compressor-priority=PRIORITY:NAME
|
||||
]
|
||||
[
|
||||
.B -L,--list-compressors
|
||||
]
|
||||
[
|
||||
.B -t,--test-compression
|
||||
]
|
||||
[
|
||||
.B -h,--help
|
||||
]
|
||||
[
|
||||
.B -v,--verbose
|
||||
]
|
||||
[
|
||||
.B -V,--version
|
||||
]
|
||||
[
|
||||
.B -i,--incremental
|
||||
.I image.jffs2
|
||||
]
|
||||
|
||||
.SH DESCRIPTION
|
||||
The program
|
||||
.B mkfs.jffs2
|
||||
creates a JFFS2 (Second Journalling Flash File System) file system
|
||||
image and writes the resulting image to the file specified by the
|
||||
.B -o
|
||||
option or by default to the standard output, unless the standard
|
||||
output is a terminal device in which case mkfs.jffs2 will abort.
|
||||
|
||||
The file system image is created using the files and directories
|
||||
contained in the directory specified by the option
|
||||
.B -r
|
||||
or the present directory, if the
|
||||
.B -r
|
||||
option is not specified.
|
||||
|
||||
Each block of the files to be placed into the file system image
|
||||
are compressed using one of the 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 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)
|
File diff suppressed because it is too large
Load Diff
@ -1,40 +0,0 @@
|
||||
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.
|
||||
|
@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2d3D, Inc.
|
||||
* Written by Abraham vd Merwe <abraham@2d3d.co.za>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of other contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -1,403 +0,0 @@
|
||||
/*
|
||||
* nanddump.c
|
||||
*
|
||||
* Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org)
|
||||
* Steven J. Hill (sjhill@realitydiluted.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Overview:
|
||||
* This utility dumps the contents of raw NAND chips or NAND
|
||||
* chips contained in DoC devices.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
|
||||
#define PROGRAM "nanddump"
|
||||
#define VERSION "$Revision: 1.1.1.1 $"
|
||||
|
||||
struct nand_oobinfo none_oobinfo = {
|
||||
.useecc = MTD_NANDECC_OFF,
|
||||
};
|
||||
|
||||
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"
|
||||
"-s addr --startaddress=addr start address\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void display_version (void)
|
||||
{
|
||||
printf(PROGRAM " " VERSION "\n"
|
||||
"\n"
|
||||
PROGRAM " comes with NO WARRANTY\n"
|
||||
"to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"You may redistribute copies of " PROGRAM "\n"
|
||||
"under the terms of the GNU General Public Licence.\n"
|
||||
"See the file `COPYING' for more information.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Option variables
|
||||
|
||||
int ignoreerrors; // ignore errors
|
||||
int pretty_print; // print nice in ascii
|
||||
int noecc; // don't error correct
|
||||
int omitoob; // omit oob data
|
||||
unsigned long start_addr; // start address
|
||||
unsigned long length; // dump length
|
||||
char *mtddev; // mtd device name
|
||||
char *dumpfile; // dump file name
|
||||
int omitbad;
|
||||
|
||||
void process_options (int argc, char *argv[])
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "bs:f:il:opn";
|
||||
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'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int c = getopt_long(argc, argv, short_options,
|
||||
long_options, &option_index);
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
switch (option_index) {
|
||||
case 0:
|
||||
display_help();
|
||||
break;
|
||||
case 1:
|
||||
display_version();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
omitbad = 1;
|
||||
break;
|
||||
case 's':
|
||||
start_addr = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'f':
|
||||
if (!(dumpfile = strdup(optarg))) {
|
||||
perror("stddup");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
ignoreerrors = 1;
|
||||
break;
|
||||
case 'l':
|
||||
length = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'o':
|
||||
omitoob = 1;
|
||||
break;
|
||||
case 'p':
|
||||
pretty_print = 1;
|
||||
break;
|
||||
case 'n':
|
||||
noecc = 1;
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 1 || error)
|
||||
display_help ();
|
||||
|
||||
mtddev = argv[optind];
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffers for reading data from flash
|
||||
*/
|
||||
unsigned char readbuf[8192];
|
||||
unsigned char oobbuf[256];
|
||||
|
||||
/*
|
||||
* Main program
|
||||
*/
|
||||
int main(int argc, char **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};
|
||||
mtd_info_t meminfo;
|
||||
char pretty_buf[80];
|
||||
int oobinfochanged = 0 ;
|
||||
struct nand_oobinfo old_oobinfo;
|
||||
struct mtd_ecc_stats stat1, stat2;
|
||||
int eccstats = 0;
|
||||
|
||||
process_options(argc, argv);
|
||||
|
||||
/* Open MTD device */
|
||||
if ((fd = open(mtddev, O_RDONLY)) == -1) {
|
||||
perror("open flash");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Fill in MTD device capability structure */
|
||||
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
|
||||
perror("MEMGETINFO");
|
||||
close(fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Make sure device page sizes are valid */
|
||||
if (!(meminfo.oobsize == 256 && meminfo.writesize == 8192) &&
|
||||
!(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(1);
|
||||
}
|
||||
/* Read the real oob length */
|
||||
oob.length = meminfo.oobsize;
|
||||
|
||||
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 (1);
|
||||
}
|
||||
if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
oobinfochanged = 1;
|
||||
break;
|
||||
default:
|
||||
perror ("MTDFILEMODE");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
/* check if we can read ecc stats */
|
||||
if (!ioctl(fd, ECCGETSTATS, &stat1)) {
|
||||
eccstats = 1;
|
||||
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 ("open outfile");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Initialize start/end addresses and block size */
|
||||
if (length)
|
||||
end_addr = start_addr + length;
|
||||
if (!length || end_addr > meminfo.size)
|
||||
end_addr = meminfo.size;
|
||||
|
||||
bs = meminfo.writesize;
|
||||
|
||||
/* Print informative message */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
close(fd);
|
||||
close(ofd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* Close the output file and MTD device */
|
||||
close(fd);
|
||||
close(ofd);
|
||||
|
||||
/* Exit happy */
|
||||
return 0;
|
||||
|
||||
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);
|
||||
exit(1);
|
||||
}
|
@ -1,365 +0,0 @@
|
||||
/*
|
||||
* nanddump.c
|
||||
*
|
||||
* Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org)
|
||||
* Steven J. Hill (sjhill@realitydiluted.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Overview:
|
||||
* This utility dumps the contents of raw NAND chips or NAND
|
||||
* chips contained in DoC devices.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
|
||||
#define PROGRAM "nanddump"
|
||||
#define VERSION "$Revision: 1.1.1.1 $"
|
||||
|
||||
struct nand_oobinfo none_oobinfo = {
|
||||
.useecc = MTD_NANDECC_OFF,
|
||||
};
|
||||
|
||||
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"
|
||||
"-s addr --startaddress=addr start address\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void display_version (void)
|
||||
{
|
||||
printf(PROGRAM " " VERSION "\n"
|
||||
"\n"
|
||||
PROGRAM " comes with NO WARRANTY\n"
|
||||
"to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"You may redistribute copies of " PROGRAM "\n"
|
||||
"under the terms of the GNU General Public Licence.\n"
|
||||
"See the file `COPYING' for more information.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Option variables
|
||||
|
||||
int ignoreerrors; // ignore errors
|
||||
int pretty_print; // print nice in ascii
|
||||
int noecc; // don't error correct
|
||||
int omitoob; // omit oob data
|
||||
unsigned long start_addr; // start address
|
||||
unsigned long length; // dump length
|
||||
char *mtddev; // mtd device name
|
||||
char *dumpfile; // dump file name
|
||||
int omitbad;
|
||||
|
||||
void process_options (int argc, char *argv[])
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "bs:f:il:opn";
|
||||
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'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int c = getopt_long(argc, argv, short_options,
|
||||
long_options, &option_index);
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
switch (option_index) {
|
||||
case 0:
|
||||
display_help();
|
||||
break;
|
||||
case 1:
|
||||
display_version();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
omitbad = 1;
|
||||
break;
|
||||
case 's':
|
||||
start_addr = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'f':
|
||||
if (!(dumpfile = strdup(optarg))) {
|
||||
perror("stddup");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
ignoreerrors = 1;
|
||||
break;
|
||||
case 'l':
|
||||
length = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'o':
|
||||
omitoob = 1;
|
||||
break;
|
||||
case 'p':
|
||||
pretty_print = 1;
|
||||
break;
|
||||
case 'n':
|
||||
noecc = 1;
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 1 || error)
|
||||
display_help ();
|
||||
|
||||
mtddev = argv[optind];
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffers for reading data from flash
|
||||
*/
|
||||
unsigned char readbuf[8192];
|
||||
unsigned char oobbuf[256];
|
||||
|
||||
/*
|
||||
* Main program
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned long ofs, end_addr = 0;
|
||||
unsigned long long blockstart = 1;
|
||||
int ret, fd, ofd, bs, badblock = 0;
|
||||
struct mtd_oob_buf oob = {0, 16, oobbuf};
|
||||
mtd_info_t meminfo;
|
||||
int oobinfochanged = 0 ;
|
||||
struct nand_oobinfo old_oobinfo;
|
||||
struct mtd_ecc_stats stat1, stat2;
|
||||
int eccstats = 0;
|
||||
|
||||
process_options(argc, argv);
|
||||
|
||||
/* Open MTD device */
|
||||
if ((fd = open(mtddev, O_RDONLY)) == -1) {
|
||||
perror("open flash");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Fill in MTD device capability structure */
|
||||
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
|
||||
perror("MEMGETINFO");
|
||||
close(fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Make sure device page sizes are valid */
|
||||
if (!(meminfo.oobsize == 256 && meminfo.writesize == 8192) &&
|
||||
!(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(1);
|
||||
}
|
||||
/* Read the real oob length */
|
||||
oob.length = meminfo.oobsize;
|
||||
|
||||
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 (1);
|
||||
}
|
||||
if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
oobinfochanged = 1;
|
||||
break;
|
||||
default:
|
||||
perror ("MTDFILEMODE");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
/* check if we can read ecc stats */
|
||||
if (!ioctl(fd, ECCGETSTATS, &stat1)) {
|
||||
eccstats = 1;
|
||||
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 ("open outfile");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Initialize start/end addresses and block size */
|
||||
if (length)
|
||||
end_addr = start_addr + length;
|
||||
if (!length || end_addr > meminfo.size)
|
||||
end_addr = meminfo.size;
|
||||
|
||||
bs = meminfo.writesize;
|
||||
|
||||
/* Print informative message */
|
||||
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) {
|
||||
//skip bad block;
|
||||
ofs += meminfo.erasesize - bs;
|
||||
continue;
|
||||
} 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 (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;
|
||||
}
|
||||
if(oobbuf[2]==0xff && oobbuf[3]==0xff && oobbuf[4]==0xff && oobbuf[5]==0xff){
|
||||
//skip free block;
|
||||
ofs += meminfo.erasesize - bs;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out page data */
|
||||
write(ofd, readbuf, bs);
|
||||
|
||||
if (omitoob)
|
||||
continue;
|
||||
|
||||
/* Write out OOB data */
|
||||
write(ofd, oobbuf, meminfo.oobsize);
|
||||
}
|
||||
|
||||
/* reset oobinfo */
|
||||
if (oobinfochanged == 1) {
|
||||
if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close(fd);
|
||||
close(ofd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* Close the output file and MTD device */
|
||||
close(fd);
|
||||
close(ofd);
|
||||
|
||||
/* Exit happy */
|
||||
return 0;
|
||||
|
||||
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);
|
||||
exit(1);
|
||||
}
|
@ -1,284 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include "mtd/mtd-user.h"
|
||||
|
||||
void usage(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"
|
||||
"Warning: it is just used for SLC NAND!\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct mtd_info_user meminfo;
|
||||
struct mtd_ecc_stats oldstats, newstats;
|
||||
int fd;
|
||||
int markbad=0;
|
||||
int seed;
|
||||
|
||||
int erase_and_write(loff_mtd_t ofs, unsigned char *data, unsigned char *rbuf)
|
||||
{
|
||||
struct erase_info_user er;
|
||||
ssize_t len;
|
||||
int i;
|
||||
|
||||
printf("\r%09llx: erasing... ", (loff_mtd_t)ofs);
|
||||
fflush(stdout);
|
||||
|
||||
er.start = ofs;
|
||||
er.length = meminfo.erasesize;
|
||||
|
||||
if (ioctl(fd, MEMERASE, &er)) {
|
||||
perror("MEMERASE");
|
||||
if (markbad) {
|
||||
printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs);
|
||||
ioctl(fd, MEMSETBADBLOCK, &ofs);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\r%09llx: writing...", (loff_mtd_t)ofs);
|
||||
fflush(stdout);
|
||||
|
||||
len = pwrite(fd, data, meminfo.erasesize, ofs);
|
||||
if (len < 0) {
|
||||
printf("\n");
|
||||
perror("write");
|
||||
if (markbad) {
|
||||
printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs);
|
||||
ioctl(fd, MEMSETBADBLOCK, &ofs);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (len < meminfo.erasesize) {
|
||||
printf("\n");
|
||||
fprintf(stderr, "Short write (%d bytes)\n", len);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("\r%09llx: reading...", (loff_mtd_t)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);
|
||||
else
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ioctl(fd, ECCGETSTATS, &newstats)) {
|
||||
printf("\n");
|
||||
perror("ECCGETSTATS");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (newstats.corrected > oldstats.corrected) {
|
||||
printf("\nECC corrected at %09llx\n", (loff_mtd_t) ofs);
|
||||
oldstats.corrected = newstats.corrected;
|
||||
}
|
||||
if (newstats.failed > oldstats.failed) {
|
||||
printf("\nECC failed at %09llx\n", (loff_mtd_t) ofs);
|
||||
oldstats.corrected = newstats.corrected;
|
||||
}
|
||||
if (len < meminfo.erasesize)
|
||||
exit(1);
|
||||
|
||||
printf("\r%09llx: checking...", (loff_mtd_t)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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
uint64_t offset = 0;
|
||||
uint64_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 %09llx not multiple of erase size %x\n",
|
||||
offset, meminfo.erasesize);
|
||||
exit(1);
|
||||
}
|
||||
if (length % meminfo.erasesize) {
|
||||
fprintf(stderr, "Length %09llx not multiple of erase size %x\n",
|
||||
length, meminfo.erasesize);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (length + offset > meminfo.size) {
|
||||
fprintf(stderr, "Length %09llx + offset %09llx exceeds device size %09llx\n",
|
||||
length, offset, meminfo.size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wbuf = malloc(meminfo.erasesize * 3);
|
||||
if (!wbuf) {
|
||||
fprintf(stderr, "Could not allocate %d bytes for buffer\n",
|
||||
meminfo.erasesize * 2);
|
||||
exit(1);
|
||||
}
|
||||
rbuf = wbuf + meminfo.erasesize;
|
||||
kbuf = rbuf + meminfo.erasesize;
|
||||
|
||||
if (ioctl(fd, ECCGETSTATS, &oldstats)) {
|
||||
perror("ECCGETSTATS");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("ECC corrections: %d\n", oldstats.corrected);
|
||||
printf("ECC failures : %d\n", oldstats.failed);
|
||||
printf("Bad blocks : %d\n", oldstats.badblocks);
|
||||
printf("BBT blocks : %d\n", oldstats.bbtblocks);
|
||||
|
||||
for (pass = 0; pass < nr_passes; pass++) {
|
||||
loff_mtd_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%09llx\n", test_ofs);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i=0; i<meminfo.erasesize; i++)
|
||||
wbuf[i] = rand();
|
||||
|
||||
if (keep_contents) {
|
||||
printf("\r%09llx: reading... ", test_ofs);
|
||||
fflush(stdout);
|
||||
|
||||
len = pread(fd, rbuf, meminfo.erasesize, test_ofs);
|
||||
if (len < meminfo.erasesize) {
|
||||
printf("\n");
|
||||
if (len)
|
||||
fprintf(stderr, "Short read (%d bytes)\n", len);
|
||||
else
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (erase_and_write(test_ofs, wbuf, rbuf))
|
||||
continue;
|
||||
if (keep_contents)
|
||||
erase_and_write(test_ofs, kbuf, rbuf);
|
||||
}
|
||||
printf("\nFinished pass %d successfully\n", pass+1);
|
||||
}
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
@ -1,525 +0,0 @@
|
||||
/*
|
||||
* nandwrite.c
|
||||
*
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* 2003 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Overview:
|
||||
* This utility writes a binary image directly to a NAND flash
|
||||
* chip or NAND chips contained in DoC devices. This is the
|
||||
* "inverse operation" of nanddump.
|
||||
*
|
||||
* tglx: Major rewrite to handle bad blocks, write data with or without ECC
|
||||
* write oob data only on request
|
||||
*
|
||||
* Bug/ToDo:
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include "mtd/mtd-user.h"
|
||||
|
||||
#define PROGRAM "nandwrite"
|
||||
#define VERSION "$Revision: 1.1.1.1 $"
|
||||
|
||||
#define MAX_PAGE_SIZE 4096
|
||||
#define MAX_OOB_SIZE 128
|
||||
|
||||
/*
|
||||
* Buffer array used for writing data
|
||||
*/
|
||||
unsigned char writebuf[MAX_PAGE_SIZE];
|
||||
unsigned char oobbuf[MAX_OOB_SIZE];
|
||||
unsigned char oobreadbuf[MAX_OOB_SIZE];
|
||||
|
||||
// oob layouts to pass into the kernel as default
|
||||
struct nand_oobinfo none_oobinfo = {
|
||||
.useecc = MTD_NANDECC_OFF,
|
||||
};
|
||||
|
||||
struct nand_oobinfo jffs2_oobinfo = {
|
||||
.useecc = MTD_NANDECC_PLACE,
|
||||
.eccbytes = 6,
|
||||
.eccpos = { 0, 1, 2, 3, 6, 7 }
|
||||
};
|
||||
|
||||
struct nand_oobinfo yaffs_oobinfo = {
|
||||
.useecc = MTD_NANDECC_PLACE,
|
||||
.eccbytes = 6,
|
||||
.eccpos = { 8, 9, 10, 13, 14, 15}
|
||||
};
|
||||
|
||||
struct nand_oobinfo autoplace_oobinfo = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE
|
||||
};
|
||||
|
||||
void display_help (void)
|
||||
{
|
||||
printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n"
|
||||
"Writes to the specified MTD device.\n"
|
||||
"\n"
|
||||
" -a, --autoplace Use auto oob layout\n"
|
||||
" -j, --jffs2 force jffs2 oob layout (legacy support)\n"
|
||||
" -y, --yaffs force yaffs oob layout (legacy support)\n"
|
||||
" -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n"
|
||||
" -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(0);
|
||||
}
|
||||
|
||||
void display_version (void)
|
||||
{
|
||||
printf(PROGRAM " " VERSION "\n"
|
||||
"\n"
|
||||
"Copyright (C) 2003 Thomas Gleixner \n"
|
||||
"\n"
|
||||
PROGRAM " comes with NO WARRANTY\n"
|
||||
"to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"You may redistribute copies of " PROGRAM "\n"
|
||||
"under the terms of the GNU General Public Licence.\n"
|
||||
"See the file `COPYING' for more information.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
char *mtd_device, *img;
|
||||
int mtdoffset = 0;
|
||||
int quiet = 0;
|
||||
int writeoob = 0;
|
||||
int markbad = 0;
|
||||
int autoplace = 0;
|
||||
int forcejffs2 = 0;
|
||||
int forceyaffs = 0;
|
||||
int forcelegacy = 0;
|
||||
int noecc = 0;
|
||||
int pad = 0;
|
||||
int blockalign = 1; /*default to using 16K block size */
|
||||
|
||||
void process_options (int argc, char *argv[])
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "ab: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 = 1;
|
||||
break;
|
||||
case 'a':
|
||||
autoplace = 1;
|
||||
break;
|
||||
case 'j':
|
||||
forcejffs2 = 1;
|
||||
break;
|
||||
case 'y':
|
||||
forceyaffs = 1;
|
||||
break;
|
||||
case 'f':
|
||||
forcelegacy = 1;
|
||||
break;
|
||||
case 'n':
|
||||
noecc = 1;
|
||||
break;
|
||||
case 'm':
|
||||
markbad = 1;
|
||||
break;
|
||||
case 'o':
|
||||
writeoob = 1;
|
||||
break;
|
||||
case 'p':
|
||||
pad = 1;
|
||||
break;
|
||||
case 's':
|
||||
mtdoffset = strtol (optarg, NULL, 0);
|
||||
break;
|
||||
case 'b':
|
||||
blockalign = atoi (optarg);
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 2 || error)
|
||||
display_help ();
|
||||
|
||||
mtd_device = argv[optind++];
|
||||
img = argv[optind];
|
||||
}
|
||||
|
||||
/*
|
||||
* Main program
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1;
|
||||
struct mtd_info_user meminfo;
|
||||
struct mtd_oob_buf oob;
|
||||
loff_mtd_t offs;
|
||||
int ret, readlen;
|
||||
int oobinfochanged = 0;
|
||||
struct nand_oobinfo old_oobinfo;
|
||||
|
||||
printf("Warning: nandwrite_mlc instead of nandwrite is used for MLC NAND!\n");
|
||||
|
||||
process_options(argc, argv);
|
||||
|
||||
memset(oobbuf, 0xff, sizeof(oobbuf));
|
||||
|
||||
if (pad && writeoob) {
|
||||
fprintf(stderr, "Can't pad when oob data is present.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Open the device */
|
||||
if ((fd = open(mtd_device, O_RDWR)) == -1) {
|
||||
perror("open flash");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Fill in MTD device capability structure */
|
||||
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
|
||||
perror("MEMGETINFO");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set erasesize to specified number of blocks - to match jffs2
|
||||
* (virtual) block size */
|
||||
meminfo.erasesize *= blockalign;
|
||||
|
||||
/* Make sure device page sizes are valid */
|
||||
if (!(meminfo.oobsize == 16 && meminfo.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(1);
|
||||
}
|
||||
|
||||
if (autoplace) {
|
||||
/* Read the current oob info */
|
||||
if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
|
||||
perror ("MEMGETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// autoplace ECC ?
|
||||
if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
|
||||
|
||||
if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
oobinfochanged = 1;
|
||||
}
|
||||
}
|
||||
|
||||
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 (1);
|
||||
}
|
||||
if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
oobinfochanged = 1;
|
||||
break;
|
||||
default:
|
||||
perror ("MTDFILEMODE");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* force oob layout for jffs2 or yaffs ?
|
||||
* Legacy support
|
||||
*/
|
||||
if (forcejffs2 || forceyaffs) {
|
||||
struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
|
||||
|
||||
if (autoplace) {
|
||||
fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n");
|
||||
goto restoreoob;
|
||||
}
|
||||
if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) {
|
||||
fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n");
|
||||
goto restoreoob;
|
||||
}
|
||||
if (meminfo.oobsize == 8) {
|
||||
if (forceyaffs) {
|
||||
fprintf (stderr, "YAFSS cannot operate on 256 Byte page size");
|
||||
goto restoreoob;
|
||||
}
|
||||
/* Adjust number of ecc bytes */
|
||||
jffs2_oobinfo.eccbytes = 3;
|
||||
}
|
||||
|
||||
if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
goto restoreoob;
|
||||
}
|
||||
}
|
||||
|
||||
oob.length = meminfo.oobsize;
|
||||
oob.ptr = noecc ? oobreadbuf : oobbuf;
|
||||
|
||||
/* Open the input file */
|
||||
if ((ifd = open(img, O_RDONLY)) == -1) {
|
||||
perror("open input file");
|
||||
goto restoreoob;
|
||||
}
|
||||
|
||||
// get image length
|
||||
imglen = lseek(ifd, 0, SEEK_END);
|
||||
lseek (ifd, 0, SEEK_SET);
|
||||
|
||||
pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0);
|
||||
|
||||
// Check, if file is pagealigned
|
||||
if ((!pad) && ((imglen % pagelen) != 0)) {
|
||||
fprintf (stderr, "Input file is not page aligned\n");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
// Check, if length fits into device
|
||||
if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
|
||||
fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %llu bytes\n",
|
||||
imglen, pagelen, meminfo.writesize, meminfo.size);
|
||||
perror ("Input file does not fit into device");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
/* Get data from input and write to the device */
|
||||
while (imglen && (mtdoffset < meminfo.size)) {
|
||||
// new eraseblock , check for bad block(s)
|
||||
// Stay in the loop to be sure if the mtdoffset changes because
|
||||
// of a bad block, that the next block that will be written to
|
||||
// is also checked. Thus avoiding errors if the block(s) after the
|
||||
// skipped block(s) is also bad (number of blocks depending on
|
||||
// the blockalign
|
||||
while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
|
||||
blockstart = mtdoffset & (~meminfo.erasesize + 1);
|
||||
offs = blockstart;
|
||||
baderaseblock = 0;
|
||||
if (!quiet)
|
||||
fprintf (stdout, "Writing data to block %x\n", blockstart);
|
||||
|
||||
/* Check all the blocks in an erase block for bad blocks */
|
||||
do {
|
||||
if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
|
||||
perror("ioctl(MEMGETBADBLOCK)");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
if (ret == 1) {
|
||||
baderaseblock = 1;
|
||||
if (!quiet)
|
||||
fprintf (stderr, "Bad block at %x, %u block(s) "
|
||||
"from %x will be skipped\n",
|
||||
(int) offs, blockalign, blockstart);
|
||||
}
|
||||
|
||||
if (baderaseblock) {
|
||||
mtdoffset = blockstart + meminfo.erasesize;
|
||||
}
|
||||
offs += meminfo.erasesize / blockalign ;
|
||||
} while ( offs < blockstart + meminfo.erasesize );
|
||||
|
||||
}
|
||||
|
||||
readlen = meminfo.writesize;
|
||||
if (pad && (imglen < readlen))
|
||||
{
|
||||
readlen = imglen;
|
||||
memset(writebuf + readlen, 0xff, meminfo.writesize - readlen);
|
||||
}
|
||||
|
||||
/* Read Page Data from input file */
|
||||
if ((cnt = read(ifd, writebuf, readlen)) != readlen) {
|
||||
if (cnt == 0) // EOF
|
||||
break;
|
||||
perror ("File I/O error on input file");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
if (writeoob) {
|
||||
/* Read OOB data from input file, exit on failure */
|
||||
if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) {
|
||||
perror ("File I/O error on input file");
|
||||
goto closeall;
|
||||
}
|
||||
if (!noecc) {
|
||||
int i, start, len;
|
||||
/*
|
||||
* We use autoplacement and have the oobinfo with the autoplacement
|
||||
* information from the kernel available
|
||||
*
|
||||
* Modified to support out of order oobfree segments,
|
||||
* such as the layout used by diskonchip.c
|
||||
*/
|
||||
if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) {
|
||||
for (i = 0;old_oobinfo.oobfree[i][1]; i++) {
|
||||
/* Set the reserved bytes to 0xff */
|
||||
start = old_oobinfo.oobfree[i][0];
|
||||
len = old_oobinfo.oobfree[i][1];
|
||||
memcpy(oobbuf + start,
|
||||
oobreadbuf + start,
|
||||
len);
|
||||
}
|
||||
} else {
|
||||
/* Set at least the ecc byte positions to 0xff */
|
||||
start = old_oobinfo.eccbytes;
|
||||
len = meminfo.oobsize - start;
|
||||
memcpy(oobbuf + start,
|
||||
oobreadbuf + start,
|
||||
len);
|
||||
}
|
||||
}
|
||||
/* Write OOB data first, as ecc will be placed in there*/
|
||||
oob.start = mtdoffset;
|
||||
if (ioctl(fd, MEMWRITEOOB, &oob) != 0) {
|
||||
perror ("ioctl(MEMWRITEOOB)");
|
||||
goto closeall;
|
||||
}
|
||||
imglen -= meminfo.oobsize;
|
||||
}
|
||||
|
||||
/* Write out the Page data */
|
||||
if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) {
|
||||
int rewind_blocks;
|
||||
off_t rewind_bytes;
|
||||
erase_info_t erase;
|
||||
|
||||
perror ("pwrite");
|
||||
/* Must rewind to blockstart if we can */
|
||||
rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */
|
||||
rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
|
||||
if (writeoob)
|
||||
rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize;
|
||||
if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) {
|
||||
perror("lseek");
|
||||
fprintf(stderr, "Failed to seek backwards to recover from write error\n");
|
||||
goto closeall;
|
||||
}
|
||||
erase.start = blockstart;
|
||||
erase.length = meminfo.erasesize;
|
||||
fprintf(stderr, "Erasing failed write from 0x%09llx-0x%09llx\n",
|
||||
erase.start, erase.start+erase.length-1);
|
||||
if (ioctl(fd, MEMERASE, &erase) != 0) {
|
||||
perror("MEMERASE");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
if (markbad) {
|
||||
loff_mtd_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1);
|
||||
fprintf(stderr, "Marking block at %09llx bad\n", (long long)bad_addr);
|
||||
if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
|
||||
perror("MEMSETBADBLOCK");
|
||||
/* But continue anyway */
|
||||
}
|
||||
}
|
||||
mtdoffset = blockstart + meminfo.erasesize;
|
||||
imglen += rewind_blocks * meminfo.writesize;
|
||||
|
||||
continue;
|
||||
}
|
||||
imglen -= readlen;
|
||||
mtdoffset += meminfo.writesize;
|
||||
}
|
||||
|
||||
closeall:
|
||||
close(ifd);
|
||||
|
||||
restoreoob:
|
||||
if (oobinfochanged == 1) {
|
||||
if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if (imglen > 0) {
|
||||
perror ("Data was only partially written due to error\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
@ -1,446 +0,0 @@
|
||||
/*
|
||||
* nandwrite.c
|
||||
*
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* 2003 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Overview:
|
||||
* This utility writes a binary image directly to a NAND flash
|
||||
* chip or NAND chips contained in DoC devices. This is the
|
||||
* "inverse operation" of nanddump.
|
||||
*
|
||||
* tglx: Major rewrite to handle bad blocks, write data with or without ECC
|
||||
* write oob data only on request
|
||||
*
|
||||
* Bug/ToDo:
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include "mtd/mtd-user.h"
|
||||
|
||||
#define PROGRAM "nandwrite"
|
||||
#define VERSION "$Revision: 1.1.1.1 $"
|
||||
|
||||
#define MAX_PAGE_SIZE 8192
|
||||
#define MAX_OOB_SIZE 256
|
||||
/*
|
||||
* Buffer array used for writing data
|
||||
*/
|
||||
unsigned char writebuf[MAX_PAGE_SIZE];
|
||||
unsigned char oobreadbuf[MAX_OOB_SIZE];
|
||||
|
||||
// oob layouts to pass into the kernel as default
|
||||
struct nand_oobinfo none_oobinfo = {
|
||||
.useecc = MTD_NANDECC_OFF,
|
||||
};
|
||||
|
||||
struct nand_oobinfo jffs2_oobinfo = {
|
||||
.useecc = MTD_NANDECC_PLACE,
|
||||
.eccbytes = 6,
|
||||
.eccpos = { 0, 1, 2, 3, 6, 7 }
|
||||
};
|
||||
|
||||
struct nand_oobinfo yaffs_oobinfo = {
|
||||
.useecc = MTD_NANDECC_PLACE,
|
||||
.eccbytes = 6,
|
||||
.eccpos = { 8, 9, 10, 13, 14, 15}
|
||||
};
|
||||
|
||||
struct nand_oobinfo autoplace_oobinfo = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
.eccbytes = 36
|
||||
};
|
||||
|
||||
void display_help (void)
|
||||
{
|
||||
printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n"
|
||||
"Writes to the specified MTD device.\n"
|
||||
"\n"
|
||||
" -a, --autoplace Use auto oob layout\n"
|
||||
" -j, --jffs2 force jffs2 oob layout (legacy support)\n"
|
||||
" -y, --yaffs force yaffs oob layout (legacy support)\n"
|
||||
" -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n"
|
||||
" -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(0);
|
||||
}
|
||||
|
||||
void display_version (void)
|
||||
{
|
||||
printf(PROGRAM " " VERSION "\n"
|
||||
"\n"
|
||||
"Copyright (C) 2003 Thomas Gleixner \n"
|
||||
"\n"
|
||||
PROGRAM " comes with NO WARRANTY\n"
|
||||
"to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"You may redistribute copies of " PROGRAM "\n"
|
||||
"under the terms of the GNU General Public Licence.\n"
|
||||
"See the file `COPYING' for more information.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
char *mtd_device, *img;
|
||||
unsigned long long mtdoffset = 0;
|
||||
int quiet = 0;
|
||||
int writeoob = 0;
|
||||
int markbad = 1;
|
||||
int autoplace = 0;
|
||||
int forcejffs2 = 0;
|
||||
int forceyaffs = 0;
|
||||
int forcelegacy = 0;
|
||||
int noecc = 0;
|
||||
int pad = 0;
|
||||
int blockalign = 1; /*default to using 16K block size */
|
||||
|
||||
void process_options (int argc, char *argv[])
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for (;;) {
|
||||
int option_index = 0;
|
||||
static const char *short_options = "ab: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 = 1;
|
||||
break;
|
||||
case 'a':
|
||||
autoplace = 1;
|
||||
break;
|
||||
case 'j':
|
||||
forcejffs2 = 1;
|
||||
break;
|
||||
case 'y':
|
||||
forceyaffs = 1;
|
||||
break;
|
||||
case 'f':
|
||||
forcelegacy = 1;
|
||||
break;
|
||||
case 'n':
|
||||
noecc = 1;
|
||||
break;
|
||||
case 'm':
|
||||
markbad = 1;
|
||||
break;
|
||||
case 'o':
|
||||
writeoob = 1;
|
||||
break;
|
||||
case 'p':
|
||||
pad = 1;
|
||||
break;
|
||||
case 's':
|
||||
mtdoffset = strtol (optarg, NULL, 0);
|
||||
break;
|
||||
case 'b':
|
||||
blockalign = atoi (optarg);
|
||||
break;
|
||||
case '?':
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 2 || error)
|
||||
display_help ();
|
||||
|
||||
mtd_device = argv[optind++];
|
||||
img = argv[optind];
|
||||
}
|
||||
|
||||
/*
|
||||
* Main program
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1;
|
||||
struct mtd_info_user meminfo;
|
||||
struct mtd_page_buf oob;
|
||||
loff_mtd_t offs;
|
||||
int ret, readlen;
|
||||
int oobinfochanged = 0;
|
||||
struct nand_oobinfo old_oobinfo;
|
||||
int i;
|
||||
|
||||
process_options(argc, argv);
|
||||
|
||||
if (pad && writeoob) {
|
||||
fprintf(stderr, "Can't pad when oob data is present.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Open the device */
|
||||
if ((fd = open(mtd_device, O_RDWR)) == -1) {
|
||||
perror("open flash");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Fill in MTD device capability structure */
|
||||
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
|
||||
perror("MEMGETINFO");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set erasesize to specified number of blocks - to match jffs2
|
||||
* (virtual) block size */
|
||||
meminfo.erasesize *= blockalign;
|
||||
|
||||
/* Make sure device page sizes are valid */
|
||||
if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) &&
|
||||
!(meminfo.oobsize == 8 && meminfo.writesize == 256) &&
|
||||
!(meminfo.oobsize == 64 && meminfo.writesize == 2048) &&
|
||||
!(meminfo.oobsize == 128 && meminfo.writesize == 4096) &&
|
||||
!(meminfo.oobsize == 256 && meminfo.writesize == 8192)) {
|
||||
fprintf(stderr, "Unknown flash (not normal NAND)\n");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (autoplace) {
|
||||
/* Read the current oob info */
|
||||
if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
|
||||
perror ("MEMGETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// autoplace ECC ?
|
||||
if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
|
||||
|
||||
if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
oobinfochanged = 1;
|
||||
}
|
||||
}
|
||||
|
||||
memset(oobreadbuf, 0xff, MAX_OOB_SIZE);
|
||||
|
||||
if (autoplace) {
|
||||
oob.ooblength = meminfo.oobsize-old_oobinfo.eccbytes; /* Get ooblength from kernel */
|
||||
printf("oobsize=%d eccbytes=%d\n", meminfo.oobsize, old_oobinfo.eccbytes);
|
||||
} else {
|
||||
oob.ooblength = meminfo.oobsize-autoplace_oobinfo.eccbytes;
|
||||
printf("oobsize=%d eccbytes=%d\n", meminfo.oobsize, autoplace_oobinfo.eccbytes);
|
||||
}
|
||||
|
||||
oob.oobptr = oobreadbuf;
|
||||
oob.datptr = writebuf;
|
||||
|
||||
/* Open the input file */
|
||||
if ((ifd = open(img, O_RDONLY)) == -1) {
|
||||
perror("open input file");
|
||||
goto restoreoob;
|
||||
}
|
||||
|
||||
// get image length
|
||||
imglen = lseek(ifd, 0, SEEK_END);
|
||||
lseek (ifd, 0, SEEK_SET);
|
||||
|
||||
pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0);
|
||||
|
||||
// Check, if file is pagealigned
|
||||
if ((!pad) && ((imglen % pagelen) != 0)) {
|
||||
fprintf (stderr, "Input file is not page aligned\n");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
// Check, if length fits into device
|
||||
if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
|
||||
fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %lld bytes\n",
|
||||
imglen, pagelen, meminfo.writesize, meminfo.size);
|
||||
perror ("Input file does not fit into device");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
/* Get data from input and write to the device */
|
||||
while (imglen && (mtdoffset < meminfo.size)) {
|
||||
// new eraseblock , check for bad block(s)
|
||||
// Stay in the loop to be sure if the mtdoffset changes because
|
||||
// of a bad block, that the next block that will be written to
|
||||
// is also checked. Thus avoiding errors if the block(s) after the
|
||||
// skipped block(s) is also bad (number of blocks depending on
|
||||
// the blockalign
|
||||
while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
|
||||
blockstart = mtdoffset & (~meminfo.erasesize + 1);
|
||||
offs = blockstart;
|
||||
baderaseblock = 0;
|
||||
i=0;
|
||||
if (!quiet)
|
||||
fprintf (stdout, "Writing data to block 0x%x\n", blockstart);
|
||||
|
||||
/* Check all the blocks in an erase block for bad blocks */
|
||||
do {
|
||||
if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
|
||||
perror("ioctl(MEMGETBADBLOCK)");
|
||||
goto closeall;
|
||||
}
|
||||
if (ret == 1) {
|
||||
baderaseblock = 1;
|
||||
if (!quiet)
|
||||
fprintf (stderr, "Bad block at 0x%llx, %u block(s) "
|
||||
"from 0x%x will be skipped\n",
|
||||
offs, blockalign, blockstart);
|
||||
}
|
||||
|
||||
if (baderaseblock) {
|
||||
mtdoffset = blockstart + meminfo.erasesize;
|
||||
}
|
||||
offs += meminfo.erasesize / blockalign ;
|
||||
} while ( offs < blockstart + meminfo.erasesize );
|
||||
|
||||
}
|
||||
|
||||
readlen = meminfo.writesize;
|
||||
if (pad && (imglen < readlen))
|
||||
{
|
||||
readlen = imglen;
|
||||
memset(writebuf + readlen, 0xff, meminfo.writesize - readlen);
|
||||
}
|
||||
|
||||
/* Read Page Data from input file */
|
||||
if ((cnt = read(ifd, writebuf, readlen)) != readlen) {
|
||||
if (cnt == 0) // EOF
|
||||
break;
|
||||
perror ("File I/O error 1 on input file");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
/* Read OOB data from input file, exit on failure */
|
||||
if(writeoob) {
|
||||
if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) {
|
||||
perror ("File I/O error 2 on input file");
|
||||
goto closeall;
|
||||
}
|
||||
}
|
||||
oob.start = mtdoffset;
|
||||
|
||||
// write a page include its oob to nand
|
||||
ioctl(fd, MEMWRITEPAGE, &oob);
|
||||
if(oob.datlength != meminfo.writesize){
|
||||
perror ("ioctl(MEMWRITEPAGE)");
|
||||
|
||||
int rewind_blocks;
|
||||
off_t rewind_bytes;
|
||||
erase_info_t erase;
|
||||
|
||||
/* Must rewind to blockstart if we can */
|
||||
rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */
|
||||
rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
|
||||
if (writeoob)
|
||||
rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize;
|
||||
if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) {
|
||||
perror("lseek");
|
||||
fprintf(stderr, "Failed to seek backwards to recover from write error\n");
|
||||
goto closeall;
|
||||
}
|
||||
erase.start = blockstart;
|
||||
erase.length = meminfo.erasesize;
|
||||
fprintf(stderr, "Erasing failed write from 0x%09llx-0x%09llx\n",
|
||||
erase.start, erase.start+erase.length-1);
|
||||
if (ioctl(fd, MEMERASE, &erase) != 0) {
|
||||
perror("MEMERASE");
|
||||
goto closeall;
|
||||
}
|
||||
|
||||
if (markbad) {
|
||||
loff_mtd_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1);
|
||||
fprintf(stderr, "Marking block at %09llx bad\n", (long long)bad_addr);
|
||||
if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
|
||||
perror("MEMSETBADBLOCK");
|
||||
/* But continue anyway */
|
||||
}
|
||||
}
|
||||
mtdoffset = blockstart + meminfo.erasesize;
|
||||
imglen += rewind_blocks * meminfo.writesize;
|
||||
|
||||
continue;
|
||||
}
|
||||
if(writeoob)
|
||||
imglen -= meminfo.oobsize;
|
||||
imglen -= readlen;
|
||||
mtdoffset += meminfo.writesize;
|
||||
}
|
||||
|
||||
closeall:
|
||||
close(ifd);
|
||||
|
||||
restoreoob:
|
||||
if (oobinfochanged == 1) {
|
||||
if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
|
||||
perror ("MEMSETOOBSEL");
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if (imglen > 0) {
|
||||
perror ("Data was only partially written due to error\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
@ -1,419 +0,0 @@
|
||||
/*
|
||||
* nftl_format.c: Creating a NFTL/INFTL partition on an MTD device
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* ToDo:
|
||||
* 1. UnitSizeFactor != 0xFF cases
|
||||
* 2. test, test, and test !!!
|
||||
*/
|
||||
|
||||
#define _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);
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
/*
|
||||
* nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk"
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* ToDo:
|
||||
* 1. UnitSizeFactor != 0xFF cases
|
||||
* 2. test, test, and test !!!
|
||||
*/
|
||||
|
||||
#define _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);
|
||||
}
|
@ -1,390 +0,0 @@
|
||||
/*
|
||||
Red Black Trees
|
||||
(C) 1999 Andrea Arcangeli <andrea@suse.de>
|
||||
(C) 2002 David Woodhouse <dwmw2@infradead.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
linux/lib/rbtree.c
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "rbtree.h"
|
||||
|
||||
static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
|
||||
{
|
||||
struct rb_node *right = node->rb_right;
|
||||
struct rb_node *parent = rb_parent(node);
|
||||
|
||||
if ((node->rb_right = right->rb_left))
|
||||
rb_set_parent(right->rb_left, node);
|
||||
right->rb_left = node;
|
||||
|
||||
rb_set_parent(right, parent);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
if (node == parent->rb_left)
|
||||
parent->rb_left = right;
|
||||
else
|
||||
parent->rb_right = right;
|
||||
}
|
||||
else
|
||||
root->rb_node = right;
|
||||
rb_set_parent(node, right);
|
||||
}
|
||||
|
||||
static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
|
||||
{
|
||||
struct rb_node *left = node->rb_left;
|
||||
struct rb_node *parent = rb_parent(node);
|
||||
|
||||
if ((node->rb_left = left->rb_right))
|
||||
rb_set_parent(left->rb_right, node);
|
||||
left->rb_right = node;
|
||||
|
||||
rb_set_parent(left, parent);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
if (node == parent->rb_right)
|
||||
parent->rb_right = left;
|
||||
else
|
||||
parent->rb_left = left;
|
||||
}
|
||||
else
|
||||
root->rb_node = left;
|
||||
rb_set_parent(node, left);
|
||||
}
|
||||
|
||||
void rb_insert_color(struct rb_node *node, struct rb_root *root)
|
||||
{
|
||||
struct rb_node *parent, *gparent;
|
||||
|
||||
while ((parent = rb_parent(node)) && rb_is_red(parent))
|
||||
{
|
||||
gparent = rb_parent(parent);
|
||||
|
||||
if (parent == gparent->rb_left)
|
||||
{
|
||||
{
|
||||
register struct rb_node *uncle = gparent->rb_right;
|
||||
if (uncle && rb_is_red(uncle))
|
||||
{
|
||||
rb_set_black(uncle);
|
||||
rb_set_black(parent);
|
||||
rb_set_red(gparent);
|
||||
node = gparent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->rb_right == node)
|
||||
{
|
||||
register struct rb_node *tmp;
|
||||
__rb_rotate_left(parent, root);
|
||||
tmp = parent;
|
||||
parent = node;
|
||||
node = tmp;
|
||||
}
|
||||
|
||||
rb_set_black(parent);
|
||||
rb_set_red(gparent);
|
||||
__rb_rotate_right(gparent, root);
|
||||
} else {
|
||||
{
|
||||
register struct rb_node *uncle = gparent->rb_left;
|
||||
if (uncle && rb_is_red(uncle))
|
||||
{
|
||||
rb_set_black(uncle);
|
||||
rb_set_black(parent);
|
||||
rb_set_red(gparent);
|
||||
node = gparent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->rb_left == node)
|
||||
{
|
||||
register struct rb_node *tmp;
|
||||
__rb_rotate_right(parent, root);
|
||||
tmp = parent;
|
||||
parent = node;
|
||||
node = tmp;
|
||||
}
|
||||
|
||||
rb_set_black(parent);
|
||||
rb_set_red(gparent);
|
||||
__rb_rotate_left(gparent, root);
|
||||
}
|
||||
}
|
||||
|
||||
rb_set_black(root->rb_node);
|
||||
}
|
||||
|
||||
static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
|
||||
struct rb_root *root)
|
||||
{
|
||||
struct rb_node *other;
|
||||
|
||||
while ((!node || rb_is_black(node)) && node != root->rb_node)
|
||||
{
|
||||
if (parent->rb_left == node)
|
||||
{
|
||||
other = parent->rb_right;
|
||||
if (rb_is_red(other))
|
||||
{
|
||||
rb_set_black(other);
|
||||
rb_set_red(parent);
|
||||
__rb_rotate_left(parent, root);
|
||||
other = parent->rb_right;
|
||||
}
|
||||
if ((!other->rb_left || rb_is_black(other->rb_left)) &&
|
||||
(!other->rb_right || rb_is_black(other->rb_right)))
|
||||
{
|
||||
rb_set_red(other);
|
||||
node = parent;
|
||||
parent = rb_parent(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!other->rb_right || rb_is_black(other->rb_right))
|
||||
{
|
||||
struct rb_node *o_left;
|
||||
if ((o_left = other->rb_left))
|
||||
rb_set_black(o_left);
|
||||
rb_set_red(other);
|
||||
__rb_rotate_right(other, root);
|
||||
other = parent->rb_right;
|
||||
}
|
||||
rb_set_color(other, rb_color(parent));
|
||||
rb_set_black(parent);
|
||||
if (other->rb_right)
|
||||
rb_set_black(other->rb_right);
|
||||
__rb_rotate_left(parent, root);
|
||||
node = root->rb_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
other = parent->rb_left;
|
||||
if (rb_is_red(other))
|
||||
{
|
||||
rb_set_black(other);
|
||||
rb_set_red(parent);
|
||||
__rb_rotate_right(parent, root);
|
||||
other = parent->rb_left;
|
||||
}
|
||||
if ((!other->rb_left || rb_is_black(other->rb_left)) &&
|
||||
(!other->rb_right || rb_is_black(other->rb_right)))
|
||||
{
|
||||
rb_set_red(other);
|
||||
node = parent;
|
||||
parent = rb_parent(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!other->rb_left || rb_is_black(other->rb_left))
|
||||
{
|
||||
register struct rb_node *o_right;
|
||||
if ((o_right = other->rb_right))
|
||||
rb_set_black(o_right);
|
||||
rb_set_red(other);
|
||||
__rb_rotate_left(other, root);
|
||||
other = parent->rb_left;
|
||||
}
|
||||
rb_set_color(other, rb_color(parent));
|
||||
rb_set_black(parent);
|
||||
if (other->rb_left)
|
||||
rb_set_black(other->rb_left);
|
||||
__rb_rotate_right(parent, root);
|
||||
node = root->rb_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node)
|
||||
rb_set_black(node);
|
||||
}
|
||||
|
||||
void rb_erase(struct rb_node *node, struct rb_root *root)
|
||||
{
|
||||
struct rb_node *child, *parent;
|
||||
int color;
|
||||
|
||||
if (!node->rb_left)
|
||||
child = node->rb_right;
|
||||
else if (!node->rb_right)
|
||||
child = node->rb_left;
|
||||
else
|
||||
{
|
||||
struct rb_node *old = node, *left;
|
||||
|
||||
node = node->rb_right;
|
||||
while ((left = node->rb_left) != NULL)
|
||||
node = left;
|
||||
child = node->rb_right;
|
||||
parent = rb_parent(node);
|
||||
color = rb_color(node);
|
||||
|
||||
if (child)
|
||||
rb_set_parent(child, parent);
|
||||
if (parent == old) {
|
||||
parent->rb_right = child;
|
||||
parent = node;
|
||||
} else
|
||||
parent->rb_left = child;
|
||||
|
||||
node->rb_parent_color = old->rb_parent_color;
|
||||
node->rb_right = old->rb_right;
|
||||
node->rb_left = old->rb_left;
|
||||
|
||||
if (rb_parent(old))
|
||||
{
|
||||
if (rb_parent(old)->rb_left == old)
|
||||
rb_parent(old)->rb_left = node;
|
||||
else
|
||||
rb_parent(old)->rb_right = node;
|
||||
} else
|
||||
root->rb_node = node;
|
||||
|
||||
rb_set_parent(old->rb_left, node);
|
||||
if (old->rb_right)
|
||||
rb_set_parent(old->rb_right, node);
|
||||
goto color;
|
||||
}
|
||||
|
||||
parent = rb_parent(node);
|
||||
color = rb_color(node);
|
||||
|
||||
if (child)
|
||||
rb_set_parent(child, parent);
|
||||
if (parent)
|
||||
{
|
||||
if (parent->rb_left == node)
|
||||
parent->rb_left = child;
|
||||
else
|
||||
parent->rb_right = child;
|
||||
}
|
||||
else
|
||||
root->rb_node = child;
|
||||
|
||||
color:
|
||||
if (color == RB_BLACK)
|
||||
__rb_erase_color(child, parent, root);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the first node (in sort order) of the tree.
|
||||
*/
|
||||
struct rb_node *rb_first(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *n;
|
||||
|
||||
n = root->rb_node;
|
||||
if (!n)
|
||||
return NULL;
|
||||
while (n->rb_left)
|
||||
n = n->rb_left;
|
||||
return n;
|
||||
}
|
||||
|
||||
struct rb_node *rb_last(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *n;
|
||||
|
||||
n = root->rb_node;
|
||||
if (!n)
|
||||
return NULL;
|
||||
while (n->rb_right)
|
||||
n = n->rb_right;
|
||||
return n;
|
||||
}
|
||||
|
||||
struct rb_node *rb_next(struct rb_node *node)
|
||||
{
|
||||
struct rb_node *parent;
|
||||
|
||||
if (rb_parent(node) == node)
|
||||
return NULL;
|
||||
|
||||
/* If we have a right-hand child, go down and then left as far
|
||||
as we can. */
|
||||
if (node->rb_right) {
|
||||
node = node->rb_right;
|
||||
while (node->rb_left)
|
||||
node=node->rb_left;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* No right-hand children. Everything down and left is
|
||||
smaller than us, so any 'next' node must be in the general
|
||||
direction of our parent. Go up the tree; any time the
|
||||
ancestor is a right-hand child of its parent, keep going
|
||||
up. First time it's a left-hand child of its parent, said
|
||||
parent is our 'next' node. */
|
||||
while ((parent = rb_parent(node)) && node == parent->rb_right)
|
||||
node = parent;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
struct rb_node *rb_prev(struct rb_node *node)
|
||||
{
|
||||
struct rb_node *parent;
|
||||
|
||||
if (rb_parent(node) == node)
|
||||
return NULL;
|
||||
|
||||
/* If we have a left-hand child, go down and then right as far
|
||||
as we can. */
|
||||
if (node->rb_left) {
|
||||
node = node->rb_left;
|
||||
while (node->rb_right)
|
||||
node=node->rb_right;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* No left-hand children. Go up till we find an ancestor which
|
||||
is a right-hand child of its parent */
|
||||
while ((parent = rb_parent(node)) && node == parent->rb_left)
|
||||
node = parent;
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
void rb_replace_node(struct rb_node *victim, struct rb_node *new,
|
||||
struct rb_root *root)
|
||||
{
|
||||
struct rb_node *parent = rb_parent(victim);
|
||||
|
||||
/* Set the surrounding nodes to point to the replacement */
|
||||
if (parent) {
|
||||
if (victim == parent->rb_left)
|
||||
parent->rb_left = new;
|
||||
else
|
||||
parent->rb_right = new;
|
||||
} else {
|
||||
root->rb_node = new;
|
||||
}
|
||||
if (victim->rb_left)
|
||||
rb_set_parent(victim->rb_left, new);
|
||||
if (victim->rb_right)
|
||||
rb_set_parent(victim->rb_right, new);
|
||||
|
||||
/* Copy the pointers/colour from the victim to the replacement */
|
||||
*new = *victim;
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
Red Black Trees
|
||||
(C) 1999 Andrea Arcangeli <andrea@suse.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
linux/include/linux/rbtree.h
|
||||
|
||||
To use rbtrees you'll have to implement your own insert and search cores.
|
||||
This will avoid us to use callbacks and to drop drammatically performances.
|
||||
I know it's not the cleaner way, but in C (not in C++) to get
|
||||
performances and genericity...
|
||||
|
||||
Some example of insert and search follows here. The search is a plain
|
||||
normal search over an ordered tree. The insert instead must be implemented
|
||||
int two steps: as first thing the code must insert the element in
|
||||
order as a red leaf in the tree, then the support library function
|
||||
rb_insert_color() must be called. Such function will do the
|
||||
not trivial work to rebalance the rbtree if necessary.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
static inline struct page * rb_search_page_cache(struct inode * inode,
|
||||
unsigned long offset)
|
||||
{
|
||||
struct rb_node * n = inode->i_rb_page_cache.rb_node;
|
||||
struct page * page;
|
||||
|
||||
while (n)
|
||||
{
|
||||
page = rb_entry(n, struct page, rb_page_cache);
|
||||
|
||||
if (offset < page->offset)
|
||||
n = n->rb_left;
|
||||
else if (offset > page->offset)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return page;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct page * __rb_insert_page_cache(struct inode * inode,
|
||||
unsigned long offset,
|
||||
struct rb_node * node)
|
||||
{
|
||||
struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
|
||||
struct rb_node * parent = NULL;
|
||||
struct page * page;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
parent = *p;
|
||||
page = rb_entry(parent, struct page, rb_page_cache);
|
||||
|
||||
if (offset < page->offset)
|
||||
p = &(*p)->rb_left;
|
||||
else if (offset > page->offset)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
return page;
|
||||
}
|
||||
|
||||
rb_link_node(node, parent, p);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct page * rb_insert_page_cache(struct inode * inode,
|
||||
unsigned long offset,
|
||||
struct rb_node * node)
|
||||
{
|
||||
struct page * ret;
|
||||
if ((ret = __rb_insert_page_cache(inode, offset, node)))
|
||||
goto out;
|
||||
rb_insert_color(node, &inode->i_rb_page_cache);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
-----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_RBTREE_H
|
||||
#define _LINUX_RBTREE_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
struct rb_node
|
||||
{
|
||||
unsigned long rb_parent_color;
|
||||
#define RB_RED 0
|
||||
#define RB_BLACK 1
|
||||
struct rb_node *rb_right;
|
||||
struct rb_node *rb_left;
|
||||
} __attribute__((aligned(sizeof(long))));
|
||||
/* The alignment might seem pointless, but allegedly CRIS needs it */
|
||||
|
||||
struct rb_root
|
||||
{
|
||||
struct rb_node *rb_node;
|
||||
};
|
||||
|
||||
|
||||
#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
|
||||
#define rb_color(r) ((r)->rb_parent_color & 1)
|
||||
#define rb_is_red(r) (!rb_color(r))
|
||||
#define rb_is_black(r) rb_color(r)
|
||||
#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
|
||||
#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
|
||||
|
||||
static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
|
||||
{
|
||||
rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
|
||||
}
|
||||
static inline void rb_set_color(struct rb_node *rb, int color)
|
||||
{
|
||||
rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
|
||||
}
|
||||
|
||||
#define RB_ROOT (struct rb_root) { NULL, }
|
||||
|
||||
#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 */
|
@ -1,484 +0,0 @@
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
#define _USE_MISC
|
||||
|
||||
#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 <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 %08lx len %08lx (bad write was at %08lx)\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;
|
||||
}
|
@ -1,336 +0,0 @@
|
||||
/*
|
||||
* rfddump.c
|
||||
*
|
||||
* Copyright (C) 2005 Sean Young <sean@mess.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define _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;
|
||||
}
|
||||
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* rfdformat.c
|
||||
*
|
||||
* Copyright (C) 2005 Sean Young <sean@mess.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is very easy: just erase all the blocks and put the magic at
|
||||
* the beginning of each block.
|
||||
*/
|
||||
|
||||
#define _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;
|
||||
}
|
@ -1,299 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
|
||||
* Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* Patrik Kluba <pajko@halom.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*/
|
||||
|
||||
#ifndef JFFS2_SUMMARY_H
|
||||
#define JFFS2_SUMMARY_H
|
||||
|
||||
#include <linux/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
|
@ -1,951 +0,0 @@
|
||||
/*
|
||||
* sumtool.c
|
||||
*
|
||||
* Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* Ferenc Havasi <havasi@inf.u-szeged.hu>
|
||||
* University of Szeged, Hungary
|
||||
* 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Overview:
|
||||
* This is a utility insert summary information into JFFS2 image for
|
||||
* faster mount time
|
||||
*
|
||||
*/
|
||||
|
||||
#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.1.1.1 $";
|
||||
|
||||
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;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
|
||||
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
|
@ -1,173 +0,0 @@
|
||||
$Id: README,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $
|
||||
$Log: not supported by cvs2svn $
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
@ -1,695 +0,0 @@
|
||||
/*
|
||||
|
||||
* 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.1.1.1 2008-05-13 07:15:32 yrtan Exp $
|
||||
$Log: not supported by cvs2svn $
|
||||
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()*/
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
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.1.1.1 2008-05-13 07:15:32 yrtan Exp $
|
||||
$Log: not supported by cvs2svn $
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
/* $Id: common.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan 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
|
@ -1,264 +0,0 @@
|
||||
/*
|
||||
|
||||
* 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.1.1.1 2008-05-13 07:15:32 yrtan 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()*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
|
||||
SUBDIRS = lib simple stress integrity utils
|
||||
|
||||
all clean tests: $(SUBDIRS)
|
||||
|
||||
.PHONY: $(SUBDIRS)
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@ $(MAKECMDGOALS)
|
@ -1,27 +0,0 @@
|
||||
#!/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 -------------------------------------------------------------------------------
|
@ -1,22 +0,0 @@
|
||||
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +0,0 @@
|
||||
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -1,199 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/* 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;
|
||||
|
||||
#endif
|
@ -1,49 +0,0 @@
|
||||
#!/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
|
@ -1,28 +0,0 @@
|
||||
|
||||
ifeq ($(origin CC),default)
|
||||
CC = gcc
|
||||
endif
|
||||
|
||||
CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
|
||||
|
||||
LDFLAGS := $(LDFLAGS)
|
||||
|
||||
TARGETS = test_1 \
|
||||
test_2 \
|
||||
ftrunc \
|
||||
orph
|
||||
|
||||
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
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
|
||||
SUBDIRS = atoms
|
||||
|
||||
all tests: $(SUBDIRS)
|
||||
|
||||
clean: $(SUBDIRS)
|
||||
rm -rf run_pdf_test_file_*
|
||||
|
||||
.PHONY: $(SUBDIRS)
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@ $(MAKECMDGOALS)
|
@ -1,40 +0,0 @@
|
||||
|
||||
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
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
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;
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,431 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#!/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
|
@ -1,40 +0,0 @@
|
||||
#!/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
|
@ -1,19 +0,0 @@
|
||||
|
||||
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
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -1,340 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user