pax_global_header00006660000000000000000000000064117516736130014524gustar00rootroot0000000000000052 comment=ca39eb1d98e736109c64ff9c1aa2a6ecca222d8f mtd-utils-1.5.0/000077500000000000000000000000001175167361300134515ustar00rootroot00000000000000mtd-utils-1.5.0/.gitignore000066400000000000000000000013741175167361300154460ustar00rootroot00000000000000# # NOTE! Don't add files that are generated in specific # subdirectories here. Add them in the ".gitignore" file # in that subdirectory instead. # # Normal rules # .* *.gdb *.o *.a *.s *.ko *.so *.mod.c *~ # # Our programs # /doc_loadbios /docfdisk /flash_erase /flash_info /flash_lock /flash_otp_dump /flash_otp_info /flash_unlock /flashcp /ftl_check /ftl_format /jffs2dump /jffs2reader /mkfs.jffs2 /mtd_debug /nanddump /nandtest /nandwrite /nftl_format /nftldump /recv_image /rfddump /rfdformat /serve_image /sumtool # # Top-level generic files # # # git files that we don't want to ignore even it they are dot-files # !.gitignore # # Generated include files # /include/version.h # stgit generated dirs patches-* *.patch # cscope files cscope.* ncscope.* mtd-utils-1.5.0/COPYING000066400000000000000000000431271175167361300145130ustar00rootroot00000000000000 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. Copyright (C) 19yy 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. , 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. mtd-utils-1.5.0/MAKEDEV000077500000000000000000000014221175167361300144520ustar00rootroot00000000000000#!/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 mtd-utils-1.5.0/Makefile000066400000000000000000000060761175167361300151220ustar00rootroot00000000000000 # -*- sh -*- VERSION = 1.5.0 CPPFLAGS += -I./include -I$(BUILDDIR)/include -I./ubi-utils/include $(ZLIBCPPFLAGS) $(LZOCPPFLAGS) ifeq ($(WITHOUT_XATTR), 1) CPPFLAGS += -DWITHOUT_XATTR endif ifeq ($(WITHOUT_LZO), 1) CPPFLAGS += -DWITHOUT_LZO else LZOLDLIBS = -llzo2 endif TESTS = tests MTD_BINS = \ ftl_format flash_erase nanddump doc_loadbios \ ftl_check mkfs.jffs2 flash_lock flash_unlock \ flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite nandtest \ jffs2dump \ nftldump nftl_format docfdisk \ rfddump rfdformat \ serve_image recv_image \ sumtool jffs2reader UBI_BINS = \ ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol BINS = $(MTD_BINS) BINS += mkfs.ubifs/mkfs.ubifs BINS += $(addprefix ubi-utils/,$(UBI_BINS)) SCRIPTS = flash_eraseall TARGETS = $(BINS) TARGETS += lib/libmtd.a TARGETS += ubi-utils/libubi.a OBJDEPS = $(BUILDDIR)/include/version.h include common.mk CLEAN_FIND = find "$(BUILDDIR)/" -xdev '(' -name '*.[ao]' -o -name '.*.c.dep' ')' clean:: ifneq ($(BUILDDIR)/.git,) ifneq ($(BUILDDIR),.) ifneq ($(BUILDDIR),$(CURDIR)) rm -rf $(BUILDDIR) endif endif endif # findutils v4.1.x (RHEL 4) do not have '+' syntax @if test -d "$(BUILDDIR)/"; then \ $(CLEAN_FIND) -exec rm -f {} + 2> /dev/null || \ $(CLEAN_FIND) -exec rm -f {} \; ; \ fi rm -f $(BUILDDIR)/include/version.h $(MAKE) -C $(TESTS) clean install:: $(addprefix $(BUILDDIR)/,${BINS}) ${SCRIPTS} mkdir -p ${DESTDIR}/${SBINDIR} install -m 0755 $^ ${DESTDIR}/${SBINDIR}/ mkdir -p ${DESTDIR}/${MANDIR}/man1 install -m 0644 mkfs.jffs2.1 ${DESTDIR}/${MANDIR}/man1/ -gzip -9f ${DESTDIR}/${MANDIR}/man1/*.1 tests:: $(MAKE) -C $(TESTS) cscope: cscope -bR $(BUILDDIR)/include/version.h: $(BUILDDIR)/include/version.h.tmp $(call BECHO,CHK) $(Q)cmp -s $@ $@.tmp && rm -f $@.tmp || mv $@.tmp $@ $(BUILDDIR)/include/version.h.tmp: ${Q}mkdir -p $(dir $@) $(Q)echo '#define VERSION "$(VERSION)"' > $@ # # Utils in top level # obj-mkfs.jffs2 = compr_rtime.o compr_zlib.o compr_lzo.o compr.o rbtree.o LDFLAGS_mkfs.jffs2 = $(ZLIBLDFLAGS) $(LZOLDFLAGS) LDLIBS_mkfs.jffs2 = -lz $(LZOLDLIBS) LDFLAGS_jffs2reader = $(ZLIBLDFLAGS) $(LZOLDFLAGS) LDLIBS_jffs2reader = -lz $(LZOLDLIBS) $(foreach v,$(MTD_BINS),$(eval $(call mkdep,,$(v)))) # # Common libmtd # obj-libmtd.a = libmtd.o libmtd_legacy.o libcrc32.o libfec.o $(call _mkdep,lib/,libmtd.a) # # Utils in mkfs.ubifs subdir # obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \ hashtable/hashtable.o hashtable/hashtable_itr.o LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid $(call mkdep,mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a) # # Utils in ubi-utils/ subdir # obj-libiniparser.a = libiniparser.o dictionary.o obj-libscan.a = libscan.o obj-libubi.a = libubi.o obj-libubigen.a = libubigen.o obj-mtdinfo = libubigen.a obj-ubinize = libubigen.a libiniparser.a obj-ubiformat = libubigen.a libscan.a $(foreach v,libubi.a libubigen.a libiniparser.a libscan.a,$(eval $(call _mkdep,ubi-utils/,$(v)))) $(foreach v,$(UBI_BINS),$(eval $(call mkdep,ubi-utils/,$(v),libubi.a ubiutils-common.o))) mtd-utils-1.5.0/common.mk000066400000000000000000000037271175167361300153030ustar00rootroot00000000000000CC := $(CROSS)gcc AR := $(CROSS)ar RANLIB := $(CROSS)ranlib # Stolen from Linux build system comma = , try-run = $(shell set -e; ($(1)) >/dev/null 2>&1 && echo "$(2)" || echo "$(3)") cc-option = $(call try-run, $(CC) $(1) -c -xc /dev/null -o /dev/null,$(1),$(2)) CFLAGS ?= -O2 -g WFLAGS := -Wall \ $(call cc-option,-Wextra) \ $(call cc-option,-Wwrite-strings) \ $(call cc-option,-Wno-sign-compare) CFLAGS += $(WFLAGS) SECTION_CFLAGS := $(call cc-option,-ffunction-sections -fdata-sections -Wl$(comma)--gc-sections) CFLAGS += $(SECTION_CFLAGS) ifneq ($(WITHOUT_LARGEFILE), 1) CPPFLAGS += -D_FILE_OFFSET_BITS=64 endif DESTDIR?= PREFIX=/usr EXEC_PREFIX=$(PREFIX) SBINDIR=$(EXEC_PREFIX)/sbin MANDIR=$(PREFIX)/share/man INCLUDEDIR=$(PREFIX)/include ifndef BUILDDIR ifeq ($(origin CROSS),undefined) BUILDDIR := $(CURDIR) else # Remove the trailing slash to make the directory name BUILDDIR := $(CURDIR)/$(CROSS:-=) endif endif override BUILDDIR := $(patsubst %/,%,$(BUILDDIR)) override TARGETS := $(addprefix $(BUILDDIR)/,$(TARGETS)) ifeq ($(V),1) XECHO = @: XPRINTF = @: Q = else XECHO = @echo XPRINTF = @printf Q = @ endif define BECHO $(XPRINTF) ' %-7s %s\n' "$1" "$(subst $(BUILDDIR)/,,$@)" endef all:: $(TARGETS) clean:: rm -f $(BUILDDIR)/*.o $(TARGETS) $(BUILDDIR)/.*.c.dep install:: $(TARGETS) define _mkdep $(BUILDDIR)/$1$2: $(addprefix $(BUILDDIR)/$1,$(obj-$2) $3) $(addprefix $(BUILDDIR)/,$4) endef define mkdep $(call _mkdep,$1,$2,$3 $2.o,$4 lib/libmtd.a) endef %: %.o $(LDDEPS) $(call BECHO,LD) $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_$(notdir $@)) -g -o $@ $^ $(LDLIBS) $(LDLIBS_$(notdir $@)) $(BUILDDIR)/%.a: $(call BECHO,AR) $(Q)$(AR) cr $@ $^ $(Q)$(RANLIB) $@ $(BUILDDIR)/%.o: %.c $(OBJDEPS) ifneq ($(BUILDDIR),$(CURDIR)) $(Q)mkdir -p $(dir $@) endif $(call BECHO,CC) $(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< -g -Wp,-MD,$(BUILDDIR)/.$(, * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory * in the jffs2 directory. */ #include "compr.h" #include #include #include #define FAVOUR_LZO_PERCENT 80 extern int page_size; /* LIST IMPLEMENTATION (from linux/list.h) */ #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } static inline void __list_del(struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = (void *) 0; entry->prev = (void *) 0; } #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /* Available compressors are on this list */ static LIST_HEAD(jffs2_compressor_list); /* Actual compression mode */ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; void jffs2_set_compression_mode(int mode) { jffs2_compression_mode = mode; } int jffs2_get_compression_mode(void) { return jffs2_compression_mode; } /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; /* Compression test stuffs */ static int jffs2_compression_check = 0; static unsigned char *jffs2_compression_check_buf = NULL; void jffs2_compression_check_set(int yesno) { jffs2_compression_check = yesno; } int jffs2_compression_check_get(void) { return jffs2_compression_check; } static int jffs2_error_cnt = 0; int jffs2_compression_check_errorcnt_get(void) { return jffs2_error_cnt; } #define JFFS2_BUFFER_FILL 0x55 /* Called before compression (if compression_check is setted) to prepare the buffer for buffer overflow test */ static void jffs2_decompression_test_prepare(unsigned char *buf, int size) { memset(buf,JFFS2_BUFFER_FILL,size+1); } /* Called after compression (if compression_check is setted) to test the result */ static void jffs2_decompression_test(struct jffs2_compressor *compr, unsigned char *data_in, unsigned char *output_buf, uint32_t cdatalen, uint32_t datalen, uint32_t buf_size) { uint32_t i; /* buffer overflow test */ for (i=buf_size;i>cdatalen;i--) { if (output_buf[i]!=JFFS2_BUFFER_FILL) { fprintf(stderr,"COMPR_ERROR: buffer overflow at %s. " "(bs=%d csize=%d b[%d]=%d)\n", compr->name, buf_size, cdatalen, i, (int)(output_buf[i])); jffs2_error_cnt++; return; } } /* allocing temporary buffer for decompression */ if (!jffs2_compression_check_buf) { jffs2_compression_check_buf = malloc(page_size); if (!jffs2_compression_check_buf) { fprintf(stderr,"No memory for buffer allocation. Compression check disabled.\n"); jffs2_compression_check = 0; return; } } /* decompressing */ if (!compr->decompress) { fprintf(stderr,"JFFS2 compression check: there is no decompress function at %s.\n", compr->name); jffs2_error_cnt++; return; } if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen)) { fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name); jffs2_error_cnt++; } /* validate decompression */ else { for (i=0;iname, i); jffs2_error_cnt++; break; } } } } /* * Return 1 to use this compression */ static int jffs2_is_best_compression(struct jffs2_compressor *this, struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) { switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_SIZE: if (bestsize > size) return 1; return 0; case JFFS2_COMPR_MODE_FAVOURLZO: if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) return 1; if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) return 1; if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) return 1; if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) return 1; return 0; } /* Shouldn't happen */ return 0; } /* jffs2_compress: * @data: Pointer to uncompressed data * @cdata: Pointer to returned pointer to buffer for compressed data * @datalen: On entry, holds the amount of data available for compression. * On exit, expected to hold the amount of data actually compressed. * @cdatalen: On entry, holds the amount of space available for compressed * data. On exit, expected to hold the actual size of the compressed * data. * * Returns: Lower byte to be stored with data indicating compression type used. * Zero is used to show that the data could not be compressed - the * compressed version was actually larger than the original. * Upper byte will be used later. (soon) * * If the cdata buffer isn't large enough to hold all the uncompressed data, * jffs2_compress should compress as much as will fit, and should set * *datalen accordingly to show the amount of data which were compressed. */ uint16_t jffs2_compress( unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen) { int ret = JFFS2_COMPR_NONE; int compr_ret; struct jffs2_compressor *this, *best=NULL; unsigned char *output_buf = NULL, *tmp_buf; uint32_t orig_slen, orig_dlen; uint32_t best_slen=0, best_dlen=0; switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_NONE: break; case JFFS2_COMPR_MODE_PRIORITY: orig_slen = *datalen; orig_dlen = *cdatalen; output_buf = malloc(orig_dlen+jffs2_compression_check); if (!output_buf) { fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. Compression failed.\n"); goto out; } list_for_each_entry(this, &jffs2_compressor_list, list) { /* Skip decompress-only backwards-compatibility and disabled modules */ if ((!this->compress)||(this->disabled)) continue; this->usecount++; if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ jffs2_decompression_test_prepare(output_buf, orig_dlen); *datalen = orig_slen; *cdatalen = orig_dlen; compr_ret = this->compress(data_in, output_buf, datalen, cdatalen); this->usecount--; if (!compr_ret) { ret = this->compr; this->stat_compr_blocks++; this->stat_compr_orig_size += *datalen; this->stat_compr_new_size += *cdatalen; if (jffs2_compression_check) jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen); break; } } if (ret == JFFS2_COMPR_NONE) free(output_buf); break; case JFFS2_COMPR_MODE_FAVOURLZO: case JFFS2_COMPR_MODE_SIZE: orig_slen = *datalen; orig_dlen = *cdatalen; list_for_each_entry(this, &jffs2_compressor_list, list) { uint32_t needed_buf_size; if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) needed_buf_size = orig_slen + jffs2_compression_check; else needed_buf_size = orig_dlen + jffs2_compression_check; /* Skip decompress-only backwards-compatibility and disabled modules */ if ((!this->compress)||(this->disabled)) continue; /* Allocating memory for output buffer if necessary */ if ((this->compr_buf_size < needed_buf_size) && (this->compr_buf)) { free(this->compr_buf); this->compr_buf_size=0; this->compr_buf=NULL; } if (!this->compr_buf) { tmp_buf = malloc(needed_buf_size); if (!tmp_buf) { fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); continue; } else { this->compr_buf = tmp_buf; this->compr_buf_size = orig_dlen; } } this->usecount++; if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size); *datalen = orig_slen; *cdatalen = orig_dlen; compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen); this->usecount--; if (!compr_ret) { if (jffs2_compression_check) jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size); if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) && (*cdatalen < *datalen)) { best_dlen = *cdatalen; best_slen = *datalen; best = this; } } } if (best_dlen) { *cdatalen = best_dlen; *datalen = best_slen; output_buf = best->compr_buf; best->compr_buf = NULL; best->compr_buf_size = 0; best->stat_compr_blocks++; best->stat_compr_orig_size += best_slen; best->stat_compr_new_size += best_dlen; ret = best->compr; } break; default: fprintf(stderr,"mkfs.jffs2: unknown compression mode.\n"); } out: if (ret == JFFS2_COMPR_NONE) { *cpage_out = data_in; *datalen = *cdatalen; none_stat_compr_blocks++; none_stat_compr_size += *datalen; } else { *cpage_out = output_buf; } return ret; } int jffs2_register_compressor(struct jffs2_compressor *comp) { struct jffs2_compressor *this; if (!comp->name) { fprintf(stderr,"NULL compressor name at registering JFFS2 compressor. Failed.\n"); return -1; } comp->compr_buf_size=0; comp->compr_buf=NULL; comp->usecount=0; comp->stat_compr_orig_size=0; comp->stat_compr_new_size=0; comp->stat_compr_blocks=0; comp->stat_decompr_blocks=0; list_for_each_entry(this, &jffs2_compressor_list, list) { if (this->priority < comp->priority) { list_add(&comp->list, this->list.prev); goto out; } } list_add_tail(&comp->list, &jffs2_compressor_list); out: return 0; } int jffs2_unregister_compressor(struct jffs2_compressor *comp) { if (comp->usecount) { fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n"); return -1; } list_del(&comp->list); return 0; } #define JFFS2_STAT_BUF_SIZE 16000 char *jffs2_list_compressors(void) { struct jffs2_compressor *this; char *buf, *act_buf; act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); list_for_each_entry(this, &jffs2_compressor_list, list) { act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); if ((this->disabled)||(!this->compress)) act_buf += sprintf(act_buf,"disabled"); else act_buf += sprintf(act_buf,"enabled"); act_buf += sprintf(act_buf,"\n"); } return buf; } char *jffs2_stats(void) { struct jffs2_compressor *this; char *buf, *act_buf; act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); act_buf += sprintf(act_buf,"Compression mode: "); switch (jffs2_compression_mode) { case JFFS2_COMPR_MODE_NONE: act_buf += sprintf(act_buf,"none"); break; case JFFS2_COMPR_MODE_PRIORITY: act_buf += sprintf(act_buf,"priority"); break; case JFFS2_COMPR_MODE_SIZE: act_buf += sprintf(act_buf,"size"); break; case JFFS2_COMPR_MODE_FAVOURLZO: act_buf += sprintf(act_buf, "favourlzo"); break; default: act_buf += sprintf(act_buf, "unknown"); break; } act_buf += sprintf(act_buf,"\nCompressors:\n"); act_buf += sprintf(act_buf,"%10s ","none"); act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, none_stat_compr_size, none_stat_decompr_blocks); list_for_each_entry(this, &jffs2_compressor_list, list) { act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority); if ((this->disabled)||(!this->compress)) act_buf += sprintf(act_buf,"- "); else act_buf += sprintf(act_buf,"+ "); act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, this->stat_compr_new_size, this->stat_compr_orig_size, this->stat_decompr_blocks); act_buf += sprintf(act_buf,"\n"); } return buf; } int jffs2_set_compression_mode_name(const char *name) { if (!strcmp("none",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; return 0; } if (!strcmp("priority",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; return 0; } if (!strcmp("size",name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; return 0; } if (!strcmp("favourlzo", name)) { jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; return 0; } return 1; } static int jffs2_compressor_Xable(const char *name, int disabled) { struct jffs2_compressor *this; list_for_each_entry(this, &jffs2_compressor_list, list) { if (!strcmp(this->name, name)) { this->disabled = disabled; return 0; } } return 1; } int jffs2_enable_compressor_name(const char *name) { return jffs2_compressor_Xable(name, 0); } int jffs2_disable_compressor_name(const char *name) { return jffs2_compressor_Xable(name, 1); } int jffs2_set_compressor_priority(const char *name, int priority) { struct jffs2_compressor *this,*comp; list_for_each_entry(this, &jffs2_compressor_list, list) { if (!strcmp(this->name, name)) { this->priority = priority; comp = this; goto reinsert; } } fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name); return 1; reinsert: /* list is sorted in the order of priority, so if we change it we have to reinsert it into the good place */ list_del(&comp->list); list_for_each_entry(this, &jffs2_compressor_list, list) { if (this->priority < comp->priority) { list_add(&comp->list, this->list.prev); return 0; } } list_add_tail(&comp->list, &jffs2_compressor_list); return 0; } int jffs2_compressors_init(void) { #ifdef CONFIG_JFFS2_ZLIB jffs2_zlib_init(); #endif #ifdef CONFIG_JFFS2_RTIME jffs2_rtime_init(); #endif #ifdef CONFIG_JFFS2_LZO jffs2_lzo_init(); #endif return 0; } int jffs2_compressors_exit(void) { #ifdef CONFIG_JFFS2_RTIME jffs2_rtime_exit(); #endif #ifdef CONFIG_JFFS2_ZLIB jffs2_zlib_exit(); #endif #ifdef CONFIG_JFFS2_LZO jffs2_lzo_exit(); #endif return 0; } mtd-utils-1.5.0/compr.h000066400000000000000000000064171175167361300147520ustar00rootroot00000000000000/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. */ #ifndef __JFFS2_COMPR_H__ #define __JFFS2_COMPR_H__ #include #include #include #include "linux/jffs2.h" #define CONFIG_JFFS2_ZLIB #define CONFIG_JFFS2_RTIME #define CONFIG_JFFS2_LZO #define JFFS2_RUBINMIPS_PRIORITY 10 #define JFFS2_DYNRUBIN_PRIORITY 20 #define JFFS2_RTIME_PRIORITY 50 #define JFFS2_ZLIB_PRIORITY 60 #define JFFS2_LZO_PRIORITY 80 #define JFFS2_COMPR_MODE_NONE 0 #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 #define JFFS2_COMPR_MODE_FAVOURLZO 3 #define kmalloc(a,b) malloc(a) #define kfree(a) free(a) #ifndef GFP_KERNEL #define GFP_KERNEL 0 #endif #define vmalloc(a) malloc(a) #define vfree(a) free(a) #define printk(...) fprintf(stderr,__VA_ARGS__) #define KERN_EMERG #define KERN_ALERT #define KERN_CRIT #define KERN_ERR #define KERN_WARNING #define KERN_NOTICE #define KERN_INFO #define KERN_DEBUG struct list_head { struct list_head *next, *prev; }; void jffs2_set_compression_mode(int mode); int jffs2_get_compression_mode(void); int jffs2_set_compression_mode_name(const char *mode_name); int jffs2_enable_compressor_name(const char *name); int jffs2_disable_compressor_name(const char *name); int jffs2_set_compressor_priority(const char *name, int priority); struct jffs2_compressor { struct list_head list; int priority; /* used by prirority comr. mode */ const char *name; char compr; /* JFFS2_COMPR_XXX */ int (*compress)(unsigned char *data_in, unsigned char *cpage_out, uint32_t *srclen, uint32_t *destlen); int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); int usecount; int disabled; /* if seted the compressor won't compress */ unsigned char *compr_buf; /* used by size compr. mode */ uint32_t compr_buf_size; /* used by size compr. mode */ uint32_t stat_compr_orig_size; uint32_t stat_compr_new_size; uint32_t stat_compr_blocks; uint32_t stat_decompr_blocks; }; int jffs2_register_compressor(struct jffs2_compressor *comp); int jffs2_unregister_compressor(struct jffs2_compressor *comp); int jffs2_compressors_init(void); int jffs2_compressors_exit(void); uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen); /* If it is setted, a decompress will be called after every compress */ void jffs2_compression_check_set(int yesno); int jffs2_compression_check_get(void); int jffs2_compression_check_errorcnt_get(void); char *jffs2_list_compressors(void); char *jffs2_stats(void); /* Compressor modules */ /* These functions will be called by jffs2_compressors_init/exit */ #ifdef CONFIG_JFFS2_ZLIB int jffs2_zlib_init(void); void jffs2_zlib_exit(void); #endif #ifdef CONFIG_JFFS2_RTIME int jffs2_rtime_init(void); void jffs2_rtime_exit(void); #endif #ifdef CONFIG_JFFS2_LZO int jffs2_lzo_init(void); void jffs2_lzo_exit(void); #endif #endif /* __JFFS2_COMPR_H__ */ mtd-utils-1.5.0/compr_lzo.c000066400000000000000000000057371175167361300156350ustar00rootroot00000000000000/* * JFFS2 LZO Compression Interface. * * Copyright (C) 2007 Nokia Corporation. All rights reserved. * * Author: Richard Purdie * * 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 #include #include #ifndef WITHOUT_LZO #include #include #include #include "compr.h" extern int page_size; static void *lzo_mem; static void *lzo_compress_buf; /* * Note about LZO compression. * * We want to use the _999_ compression routine which gives better compression * rates at the expense of time. Decompression time is unaffected. We might as * well use the standard lzo library routines for this but they will overflow * the destination buffer since they don't check the destination size. * * We therefore compress to a temporary buffer and copy if it will fit. * */ static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { lzo_uint compress_size; int ret; ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); if (ret != LZO_E_OK) return -1; if (compress_size > *dstlen) return -1; memcpy(cpage_out, lzo_compress_buf, compress_size); *dstlen = compress_size; return 0; } static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen) { int ret; lzo_uint dl; ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); if (ret != LZO_E_OK || dl != destlen) return -1; return 0; } static struct jffs2_compressor jffs2_lzo_comp = { .priority = JFFS2_LZO_PRIORITY, .name = "lzo", .compr = JFFS2_COMPR_LZO, .compress = &jffs2_lzo_cmpr, .decompress = &jffs2_lzo_decompress, .disabled = 1, }; int jffs2_lzo_init(void) { int ret; lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); if (!lzo_mem) return -1; /* Worse case LZO compression size from their FAQ */ lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); if (!lzo_compress_buf) { free(lzo_mem); return -1; } ret = jffs2_register_compressor(&jffs2_lzo_comp); if (ret < 0) { free(lzo_compress_buf); free(lzo_mem); } return ret; } void jffs2_lzo_exit(void) { jffs2_unregister_compressor(&jffs2_lzo_comp); free(lzo_compress_buf); free(lzo_mem); } #else int jffs2_lzo_init(void) { return 0; } void jffs2_lzo_exit(void) { } #endif mtd-utils-1.5.0/compr_rtime.c000066400000000000000000000051431175167361300161400ustar00rootroot00000000000000/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by Arjan van de Ven * * 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 #include #include "compr.h" /* _compress returns the compressed size, -1 if bigger */ static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { short positions[256]; int outpos = 0; int pos=0; memset(positions,0,sizeof(positions)); while (pos < (*sourcelen) && outpos+2 <= (*dstlen)) { int backpos, runlen=0; unsigned char value; value = data_in[pos]; cpage_out[outpos++] = data_in[pos++]; backpos = positions[value]; positions[value]=pos; while ((backpos < pos) && (pos < (*sourcelen)) && (data_in[pos]==data_in[backpos++]) && (runlen<255)) { pos++; runlen++; } cpage_out[outpos++] = runlen; } if (outpos >= pos) { /* We failed */ return -1; } /* Tell the caller how much we managed to compress, and how much space it took */ *sourcelen = pos; *dstlen = outpos; return 0; } static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __attribute__((unused)) uint32_t srclen, uint32_t destlen) { short positions[256]; int outpos = 0; int pos=0; memset(positions,0,sizeof(positions)); while (outpos= 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); } mtd-utils-1.5.0/compr_zlib.c000066400000000000000000000075451175167361300157700ustar00rootroot00000000000000/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001 Red Hat, Inc. * * Created by David Woodhouse * * The original JFFS, from which the design for JFFS2 was derived, * was designed and implemented by Axis Communications AB. * * The contents of this file are subject to the Red Hat eCos Public * License Version 1.1 (the "Licence"); you may not use this file * except in compliance with the Licence. You may obtain a copy of * the Licence at http://www.redhat.com/ * * Software distributed under the Licence is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. * See the Licence for the specific language governing rights and * limitations under the Licence. * * The Original Code is JFFS2 - Journalling Flash File System, version 2 * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License version 2 (the "GPL"), in * which case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the RHEPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. */ #define PROGRAM_NAME "compr_zlib" #include #define crc32 __zlib_crc32 #include #undef crc32 #include #include #include #include "common.h" #include "compr.h" /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. If it doesn't manage to finish, call it again with avail_in == 0 and avail_out set to the remaining 12 bytes for it to clean up. Q: Is 12 bytes sufficient? */ #define STREAM_END_SPACE 12 static int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { z_stream strm; int ret; if (*dstlen <= STREAM_END_SPACE) return -1; strm.zalloc = (void *)0; strm.zfree = (void *)0; if (Z_OK != deflateInit(&strm, 3)) { return -1; } strm.next_in = data_in; strm.total_in = 0; strm.next_out = cpage_out; strm.total_out = 0; while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); ret = deflate(&strm, Z_PARTIAL_FLUSH); if (ret != Z_OK) { deflateEnd(&strm); return -1; } } strm.avail_out += STREAM_END_SPACE; strm.avail_in = 0; ret = deflate(&strm, Z_FINISH); if (ret != Z_STREAM_END) { deflateEnd(&strm); return -1; } deflateEnd(&strm); if (strm.total_out >= strm.total_in) return -1; *dstlen = strm.total_out; *sourcelen = strm.total_in; return 0; } static int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen) { z_stream strm; int ret; strm.zalloc = (void *)0; strm.zfree = (void *)0; if (Z_OK != inflateInit(&strm)) { return 1; } strm.next_in = data_in; strm.avail_in = srclen; strm.total_in = 0; strm.next_out = cpage_out; strm.avail_out = destlen; strm.total_out = 0; while((ret = inflate(&strm, Z_FINISH)) == Z_OK) ; inflateEnd(&strm); return 0; } static struct jffs2_compressor jffs2_zlib_comp = { .priority = JFFS2_ZLIB_PRIORITY, .name = "zlib", .disabled = 0, .compr = JFFS2_COMPR_ZLIB, .compress = &jffs2_zlib_compress, .decompress = &jffs2_zlib_decompress, }; int jffs2_zlib_init(void) { return jffs2_register_compressor(&jffs2_zlib_comp); } void jffs2_zlib_exit(void) { jffs2_unregister_compressor(&jffs2_zlib_comp); } mtd-utils-1.5.0/device_table.txt000066400000000000000000000102241175167361300166170ustar00rootroot00000000000000# 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: # # 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 # # /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 - mtd-utils-1.5.0/doc_loadbios.c000066400000000000000000000063451175167361300162460ustar00rootroot00000000000000#define PROGRAM_NAME "doc_loadbios" #include #include #include #include #include #include #include #include #include #include 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; } mtd-utils-1.5.0/docfdisk.c000066400000000000000000000213051175167361300154040ustar00rootroot00000000000000/* * docfdisk.c: Modify INFTL partition tables * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define PROGRAM_NAME "docfdisk" #define _XOPEN_SOURCE 500 /* for pread/pwrite */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 [ [ [ [ 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; } mtd-utils-1.5.0/feature-removal-schedule.txt000066400000000000000000000004641175167361300211060ustar00rootroot00000000000000The 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. mtd-utils-1.5.0/fectest.c000066400000000000000000000036741175167361300152640ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "mcast_image.h" #include #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; } mtd-utils-1.5.0/flash_erase.c000066400000000000000000000166221175167361300161000ustar00rootroot00000000000000/* flash_erase.c -- erase MTD devices Copyright (C) 2000 Arcom Control System Ltd Copyright (C) 2010 Mike Frysinger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #define PROGRAM_NAME "flash_erase" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *mtd_device; static int quiet; /* true -- don't output progress */ static int jffs2; /* format for jffs2 usage */ static int noskipbad; /* do not skip bad blocks */ static int unlock; /* unlock sectors before erasing */ static struct jffs2_unknown_node cleanmarker; int target_endian = __BYTE_ORDER; static void show_progress(struct mtd_dev_info *mtd, uint64_t start, int eb, int eb_start, int eb_cnt) { bareverbose(!quiet, "\rErasing %d Kibyte @ %"PRIx64" -- %2i %% complete ", mtd->eb_size / 1024, start, ((eb - eb_start) * 100) / eb_cnt); fflush(stdout); } static void display_help (void) { printf("Usage: %s [options] MTD_DEVICE \n" "Erase blocks of the specified MTD device.\n" "Specify a count of 0 to erase to end of device.\n" "\n" " -j, --jffs2 format the device for jffs2\n" " -N, --noskipbad don't skip bad blocks\n" " -u, --unlock unlock sectors before erasing\n" " -q, --quiet do not display progress messages\n" " --silent same as --quiet\n" " --help display this help and exit\n" " --version output version information and exit\n", PROGRAM_NAME); } static void display_version (void) { printf("%1$s version " VERSION "\n" "\n" "Copyright (C) 2000 Arcom Control Systems Ltd\n" "\n" "%1$s comes with NO WARRANTY\n" "to the extent permitted by law.\n" "\n" "You may redistribute copies of %1$s\n" "under the terms of the GNU General Public Licence.\n" "See the file `COPYING' for more information.\n", PROGRAM_NAME); } int main(int argc, char *argv[]) { libmtd_t mtd_desc; struct mtd_dev_info mtd; int fd, clmpos = 0, clmlen = 8; unsigned long long start; unsigned int eb, eb_start, eb_cnt; int isNAND; int error = 0; uint64_t offset = 0; /* * Process user arguments */ for (;;) { int option_index = 0; static const char *short_options = "jNqu"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"jffs2", no_argument, 0, 'j'}, {"noskipbad", no_argument, 0, 'N'}, {"quiet", no_argument, 0, 'q'}, {"silent", no_argument, 0, 'q'}, {"unlock", no_argument, 0, 'u'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) break; switch (c) { case 0: switch (option_index) { case 0: display_help(); return 0; case 1: display_version(); return 0; } break; case 'j': jffs2 = 1; break; case 'N': noskipbad = 1; break; case 'q': quiet = 1; break; case 'u': unlock = 1; break; case '?': error = 1; break; } } switch (argc - optind) { case 3: mtd_device = argv[optind]; start = simple_strtoull(argv[optind + 1], &error); eb_cnt = simple_strtoul(argv[optind + 2], &error); break; default: case 0: errmsg("no MTD device specified"); case 1: errmsg("no start erase block specified"); case 2: errmsg("no erase block count specified"); error = 1; break; } if (error) return errmsg("Try `--help' for more information"); /* * Locate MTD and prepare for erasure */ mtd_desc = libmtd_open(); if (mtd_desc == NULL) return errmsg("can't initialize libmtd"); if ((fd = open(mtd_device, O_RDWR)) < 0) return sys_errmsg("%s", mtd_device); if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) return errmsg("mtd_get_dev_info failed"); eb_start = start / mtd.eb_size; isNAND = mtd.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(cleanmarker)); else { struct nand_oobinfo oobinfo; if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); /* Check for autoplacement */ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { /* Get the position of the free bytes */ if (!oobinfo.oobfree[0][1]) return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); clmpos = oobinfo.oobfree[0][0]; clmlen = oobinfo.oobfree[0][1]; if (clmlen > 8) clmlen = 8; } else { /* Legacy mode */ switch (mtd.oob_size) { case 8: clmpos = 6; clmlen = 2; break; case 16: clmpos = 8; clmlen = 8; break; case 64: clmpos = 16; clmlen = 8; break; } } cleanmarker.totlen = cpu_to_je32(8); } cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); } /* * Now do the actual erasing of the MTD device */ if (eb_cnt == 0) eb_cnt = (mtd.size / mtd.eb_size) - eb_start; for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { offset = (uint64_t)eb * mtd.eb_size; if (!noskipbad) { int ret = mtd_is_bad(&mtd, fd, eb); if (ret > 0) { verbose(!quiet, "Skipping bad block at %08"PRIx64, offset); continue; } else if (ret < 0) { if (errno == EOPNOTSUPP) { noskipbad = 1; if (isNAND) return errmsg("%s: Bad block check not available", mtd_device); } else return sys_errmsg("%s: MTD get bad block failed", mtd_device); } } show_progress(&mtd, offset, eb, eb_start, eb_cnt); if (unlock) { if (mtd_unlock(&mtd, fd, eb) != 0) { sys_errmsg("%s: MTD unlock failure", mtd_device); continue; } } if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { sys_errmsg("%s: MTD Erase failure", mtd_device); continue; } /* format for JFFS2 ? */ if (!jffs2) continue; /* write cleanmarker */ if (isNAND) { if (mtd_write_oob(mtd_desc, &mtd, fd, offset + clmpos, clmlen, &cleanmarker) != 0) { sys_errmsg("%s: MTD writeoob failure", mtd_device); continue; } } else { if (lseek(fd, (loff_t)offset, SEEK_SET) < 0) { sys_errmsg("%s: MTD lseek failure", mtd_device); continue; } if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) { sys_errmsg("%s: MTD write failure", mtd_device); continue; } } verbose(!quiet, " Cleanmarker written at %"PRIx64, offset); } show_progress(&mtd, offset, eb, eb_start, eb_cnt); bareverbose(!quiet, "\n"); return 0; } mtd-utils-1.5.0/flash_eraseall000077500000000000000000000002261175167361300163440ustar00rootroot00000000000000#!/bin/sh echo "${0##*/} has been replaced by \`flash_erase 0 0\`; please use it" 1>&2 [ $# -ne 0 ] && set -- "$@" 0 0 exec flash_erase "$@" mtd-utils-1.5.0/flash_lock.c000066400000000000000000000002321175167361300157170ustar00rootroot00000000000000/* * flash_{lock,unlock} * * utilities for locking/unlocking sectors of flash devices */ #define PROGRAM_NAME "flash_lock" #include "flash_unlock.c" mtd-utils-1.5.0/flash_otp_dump.c000066400000000000000000000020751175167361300166250ustar00rootroot00000000000000/* * flash_otp_dump.c -- display One-Time-Programm data */ #define PROGRAM_NAME "flash_otp_dump" #include #include #include #include #include #include #include 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 ] \n", PROGRAM_NAME); return EINVAL; } fd = open(argv[2], O_RDONLY); if (fd < 0) { perror(argv[2]); return errno; } val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER; ret = ioctl(fd, OTPSELECT, &val); if (ret < 0) { perror("OTPSELECT"); return errno; } printf("OTP %s data for %s\n", argv[1][1] == 'f' ? "factory" : "user", argv[2]); offset = 0; while ((ret = read(fd, buf, sizeof(buf)))) { if (ret < 0) { perror("read()"); return errno; } printf("0x%04x:", offset); for (i = 0; i < ret; i++) printf(" %02x", buf[i]); printf("\n"); offset += ret; } close(fd); return 0; } mtd-utils-1.5.0/flash_otp_info.c000066400000000000000000000024341175167361300166120ustar00rootroot00000000000000/* * flash_otp_info.c -- print info about One-Time-Programm data */ #define PROGRAM_NAME "flash_otp_info" #include #include #include #include #include #include #include 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 ] \n", PROGRAM_NAME); return EINVAL; } fd = open(argv[2], O_RDONLY); if (fd < 0) { perror(argv[2]); return errno; } val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER; ret = ioctl(fd, OTPSELECT, &val); if (ret < 0) { perror("OTPSELECT"); return errno; } ret = ioctl(fd, OTPGETREGIONCOUNT, &val); if (ret < 0) { perror("OTPGETREGIONCOUNT"); return errno; } printf("Number of OTP %s blocks on %s: %d\n", argv[1][1] == 'f' ? "factory" : "user", argv[2], val); if (val > 0) { struct otp_info info[val]; ret = ioctl(fd, OTPGETREGIONINFO, &info); if (ret < 0) { perror("OTPGETREGIONCOUNT"); return errno; } for (i = 0; i < val; i++) printf("block %2d: offset = 0x%04x " "size = %2d bytes %s\n", i, info[i].start, info[i].length, info[i].locked ? "[locked]" : "[unlocked]"); } close(fd); return 0; } mtd-utils-1.5.0/flash_otp_lock.c000066400000000000000000000030641175167361300166070ustar00rootroot00000000000000/* * flash_otp_lock.c -- lock area of One-Time-Program data */ #define PROGRAM_NAME "flash_otp_lock" #include #include #include #include #include #include #include #include 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 \n", PROGRAM_NAME); fprintf(stderr, "offset and size must match on OTP region boundaries\n"); fprintf(stderr, "CAUTION! ONCE LOCKED, OTP REGIONS CAN'T BE UNLOCKED!\n"); return EINVAL; } fd = open(argv[2], O_WRONLY); if (fd < 0) { perror(argv[2]); return errno; } val = MTD_OTP_USER; ret = ioctl(fd, OTPSELECT, &val); if (ret < 0) { perror("OTPSELECT"); return errno; } offset = strtoul(argv[3], &p, 0); if (argv[3][0] == 0 || *p != 0) { fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME); return ERANGE; } size = strtoul(argv[4], &p, 0); if (argv[4][0] == 0 || *p != 0) { fprintf(stderr, "%s: bad size value\n", PROGRAM_NAME); return ERANGE; } printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", argv[2], offset, offset + size); 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; } mtd-utils-1.5.0/flash_otp_write.c000066400000000000000000000037431175167361300170150ustar00rootroot00000000000000/* * flash_otp_write.c -- write One-Time-Program data */ #define PROGRAM_NAME "flash_otp_write" #include #include #include #include #include #include #include #include #include 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 \n", PROGRAM_NAME); fprintf(stderr, "the raw data to write should be provided on stdin\n"); fprintf(stderr, "CAUTION! ONCE SET TO 0, OTP DATA BITS CAN'T BE ERASED!\n"); return EINVAL; } fd = open(argv[2], O_WRONLY); if (fd < 0) { perror(argv[2]); return errno; } val = MTD_OTP_USER; ret = ioctl(fd, OTPSELECT, &val); if (ret < 0) { perror("OTPSELECT"); return errno; } if (ioctl(fd, MEMGETINFO, &mtdInfo)) { perror("MEMGETINFO"); return errno; } offset = strtoul(argv[3], &p, 0); if (argv[3][0] == 0 || *p != 0) { fprintf(stderr, "%s: bad offset value\n", PROGRAM_NAME); return ERANGE; } if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { perror("lseek()"); return errno; } printf("Writing OTP user data on %s at offset 0x%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; } mtd-utils-1.5.0/flash_unlock.c000066400000000000000000000041561175167361300162730ustar00rootroot00000000000000/* * flash_{lock,unlock} * * utilities for locking/unlocking sectors of flash devices */ #ifndef PROGRAM_NAME #define PROGRAM_NAME "flash_unlock" #define FLASH_MSG "unlock" #define FLASH_UNLOCK 1 #else #define FLASH_MSG "lock" #define FLASH_UNLOCK 0 #endif #include #include #include #include #include #include #include #include #include "common.h" #include static void usage(int status) { fprintf(status ? stderr : stdout, "Usage: %s [offset] [block count]\n\n" "If offset is not specified, it defaults to 0.\n" "If block count is not specified, it defaults to all blocks.\n", PROGRAM_NAME); exit(status); } int main(int argc, char *argv[]) { int fd, request; struct mtd_info_user mtdInfo; struct erase_info_user mtdLockInfo; int count; const char *dev; /* Parse command line options */ if (argc < 2 || argc > 4) usage(1); if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) usage(0); dev = argv[1]; /* Get the device info to compare to command line sizes */ fd = open(dev, O_RDWR); if (fd < 0) sys_errmsg_die("could not open: %s", dev); if (ioctl(fd, MEMGETINFO, &mtdInfo)) sys_errmsg_die("could not get mtd info: %s", dev); /* Make sure user options are valid */ if (argc > 2) mtdLockInfo.start = strtol(argv[2], NULL, 0); else mtdLockInfo.start = 0; if (mtdLockInfo.start > mtdInfo.size) errmsg_die("%#x is beyond device size %#x", mtdLockInfo.start, mtdInfo.size); if (argc > 3) { count = strtol(argv[3], NULL, 0); if (count == -1) mtdLockInfo.length = mtdInfo.size; else mtdLockInfo.length = mtdInfo.erasesize * count; } else mtdLockInfo.length = mtdInfo.size; if (mtdLockInfo.start + mtdLockInfo.length > mtdInfo.size) errmsg_die("range is more than device supports: %#x + %#x > %#x", mtdLockInfo.start, mtdLockInfo.length, mtdInfo.size); /* Finally do the operation */ request = FLASH_UNLOCK ? MEMUNLOCK : MEMLOCK; if (ioctl(fd, request, &mtdLockInfo)) sys_errmsg_die("could not %s device: %s\n", FLASH_MSG, dev); return 0; } mtd-utils-1.5.0/flashcp.c000066400000000000000000000251011175167361300152340ustar00rootroot00000000000000/* * Copyright (c) 2d3D, Inc. * Written by Abraham vd Merwe * All rights reserved. * * Renamed to flashcp.c to avoid conflicts with fcp from fsh package * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define PROGRAM_NAME "flashcp" #include #include #include #include #include #include #include #include #include #include #include typedef int bool; #define true 1 #define false 0 #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 /* for debugging purposes only */ #ifdef DEBUG #undef DEBUG #define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); } #else #undef DEBUG #define DEBUG(fmt,args...) #endif #define KB(x) ((x) / 1024) #define PERCENTAGE(x,total) (((x) * 100) / (total)) /* size of read/write buffer */ #define BUFSIZE (10 * 1024) /* cmd-line flags */ #define FLAG_NONE 0x00 #define FLAG_VERBOSE 0x01 #define FLAG_HELP 0x02 #define FLAG_FILENAME 0x04 #define FLAG_DEVICE 0x08 /* error levels */ #define LOG_NORMAL 1 #define LOG_ERROR 2 static void log_printf (int level,const char *fmt, ...) { FILE *fp = level == LOG_NORMAL ? stdout : stderr; va_list ap; va_start (ap,fmt); vfprintf (fp,fmt,ap); va_end (ap); fflush (fp); } static void showusage(bool error) { int level = error ? LOG_ERROR : LOG_NORMAL; log_printf (level, "\n" "Flash Copy - Written by Abraham van der Merwe \n" "\n" "usage: %1$s [ -v | --verbose ] \n" " %1$s -h | --help\n" "\n" " -h | --help Show this help message\n" " -v | --verbose Show progress reports\n" " File which you want to copy to flash\n" " Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n" "\n", PROGRAM_NAME); exit (error ? EXIT_FAILURE : EXIT_SUCCESS); } static int safe_open (const char *pathname,int flags) { int fd; fd = open (pathname,flags); if (fd < 0) { log_printf (LOG_ERROR,"While trying to open %s",pathname); if (flags & O_RDWR) log_printf (LOG_ERROR," for read/write access"); else if (flags & O_RDONLY) log_printf (LOG_ERROR," for read access"); else if (flags & O_WRONLY) log_printf (LOG_ERROR," for write access"); log_printf (LOG_ERROR,": %m\n"); exit (EXIT_FAILURE); } return (fd); } static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose) { ssize_t result; result = read (fd,buf,count); if (count != result) { if (verbose) log_printf (LOG_NORMAL,"\n"); if (result < 0) { log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename); exit (EXIT_FAILURE); } log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename); exit (EXIT_FAILURE); } } static void safe_rewind (int fd,const char *filename) { if (lseek (fd,0L,SEEK_SET) < 0) { log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename); exit (EXIT_FAILURE); } } /******************************************************************************/ static int dev_fd = -1,fil_fd = -1; static void cleanup (void) { if (dev_fd > 0) close (dev_fd); if (fil_fd > 0) close (fil_fd); } int main (int argc,char *argv[]) { const char *filename = NULL,*device = NULL; int i,flags = FLAG_NONE; ssize_t result; size_t size,written; struct mtd_info_user mtd; struct erase_info_user erase; struct stat filestat; unsigned char src[BUFSIZE],dest[BUFSIZE]; /********************* * parse cmd-line *****************/ for (;;) { int option_index = 0; static const char *short_options = "hv"; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) { break; } switch (c) { case 'h': flags |= FLAG_HELP; DEBUG("Got FLAG_HELP\n"); break; case 'v': flags |= FLAG_VERBOSE; DEBUG("Got FLAG_VERBOSE\n"); break; default: DEBUG("Unknown parameter: %s\n",argv[option_index]); showusage(true); } } if (optind+2 == argc) { flags |= FLAG_FILENAME; filename = argv[optind]; DEBUG("Got filename: %s\n",filename); flags |= FLAG_DEVICE; device = argv[optind+1]; DEBUG("Got device: %s\n",device); } if (flags & FLAG_HELP || device == NULL) showusage(flags != FLAG_HELP); atexit (cleanup); /* get some info about the flash device */ dev_fd = safe_open (device,O_SYNC | O_RDWR); if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0) { DEBUG("ioctl(): %m\n"); log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n"); exit (EXIT_FAILURE); } /* get some info about the file we want to copy */ fil_fd = safe_open (filename,O_RDONLY); if (fstat (fil_fd,&filestat) < 0) { log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename); exit (EXIT_FAILURE); } /* does it fit into the device/partition? */ if (filestat.st_size > mtd.size) { log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device); exit (EXIT_FAILURE); } /***************************************************** * erase enough blocks so that we can write the file * *****************************************************/ #warning "Check for smaller erase regions" erase.start = 0; erase.length = (filestat.st_size + mtd.erasesize - 1) / mtd.erasesize; erase.length *= mtd.erasesize; if (flags & FLAG_VERBOSE) { /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */ int blocks = erase.length / mtd.erasesize; erase.length = mtd.erasesize; log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks); for (i = 1; i <= blocks; i++) { log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks)); if (ioctl (dev_fd,MEMERASE,&erase) < 0) { log_printf (LOG_NORMAL,"\n"); log_printf (LOG_ERROR, "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n", (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); exit (EXIT_FAILURE); } erase.start += mtd.erasesize; } log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks); } else { /* if not, erase the whole chunk in one shot */ if (ioctl (dev_fd,MEMERASE,&erase) < 0) { log_printf (LOG_ERROR, "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n", (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); exit (EXIT_FAILURE); } } DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size); /********************************** * write the entire file to flash * **********************************/ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size)); size = filestat.st_size; i = BUFSIZE; written = 0; while (size) { if (size < BUFSIZE) i = size; if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)", KB (written + i), KB (filestat.st_size), PERCENTAGE (written + i,filestat.st_size)); /* read from filename */ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); /* write to device */ result = write (dev_fd,src,i); if (i != result) { if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n"); if (result < 0) { log_printf (LOG_ERROR, "While writing data to 0x%.8x-0x%.8x on %s: %m\n", written,written + i,device); exit (EXIT_FAILURE); } log_printf (LOG_ERROR, "Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n", written,written + i,device,written + result,filestat.st_size); exit (EXIT_FAILURE); } written += i; size -= i; } if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL, "\rWriting data: %luk/%luk (100%%)\n", KB (filestat.st_size), KB (filestat.st_size)); DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size); /********************************** * verify that flash == file data * **********************************/ safe_rewind (fil_fd,filename); safe_rewind (dev_fd,device); size = filestat.st_size; i = BUFSIZE; written = 0; if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size)); while (size) { if (size < BUFSIZE) i = size; if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL, "\rVerifying data: %dk/%luk (%lu%%)", KB (written + i), KB (filestat.st_size), PERCENTAGE (written + i,filestat.st_size)); /* read from filename */ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); /* read from device */ safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE); /* compare buffers */ if (memcmp (src,dest,i)) { log_printf (LOG_ERROR, "File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n", written,written + i); exit (EXIT_FAILURE); } written += i; size -= i; } if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL, "\rVerifying data: %luk/%luk (100%%)\n", KB (filestat.st_size), KB (filestat.st_size)); DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size); exit (EXIT_SUCCESS); } mtd-utils-1.5.0/ftl_check.c000066400000000000000000000144331175167361300155440ustar00rootroot00000000000000/* 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 . Portions created by David A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights Reserved. Alternatively, the contents of this file may be used under the terms of the GNU Public License version 2 (the "GPL"), in which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. ======================================================================*/ #define PROGRAM_NAME "ftl_check" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { 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(void) { fprintf(stderr, "usage: %s device\n", PROGRAM_NAME); } /*====================================================================*/ int main(int argc, char *argv[]) { int optch, errflg, fd; struct stat buf; errflg = 0; while ((optch = getopt(argc, argv, "h")) != -1) { switch (optch) { case 'h': errflg = 1; break; default: errflg = -1; break; } } if (errflg || (optind != argc-1)) { showusage(); exit(errflg > 0 ? 0 : EXIT_FAILURE); } if (stat(argv[optind], &buf) != 0) { perror("status check failed"); exit(EXIT_FAILURE); } if (!(buf.st_mode & S_IFCHR)) { fprintf(stderr, "%s is not a character special device\n", argv[optind]); exit(EXIT_FAILURE); } fd = open(argv[optind], O_RDONLY); if (fd == -1) { perror("open failed"); exit(EXIT_FAILURE); } check_partition(fd); close(fd); exit(EXIT_SUCCESS); return 0; } mtd-utils-1.5.0/ftl_format.c000066400000000000000000000224151175167361300157560ustar00rootroot00000000000000/* 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 . Portions created by David A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights Reserved. Alternatively, the contents of this file may be used under the terms of the GNU Public License version 2 (the "GPL"), in which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. ======================================================================*/ #define PROGRAM_NAME "ftl_format" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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", PROGRAM_NAME); exit(EXIT_FAILURE); } if (stat(argv[optind], &buf) != 0) { perror("status check failed"); exit(EXIT_FAILURE); } if (!(buf.st_mode & S_IFCHR)) { fprintf(stderr, "%s is not a character special device\n", argv[optind]); exit(EXIT_FAILURE); } fd = open(argv[optind], O_RDWR); if (fd == -1) { perror("open failed"); exit(EXIT_FAILURE); } ret = format_partition(fd, quiet, interrogate, spare, reserve, bootsize); if (!quiet) { if (ret) printf("format failed.\n"); else printf("format successful.\n"); } close(fd); exit((ret) ? EXIT_FAILURE : EXIT_SUCCESS); return 0; } mtd-utils-1.5.0/include/000077500000000000000000000000001175167361300150745ustar00rootroot00000000000000mtd-utils-1.5.0/include/common.h000066400000000000000000000107651175167361300165460ustar00rootroot00000000000000/* * Copyright (c) Artem Bityutskiy, 2007, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __MTD_UTILS_COMMON_H__ #define __MTD_UTILS_COMMON_H__ #include #include #include #include #include #include #include "version.h" #ifndef PROGRAM_NAME # error "You must define PROGRAM_NAME before including this header" #endif #ifdef __cplusplus extern "C" { #endif #ifndef MIN /* some C lib headers define this for us */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #define min(a, b) MIN(a, b) /* glue for linux kernel source */ #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* Verbose messages */ #define bareverbose(verbose, fmt, ...) do { \ if (verbose) \ printf(fmt, ##__VA_ARGS__); \ } while(0) #define verbose(verbose, fmt, ...) \ bareverbose(verbose, "%s: " fmt "\n", PROGRAM_NAME, ##__VA_ARGS__) /* Normal messages */ #define normsg_cont(fmt, ...) do { \ printf("%s: " fmt, PROGRAM_NAME, ##__VA_ARGS__); \ } while(0) #define normsg(fmt, ...) do { \ normsg_cont(fmt "\n", ##__VA_ARGS__); \ } while(0) /* Error messages */ #define errmsg(fmt, ...) ({ \ fprintf(stderr, "%s: error!: " fmt "\n", PROGRAM_NAME, ##__VA_ARGS__); \ -1; \ }) #define errmsg_die(fmt, ...) do { \ exit(errmsg(fmt, ##__VA_ARGS__)); \ } while(0) /* System error messages */ #define sys_errmsg(fmt, ...) ({ \ int _err = errno; \ errmsg(fmt, ##__VA_ARGS__); \ fprintf(stderr, "%*serror %d (%s)\n", (int)sizeof(PROGRAM_NAME) + 1,\ "", _err, strerror(_err)); \ -1; \ }) #define sys_errmsg_die(fmt, ...) do { \ exit(sys_errmsg(fmt, ##__VA_ARGS__)); \ } while(0) /* Warnings */ #define warnmsg(fmt, ...) do { \ fprintf(stderr, "%s: warning!: " fmt "\n", PROGRAM_NAME, ##__VA_ARGS__); \ } while(0) static inline int is_power_of_2(unsigned long long n) { return (n != 0 && ((n & (n - 1)) == 0)); } /** * simple_strtoX - convert a hex/dec/oct string into a number * @snum: buffer to convert * @error: set to 1 when buffer isn't fully consumed * * These functions are similar to the standard strtoX() functions, but they are * a little bit easier to use if you want to convert full string of digits into * the binary form. The typical usage: * * int error = 0; * unsigned long num; * * num = simple_strtoul(str, &error); * if (error || ... if needed, your check that num is not out of range ...) * error_happened(); */ #define simple_strtoX(func, type) \ static inline type simple_##func(const char *snum, int *error) \ { \ char *endptr; \ type ret = func(snum, &endptr, 0); \ \ if (error && (!*snum || *endptr)) { \ errmsg("%s: unable to parse the number '%s'", #func, snum); \ *error = 1; \ } \ \ return ret; \ } simple_strtoX(strtol, long int) simple_strtoX(strtoll, long long int) simple_strtoX(strtoul, unsigned long int) simple_strtoX(strtoull, unsigned long long int) /* Simple version-printing for utils */ #define common_print_version() \ do { \ printf("%s %s\n", PROGRAM_NAME, VERSION); \ } while (0) #include "xalloc.h" #ifdef __cplusplus } #endif #endif /* !__MTD_UTILS_COMMON_H__ */ mtd-utils-1.5.0/include/crc32.h000066400000000000000000000004461175167361300161650ustar00rootroot00000000000000/* * This code was taken from the linux kernel. The license is GPL Version 2. */ #ifndef __CRC32_H__ #define __CRC32_H__ #include /* Return a 32-bit CRC of the contents of the buffer */ extern uint32_t mtd_crc32(uint32_t val, const void *ss, int len); #endif /* __CRC32_H__ */ mtd-utils-1.5.0/include/libmtd.h000066400000000000000000000266151175167361300165320ustar00rootroot00000000000000/* * Copyright (C) 2008, 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * MTD library. */ #ifndef __LIBMTD_H__ #define __LIBMTD_H__ #ifdef __cplusplus extern "C" { #endif /* Maximum MTD device name length */ #define MTD_NAME_MAX 127 /* Maximum MTD device type string length */ #define MTD_TYPE_MAX 64 /* MTD library descriptor */ typedef void * libmtd_t; /* Forward decls */ struct region_info_user; /** * @mtd_dev_cnt: count of MTD devices in system * @lowest_mtd_num: lowest MTD device number in system * @highest_mtd_num: highest MTD device number in system * @sysfs_supported: non-zero if sysfs is supported by MTD */ struct mtd_info { int mtd_dev_cnt; int lowest_mtd_num; int highest_mtd_num; unsigned int sysfs_supported:1; }; /** * struct mtd_dev_info - information about an MTD device. * @mtd_num: MTD device number * @major: major number of corresponding character device * @minor: minor number of corresponding character device * @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h) * @type_str: static R/O flash type string * @name: device name * @size: device size in bytes * @eb_cnt: count of eraseblocks * @eb_size: eraseblock size * @min_io_size: minimum input/output unit size * @subpage_size: sub-page size * @oob_size: OOB size (zero if the device does not have OOB area) * @region_cnt: count of additional erase regions * @writable: zero if the device is read-only * @bb_allowed: non-zero if the MTD device may have bad eraseblocks */ struct mtd_dev_info { int mtd_num; int major; int minor; int type; const char type_str[MTD_TYPE_MAX + 1]; const char name[MTD_NAME_MAX + 1]; long long size; int eb_cnt; int eb_size; int min_io_size; int subpage_size; int oob_size; int region_cnt; unsigned int writable:1; unsigned int bb_allowed:1; }; /** * libmtd_open - open MTD library. * * This function initializes and opens the MTD library and returns MTD library * descriptor in case of success and %NULL in case of failure. In case of * failure, errno contains zero if MTD is not present in the system, or * contains the error code if a real error happened. */ libmtd_t libmtd_open(void); /** * libmtd_close - close MTD library. * @desc: MTD library descriptor */ void libmtd_close(libmtd_t desc); /** * mtd_dev_present - check whether a MTD device is present. * @desc: MTD library descriptor * @mtd_num: MTD device number to check * * This function returns %1 if MTD device is present and %0 if not. */ int mtd_dev_present(libmtd_t desc, int mtd_num); /** * mtd_get_info - get general MTD information. * @desc: MTD library descriptor * @info: the MTD device information is returned here * * This function fills the passed @info object with general MTD information and * returns %0 in case of success and %-1 in case of failure. If MTD subsystem is * not present in the system, errno is set to @ENODEV. */ int mtd_get_info(libmtd_t desc, struct mtd_info *info); /** * mtd_get_dev_info - get information about an MTD device. * @desc: MTD library descriptor * @node: name of the MTD device node * @mtd: the MTD device information is returned here * * This function gets information about MTD device defined by the @node device * node file and saves this information in the @mtd object. Returns %0 in case * of success and %-1 in case of failure. If MTD subsystem is not present in the * system, or the MTD device does not exist, errno is set to @ENODEV. */ int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd); /** * mtd_get_dev_info1 - get information about an MTD device. * @desc: MTD library descriptor * @mtd_num: MTD device number to fetch information about * @mtd: the MTD device information is returned here * * This function is identical to 'mtd_get_dev_info()' except that it accepts * MTD device number, not MTD character device. */ int mtd_get_dev_info1(libmtd_t desc, int mtd_num, struct mtd_dev_info *mtd); /** * mtd_lock - lock eraseblocks. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to lock * * This function locks eraseblock @eb. Returns %0 in case of success and %-1 * in case of failure. */ int mtd_lock(const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_unlock - unlock eraseblocks. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to lock * * This function unlocks eraseblock @eb. Returns %0 in case of success and %-1 * in case of failure. */ int mtd_unlock(const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_erase - erase an eraseblock. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to erase * * This function erases eraseblock @eb of MTD device described by @fd. Returns * %0 in case of success and %-1 in case of failure. */ int mtd_erase(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_regioninfo - get information about an erase region. * @fd: MTD device node file descriptor * @regidx: index of region to look up * @reginfo: the region information is returned here * * This function gets information about an erase region defined by the * @regidx index and saves this information in the @reginfo object. * Returns %0 in case of success and %-1 in case of failure. If the * @regidx is not valid or unavailable, errno is set to @ENODEV. */ int mtd_regioninfo(int fd, int regidx, struct region_info_user *reginfo); /** * mtd_is_locked - see if the specified eraseblock is locked. * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to check * * This function checks to see if eraseblock @eb of MTD device described * by @fd is locked. Returns %0 if it is unlocked, %1 if it is locked, and * %-1 in case of failure. If the ioctl is not supported (support was added in * Linux kernel 2.6.36) or this particular device does not support it, errno is * set to @ENOTSUPP. */ int mtd_is_locked(const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_torture - torture an eraseblock. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to torture * * This function tortures eraseblock @eb. Returns %0 in case of success and %-1 * in case of failure. */ int mtd_torture(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_is_bad - check if eraseblock is bad. * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to check * * This function checks if eraseblock @eb is bad. Returns %0 if not, %1 if yes, * and %-1 in case of failure. */ int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_mark_bad - mark an eraseblock as bad. * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to mark as bad * * This function marks eraseblock @eb as bad. Returns %0 in case of success and * %-1 in case of failure. */ int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb); /** * mtd_read - read data from an MTD device. * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to read from * @offs: offset withing the eraseblock to read from * @buf: buffer to read data to * @len: how many bytes to read * * This function reads @len bytes of data from eraseblock @eb and offset @offs * of the MTD device defined by @mtd and stores the read data at buffer @buf. * Returns %0 in case of success and %-1 in case of failure. */ int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs, void *buf, int len); /** * mtd_write - write data to an MTD device. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to write to * @offs: offset withing the eraseblock to write to * @data: data buffer to write * @len: how many data bytes to write * @oob: OOB buffer to write * @ooblen: how many OOB bytes to write * @mode: write mode (e.g., %MTD_OOB_PLACE, %MTD_OOB_RAW) * * This function writes @len bytes of data to eraseblock @eb and offset @offs * of the MTD device defined by @mtd. Returns %0 in case of success and %-1 in * case of failure. * * Can only write to a single page at a time if writing to OOB. */ int mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb, int offs, void *data, int len, void *oob, int ooblen, uint8_t mode); /** * mtd_read_oob - read out-of-band area. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @start: page-aligned start address * @length: number of OOB bytes to read * @data: read buffer * * This function reads @length OOB bytes starting from address @start on * MTD device described by @fd. The address is specified as page byte offset * from the beginning of the MTD device. This function returns %0 in case of * success and %-1 in case of failure. */ int mtd_read_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, uint64_t start, uint64_t length, void *data); /** * mtd_write_oob - write out-of-band area. * @desc: MTD library descriptor * @mtd: MTD device description object * @fd: MTD device node file descriptor * @start: page-aligned start address * @length: number of OOB bytes to write * @data: write buffer * * This function writes @length OOB bytes starting from address @start on * MTD device described by @fd. The address is specified as page byte offset * from the beginning of the MTD device. Returns %0 in case of success and %-1 * in case of failure. */ int mtd_write_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, uint64_t start, uint64_t length, void *data); /** * mtd_write_img - write a file to MTD device. * @mtd: MTD device description object * @fd: MTD device node file descriptor * @eb: eraseblock to write to * @offs: offset withing the eraseblock to write to * @img_name: the file to write * * This function writes an image @img_name the MTD device defined by @mtd. @eb * and @offs are the starting eraseblock and offset on the MTD device. Returns * %0 in case of success and %-1 in case of failure. */ int mtd_write_img(const struct mtd_dev_info *mtd, int fd, int eb, int offs, const char *img_name); /** * mtd_probe_node - test MTD node. * @desc: MTD library descriptor * @node: the node to test * * This function tests whether @node is an MTD device node and returns %1 if it * is, and %-1 if it is not (errno is %ENODEV in this case) or if an error * occurred. */ int mtd_probe_node(libmtd_t desc, const char *node); #ifdef __cplusplus } #endif #endif /* __LIBMTD_H__ */ mtd-utils-1.5.0/include/linux/000077500000000000000000000000001175167361300162335ustar00rootroot00000000000000mtd-utils-1.5.0/include/linux/jffs2.h000066400000000000000000000155711175167361300174270ustar00rootroot00000000000000/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $ * */ #ifndef __LINUX_JFFS2_H__ #define __LINUX_JFFS2_H__ /* You must include something which defines the C99 uintXX_t types. We don't do it from here because this file is used in too many different environments. */ #define JFFS2_SUPER_MAGIC 0x72b6 /* Values we may expect to find in the 'magic' field */ #define JFFS2_OLD_MAGIC_BITMASK 0x1984 #define JFFS2_MAGIC_BITMASK 0x1985 #define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ #define JFFS2_EMPTY_BITMASK 0xffff #define JFFS2_DIRTY_BITMASK 0x0000 /* Summary node MAGIC marker */ #define JFFS2_SUM_MAGIC 0x02851885 /* We only allow a single char for length, and 0xFF is empty flash so we don't want it confused with a real length. Hence max 254. */ #define JFFS2_MAX_NAME_LEN 254 /* How small can we sensibly write nodes? */ #define JFFS2_MIN_DATA_LEN 128 #define JFFS2_COMPR_NONE 0x00 #define JFFS2_COMPR_ZERO 0x01 #define JFFS2_COMPR_RTIME 0x02 #define JFFS2_COMPR_RUBINMIPS 0x03 #define JFFS2_COMPR_COPY 0x04 #define JFFS2_COMPR_DYNRUBIN 0x05 #define JFFS2_COMPR_ZLIB 0x06 #define JFFS2_COMPR_LZO 0x07 /* Compatibility flags. */ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ #define JFFS2_NODE_ACCURATE 0x2000 /* INCOMPAT: Fail to mount the filesystem */ #define JFFS2_FEATURE_INCOMPAT 0xc000 /* ROCOMPAT: Mount read-only */ #define JFFS2_FEATURE_ROCOMPAT 0x8000 /* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */ #define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000 /* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */ #define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) #define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) #define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) #define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) /* XATTR Related */ #define JFFS2_XPREFIX_USER 1 /* for "user." */ #define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ #define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */ #define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ #define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */ #define JFFS2_ACL_VERSION 0x0001 // Maybe later... //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at mount time, don't wait for it to happen later */ #define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific compression type */ /* These can go once we've made sure we've caught all uses without byteswapping */ typedef struct { uint32_t v32; } __attribute__((packed)) jint32_t; typedef struct { uint32_t m; } __attribute__((packed)) jmode_t; typedef struct { uint16_t v16; } __attribute__((packed)) jint16_t; struct jffs2_unknown_node { /* All start like this */ jint16_t magic; jint16_t nodetype; jint32_t totlen; /* So we can skip over nodes we don't grok */ jint32_t hdr_crc; } __attribute__((packed)); struct jffs2_raw_dirent { jint16_t magic; jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */ jint32_t totlen; jint32_t hdr_crc; jint32_t pino; jint32_t version; jint32_t ino; /* == zero for unlink */ jint32_t mctime; uint8_t nsize; uint8_t type; uint8_t unused[2]; jint32_t node_crc; jint32_t name_crc; uint8_t name[0]; } __attribute__((packed)); /* The JFFS2 raw inode structure: Used for storage on physical media. */ /* The uid, gid, atime, mtime and ctime members could be longer, but are left like this for space efficiency. If and when people decide they really need them extended, it's simple enough to add support for a new type of raw node. */ struct jffs2_raw_inode { jint16_t magic; /* A constant magic number. */ jint16_t nodetype; /* == JFFS2_NODETYPE_INODE */ jint32_t totlen; /* Total length of this node (inc data, etc.) */ jint32_t hdr_crc; jint32_t ino; /* Inode number. */ jint32_t version; /* Version number. */ jmode_t mode; /* The file's type or mode. */ jint16_t uid; /* The file's owner. */ jint16_t gid; /* The file's group. */ jint32_t isize; /* Total resultant size of this inode (used for truncations) */ jint32_t atime; /* Last access time. */ jint32_t mtime; /* Last modification time. */ jint32_t ctime; /* Change time. */ jint32_t offset; /* Where to begin to write. */ jint32_t csize; /* (Compressed) data size */ jint32_t dsize; /* Size of the node's data. (after decompression) */ uint8_t compr; /* Compression algorithm used */ uint8_t usercompr; /* Compression algorithm requested by the user */ jint16_t flags; /* See JFFS2_INO_FLAG_* */ jint32_t data_crc; /* CRC for the (compressed) data. */ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ uint8_t data[0]; } __attribute__((packed)); struct jffs2_raw_xattr { jint16_t magic; jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */ jint32_t totlen; jint32_t hdr_crc; jint32_t xid; /* XATTR identifier number */ jint32_t version; uint8_t xprefix; uint8_t name_len; jint16_t value_len; jint32_t data_crc; jint32_t node_crc; uint8_t data[0]; } __attribute__((packed)); struct jffs2_raw_xref { jint16_t magic; jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */ jint32_t totlen; jint32_t hdr_crc; jint32_t ino; /* inode number */ jint32_t xid; /* XATTR identifier number */ jint32_t xseqno; /* xref sequencial number */ jint32_t node_crc; } __attribute__((packed)); struct jffs2_raw_summary { jint16_t magic; jint16_t nodetype; /* = JFFS2_NODETYPE_SUMMARY */ jint32_t totlen; jint32_t hdr_crc; jint32_t sum_num; /* number of sum entries*/ jint32_t cln_mkr; /* clean marker size, 0 = no cleanmarker */ jint32_t padded; /* sum of the size of padding nodes */ jint32_t sum_crc; /* summary information crc */ jint32_t node_crc; /* node crc */ jint32_t sum[0]; /* inode summary info */ } __attribute__((packed)); union jffs2_node_union { struct jffs2_raw_inode i; struct jffs2_raw_dirent d; struct jffs2_raw_xattr x; struct jffs2_raw_xref r; struct jffs2_raw_summary s; struct jffs2_unknown_node u; }; #endif /* __LINUX_JFFS2_H__ */ mtd-utils-1.5.0/include/mtd/000077500000000000000000000000001175167361300156605ustar00rootroot00000000000000mtd-utils-1.5.0/include/mtd/ftl-user.h000066400000000000000000000051261175167361300175760ustar00rootroot00000000000000/* * $Id: ftl.h,v 1.7 2005/11/07 11:14:54 gleixner Exp $ * * Derived from (and probably identical to): * ftl.h 1.7 1999/10/25 20:23:17 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * * The initial developer of the original code is David A. Hinds * . 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__ */ mtd-utils-1.5.0/include/mtd/inftl-user.h000066400000000000000000000032121175167361300201170ustar00rootroot00000000000000/* * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ * * Parts of INFTL headers shared with userspace * */ #ifndef __MTD_INFTL_USER_H__ #define __MTD_INFTL_USER_H__ #define OSAK_VERSION 0x5120 #define PERCENTUSED 98 #define SECTORSIZE 512 /* Block Control Information */ struct inftl_bci { uint8_t ECCsig[6]; uint8_t Status; uint8_t Status1; } __attribute__((packed)); struct inftl_unithead1 { uint16_t virtualUnitNo; uint16_t prevUnitNo; uint8_t ANAC; uint8_t NACs; uint8_t parityPerField; uint8_t discarded; } __attribute__((packed)); struct inftl_unithead2 { uint8_t parityPerField; uint8_t ANAC; uint16_t prevUnitNo; uint16_t virtualUnitNo; uint8_t NACs; uint8_t discarded; } __attribute__((packed)); struct inftl_unittail { uint8_t Reserved[4]; uint16_t EraseMark; uint16_t EraseMark1; } __attribute__((packed)); union inftl_uci { struct inftl_unithead1 a; struct inftl_unithead2 b; struct inftl_unittail c; }; struct inftl_oob { struct inftl_bci b; union inftl_uci u; }; /* INFTL Media Header */ struct INFTLPartition { __u32 virtualUnits; __u32 firstUnit; __u32 lastUnit; __u32 flags; __u32 spareUnits; __u32 Reserved0; __u32 Reserved1; } __attribute__((packed)); struct INFTLMediaHeader { char bootRecordID[8]; __u32 NoOfBootImageBlocks; __u32 NoOfBinaryPartitions; __u32 NoOfBDTLPartitions; __u32 BlockMultiplierBits; __u32 FormatFlags; __u32 OsakVersion; __u32 PercentUsed; struct INFTLPartition Partitions[4]; } __attribute__((packed)); /* Partition flag types */ #define INFTL_BINARY 0x20000000 #define INFTL_BDTL 0x40000000 #define INFTL_LAST 0x80000000 #endif /* __MTD_INFTL_USER_H__ */ mtd-utils-1.5.0/include/mtd/jffs2-user.h000066400000000000000000000045031175167361300200210ustar00rootroot00000000000000/* * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $ * * JFFS2 definitions for use in user space only */ #ifndef __JFFS2_USER_H__ #define __JFFS2_USER_H__ /* This file is blessed for inclusion by userspace */ #include #include #include #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__ */ mtd-utils-1.5.0/include/mtd/mtd-abi.h000066400000000000000000000221301175167361300173440ustar00rootroot00000000000000/* * Copyright © 1999-2010 David Woodhouse et al. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef __MTD_ABI_H__ #define __MTD_ABI_H__ #include struct erase_info_user { __u32 start; __u32 length; }; struct erase_info_user64 { __u64 start; __u64 length; }; struct mtd_oob_buf { __u32 start; __u32 length; unsigned char *ptr; }; struct mtd_oob_buf64 { __u64 start; __u32 pad; __u32 length; __u64 usr_ptr; }; /** * MTD operation modes * * @MTD_OPS_PLACE_OOB: OOB data are placed at the given offset (default) * @MTD_OPS_AUTO_OOB: OOB data are automatically placed at the free areas * which are defined by the internal ecclayout * @MTD_OPS_RAW: data are transferred as-is, with no error correction; * this mode implies %MTD_OPS_PLACE_OOB * * These modes can be passed to ioctl(MEMWRITE) and are also used internally. * See notes on "MTD file modes" for discussion on %MTD_OPS_RAW vs. * %MTD_FILE_MODE_RAW. */ enum { MTD_OPS_PLACE_OOB = 0, MTD_OPS_AUTO_OOB = 1, MTD_OPS_RAW = 2, }; /** * struct mtd_write_req - data structure for requesting a write operation * * @start: start address * @len: length of data buffer * @ooblen: length of OOB buffer * @usr_data: user-provided data buffer * @usr_oob: user-provided OOB buffer * @mode: MTD mode (see "MTD operation modes") * @padding: reserved, must be set to 0 * * This structure supports ioctl(MEMWRITE) operations, allowing data and/or OOB * writes in various modes. To write to OOB-only, set @usr_data == NULL, and to * write data-only, set @usr_oob == NULL. However, setting both @usr_data and * @usr_oob to NULL is not allowed. */ struct mtd_write_req { __u64 start; __u64 len; __u64 ooblen; __u64 usr_data; __u64 usr_oob; __u8 mode; __u8 padding[7]; }; #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_MLCNANDFLASH 8 #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_POWERUP_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) /* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */ #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 { __u8 type; __u32 flags; __u32 size; /* Total size of the MTD */ __u32 erasesize; __u32 writesize; __u32 oobsize; /* Amount of OOB data per block (e.g. 16) */ __u64 padding; /* Old obsolete field; do not use */ }; struct region_info_user { __u32 offset; /* At which this region starts, * from the beginning of the MTD */ __u32 erasesize; /* For this region */ __u32 numblocks; /* Number of blocks in this region */ __u32 regionindex; }; struct otp_info { __u32 start; __u32 length; __u32 locked; }; /* * Note, the following ioctl existed in the past and was removed: * #define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) * Try to avoid adding a new ioctl with the same ioctl number. */ /* Get basic MTD characteristics info (better to use sysfs) */ #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) /* Erase segment of MTD */ #define MEMERASE _IOW('M', 2, struct erase_info_user) /* Write out-of-band data from MTD */ #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) /* Read out-of-band data from MTD */ #define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) /* Lock a chip (for MTD that supports it) */ #define MEMLOCK _IOW('M', 5, struct erase_info_user) /* Unlock a chip (for MTD that supports it) */ #define MEMUNLOCK _IOW('M', 6, struct erase_info_user) /* Get the number of different erase regions */ #define MEMGETREGIONCOUNT _IOR('M', 7, int) /* Get information about the erase region for a specific index */ #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) /* Get info about OOB modes (e.g., RAW, PLACE, AUTO) - legacy interface */ #define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) /* Check if an eraseblock is bad */ #define MEMGETBADBLOCK _IOW('M', 11, __kernel_loff_t) /* Mark an eraseblock as bad */ #define MEMSETBADBLOCK _IOW('M', 12, __kernel_loff_t) /* Set OTP (One-Time Programmable) mode (factory vs. user) */ #define OTPSELECT _IOR('M', 13, int) /* Get number of OTP (One-Time Programmable) regions */ #define OTPGETREGIONCOUNT _IOW('M', 14, int) /* Get all OTP (One-Time Programmable) info about MTD */ #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) /* Lock a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */ #define OTPLOCK _IOR('M', 16, struct otp_info) /* Get ECC layout (deprecated) */ #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout_user) /* Get statistics about corrected/uncorrected errors */ #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) /* Set MTD mode on a per-file-descriptor basis (see "MTD file modes") */ #define MTDFILEMODE _IO('M', 19) /* Erase segment of MTD (supports 64-bit address) */ #define MEMERASE64 _IOW('M', 20, struct erase_info_user64) /* Write data to OOB (64-bit version) */ #define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64) /* Read data from OOB (64-bit version) */ #define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64) /* Check if chip is locked (for MTD that supports it) */ #define MEMISLOCKED _IOR('M', 23, struct erase_info_user) /* * Most generic write interface; can write in-band and/or out-of-band in various * modes (see "struct mtd_write_req") */ #define MEMWRITE _IOWR('M', 24, struct mtd_write_req) /* * Obsolete legacy interface. Keep it in order not to break userspace * interfaces */ struct nand_oobinfo { __u32 useecc; __u32 eccbytes; __u32 oobfree[8][2]; __u32 eccpos[32]; }; struct nand_oobfree { __u32 offset; __u32 length; }; #define MTD_MAX_OOBFREE_ENTRIES 8 #define MTD_MAX_ECCPOS_ENTRIES 64 /* * OBSOLETE: ECC layout control structure. Exported to user-space via ioctl * ECCGETLAYOUT for backwards compatbility and should not be mistaken as a * complete set of ECC information. The ioctl truncates the larger internal * structure to retain binary compatibility with the static declaration of the * ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of * the user struct, not the MAX size of the internal struct nand_ecclayout. */ struct nand_ecclayout_user { __u32 eccbytes; __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES]; __u32 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 { __u32 corrected; __u32 failed; __u32 badblocks; __u32 bbtblocks; }; /* * MTD file modes - for read/write access to MTD * * @MTD_FILE_MODE_NORMAL: OTP disabled, ECC enabled * @MTD_FILE_MODE_OTP_FACTORY: OTP enabled in factory mode * @MTD_FILE_MODE_OTP_USER: OTP enabled in user mode * @MTD_FILE_MODE_RAW: OTP disabled, ECC disabled * * These modes can be set via ioctl(MTDFILEMODE). The mode mode will be retained * separately for each open file descriptor. * * Note: %MTD_FILE_MODE_RAW provides the same functionality as %MTD_OPS_RAW - * raw access to the flash, without error correction or autoplacement schemes. * Wherever possible, the MTD_OPS_* mode will override the MTD_FILE_MODE_* mode * (e.g., when using ioctl(MEMWRITE)), but in some cases, the MTD_FILE_MODE is * used out of necessity (e.g., `write()', ioctl(MEMWRITEOOB64)). */ enum mtd_file_modes { MTD_FILE_MODE_NORMAL = MTD_OTP_OFF, MTD_FILE_MODE_OTP_FACTORY = MTD_OTP_FACTORY, MTD_FILE_MODE_OTP_USER = MTD_OTP_USER, MTD_FILE_MODE_RAW, }; #endif /* __MTD_ABI_H__ */ mtd-utils-1.5.0/include/mtd/mtd-user.h000066400000000000000000000022571175167361300175770ustar00rootroot00000000000000/* * Copyright © 1999-2010 David Woodhouse * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef __MTD_USER_H__ #define __MTD_USER_H__ #include /* This file is blessed for inclusion by userspace */ #include 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_user nand_ecclayout_t; #endif /* __MTD_USER_H__ */ mtd-utils-1.5.0/include/mtd/nftl-user.h000066400000000000000000000025711175167361300177550ustar00rootroot00000000000000/* * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ * * Parts of NFTL headers shared with userspace * */ #ifndef __MTD_NFTL_USER_H__ #define __MTD_NFTL_USER_H__ /* Block Control Information */ struct nftl_bci { unsigned char ECCSig[6]; uint8_t Status; uint8_t Status1; }__attribute__((packed)); /* Unit Control Information */ struct nftl_uci0 { uint16_t VirtUnitNum; uint16_t ReplUnitNum; uint16_t SpareVirtUnitNum; uint16_t SpareReplUnitNum; } __attribute__((packed)); struct nftl_uci1 { uint32_t WearInfo; uint16_t EraseMark; uint16_t EraseMark1; } __attribute__((packed)); struct nftl_uci2 { uint16_t FoldMark; uint16_t FoldMark1; uint32_t unused; } __attribute__((packed)); union nftl_uci { struct nftl_uci0 a; struct nftl_uci1 b; struct nftl_uci2 c; }; struct nftl_oob { struct nftl_bci b; union nftl_uci u; }; /* NFTL Media Header */ struct NFTLMediaHeader { char DataOrgID[6]; uint16_t NumEraseUnits; uint16_t FirstPhysicalEUN; uint32_t FormattedSize; unsigned char UnitSizeFactor; } __attribute__((packed)); #define MAX_ERASE_ZONES (8192 - 512) #define ERASE_MARK 0x3c69 #define SECTOR_FREE 0xff #define SECTOR_USED 0x55 #define SECTOR_IGNORE 0x11 #define SECTOR_DELETED 0x00 #define FOLD_MARK_IN_PROGRESS 0x5555 #define ZONE_GOOD 0xff #define ZONE_BAD_ORIGINAL 0 #define ZONE_BAD_MARKED 7 #endif /* __MTD_NFTL_USER_H__ */ mtd-utils-1.5.0/include/mtd/ubi-media.h000066400000000000000000000410411175167361300176650ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: Artem Bityutskiy (Битюцкий Артём) * Thomas Gleixner * Frank Haverkamp * Oliver Lohmann * Andreas Arnez */ /* * This file defines the layout of UBI headers and all the other UBI on-flash * data structures. */ #ifndef __UBI_MEDIA_H__ #define __UBI_MEDIA_H__ #include /* The version of UBI images supported by this implementation */ #define UBI_VERSION 1 /* The highest erase counter value supported by this implementation */ #define UBI_MAX_ERASECOUNTER 0x7FFFFFFF /* The initial CRC32 value used when calculating CRC checksums */ #define UBI_CRC32_INIT 0xFFFFFFFFU /* Erase counter header magic number (ASCII "UBI#") */ #define UBI_EC_HDR_MAGIC 0x55424923 /* Volume identifier header magic number (ASCII "UBI!") */ #define UBI_VID_HDR_MAGIC 0x55424921 /* * Volume type constants used in the volume identifier header. * * @UBI_VID_DYNAMIC: dynamic volume * @UBI_VID_STATIC: static volume */ enum { UBI_VID_DYNAMIC = 1, UBI_VID_STATIC = 2 }; /* * Volume flags used in the volume table record. * * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume * * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume * table. UBI automatically re-sizes the volume which has this flag and makes * the volume to be of largest possible size. This means that if after the * initialization UBI finds out that there are available physical eraseblocks * present on the device, it automatically appends all of them to the volume * (the physical eraseblocks reserved for bad eraseblocks handling and other * reserved physical eraseblocks are not taken). So, if there is a volume with * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical * eraseblocks will be zero after UBI is loaded, because all of them will be * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared * after the volume had been initialized. * * The auto-resize feature is useful for device production purposes. For * example, different NAND flash chips may have different amount of initial bad * eraseblocks, depending of particular chip instance. Manufacturers of NAND * chips usually guarantee that the amount of initial bad eraseblocks does not * exceed certain percent, e.g. 2%. When one creates an UBI image which will be * flashed to the end devices in production, he does not know the exact amount * of good physical eraseblocks the NAND chip on the device will have, but this * number is required to calculate the volume sized and put them to the volume * table of the UBI image. In this case, one of the volumes (e.g., the one * which will store the root file system) is marked as "auto-resizable", and * UBI will adjust its size on the first boot if needed. * * Note, first UBI reserves some amount of physical eraseblocks for bad * eraseblock handling, and then re-sizes the volume, not vice-versa. This * means that the pool of reserved physical eraseblocks will always be present. */ enum { UBI_VTBL_AUTORESIZE_FLG = 0x01, }; /* * Compatibility constants used by internal volumes. * * @UBI_COMPAT_DELETE: delete this internal volume before anything is written * to the flash * @UBI_COMPAT_RO: attach this device in read-only mode * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its * physical eraseblocks, don't allow the wear-leveling * sub-system to move them * @UBI_COMPAT_REJECT: reject this UBI image */ enum { UBI_COMPAT_DELETE = 1, UBI_COMPAT_RO = 2, UBI_COMPAT_PRESERVE = 4, UBI_COMPAT_REJECT = 5 }; /* Sizes of UBI headers */ #define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) #define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) /* Sizes of UBI headers without the ending CRC */ #define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) #define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) /** * struct ubi_ec_hdr - UBI erase counter header. * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) * @version: version of UBI implementation which is supposed to accept this * UBI image * @padding1: reserved for future, zeroes * @ec: the erase counter * @vid_hdr_offset: where the VID header starts * @data_offset: where the user data start * @image_seq: image sequence number * @padding2: reserved for future, zeroes * @hdr_crc: erase counter header CRC checksum * * The erase counter header takes 64 bytes and has a plenty of unused space for * future usage. The unused fields are zeroed. The @version field is used to * indicate the version of UBI implementation which is supposed to be able to * work with this UBI image. If @version is greater than the current UBI * version, the image is rejected. This may be useful in future if something * is changed radically. This field is duplicated in the volume identifier * header. * * The @vid_hdr_offset and @data_offset fields contain the offset of the the * volume identifier header and user data, relative to the beginning of the * physical eraseblock. These values have to be the same for all physical * eraseblocks. * * The @image_seq field is used to validate a UBI image that has been prepared * for a UBI device. The @image_seq value can be any value, but it must be the * same on all eraseblocks. UBI will ensure that all new erase counter headers * also contain this value, and will check the value when scanning at start-up. * One way to make use of @image_seq is to increase its value by one every time * an image is flashed over an existing image, then, if the flashing does not * complete, UBI will detect the error when scanning. */ struct ubi_ec_hdr { __be32 magic; __u8 version; __u8 padding1[3]; __be64 ec; /* Warning: the current limit is 31-bit anyway! */ __be32 vid_hdr_offset; __be32 data_offset; __be32 image_seq; __u8 padding2[32]; __be32 hdr_crc; } __attribute__ ((packed)); /** * struct ubi_vid_hdr - on-flash UBI volume identifier header. * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) * @version: UBI implementation version which is supposed to accept this UBI * image (%UBI_VERSION) * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) * @copy_flag: if this logical eraseblock was copied from another physical * eraseblock (for wear-leveling reasons) * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) * @vol_id: ID of this volume * @lnum: logical eraseblock number * @padding1: reserved for future, zeroes * @data_size: how many bytes of data this logical eraseblock contains * @used_ebs: total number of used logical eraseblocks in this volume * @data_pad: how many bytes at the end of this physical eraseblock are not * used * @data_crc: CRC checksum of the data stored in this logical eraseblock * @padding2: reserved for future, zeroes * @sqnum: sequence number * @padding3: reserved for future, zeroes * @hdr_crc: volume identifier header CRC checksum * * The @sqnum is the value of the global sequence counter at the time when this * VID header was created. The global sequence counter is incremented each time * UBI writes a new VID header to the flash, i.e. when it maps a logical * eraseblock to a new physical eraseblock. The global sequence counter is an * unsigned 64-bit integer and we assume it never overflows. The @sqnum * (sequence number) is used to distinguish between older and newer versions of * logical eraseblocks. * * There are 2 situations when there may be more than one physical eraseblock * corresponding to the same logical eraseblock, i.e., having the same @vol_id * and @lnum values in the volume identifier header. Suppose we have a logical * eraseblock L and it is mapped to the physical eraseblock P. * * 1. Because UBI may erase physical eraseblocks asynchronously, the following * situation is possible: L is asynchronously erased, so P is scheduled for * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, * so P1 is written to, then an unclean reboot happens. Result - there are 2 * physical eraseblocks P and P1 corresponding to the same logical eraseblock * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the * flash. * * 2. From time to time UBI moves logical eraseblocks to other physical * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P * to P1, and an unclean reboot happens before P is physically erased, there * are two physical eraseblocks P and P1 corresponding to L and UBI has to * select one of them when the flash is attached. The @sqnum field says which * PEB is the original (obviously P will have lower @sqnum) and the copy. But * it is not enough to select the physical eraseblock with the higher sequence * number, because the unclean reboot could have happen in the middle of the * copying process, so the data in P is corrupted. It is also not enough to * just select the physical eraseblock with lower sequence number, because the * data there may be old (consider a case if more data was added to P1 after * the copying). Moreover, the unclean reboot may happen when the erasure of P * was just started, so it result in unstable P, which is "mostly" OK, but * still has unstable bits. * * UBI uses the @copy_flag field to indicate that this logical eraseblock is a * copy. UBI also calculates data CRC when the data is moved and stores it at * the @data_crc field of the copy (P1). So when UBI needs to pick one physical * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is * examined. If it is cleared, the situation* is simple and the newer one is * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC * checksum is correct, this physical eraseblock is selected (P1). Otherwise * the older one (P) is selected. * * There are 2 sorts of volumes in UBI: user volumes and internal volumes. * Internal volumes are not seen from outside and are used for various internal * UBI purposes. In this implementation there is only one internal volume - the * layout volume. Internal volumes are the main mechanism of UBI extensions. * For example, in future one may introduce a journal internal volume. Internal * volumes have their own reserved range of IDs. * * The @compat field is only used for internal volumes and contains the "degree * of their compatibility". It is always zero for user volumes. This field * provides a mechanism to introduce UBI extensions and to be still compatible * with older UBI binaries. For example, if someone introduced a journal in * future, he would probably use %UBI_COMPAT_DELETE compatibility for the * journal volume. And in this case, older UBI binaries, which know nothing * about the journal volume, would just delete this volume and work perfectly * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image * - it just ignores the Ext3fs journal. * * The @data_crc field contains the CRC checksum of the contents of the logical * eraseblock if this is a static volume. In case of dynamic volumes, it does * not contain the CRC checksum as a rule. The only exception is when the * data of the physical eraseblock was moved by the wear-leveling sub-system, * then the wear-leveling sub-system calculates the data CRC and stores it in * the @data_crc field. And of course, the @copy_flag is %in this case. * * The @data_size field is used only for static volumes because UBI has to know * how many bytes of data are stored in this eraseblock. For dynamic volumes, * this field usually contains zero. The only exception is when the data of the * physical eraseblock was moved to another physical eraseblock for * wear-leveling reasons. In this case, UBI calculates CRC checksum of the * contents and uses both @data_crc and @data_size fields. In this case, the * @data_size field contains data size. * * The @used_ebs field is used only for static volumes and indicates how many * eraseblocks the data of the volume takes. For dynamic volumes this field is * not used and always contains zero. * * The @data_pad is calculated when volumes are created using the alignment * parameter. So, effectively, the @data_pad field reduces the size of logical * eraseblocks of this volume. This is very handy when one uses block-oriented * software (say, cramfs) on top of the UBI volume. */ struct ubi_vid_hdr { __be32 magic; __u8 version; __u8 vol_type; __u8 copy_flag; __u8 compat; __be32 vol_id; __be32 lnum; __be32 leb_ver; __be32 data_size; __be32 used_ebs; __be32 data_pad; __be32 data_crc; __u8 padding2[4]; __be64 sqnum; __u8 padding3[12]; __be32 hdr_crc; } __attribute__ ((packed)); /* Internal UBI volumes count */ #define UBI_INT_VOL_COUNT 1 /* * Starting ID of internal volumes. There is reserved room for 4096 internal * volumes. */ #define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) /* The layout volume contains the volume table */ #define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START #define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC #define UBI_LAYOUT_VOLUME_ALIGN 1 #define UBI_LAYOUT_VOLUME_EBS 2 #define UBI_LAYOUT_VOLUME_NAME "layout volume" #define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT /* The maximum number of volumes per one UBI device */ #define UBI_MAX_VOLUMES 128 /* The maximum volume name length */ #define UBI_VOL_NAME_MAX 127 /* Size of the volume table record */ #define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) /* Size of the volume table record without the ending CRC */ #define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) /** * struct ubi_vtbl_record - a record in the volume table. * @reserved_pebs: how many physical eraseblocks are reserved for this volume * @alignment: volume alignment * @data_pad: how many bytes are unused at the end of the each physical * eraseblock to satisfy the requested alignment * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @upd_marker: if volume update was started but not finished * @name_len: volume name length * @name: the volume name * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) * @padding: reserved, zeroes * @crc: a CRC32 checksum of the record * * The volume table records are stored in the volume table, which is stored in * the layout volume. The layout volume consists of 2 logical eraseblock, each * of which contains a copy of the volume table (i.e., the volume table is * duplicated). The volume table is an array of &struct ubi_vtbl_record * objects indexed by the volume ID. * * If the size of the logical eraseblock is large enough to fit * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES * records. Otherwise, it contains as many records as it can fit (i.e., size of * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). * * The @upd_marker flag is used to implement volume update. It is set to %1 * before update and set to %0 after the update. So if the update operation was * interrupted, UBI knows that the volume is corrupted. * * The @alignment field is specified when the volume is created and cannot be * later changed. It may be useful, for example, when a block-oriented file * system works on top of UBI. The @data_pad field is calculated using the * logical eraseblock size and @alignment. The alignment must be multiple to the * minimal flash I/O unit. If @alignment is 1, all the available space of * the physical eraseblocks is used. * * Empty records contain all zeroes and the CRC checksum of those zeroes. */ struct ubi_vtbl_record { __be32 reserved_pebs; __be32 alignment; __be32 data_pad; __u8 vol_type; __u8 upd_marker; __be16 name_len; __u8 name[UBI_VOL_NAME_MAX+1]; __u8 flags; __u8 padding[23]; __be32 crc; } __attribute__ ((packed)); #endif /* !__UBI_MEDIA_H__ */ mtd-utils-1.5.0/include/mtd/ubi-user.h000066400000000000000000000362041175167361300175710ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Artem Bityutskiy (Битюцкий Артём) */ #ifndef __UBI_USER_H__ #define __UBI_USER_H__ /* * UBI device creation (the same as MTD device attachment) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI * control device. The caller has to properly fill and pass * &struct ubi_attach_req object - UBI will attach the MTD device specified in * the request and return the newly created UBI device number as the ioctl * return value. * * UBI device deletion (the same as MTD device detachment) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI * control device. * * UBI volume creation * ~~~~~~~~~~~~~~~~~~~ * * UBI volumes are created via the %UBI_IOCMKVOL ioctl command of UBI character * device. A &struct ubi_mkvol_req object has to be properly filled and a * pointer to it has to be passed to the ioctl. * * UBI volume deletion * ~~~~~~~~~~~~~~~~~~~ * * To delete a volume, the %UBI_IOCRMVOL ioctl command of the UBI character * device should be used. A pointer to the 32-bit volume ID hast to be passed * to the ioctl. * * UBI volume re-size * ~~~~~~~~~~~~~~~~~~ * * To re-size a volume, the %UBI_IOCRSVOL ioctl command of the UBI character * device should be used. A &struct ubi_rsvol_req object has to be properly * filled and a pointer to it has to be passed to the ioctl. * * UBI volumes re-name * ~~~~~~~~~~~~~~~~~~~ * * To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command * of the UBI character device should be used. A &struct ubi_rnvol_req object * has to be properly filled and a pointer to it has to be passed to the ioctl. * * UBI volume update * ~~~~~~~~~~~~~~~~~ * * Volume update should be done via the %UBI_IOCVOLUP ioctl command of the * corresponding UBI volume character device. A pointer to a 64-bit update * size should be passed to the ioctl. After this, UBI expects user to write * this number of bytes to the volume character device. The update is finished * when the claimed number of bytes is passed. So, the volume update sequence * is something like: * * fd = open("/dev/my_volume"); * ioctl(fd, UBI_IOCVOLUP, &image_size); * write(fd, buf, image_size); * close(fd); * * Logical eraseblock erase * ~~~~~~~~~~~~~~~~~~~~~~~~ * * To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the * corresponding UBI volume character device should be used. This command * unmaps the requested logical eraseblock, makes sure the corresponding * physical eraseblock is successfully erased, and returns. * * Atomic logical eraseblock change * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH * ioctl command of the corresponding UBI volume character device. A pointer to * a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the * user is expected to write the requested amount of bytes (similarly to what * should be done in case of the "volume update" ioctl). * * Logical eraseblock map * ~~~~~~~~~~~~~~~~~~~~~ * * To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP * ioctl command should be used. A pointer to a &struct ubi_map_req object is * expected to be passed. The ioctl maps the requested logical eraseblock to * a physical eraseblock and returns. Only non-mapped logical eraseblocks can * be mapped. If the logical eraseblock specified in the request is already * mapped to a physical eraseblock, the ioctl fails and returns error. * * Logical eraseblock unmap * ~~~~~~~~~~~~~~~~~~~~~~~~ * * To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP * ioctl command should be used. The ioctl unmaps the logical eraseblocks, * schedules corresponding physical eraseblock for erasure, and returns. Unlike * the "LEB erase" command, it does not wait for the physical eraseblock being * erased. Note, the side effect of this is that if an unclean reboot happens * after the unmap ioctl returns, you may find the LEB mapped again to the same * physical eraseblock after the UBI is run again. * * Check if logical eraseblock is mapped * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * To check if a logical eraseblock is mapped to a physical eraseblock, the * %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is * not mapped, and %1 if it is mapped. * * Set an UBI volume property * ~~~~~~~~~~~~~~~~~~~~~~~~~ * * To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be * used. A pointer to a &struct ubi_set_prop_req object is expected to be * passed. The object describes which property should be set, and to which value * it should be set. */ /* * When a new UBI volume or UBI device is created, users may either specify the * volume/device number they want to create or to let UBI automatically assign * the number using these constants. */ #define UBI_VOL_NUM_AUTO (-1) #define UBI_DEV_NUM_AUTO (-1) /* Maximum volume name length */ #define UBI_MAX_VOLUME_NAME 127 /* ioctl commands of UBI character devices */ #define UBI_IOC_MAGIC 'o' /* Create an UBI volume */ #define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) /* Remove an UBI volume */ #define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) /* Re-size an UBI volume */ #define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) /* Re-name volumes */ #define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req) /* ioctl commands of the UBI control character device */ #define UBI_CTRL_IOC_MAGIC 'o' /* Attach an MTD device */ #define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) /* Detach an MTD device */ #define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) /* ioctl commands of UBI volume character devices */ #define UBI_VOL_IOC_MAGIC 'O' /* Start UBI volume update */ #define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) /* LEB erasure command, used for debugging, disabled by default */ #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) /* Atomic LEB change command */ #define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) /* Map LEB command */ #define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req) /* Unmap LEB command */ #define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, int32_t) /* Check if LEB is mapped command */ #define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, int32_t) /* Set an UBI volume property */ #define UBI_IOCSETPROP _IOW(UBI_VOL_IOC_MAGIC, 6, struct ubi_set_prop_req) /* Maximum MTD device name length supported by UBI */ #define MAX_UBI_MTD_NAME_LEN 127 /* Maximum amount of UBI volumes that can be re-named at one go */ #define UBI_MAX_RNVOL 32 /* * UBI data type hint constants. * * UBI_LONGTERM: long-term data * UBI_SHORTTERM: short-term data * UBI_UNKNOWN: data persistence is unknown * * These constants are used when data is written to UBI volumes in order to * help the UBI wear-leveling unit to find more appropriate physical * eraseblocks. */ enum { UBI_LONGTERM = 1, UBI_SHORTTERM = 2, UBI_UNKNOWN = 3, }; /* * UBI volume type constants. * * @UBI_DYNAMIC_VOLUME: dynamic volume * @UBI_STATIC_VOLUME: static volume */ enum { UBI_DYNAMIC_VOLUME = 3, UBI_STATIC_VOLUME = 4, }; /* * UBI set property ioctl constants * * @UBI_PROP_DIRECT_WRITE: allow / disallow user to directly write and * erase individual eraseblocks on dynamic volumes */ enum { UBI_PROP_DIRECT_WRITE = 1, }; /** * struct ubi_attach_req - attach MTD device request. * @ubi_num: UBI device number to create * @mtd_num: MTD device number to attach * @vid_hdr_offset: VID header offset (use defaults if %0) * @padding: reserved for future, not used, has to be zeroed * * This data structure is used to specify MTD device UBI has to attach and the * parameters it has to use. The number which should be assigned to the new UBI * device is passed in @ubi_num. UBI may automatically assign the number if * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in * @ubi_num. * * Most applications should pass %0 in @vid_hdr_offset to make UBI use default * offset of the VID header within physical eraseblocks. The default offset is * the next min. I/O unit after the EC header. For example, it will be offset * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. * * But in rare cases, if this optimizes things, the VID header may be placed to * a different offset. For example, the boot-loader might do things faster if * the VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. * As the boot-loader would not normally need to read EC headers (unless it * needs UBI in RW mode), it might be faster to calculate ECC. This is weird * example, but it real-life example. So, in this example, @vid_hdr_offer would * be 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes * aligned, which is OK, as UBI is clever enough to realize this is 4th * sub-page of the first page and add needed padding. */ struct ubi_attach_req { int32_t ubi_num; int32_t mtd_num; int32_t vid_hdr_offset; int8_t padding[12]; }; /** * struct ubi_mkvol_req - volume description data structure used in * volume creation requests. * @vol_id: volume number * @alignment: volume alignment * @bytes: volume size in bytes * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @padding1: reserved for future, not used, has to be zeroed * @name_len: volume name length * @padding2: reserved for future, not used, has to be zeroed * @name: volume name * * This structure is used by user-space programs when creating new volumes. The * @used_bytes field is only necessary when creating static volumes. * * The @alignment field specifies the required alignment of the volume logical * eraseblock. This means, that the size of logical eraseblocks will be aligned * to this number, i.e., * (UBI device logical eraseblock size) mod (@alignment) = 0. * * To put it differently, the logical eraseblock of this volume may be slightly * shortened in order to make it properly aligned. The alignment has to be * multiple of the flash minimal input/output unit, or %1 to utilize the entire * available space of logical eraseblocks. * * The @alignment field may be useful, for example, when one wants to maintain * a block device on top of an UBI volume. In this case, it is desirable to fit * an integer number of blocks in logical eraseblocks of this UBI volume. With * alignment it is possible to update this volume using plane UBI volume image * BLOBs, without caring about how to properly align them. */ struct ubi_mkvol_req { int32_t vol_id; int32_t alignment; int64_t bytes; int8_t vol_type; int8_t padding1; int16_t name_len; int8_t padding2[4]; char name[UBI_MAX_VOLUME_NAME + 1]; } __attribute__ ((packed)); /** * struct ubi_rsvol_req - a data structure used in volume re-size requests. * @vol_id: ID of the volume to re-size * @bytes: new size of the volume in bytes * * Re-sizing is possible for both dynamic and static volumes. But while dynamic * volumes may be re-sized arbitrarily, static volumes cannot be made to be * smaller than the number of bytes they bear. To arbitrarily shrink a static * volume, it must be wiped out first (by means of volume update operation with * zero number of bytes). */ struct ubi_rsvol_req { int64_t bytes; int32_t vol_id; } __attribute__ ((packed)); /** * struct ubi_rnvol_req - volumes re-name request. * @count: count of volumes to re-name * @padding1: reserved for future, not used, has to be zeroed * @vol_id: ID of the volume to re-name * @name_len: name length * @padding2: reserved for future, not used, has to be zeroed * @name: new volume name * * UBI allows to re-name up to %32 volumes at one go. The count of volumes to * re-name is specified in the @count field. The ID of the volumes to re-name * and the new names are specified in the @vol_id and @name fields. * * The UBI volume re-name operation is atomic, which means that should power cut * happen, the volumes will have either old name or new name. So the possible * use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes * A and B one may create temporary volumes %A1 and %B1 with the new contents, * then atomically re-name A1->A and B1->B, in which case old %A and %B will * be removed. * * If it is not desirable to remove old A and B, the re-name request has to * contain 4 entries: A1->A, A->A1, B1->B, B->B1, in which case old A1 and B1 * become A and B, and old A and B will become A1 and B1. * * It is also OK to request: A1->A, A1->X, B1->B, B->Y, in which case old A1 * and B1 become A and B, and old A and B become X and Y. * * In other words, in case of re-naming into an existing volume name, the * existing volume is removed, unless it is re-named as well at the same * re-name request. */ struct ubi_rnvol_req { int32_t count; int8_t padding1[12]; struct { int32_t vol_id; int16_t name_len; int8_t padding2[2]; char name[UBI_MAX_VOLUME_NAME + 1]; } ents[UBI_MAX_RNVOL]; } __attribute__ ((packed)); /** * struct ubi_leb_change_req - a data structure used in atomic LEB change * requests. * @lnum: logical eraseblock number to change * @bytes: how many bytes will be written to the logical eraseblock * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) * @padding: reserved for future, not used, has to be zeroed */ struct ubi_leb_change_req { int32_t lnum; int32_t bytes; int8_t dtype; int8_t padding[7]; } __attribute__ ((packed)); /** * struct ubi_map_req - a data structure used in map LEB requests. * @lnum: logical eraseblock number to unmap * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) * @padding: reserved for future, not used, has to be zeroed */ struct ubi_map_req { int32_t lnum; int8_t dtype; int8_t padding[3]; } __attribute__ ((packed)); /** * struct ubi_set_prop_req - a data structure used to set an ubi volume * property. * @property: property to set (%UBI_PROP_DIRECT_WRITE) * @padding: reserved for future, not used, has to be zeroed * @value: value to set */ struct ubi_set_prop_req { uint8_t property; uint8_t padding[7]; uint64_t value; } __attribute__ ((packed)); #endif /* __UBI_USER_H__ */ mtd-utils-1.5.0/include/mtd_swab.h000066400000000000000000000035571175167361300170570ustar00rootroot00000000000000#ifndef MTD_SWAB_H #define MTD_SWAB_H #include #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 mtd-utils-1.5.0/include/xalloc.h000066400000000000000000000046451175167361300165400ustar00rootroot00000000000000/* * memory wrappers * * Copyright (c) Artem Bityutskiy, 2007, 2008 * Copyright 2001, 2002 Red Hat, Inc. * 2001 David A. Schleef * 2002 Axis Communications AB * 2001, 2002 Erik Andersen * 2004 University of Szeged, Hungary * 2006 KaiGai Kohei * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __MTD_UTILS_XALLOC_H__ #define __MTD_UTILS_XALLOC_H__ #include #include /* * Mark these functions as unused so that gcc does not emit warnings * when people include this header but don't use every function. */ __attribute__((unused)) static void *xmalloc(size_t size) { void *ptr = malloc(size); if (ptr == NULL && size != 0) sys_errmsg_die("out of memory"); return ptr; } __attribute__((unused)) static void *xcalloc(size_t nmemb, size_t size) { void *ptr = calloc(nmemb, size); if (ptr == NULL && nmemb != 0 && size != 0) sys_errmsg_die("out of memory"); return ptr; } __attribute__((unused)) static void *xzalloc(size_t size) { return xcalloc(1, size); } __attribute__((unused)) static void *xrealloc(void *ptr, size_t size) { ptr = realloc(ptr, size); if (ptr == NULL && size != 0) sys_errmsg_die("out of memory"); return ptr; } __attribute__((unused)) static char *xstrdup(const char *s) { char *t; if (s == NULL) return NULL; t = strdup(s); if (t == NULL) sys_errmsg_die("out of memory"); return t; } #ifdef _GNU_SOURCE #include __attribute__((unused)) static int xasprintf(char **strp, const char *fmt, ...) { int cnt; va_list ap; va_start(ap, fmt); cnt = vasprintf(strp, fmt, ap); va_end(ap); if (cnt == -1) sys_errmsg_die("out of memory"); return cnt; } #endif #endif /* !__MTD_UTILS_XALLOC_H__ */ mtd-utils-1.5.0/jffs-dump.c000066400000000000000000000206561175167361300155210ustar00rootroot00000000000000/* * Dump JFFS filesystem. * Useful when it buggers up. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #define BLOCK_SIZE 1024 #define JFFS_MAGIC 0x34383931 /* "1984" */ #define JFFS_MAX_NAME_LEN 256 #define JFFS_MIN_INO 1 #define JFFS_TRACE_INDENT 4 #define JFFS_ALIGN_SIZE 4 #define MAX_CHUNK_SIZE 32768 /* How many padding bytes should be inserted between two chunks of data on the flash? */ #define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \ - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ % JFFS_ALIGN_SIZE) #define JFFS_EMPTY_BITMASK 0xffffffff #define JFFS_MAGIC_BITMASK 0x34383931 #define JFFS_DIRTY_BITMASK 0x00000000 struct jffs_raw_inode { uint32_t magic; /* A constant magic number. */ uint32_t ino; /* Inode number. */ uint32_t pino; /* Parent's inode number. */ uint32_t version; /* Version number. */ uint32_t mode; /* file_type, mode */ uint16_t uid; uint16_t gid; uint32_t atime; uint32_t mtime; uint32_t ctime; uint32_t offset; /* Where to begin to write. */ uint32_t dsize; /* Size of the file data. */ uint32_t rsize; /* How much are going to be replaced? */ uint8_t nsize; /* Name length. */ uint8_t nlink; /* Number of links. */ uint8_t spare : 6; /* For future use. */ uint8_t rename : 1; /* Is this a special rename? */ uint8_t deleted : 1; /* Has this file been deleted? */ uint8_t accurate; /* The inode is obsolete if accurate == 0. */ uint32_t dchksum; /* Checksum for the data. */ uint16_t nchksum; /* Checksum for the name. */ uint16_t chksum; /* Checksum for the raw_inode. */ }; struct jffs_file { struct jffs_raw_inode inode; char *name; unsigned char *data; }; char *root_directory_name = NULL; int fs_pos = 0; int verbose = 0; #define ENDIAN_HOST 0 #define ENDIAN_BIG 1 #define ENDIAN_LITTLE 2 int endian = ENDIAN_HOST; static uint32_t jffs_checksum(void *data, int size); void jffs_print_trace(const char *path, int depth); int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, int depth); void write_file(struct jffs_file *f, FILE *fs, struct stat st); void read_data(struct jffs_file *f, const char *path, int offset); int mkfs(FILE *fs, const char *path, int ino, int parent, int depth); static uint32_t jffs_checksum(void *data, int size) { uint32_t sum = 0; uint8_t *ptr = (uint8_t *)data; while (size-- > 0) { sum += *ptr++; } return sum; } void jffs_print_trace(const char *path, int depth) { int path_len = strlen(path); int out_pos = depth * JFFS_TRACE_INDENT; int pos = path_len - 1; char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1); if (verbose >= 2) { fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path); } if (!out) { fprintf(stderr, "jffs_print_trace(): Allocation failed.\n"); fprintf(stderr, " path: \"%s\"\n", path); fprintf(stderr, "depth: %d\n", depth); exit(1); } memset(out, ' ', depth * JFFS_TRACE_INDENT); if (path[pos] == '/') { pos--; } while (path[pos] && (path[pos] != '/')) { pos--; } for (pos++; path[pos] && (path[pos] != '/'); pos++) { out[out_pos++] = path[pos]; } out[out_pos] = '\0'; fprintf(stderr, "%s\n", out); } /* Print the contents of a raw inode. */ void jffs_print_raw_inode(struct jffs_raw_inode *raw_inode) { fprintf(stdout, "jffs_raw_inode: inode number: %u, version %u\n", raw_inode->ino, raw_inode->version); fprintf(stdout, "{\n"); fprintf(stdout, " 0x%08x, /* magic */\n", raw_inode->magic); fprintf(stdout, " 0x%08x, /* ino */\n", raw_inode->ino); fprintf(stdout, " 0x%08x, /* pino */\n", raw_inode->pino); fprintf(stdout, " 0x%08x, /* version */\n", raw_inode->version); fprintf(stdout, " 0x%08x, /* mode */\n", raw_inode->mode); fprintf(stdout, " 0x%04x, /* uid */\n", raw_inode->uid); fprintf(stdout, " 0x%04x, /* gid */\n", raw_inode->gid); fprintf(stdout, " 0x%08x, /* atime */\n", raw_inode->atime); fprintf(stdout, " 0x%08x, /* mtime */\n", raw_inode->mtime); fprintf(stdout, " 0x%08x, /* ctime */\n", raw_inode->ctime); fprintf(stdout, " 0x%08x, /* offset */\n", raw_inode->offset); fprintf(stdout, " 0x%08x, /* dsize */\n", raw_inode->dsize); fprintf(stdout, " 0x%08x, /* rsize */\n", raw_inode->rsize); fprintf(stdout, " 0x%02x, /* nsize */\n", raw_inode->nsize); fprintf(stdout, " 0x%02x, /* nlink */\n", raw_inode->nlink); fprintf(stdout, " 0x%02x, /* spare */\n", raw_inode->spare); fprintf(stdout, " %u, /* rename */\n", raw_inode->rename); fprintf(stdout, " %u, /* deleted */\n", raw_inode->deleted); fprintf(stdout, " 0x%02x, /* accurate */\n", raw_inode->accurate); fprintf(stdout, " 0x%08x, /* dchksum */\n", raw_inode->dchksum); fprintf(stdout, " 0x%04x, /* nchksum */\n", raw_inode->nchksum); fprintf(stdout, " 0x%04x, /* chksum */\n", raw_inode->chksum); fprintf(stdout, "}\n"); } static void write_val32(uint32_t *adr, uint32_t val) { switch(endian) { case ENDIAN_HOST: *adr = val; break; case ENDIAN_LITTLE: *adr = __cpu_to_le32(val); break; case ENDIAN_BIG: *adr = __cpu_to_be32(val); break; } } static void write_val16(uint16_t *adr, uint16_t val) { switch(endian) { case ENDIAN_HOST: *adr = val; break; case ENDIAN_LITTLE: *adr = __cpu_to_le16(val); break; case ENDIAN_BIG: *adr = __cpu_to_be16(val); break; } } static uint32_t read_val32(uint32_t *adr) { uint32_t val; switch(endian) { case ENDIAN_HOST: val = *adr; break; case ENDIAN_LITTLE: val = __le32_to_cpu(*adr); break; case ENDIAN_BIG: val = __be32_to_cpu(*adr); break; } return val; } static uint16_t read_val16(uint16_t *adr) { uint16_t val; switch(endian) { case ENDIAN_HOST: val = *adr; break; case ENDIAN_LITTLE: val = __le16_to_cpu(*adr); break; case ENDIAN_BIG: val = __be16_to_cpu(*adr); break; } return val; } int main(int argc, char **argv) { int fs; struct stat sb; uint32_t wordbuf; off_t pos = 0; off_t end; struct jffs_raw_inode ino; unsigned char namebuf[4096]; int myino = -1; if (argc < 2) { printf("no filesystem given\n"); exit(1); } fs = open(argv[1], O_RDONLY); if (fs < 0) { perror("open"); exit(1); } if (argc > 2) { myino = atol(argv[2]); printf("Printing ino #%d\n" , myino); } if (fstat(fs, &sb) < 0) { perror("stat"); close(fs); exit(1); } end = sb.st_size; while (pos < end) { if (pread(fs, &wordbuf, 4, pos) < 0) { perror("pread"); exit(1); } switch(wordbuf) { case JFFS_EMPTY_BITMASK: // printf("0xff started at 0x%lx\n", pos); for (; pos < end && wordbuf == JFFS_EMPTY_BITMASK; pos += 4) { if (pread(fs, &wordbuf, 4, pos) < 0) { perror("pread"); exit(1); } } if (pos < end) pos -= 4; // printf("0xff ended at 0x%lx\n", pos); continue; case JFFS_DIRTY_BITMASK: // printf("0x00 started at 0x%lx\n", pos); for (; pos < end && wordbuf == JFFS_DIRTY_BITMASK; pos += 4) { if (pread(fs, &wordbuf, 4, pos) < 0) { perror("pread"); exit(1); } } if (pos < end) pos -=4; // printf("0x00 ended at 0x%lx\n", pos); continue; default: printf("Argh. Dirty memory at 0x%lx\n", pos); // file_hexdump(fs, pos, 128); for (pos += 4; pos < end; pos += 4) { if (pread(fs, &wordbuf, 4, pos) < 0) { perror("pread"); exit(1); } if (wordbuf == JFFS_MAGIC_BITMASK) break; } case JFFS_MAGIC_BITMASK: if (pread(fs, &ino, sizeof(ino), pos) < 0) { perror("pread"); exit(1); } if (myino == -1 || ino.ino == myino) { printf("Magic found at 0x%lx\n", pos); jffs_print_raw_inode(&ino); } pos += sizeof(ino); if (myino == -1 || ino.ino == myino) { if (ino.nsize) { if (pread(fs, namebuf, min(ino.nsize, 4095), pos) < 0) { perror("pread"); exit(1); } if (ino.nsize < 4095) namebuf[ino.nsize] = 0; else namebuf[4095] = 0; printf("Name: \"%s\"\n", namebuf); } else { printf("No Name\n"); } } pos += (ino.nsize + 3) & ~3; pos += (ino.dsize + 3) & ~3; } } } mtd-utils-1.5.0/jffs2dump.c000066400000000000000000000534271175167361300155300ustar00rootroot00000000000000/* * dumpjffs2.c * * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Overview: * This utility dumps the contents of a binary JFFS2 image * * * Bug/ToDo: */ #define PROGRAM_NAME "jffs2dump" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "summary.h" #include "common.h" #define PAD(x) (((x)+3)&~3) /* For outputting a byte-swapped version of the input image. */ #define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) #define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) #define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) #define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) // Global variables long imglen; // length of image char *data; // image data void display_help (void) { printf("Usage: %s [OPTION]... INPUTFILE\n" "Dump the contents of a binary JFFS2 image.\n\n" " --help display this help and exit\n" " --version display version information and exit\n" " -b, --bigendian image is big endian\n" " -l, --littleendian image is little endian\n" " -c, --content dump image contents\n" " -e, --endianconvert=FNAME convert image endianness, output to file fname\n" " -r, --recalccrc recalc name and data crc on endian conversion\n" " -d, --datsize=LEN size of data chunks, when oob data in binary image (NAND only)\n" " -o, --oobsize=LEN size of oob data chunk in binary image (NAND only)\n" " -v, --verbose verbose output\n", PROGRAM_NAME); exit(0); } void display_version (void) { printf("%1$s " VERSION "\n" "\n" "Copyright (C) 2003 Thomas Gleixner \n" "\n" "%1$s comes with NO WARRANTY\n" "to the extent permitted by law.\n" "\n" "You may redistribute copies of %1$s\n" "under the terms of the GNU General Public Licence.\n" "See the file `COPYING' for more information.\n", PROGRAM_NAME); exit(0); } // Option variables int verbose; // verbose output char *img; // filename of image int dumpcontent; // dump image content int target_endian = __BYTE_ORDER; // image endianess int convertendian; // convert endianness int recalccrc; // recalc name and data crc's on endian conversion char cnvfile[256]; // filename for conversion output int datsize; // Size of data chunks, when oob data is inside the binary image int oobsize; // Size of oob chunks, when oob data is inside the binary image void process_options (int argc, char *argv[]) { int error = 0; for (;;) { int option_index = 0; static const char *short_options = "blce:rd:o:v"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"bigendian", no_argument, 0, 'b'}, {"littleendian", no_argument, 0, 'l'}, {"content", no_argument, 0, 'c'}, {"endianconvert", required_argument, 0, 'e'}, {"datsize", required_argument, 0, 'd'}, {"oobsize", required_argument, 0, 'o'}, {"recalccrc", required_argument, 0, 'r'}, {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) { break; } switch (c) { case 0: switch (option_index) { case 0: display_help(); break; case 1: display_version(); break; } break; case 'v': verbose = 1; break; case 'b': target_endian = __BIG_ENDIAN; break; case 'l': target_endian = __LITTLE_ENDIAN; break; case 'c': dumpcontent = 1; break; case 'd': datsize = atoi(optarg); break; case 'o': oobsize = atoi(optarg); break; case 'e': convertendian = 1; strcpy (cnvfile, optarg); break; case 'r': recalccrc = 1; break; case '?': error = 1; break; } } if ((argc - optind) != 1 || error) display_help (); img = argv[optind]; } /* * Dump image contents */ void do_dumpcontent (void) { char *p = data, *p_free_begin; union jffs2_node_union *node; int empty = 0, dirty = 0; char name[256]; uint32_t crc; uint16_t type; int bitchbitmask = 0; int obsolete; p_free_begin = NULL; while ( p < (data + imglen)) { node = (union jffs2_node_union*) p; /* Skip empty space */ if (!p_free_begin) p_free_begin = p; if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { p += 4; empty += 4; continue; } if (p != p_free_begin) printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data); p_free_begin = NULL; if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { if (!bitchbitmask++) printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); p += 4; dirty += 4; continue; } bitchbitmask = 0; type = je16_to_cpu(node->u.nodetype); if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { obsolete = 1; type |= JFFS2_NODE_ACCURATE; } else obsolete = 0; /* Set accurate for CRC check */ node->u.nodetype = cpu_to_je16(type); crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); if (crc != je32_to_cpu (node->u.hdr_crc)) { printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); p += 4; dirty += 4; continue; } switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: printf ("%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); if (crc != je32_to_cpu (node->i.node_crc)) { printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); p += PAD(je32_to_cpu (node->i.totlen)); dirty += PAD(je32_to_cpu (node->i.totlen));; continue; } crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); if (crc != je32_to_cpu(node->i.data_crc)) { printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); p += PAD(je32_to_cpu (node->i.totlen)); dirty += PAD(je32_to_cpu (node->i.totlen));; continue; } p += PAD(je32_to_cpu (node->i.totlen)); break; case JFFS2_NODETYPE_DIRENT: memcpy (name, node->d.name, node->d.nsize); name [node->d.nsize] = 0x0; printf ("%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), node->d.nsize, name); crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); if (crc != je32_to_cpu (node->d.node_crc)) { printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); p += PAD(je32_to_cpu (node->d.totlen)); dirty += PAD(je32_to_cpu (node->d.totlen));; continue; } crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); if (crc != je32_to_cpu(node->d.name_crc)) { printf ("Wrong name_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); p += PAD(je32_to_cpu (node->d.totlen)); dirty += PAD(je32_to_cpu (node->d.totlen));; continue; } p += PAD(je32_to_cpu (node->d.totlen)); break; case JFFS2_NODETYPE_SUMMARY: { int i; struct jffs2_sum_marker * sm; printf("%8s Inode Sum node at 0x%08zx, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->s.totlen), je32_to_cpu (node->s.sum_num), je32_to_cpu (node->s.cln_mkr)); crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); if (crc != je32_to_cpu (node->s.node_crc)) { printf ("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); p += PAD(je32_to_cpu (node->s.totlen)); dirty += PAD(je32_to_cpu (node->s.totlen));; continue; } crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); if (crc != je32_to_cpu(node->s.sum_crc)) { printf ("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); p += PAD(je32_to_cpu (node->s.totlen)); dirty += PAD(je32_to_cpu (node->s.totlen));; continue; } if (verbose) { void *sp; sp = (p + sizeof(struct jffs2_raw_summary)); for(i=0; is.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%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); break; case JFFS2_NODETYPE_PADDING: if (verbose) { printf ("%8s Padding node at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); break; case 0xffff: p += 4; empty += 4; break; default: if (verbose) { printf ("%8s Unknown node at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); dirty += PAD(je32_to_cpu (node->u.totlen)); } } if (verbose) printf ("Empty space: %d, dirty space: %d\n", empty, dirty); } /* * Convert endianess */ void do_endianconvert (void) { char *p = data; union jffs2_node_union *node, newnode; int fd, len; jint32_t mode; uint32_t crc; fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); if (fd < 0) { fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); return; } while ( p < (data + imglen)) { node = (union jffs2_node_union*) p; /* Skip empty space */ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { write (fd, p, 4); p += 4; continue; } if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); newnode.u.magic = cnv_e16 (node->u.magic); newnode.u.nodetype = cnv_e16 (node->u.nodetype); write (fd, &newnode, 4); p += 4; continue; } crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); if (crc != je32_to_cpu (node->u.hdr_crc)) { printf ("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); } switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: newnode.i.magic = cnv_e16 (node->i.magic); newnode.i.nodetype = cnv_e16 (node->i.nodetype); newnode.i.totlen = cnv_e32 (node->i.totlen); newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); newnode.i.ino = cnv_e32 (node->i.ino); newnode.i.version = cnv_e32 (node->i.version); mode.v32 = node->i.mode.m; mode = cnv_e32 (mode); newnode.i.mode.m = mode.v32; newnode.i.uid = cnv_e16 (node->i.uid); newnode.i.gid = cnv_e16 (node->i.gid); newnode.i.isize = cnv_e32 (node->i.isize); newnode.i.atime = cnv_e32 (node->i.atime); newnode.i.mtime = cnv_e32 (node->i.mtime); newnode.i.ctime = cnv_e32 (node->i.ctime); newnode.i.offset = cnv_e32 (node->i.offset); newnode.i.csize = cnv_e32 (node->i.csize); newnode.i.dsize = cnv_e32 (node->i.dsize); newnode.i.compr = node->i.compr; newnode.i.usercompr = node->i.usercompr; newnode.i.flags = cnv_e16 (node->i.flags); if (recalccrc) { len = je32_to_cpu(node->i.csize); newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len)); } else newnode.i.data_crc = cnv_e32 (node->i.data_crc); newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); write (fd, &newnode, sizeof (struct jffs2_raw_inode)); write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); p += PAD(je32_to_cpu (node->i.totlen)); break; case JFFS2_NODETYPE_DIRENT: newnode.d.magic = cnv_e16 (node->d.magic); newnode.d.nodetype = cnv_e16 (node->d.nodetype); newnode.d.totlen = cnv_e32 (node->d.totlen); newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); newnode.d.pino = cnv_e32 (node->d.pino); newnode.d.version = cnv_e32 (node->d.version); newnode.d.ino = cnv_e32 (node->d.ino); newnode.d.mctime = cnv_e32 (node->d.mctime); newnode.d.nsize = node->d.nsize; newnode.d.type = node->d.type; newnode.d.unused[0] = node->d.unused[0]; newnode.d.unused[1] = node->d.unused[1]; newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); if (recalccrc) newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); else newnode.d.name_crc = cnv_e32 (node->d.name_crc); write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); p += PAD(je32_to_cpu (node->d.totlen)); break; case JFFS2_NODETYPE_CLEANMARKER: case JFFS2_NODETYPE_PADDING: newnode.u.magic = cnv_e16 (node->u.magic); newnode.u.nodetype = cnv_e16 (node->u.nodetype); newnode.u.totlen = cnv_e32 (node->u.totlen); newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); write (fd, &newnode, sizeof (struct jffs2_unknown_node)); len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); if (len > 0) write (fd, p + sizeof (struct jffs2_unknown_node), len); p += PAD(je32_to_cpu (node->u.totlen)); break; case JFFS2_NODETYPE_SUMMARY : { struct jffs2_sum_marker *sm_ptr; int i,sum_len; int counter = 0; newnode.s.magic = cnv_e16 (node->s.magic); newnode.s.nodetype = cnv_e16 (node->s.nodetype); newnode.s.totlen = cnv_e32 (node->s.totlen); newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); newnode.s.sum_num = cnv_e32 (node->s.sum_num); newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); newnode.s.padded = cnv_e32 (node->s.padded); newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); // summary header p += sizeof (struct jffs2_raw_summary); // summary data sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); for (i=0; is.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 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); // write out new node header write(fd, &newnode, sizeof (struct jffs2_raw_summary)); // write out new summary data write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); break; } case 0xffff: write (fd, p, 4); p += 4; break; default: printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); p += PAD(je32_to_cpu (node->u.totlen)); } } close (fd); } /* * Main program */ int main(int argc, char **argv) { int fd; process_options(argc, argv); /* Open the input file */ if ((fd = open(img, O_RDONLY)) == -1) { perror("open input file"); exit(1); } // get image length imglen = lseek(fd, 0, SEEK_END); lseek (fd, 0, SEEK_SET); data = malloc (imglen); if (!data) { perror("out of memory"); close (fd); exit(1); } if (datsize && oobsize) { int idx = 0; long len = imglen; uint8_t oob[oobsize]; printf ("Peeling data out of combined data/oob image\n"); while (len) { // read image data read (fd, &data[idx], datsize); read (fd, oob, oobsize); idx += datsize; imglen -= oobsize; len -= datsize + oobsize; } } else { // read image data read (fd, data, imglen); } // Close the input file close(fd); if (dumpcontent) do_dumpcontent (); if (convertendian) do_endianconvert (); // free memory free (data); // Return happy exit (0); } mtd-utils-1.5.0/jffs2reader.c000066400000000000000000000515461175167361300160250ustar00rootroot00000000000000/* vi: set sw=4 ts=4: */ /* * jffs2reader v0.0.18 A jffs2 image reader * * Copyright (c) 2001 Jari Kirma * * 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 * * In compliance with (2) above, this is hereby marked as an altered * version of this software. It has been altered as follows: * *) Listing a directory now mimics the behavior of 'ls -l' * *) Support for recursive listing has been added * *) Without options, does a recursive 'ls' on the whole filesystem * *) option parsing now uses getopt() * *) Now uses printf, and error messages go to stderr. * *) The copyright notice has been cleaned up and reformatted * *) The code has been reformatted * *) Several twisty code paths have been fixed so I can understand them. * -Erik, 1 September 2001 * * *) Made it show major/minor numbers for device nodes * *) Made it show symlink targets * -Erik, 13 September 2001 */ /* TODO: - Add CRC checking code to places marked with XXX. - Add support for other node compression types. - Test with real life images. - Maybe port into bootloader. */ /* BUGS: - Doesn't check CRC checksums. */ #define PROGRAM_NAME "jffs2reader" #include #include #include #include #include #include #include #include #include #include #include #include "mtd/jffs2-user.h" #include "common.h" #define SCRATCH_SIZE (5*1024*1024) /* macro to avoid "lvalue required as left operand of assignment" error */ #define ADD_BYTES(p, n) ((p) = (typeof(p))((char *)(p) + (n))) #define DIRENT_INO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->ino) : 0) #define DIRENT_PINO(dirent) ((dirent) !=NULL ? je32_to_cpu((dirent)->pino) : 0) struct dir { struct dir *next; uint8_t type; uint8_t nsize; uint32_t ino; char name[256]; }; int target_endian = __BYTE_ORDER; void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *); struct dir *putdir(struct dir *, struct jffs2_raw_dirent *); void printdir(char *o, size_t size, struct dir *d, const char *path, int recurse, int want_ctime); void freedir(struct dir *); struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino); struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t, char *, uint8_t); struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t); struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t); struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, const char *, uint32_t *, int); struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, const char *, uint32_t *); void lsdir(char *, size_t, const char *, int, int); void catfile(char *, size_t, char *, char *, size_t, size_t *); int main(int, char **); /* writes file node into buffer, to the proper position. */ /* reading all valid nodes in version order reconstructs the file. */ /* b - buffer bsize - buffer size rsize - result size n - node */ void putblock(char *b, size_t bsize, size_t * rsize, struct jffs2_raw_inode *n) { uLongf dlen = je32_to_cpu(n->dsize); if (je32_to_cpu(n->isize) > bsize || (je32_to_cpu(n->offset) + dlen) > bsize) errmsg_die("File does not fit into buffer!"); if (*rsize < je32_to_cpu(n->isize)) bzero(b + *rsize, je32_to_cpu(n->isize) - *rsize); switch (n->compr) { case JFFS2_COMPR_ZLIB: uncompress((Bytef *) b + je32_to_cpu(n->offset), &dlen, (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), (uLongf) je32_to_cpu(n->csize)); break; case JFFS2_COMPR_NONE: memcpy(b + je32_to_cpu(n->offset), ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); break; case JFFS2_COMPR_ZERO: bzero(b + je32_to_cpu(n->offset), dlen); break; /* [DYN]RUBIN support required! */ default: errmsg_die("Unsupported compression method!"); } *rsize = je32_to_cpu(n->isize); } /* adds/removes directory node into dir struct. */ /* reading all valid nodes in version order reconstructs the directory. */ /* dd - directory struct being processed n - node return value: directory struct value replacing dd */ struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n) { struct dir *o, *d, *p; o = dd; if (je32_to_cpu(n->ino)) { if (dd == NULL) { d = xmalloc(sizeof(struct dir)); d->type = n->type; memcpy(d->name, n->name, n->nsize); d->nsize = n->nsize; d->ino = je32_to_cpu(n->ino); d->next = NULL; return d; } while (1) { if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) { dd->type = n->type; dd->ino = je32_to_cpu(n->ino); return o; } if (dd->next == NULL) { dd->next = xmalloc(sizeof(struct dir)); dd->next->type = n->type; memcpy(dd->next->name, n->name, n->nsize); dd->next->nsize = n->nsize; dd->next->ino = je32_to_cpu(n->ino); dd->next->next = NULL; return o; } dd = dd->next; } } else { if (dd == NULL) return NULL; if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) { d = dd->next; free(dd); return d; } while (1) { p = dd; dd = dd->next; if (dd == NULL) return o; if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) { p->next = dd->next; free(dd); return o; } } } } #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) /* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ static const mode_t SBIT[] = { 0, 0, S_ISUID, 0, 0, S_ISGID, 0, 0, S_ISVTX }; /* The 9 mode bits to test */ static const mode_t MBIT[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH }; static const char MODE1[] = "rwxrwxrwx"; static const char MODE0[] = "---------"; static const char SMODE1[] = "..s..s..t"; static const char SMODE0[] = "..S..S..T"; /* * Return the standard ls-like mode string from a file mode. * This is static and so is overwritten on each call. */ const char *mode_string(int mode) { static char buf[12]; int i; buf[0] = TYPECHAR(mode); for (i = 0; i < 9; i++) { if (mode & SBIT[i]) buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i]; else buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i]; } return buf; } /* prints contents of directory structure */ /* d - dir struct */ void printdir(char *o, size_t size, struct dir *d, const char *path, int recurse, int want_ctime) { char m; char *filetime; time_t age; struct jffs2_raw_inode *ri; jint32_t mode; if (!path) return; if (strlen(path) == 1 && *path == '/') path++; while (d != NULL) { switch (d->type) { case DT_REG: m = ' '; break; case DT_FIFO: m = '|'; break; case DT_CHR: m = ' '; break; case DT_BLK: m = ' '; break; case DT_DIR: m = '/'; break; case DT_LNK: m = ' '; break; case DT_SOCK: m = '='; break; default: m = '?'; } ri = find_raw_inode(o, size, d->ino); if (!ri) { warnmsg("bug: raw_inode missing!"); d = d->next; continue; } filetime = ctime((const time_t *) &(ri->ctime)); age = time(NULL) - je32_to_cpu(ri->ctime); mode.v32 = ri->mode.m; printf("%s %-4d %-8d %-8d ", mode_string(je32_to_cpu(mode)), 1, je16_to_cpu(ri->uid), je16_to_cpu(ri->gid)); if ( d->type==DT_BLK || d->type==DT_CHR ) { dev_t rdev; size_t devsize; putblock((char*)&rdev, sizeof(rdev), &devsize, ri); printf("%4d, %3d ", major(rdev), minor(rdev)); } else { printf("%9ld ", (long)je32_to_cpu(ri->dsize)); } d->name[d->nsize]='\0'; if (want_ctime) { if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) /* hh:mm if less than 6 months old */ printf("%6.6s %5.5s ", filetime + 4, filetime + 11); else printf("%6.6s %4.4s ", filetime + 4, filetime + 20); } printf("%s/%s%c", path, d->name, m); if (d->type == DT_LNK) { char symbuf[1024]; size_t symsize; putblock(symbuf, sizeof(symbuf), &symsize, ri); symbuf[symsize] = 0; printf(" -> %s", symbuf); } printf("\n"); if (d->type == DT_DIR && recurse) { char *tmp; tmp = xmalloc(BUFSIZ); sprintf(tmp, "%s/%s", path, d->name); lsdir(o, size, tmp, recurse, want_ctime); /* Go recursive */ free(tmp); } d = d->next; } } /* frees memory used by directory structure */ /* d - dir struct */ void freedir(struct dir *d) { struct dir *t; while (d != NULL) { t = d->next; free(d); d = t; } } /* collects directory/file nodes in version order. */ /* f - file flag. if zero, collect file, compare ino to inode otherwise, collect directory, compare ino to parent inode o - filesystem image pointer size - size of filesystem image ino - inode to compare against. see f. return value: a jffs2_raw_inode that corresponds the the specified inode, or NULL */ struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino) { /* aligned! */ union jffs2_node_union *n; union jffs2_node_union *e = (union jffs2_node_union *) (o + size); union jffs2_node_union *lr; /* last block position */ union jffs2_node_union *mp = NULL; /* minimum position */ uint32_t vmin, vmint, vmaxt, vmax, vcur, v; vmin = 0; /* next to read */ vmax = ~((uint32_t) 0); /* last to read */ vmint = ~((uint32_t) 0); vmaxt = 0; /* found maximum */ vcur = 0; /* XXX what is smallest version number used? */ /* too low version number can easily result excess log rereading */ n = (union jffs2_node_union *) o; lr = n; do { while (n < e && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) ADD_BYTES(n, 4); if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_INODE && je32_to_cpu(n->i.ino) == ino && (v = je32_to_cpu(n->i.version)) > vcur) { /* XXX crc check */ if (vmaxt < v) vmaxt = v; if (vmint > v) { vmint = v; mp = n; } if (v == (vcur + 1)) return (&(n->i)); } ADD_BYTES(n, ((je32_to_cpu(n->u.totlen) + 3) & ~3)); } else n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */ if (lr == n) { /* whole loop since last read */ vmax = vmaxt; vmin = vmint; vmint = ~((uint32_t) 0); if (vcur < vmax && vcur < vmin) return (&(mp->i)); } } while (vcur < vmax); return NULL; } /* collects dir struct for selected inode */ /* o - filesystem image pointer size - size of filesystem image pino - inode of the specified directory d - input directory structure return value: result directory structure, replaces d. */ struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d) { /* aligned! */ union jffs2_node_union *n; union jffs2_node_union *e = (union jffs2_node_union *) (o + size); union jffs2_node_union *lr; /* last block position */ union jffs2_node_union *mp = NULL; /* minimum position */ uint32_t vmin, vmint, vmaxt, vmax, vcur, v; vmin = 0; /* next to read */ vmax = ~((uint32_t) 0); /* last to read */ vmint = ~((uint32_t) 0); vmaxt = 0; /* found maximum */ vcur = 0; /* XXX what is smallest version number used? */ /* too low version number can easily result excess log rereading */ n = (union jffs2_node_union *) o; lr = n; do { while (n < e && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) ADD_BYTES(n, 4); if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && je32_to_cpu(n->d.pino) == ino && (v = je32_to_cpu(n->d.version)) > vcur) { /* XXX crc check */ if (vmaxt < v) vmaxt = v; if (vmint > v) { vmint = v; mp = n; } if (v == (vcur + 1)) { d = putdir(d, &(n->d)); lr = n; vcur++; vmint = ~((uint32_t) 0); } } ADD_BYTES(n, ((je32_to_cpu(n->u.totlen) + 3) & ~3)); } else n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */ if (lr == n) { /* whole loop since last read */ vmax = vmaxt; vmin = vmint; vmint = ~((uint32_t) 0); if (vcur < vmax && vcur < vmin) { d = putdir(d, &(mp->d)); lr = n = (union jffs2_node_union *) (((char *) mp) + ((je32_to_cpu(mp->u.totlen) + 3) & ~3)); vcur = vmin; } } } while (vcur < vmax); return d; } /* resolve dirent based on criteria */ /* o - filesystem image pointer size - size of filesystem image ino - if zero, ignore, otherwise compare against dirent inode pino - if zero, ingore, otherwise compare against parent inode and use name and nsize as extra criteria name - name of wanted dirent, used if pino!=0 nsize - length of name of wanted dirent, used if pino!=0 return value: pointer to relevant dirent structure in filesystem image or NULL */ struct jffs2_raw_dirent *resolvedirent(char *o, size_t size, uint32_t ino, uint32_t pino, char *name, uint8_t nsize) { /* aligned! */ union jffs2_node_union *n; union jffs2_node_union *e = (union jffs2_node_union *) (o + size); struct jffs2_raw_dirent *dd = NULL; uint32_t vmax, v; if (!pino && ino <= 1) return dd; vmax = 0; n = (union jffs2_node_union *) o; do { while (n < e && je16_to_cpu(n->u.magic) != JFFS2_MAGIC_BITMASK) ADD_BYTES(n, 4); if (n < e && je16_to_cpu(n->u.magic) == JFFS2_MAGIC_BITMASK) { if (je16_to_cpu(n->u.nodetype) == JFFS2_NODETYPE_DIRENT && (!ino || je32_to_cpu(n->d.ino) == ino) && (v = je32_to_cpu(n->d.version)) > vmax && (!pino || (je32_to_cpu(n->d.pino) == pino && nsize == n->d.nsize && !memcmp(name, n->d.name, nsize)))) { /* XXX crc check */ if (vmax < v) { vmax = v; dd = &(n->d); } } ADD_BYTES(n, ((je32_to_cpu(n->u.totlen) + 3) & ~3)); } else return dd; } while (1); } /* resolve name under certain parent inode to dirent */ /* o - filesystem image pointer size - size of filesystem image pino - requested parent inode name - name of wanted dirent nsize - length of name of wanted dirent return value: pointer to relevant dirent structure in filesystem image or NULL */ struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino, char *name, uint8_t nsize) { return resolvedirent(o, size, 0, pino, name, nsize); } /* resolve inode to dirent */ /* o - filesystem image pointer size - size of filesystem image ino - compare against dirent inode return value: pointer to relevant dirent structure in filesystem image or NULL */ struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino) { return resolvedirent(o, size, ino, 0, NULL, 0); } /* resolve slash-style path into dirent and inode. slash as first byte marks absolute path (root=inode 1). . and .. are resolved properly, and symlinks are followed. */ /* o - filesystem image pointer size - size of filesystem image ino - root inode, used if path is relative p - path to be resolved inos - result inode, zero if failure recc - recursion count, to detect symlink loops return value: pointer to dirent struct in file system image. note that root directory doesn't have dirent struct (return value is NULL), but it has inode (*inos=1) */ struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino, const char *p, uint32_t * inos, int recc) { struct jffs2_raw_dirent *dir = NULL; int d = 1; uint32_t tino; char *next; char *path, *pp; char symbuf[1024]; size_t symsize; if (recc > 16) { /* probably symlink loop */ *inos = 0; return NULL; } pp = path = 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, const char *p, uint32_t * inos) { return resolvepath0(o, size, ino, p, inos, 0); } /* lists files on directory specified by path */ /* o - filesystem image pointer size - size of filesystem image p - path to be resolved */ void lsdir(char *o, size_t size, const char *path, int recurse, int want_ctime) { struct jffs2_raw_dirent *dd; struct dir *d = NULL; uint32_t ino; dd = resolvepath(o, size, 1, path, &ino); if (ino == 0 || (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) errmsg_die("%s: No such file or directory", path); d = collectdir(o, size, ino, d); printdir(o, size, d, path, recurse, want_ctime); freedir(d); } /* writes file specified by path to the buffer */ /* o - filesystem image pointer size - size of filesystem image p - path to be resolved b - file buffer bsize - file buffer size rsize - file result size */ void catfile(char *o, size_t size, char *path, char *b, size_t bsize, size_t * rsize) { struct jffs2_raw_dirent *dd; struct jffs2_raw_inode *ri; uint32_t ino; dd = resolvepath(o, size, 1, path, &ino); if (ino == 0) errmsg_die("%s: No such file or directory", path); if (dd == NULL || dd->type != DT_REG) errmsg_die("%s: Not a regular file", path); ri = find_raw_inode(o, size, ino); putblock(b, bsize, rsize, ri); write(1, b, *rsize); } /* usage example */ int main(int argc, char **argv) { int fd, opt, recurse = 0, want_ctime = 0; struct stat st; char *scratch, *dir = NULL, *file = NULL; size_t ssize = 0; char *buf; while ((opt = getopt(argc, argv, "rd:f:t")) > 0) { switch (opt) { case 'd': dir = optarg; break; case 'f': file = optarg; break; case 'r': recurse++; break; case 't': want_ctime++; break; default: fprintf(stderr, "Usage: %s [-d|-f] < path >\n", PROGRAM_NAME); exit(EXIT_FAILURE); } } fd = open(argv[optind], O_RDONLY); if (fd == -1) sys_errmsg_die("%s", argv[optind]); if (fstat(fd, &st)) sys_errmsg_die("%s", argv[optind]); buf = xmalloc((size_t) st.st_size); if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) sys_errmsg_die("%s", argv[optind]); if (dir) lsdir(buf, st.st_size, dir, recurse, want_ctime); if (file) { scratch = xmalloc(SCRATCH_SIZE); catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); free(scratch); } if (!dir && !file) lsdir(buf, st.st_size, "/", 1, want_ctime); free(buf); exit(EXIT_SUCCESS); } mtd-utils-1.5.0/lib/000077500000000000000000000000001175167361300142175ustar00rootroot00000000000000mtd-utils-1.5.0/lib/libcrc32.c000066400000000000000000000126771175167361300160030ustar00rootroot00000000000000/* * 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 static 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 }; uint32_t mtd_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; } mtd-utils-1.5.0/lib/libfec.c000066400000000000000000000577501175167361300156250ustar00rootroot00000000000000/* * 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 #include #include /* * stuff used for testing purposes only */ #ifdef TEST #define DEB(x) #define DDB(x) x #define DEBUG 0 /* minimal debugging */ #ifdef MSDOS #include 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 #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 const 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(void) { 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, const 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; const 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 1) { fprintf(stderr, "singular matrix\n"); goto fail ; } } } } if (icol == -1) { fprintf(stderr, "XXX pivot not found!\n"); goto fail ; } found_piv: ++(ipiv[icol]) ; /* * swap rows irow and icol, so afterwards the diagonal * element will be correct. Rarely done, not worth * optimizing. */ if (irow != icol) { for (ix = 0 ; ix < k ; ix++ ) { SWAP( src[irow*k + ix], src[icol*k + ix], gf) ; } } indxr[col] = irow ; indxc[col] = icol ; pivot_row = &src[icol*k] ; c = pivot_row[icol] ; if (c == 0) { fprintf(stderr, "singular matrix 2\n"); goto fail ; } if (c != 1 ) { /* otherwhise this is a NOP */ /* * this is done often , but optimizing is not so * fruitful, at least in the obvious ways (unrolling) */ DEB( pivswaps++ ; ) c = inverse[ c ] ; pivot_row[icol] = 1 ; for (ix = 0 ; ix < k ; ix++ ) pivot_row[ix] = gf_mul(c, pivot_row[ix] ); } /* * from all rows, remove multiples of the selected row * to zero the relevant entry (in fact, the entry is not zero * because we know it must be zero). * (Here, if we know that the pivot_row is the identity, * we can optimize the addmul). */ id_row[icol] = 1; if (memcmp(pivot_row, id_row, k*sizeof(gf)) != 0) { for (p = src, ix = 0 ; ix < k ; ix++, p += k ) { if (ix != icol) { c = p[icol] ; p[icol] = 0 ; addmul(p, pivot_row, c, k ); } } } id_row[icol] = 0; } /* done all columns */ for (col = k-1 ; col >= 0 ; col-- ) { if (indxr[col] <0 || indxr[col] >= k) fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]); else if (indxc[col] <0 || indxc[col] >= k) fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]); else if (indxr[col] != indxc[col] ) { for (row = 0 ; row < k ; row++ ) { SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ; } } } error = 0 ; fail: free(indxc); free(indxr); free(ipiv); free(id_row); free(temp_row); return error ; } /* * fast code for inverting a vandermonde matrix. * XXX NOTE: It assumes that the matrix * is not singular and _IS_ a vandermonde matrix. Only uses * the second column of the matrix, containing the p_i's. * * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but * largely revised for my purposes. * p = coefficients of the matrix (p_i) * q = values of the polynomial (known) */ int invert_vdm(gf *src, int k) { int i, j, row, col ; gf *b, *c, *p; gf t, xx ; if (k == 1) /* degenerate case, matrix must be p^0 = 1 */ return 0 ; /* * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1 * b holds the coefficient for the matrix inversion */ c = NEW_GF_MATRIX(1, k); b = NEW_GF_MATRIX(1, k); p = NEW_GF_MATRIX(1, k); for ( j=1, i = 0 ; i < k ; i++, j+=k ) { c[i] = 0 ; p[i] = src[j] ; /* p[i] */ } /* * construct coeffs. recursively. We know c[k] = 1 (implicit) * and start P_0 = x - p_0, then at each stage multiply by * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1} * After k steps we are done. */ c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */ for (i = 1 ; i < k ; i++ ) { gf p_i = p[i] ; /* see above comment */ for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ ) c[j] ^= gf_mul( p_i, c[j+1] ) ; c[k-1] ^= p_i ; } for (row = 0 ; row < k ; row++ ) { /* * synthetic division etc. */ xx = p[row] ; t = 1 ; b[k-1] = 1 ; /* this is in fact c[k] */ for (i = k-2 ; i >= 0 ; i-- ) { b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ; t = gf_mul(xx, t) ^ b[i] ; } for (col = 0 ; col < k ; col++ ) src[col*k + row] = gf_mul(inverse[t], b[col] ); } free(c) ; free(b) ; free(p) ; return 0 ; } static int fec_initialized = 0 ; static void init_fec(void) { 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 ; } ; #define COMP_FEC_MAGIC(fec) \ (((FEC_MAGIC ^ (fec)->k) ^ (fec)->n) ^ (unsigned long)((fec)->enc_matrix)) void fec_free(struct fec_parms *p) { if (p==NULL || p->magic != COMP_FEC_MAGIC(p)) { 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 = COMP_FEC_MAGIC(retval); tmp_m = NEW_GF_MATRIX(n, k); /* * fill the matrix with powers of field elements, starting from 0. * The first row is special, cannot be computed with exp. table. */ tmp_m[0] = 1 ; for (col = 1; col < k ; col++) tmp_m[col] = 0 ; for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) { for ( col = 0 ; col < k ; col ++ ) p[col] = gf_exp[modnn(row*col)]; } /* * quick code to build systematic matrix: invert the top * k*k vandermonde matrix, multiply right the bottom n-k rows * by the inverse, and construct the identity matrix at the top. */ TICK(ticks[3]); invert_vdm(tmp_m, k); /* much faster than invert_mat */ matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k); /* * the upper matrix is I so do not bother with a slow multiply */ memset(retval->enc_matrix, '\0', k*k*sizeof(gf) ); for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 ) *p = 1 ; free(tmp_m); TOCK(ticks[3]); DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", ticks[3]);) DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) return retval ; } /* * fec_encode accepts as input pointers to n data packets of size sz, * and produces as output a packet pointed to by fec, computed * with index "index". */ void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) { int i, k = code->k ; gf *p ; if (GF_BITS > 8) sz /= 2 ; if (index < k) memcpy(fec, src[index], sz*sizeof(gf) ) ; else if (index < code->n) { p = &(code->enc_matrix[index*k] ); memset(fec, '\0', sz*sizeof(gf)); for (i = 0; i < k ; i++) addmul(fec, src[i], p[i], sz ) ; } else fprintf(stderr, "Invalid index %d (max %d)\n", index, code->n - 1 ); } void fec_encode_linear(struct fec_parms *code, gf *src, gf *fec, int index, int sz) { int i, k = code->k ; gf *p ; if (GF_BITS > 8) sz /= 2 ; if (index < k) memcpy(fec, src + (index * sz), sz*sizeof(gf) ) ; else if (index < code->n) { p = &(code->enc_matrix[index*k] ); memset(fec, '\0', sz*sizeof(gf)); for (i = 0; i < k ; i++) addmul(fec, src + (i * sz), p[i], sz ) ; } else fprintf(stderr, "Invalid index %d (max %d)\n", index, code->n - 1 ); } /* * shuffle move src packets in their position */ static int shuffle(gf *pkt[], int index[], int k) { int i; for ( i = 0 ; i < k ; ) { if (index[i] >= k || index[i] == i) i++ ; else { /* * put pkt in the right position (first check for conflicts). */ int c = index[i] ; if (index[c] == c) { DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) return 1 ; } SWAP(index[i], index[c], int) ; SWAP(pkt[i], pkt[c], gf *) ; } } DEB( /* just test that it works... */ for ( i = 0 ; i < k ; i++ ) { if (index[i] < k && index[i] != i) { fprintf(stderr, "shuffle: after\n"); for (i=0; ik ; gf *p, *matrix = NEW_GF_MATRIX(k, k); TICK(ticks[9]); for (i = 0, p = matrix ; i < k ; i++, p += k ) { #if 1 /* this is simply an optimization, not very useful indeed */ if (index[i] < k) { memset(p, '\0', k*sizeof(gf) ); p[i] = 1 ; } else #endif if (index[i] < code->n ) memcpy(p, &(code->enc_matrix[index[i]*k]), k*sizeof(gf) ); else { fprintf(stderr, "decode: invalid index %d (max %d)\n", index[i], code->n - 1 ); free(matrix) ; return NULL ; } } TICK(ticks[9]); if (invert_mat(matrix, k)) { free(matrix); matrix = NULL ; } TOCK(ticks[9]); return matrix ; } /* * fec_decode receives as input a vector of packets, the indexes of * packets, and produces the correct vector as output. * * Input: * code: pointer to code descriptor * pkt: pointers to received packets. They are modified * to store the output packets (in place) * index: pointer to packet indexes (modified) * sz: size of each packet */ int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) { gf *m_dec ; gf **new_pkt ; int row, col , k = code->k ; if (GF_BITS > 8) sz /= 2 ; if (shuffle(pkt, index, k)) /* error if true */ return 1 ; m_dec = build_decode_matrix(code, index); if (m_dec == NULL) return 1 ; /* error */ /* * do the actual decoding */ new_pkt = my_malloc (k * sizeof (gf * ), "new pkt pointers" ); for (row = 0 ; row < k ; row++ ) { if (index[row] >= k) { new_pkt[row] = my_malloc (sz * sizeof (gf), "new pkt buffer" ); memset(new_pkt[row], '\0', sz * sizeof(gf) ) ; for (col = 0 ; col < k ; col++ ) addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ; } } /* * move pkts to their final destination */ for (row = 0 ; row < k ; row++ ) { if (index[row] >= k) { memcpy(pkt[row], new_pkt[row], sz*sizeof(gf)); free(new_pkt[row]); } } free(new_pkt); free(m_dec); return 0; } /*********** end of FEC code -- beginning of test code ************/ #if (TEST || DEBUG) void test_gf(void) { 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 */ mtd-utils-1.5.0/lib/libmtd.c000066400000000000000000001012311175167361300156340ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * MTD library. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libmtd_int.h" #include "common.h" /** * mkpath - compose full path from 2 given components. * @path: the first component * @name: the second component * * This function returns the resulting path in case of success and %NULL in * case of failure. */ static char *mkpath(const char *path, const char *name) { char *n; size_t len1 = strlen(path); size_t len2 = strlen(name); n = xmalloc(len1 + len2 + 2); memcpy(n, path, len1); if (n[len1 - 1] != '/') n[len1++] = '/'; memcpy(n + len1, name, len2 + 1); return n; } /** * read_data - read data from a file. * @file: the file to read from * @buf: the buffer to read to * @buf_len: buffer length * * This function returns number of read bytes in case of success and %-1 in * case of failure. Note, if the file contains more then @buf_len bytes of * date, this function fails with %EINVAL error code. */ static int read_data(const char *file, void *buf, int buf_len) { int fd, rd, tmp, tmp1; fd = open(file, O_RDONLY | O_CLOEXEC); if (fd == -1) return -1; rd = read(fd, buf, buf_len); if (rd == -1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (rd == buf_len) { errmsg("contents of \"%s\" is too long", file); errno = EINVAL; goto out_error; } ((char *)buf)[rd] = '\0'; /* Make sure all data is read */ tmp1 = read(fd, &tmp, 1); if (tmp1 == 1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (tmp1) { errmsg("file \"%s\" contains too much data (> %d bytes)", file, buf_len); errno = EINVAL; goto out_error; } if (close(fd)) { sys_errmsg("close failed on \"%s\"", file); return -1; } return rd; out_error: close(fd); return -1; } /** * read_major - read major and minor numbers from a file. * @file: name of the file to read from * @major: major number is returned here * @minor: minor number is returned here * * This function returns % in case of success, and %-1 in case of failure. */ static int read_major(const char *file, int *major, int *minor) { int ret; char buf[50]; ret = read_data(file, buf, 50); if (ret < 0) return ret; ret = sscanf(buf, "%d:%d\n", major, minor); if (ret != 2) { errno = EINVAL; return errmsg("\"%s\" does not have major:minor format", file); } if (*major < 0 || *minor < 0) { errno = EINVAL; return errmsg("bad major:minor %d:%d in \"%s\"", *major, *minor, file); } return 0; } /** * dev_get_major - get major and minor numbers of an MTD device. * @lib: libmtd descriptor * @mtd_num: MTD device number * @major: major number is returned here * @minor: minor number is returned here * * This function returns zero in case of success and %-1 in case of failure. */ static int dev_get_major(struct libmtd *lib, int mtd_num, int *major, int *minor) { char file[strlen(lib->mtd_dev) + 50]; sprintf(file, lib->mtd_dev, mtd_num); return read_major(file, major, minor); } /** * dev_read_data - read data from an MTD device's sysfs file. * @patt: file pattern to read from * @mtd_num: MTD device number * @buf: buffer to read to * @buf_len: buffer length * * This function returns number of read bytes in case of success and %-1 in * case of failure. */ static int dev_read_data(const char *patt, int mtd_num, void *buf, int buf_len) { char file[strlen(patt) + 100]; sprintf(file, patt, mtd_num); return read_data(file, buf, buf_len); } /** * read_hex_ll - read a hex 'long long' value from a file. * @file: the file to read from * @value: the result is stored here * * This function reads file @file and interprets its contents as hexadecimal * 'long long' integer. If this is not true, it fails with %EINVAL error code. * Returns %0 in case of success and %-1 in case of failure. */ static int read_hex_ll(const char *file, long long *value) { int fd, rd; char buf[50]; fd = open(file, O_RDONLY | O_CLOEXEC); if (fd == -1) return -1; rd = read(fd, buf, sizeof(buf)); if (rd == -1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (rd == sizeof(buf)) { errmsg("contents of \"%s\" is too long", file); errno = EINVAL; goto out_error; } buf[rd] = '\0'; if (sscanf(buf, "%llx\n", value) != 1) { errmsg("cannot read integer from \"%s\"\n", file); errno = EINVAL; goto out_error; } if (*value < 0) { errmsg("negative value %lld in \"%s\"", *value, file); errno = EINVAL; goto out_error; } if (close(fd)) return sys_errmsg("close failed on \"%s\"", file); return 0; out_error: close(fd); return -1; } /** * read_pos_ll - read a positive 'long long' value from a file. * @file: the file to read from * @value: the result is stored here * * This function reads file @file and interprets its contents as a positive * 'long long' integer. If this is not true, it fails with %EINVAL error code. * Returns %0 in case of success and %-1 in case of failure. */ static int read_pos_ll(const char *file, long long *value) { int fd, rd; char buf[50]; fd = open(file, O_RDONLY | O_CLOEXEC); if (fd == -1) return -1; rd = read(fd, buf, 50); if (rd == -1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (rd == 50) { errmsg("contents of \"%s\" is too long", file); errno = EINVAL; goto out_error; } if (sscanf(buf, "%lld\n", value) != 1) { errmsg("cannot read integer from \"%s\"\n", file); errno = EINVAL; goto out_error; } if (*value < 0) { errmsg("negative value %lld in \"%s\"", *value, file); errno = EINVAL; goto out_error; } if (close(fd)) return sys_errmsg("close failed on \"%s\"", file); return 0; out_error: close(fd); return -1; } /** * read_hex_int - read an 'int' value from a file. * @file: the file to read from * @value: the result is stored here * * This function is the same as 'read_pos_ll()', but it reads an 'int' * value, not 'long long'. */ static int read_hex_int(const char *file, int *value) { long long res; if (read_hex_ll(file, &res)) return -1; /* Make sure the value has correct range */ if (res > INT_MAX || res < INT_MIN) { errmsg("value %lld read from file \"%s\" is out of range", res, file); errno = EINVAL; return -1; } *value = res; return 0; } /** * read_pos_int - read a positive 'int' value from a file. * @file: the file to read from * @value: the result is stored here * * This function is the same as 'read_pos_ll()', but it reads an 'int' * value, not 'long long'. */ static int read_pos_int(const char *file, int *value) { long long res; if (read_pos_ll(file, &res)) return -1; /* Make sure the value is not too big */ if (res > INT_MAX) { errmsg("value %lld read from file \"%s\" is out of range", res, file); errno = EINVAL; return -1; } *value = res; return 0; } /** * dev_read_hex_int - read an hex 'int' value from an MTD device sysfs file. * @patt: file pattern to read from * @mtd_num: MTD device number * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_read_hex_int(const char *patt, int mtd_num, int *value) { char file[strlen(patt) + 50]; sprintf(file, patt, mtd_num); return read_hex_int(file, value); } /** * dev_read_pos_int - read a positive 'int' value from an MTD device sysfs file. * @patt: file pattern to read from * @mtd_num: MTD device number * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_read_pos_int(const char *patt, int mtd_num, int *value) { char file[strlen(patt) + 50]; sprintf(file, patt, mtd_num); return read_pos_int(file, value); } /** * dev_read_pos_ll - read a positive 'long long' value from an MTD device sysfs file. * @patt: file pattern to read from * @mtd_num: MTD device number * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_read_pos_ll(const char *patt, int mtd_num, long long *value) { char file[strlen(patt) + 50]; sprintf(file, patt, mtd_num); return read_pos_ll(file, value); } /** * type_str2int - convert MTD device type to integer. * @str: MTD device type string to convert * * This function converts MTD device type string @str, read from sysfs, into an * integer. */ static int type_str2int(const char *str) { if (!strcmp(str, "nand")) return MTD_NANDFLASH; if (!strcmp(str, "nor")) return MTD_NORFLASH; if (!strcmp(str, "rom")) return MTD_ROM; if (!strcmp(str, "absent")) return MTD_ABSENT; if (!strcmp(str, "dataflash")) return MTD_DATAFLASH; if (!strcmp(str, "ram")) return MTD_RAM; if (!strcmp(str, "ubi")) return MTD_UBIVOLUME; return -1; } /** * dev_node2num - find UBI device number by its character device node. * @lib: MTD library descriptor * @node: name of the MTD device node * @mtd_num: MTD device number is returned here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_node2num(struct libmtd *lib, const char *node, int *mtd_num) { struct stat st; int i, mjr, mnr; struct mtd_info info; if (stat(node, &st)) return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errmsg("\"%s\" is not a character device", node); errno = EINVAL; return -1; } mjr = major(st.st_rdev); mnr = minor(st.st_rdev); if (mtd_get_info((libmtd_t *)lib, &info)) return -1; for (i = info.lowest_mtd_num; i <= info.highest_mtd_num; i++) { int mjr1, mnr1, ret; ret = dev_get_major(lib, i, &mjr1, &mnr1); if (ret) { if (errno == ENOENT) continue; if (!errno) break; return -1; } if (mjr1 == mjr && mnr1 == mnr) { errno = 0; *mtd_num = i; return 0; } } errno = ENODEV; return -1; } /** * sysfs_is_supported - check whether the MTD sub-system supports MTD. * @lib: MTD library descriptor * * The Linux kernel MTD subsystem gained MTD support starting from kernel * 2.6.30 and libmtd tries to use sysfs interface if possible, because the NAND * sub-page size is available there (and not available at all in pre-sysfs * kernels). * * Very old kernels did not have "/sys/class/mtd" directory. Not very old * kernels (e.g., 2.6.29) did have "/sys/class/mtd/mtdX" directories, by there * were no files there, e.g., the "name" file was not present. So all we can do * is to check for a "/sys/class/mtd/mtdX/name" file. But this is not a * reliable check, because if this is a new system with no MTD devices - we'll * treat it as a pre-sysfs system. */ static int sysfs_is_supported(struct libmtd *lib) { int fd, num = -1; DIR *sysfs_mtd; char file[strlen(lib->mtd_name) + 10]; sysfs_mtd = opendir(lib->sysfs_mtd); if (!sysfs_mtd) { if (errno == ENOENT) { errno = 0; return 0; } return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); } /* * First of all find an "mtdX" directory. This is needed because there * may be, for example, mtd1 but no mtd0. */ while (1) { int ret, mtd_num; char tmp_buf[256]; struct dirent *dirent; dirent = readdir(sysfs_mtd); if (!dirent) break; if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_mtd, dirent->d_name); errno = EINVAL; closedir(sysfs_mtd); return -1; } ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", &mtd_num, tmp_buf); if (ret == 1) { num = mtd_num; break; } } if (closedir(sysfs_mtd)) return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); if (num == -1) /* No mtd device, treat this as pre-sysfs system */ return 0; sprintf(file, lib->mtd_name, num); fd = open(file, O_RDONLY | O_CLOEXEC); if (fd == -1) return 0; if (close(fd)) { sys_errmsg("close failed on \"%s\"", file); return -1; } return 1; } libmtd_t libmtd_open(void) { struct libmtd *lib; lib = xzalloc(sizeof(*lib)); lib->offs64_ioctls = OFFS64_IOCTLS_UNKNOWN; lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD); if (!lib->sysfs_mtd) goto out_error; lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT); if (!lib->mtd) goto out_error; lib->mtd_name = mkpath(lib->mtd, MTD_NAME); if (!lib->mtd_name) goto out_error; if (!sysfs_is_supported(lib)) { free(lib->mtd); free(lib->sysfs_mtd); free(lib->mtd_name); lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL; return lib; } lib->mtd_dev = mkpath(lib->mtd, MTD_DEV); if (!lib->mtd_dev) goto out_error; lib->mtd_type = mkpath(lib->mtd, MTD_TYPE); if (!lib->mtd_type) goto out_error; lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE); if (!lib->mtd_eb_size) goto out_error; lib->mtd_size = mkpath(lib->mtd, MTD_SIZE); if (!lib->mtd_size) goto out_error; lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE); if (!lib->mtd_min_io_size) goto out_error; lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE); if (!lib->mtd_subpage_size) goto out_error; lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE); if (!lib->mtd_oob_size) goto out_error; lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT); if (!lib->mtd_region_cnt) goto out_error; lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS); if (!lib->mtd_flags) goto out_error; lib->sysfs_supported = 1; return lib; out_error: libmtd_close((libmtd_t)lib); return NULL; } void libmtd_close(libmtd_t desc) { struct libmtd *lib = (struct libmtd *)desc; free(lib->mtd_flags); free(lib->mtd_region_cnt); free(lib->mtd_oob_size); free(lib->mtd_subpage_size); free(lib->mtd_min_io_size); free(lib->mtd_size); free(lib->mtd_eb_size); free(lib->mtd_type); free(lib->mtd_dev); free(lib->mtd_name); free(lib->mtd); free(lib->sysfs_mtd); free(lib); } int mtd_dev_present(libmtd_t desc, int mtd_num) { struct stat st; struct libmtd *lib = (struct libmtd *)desc; if (!lib->sysfs_supported) return legacy_dev_present(mtd_num); else { char file[strlen(lib->mtd) + 10]; sprintf(file, lib->mtd, mtd_num); return !stat(file, &st); } } int mtd_get_info(libmtd_t desc, struct mtd_info *info) { DIR *sysfs_mtd; struct dirent *dirent; struct libmtd *lib = (struct libmtd *)desc; memset(info, 0, sizeof(struct mtd_info)); if (!lib->sysfs_supported) return legacy_mtd_get_info(info); info->sysfs_supported = 1; /* * We have to scan the MTD sysfs directory to identify how many MTD * devices are present. */ sysfs_mtd = opendir(lib->sysfs_mtd); if (!sysfs_mtd) { if (errno == ENOENT) { errno = ENODEV; return -1; } return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); } info->lowest_mtd_num = INT_MAX; while (1) { int mtd_num, ret; char tmp_buf[256]; errno = 0; dirent = readdir(sysfs_mtd); if (!dirent) break; if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_mtd, dirent->d_name); errno = EINVAL; goto out_close; } ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", &mtd_num, tmp_buf); if (ret == 1) { info->mtd_dev_cnt += 1; if (mtd_num > info->highest_mtd_num) info->highest_mtd_num = mtd_num; if (mtd_num < info->lowest_mtd_num) info->lowest_mtd_num = mtd_num; } } if (!dirent && errno) { sys_errmsg("readdir failed on \"%s\"", lib->sysfs_mtd); goto out_close; } if (closedir(sysfs_mtd)) return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); if (info->lowest_mtd_num == INT_MAX) info->lowest_mtd_num = 0; return 0; out_close: closedir(sysfs_mtd); return -1; } int mtd_get_dev_info1(libmtd_t desc, int mtd_num, struct mtd_dev_info *mtd) { int ret; struct libmtd *lib = (struct libmtd *)desc; memset(mtd, 0, sizeof(struct mtd_dev_info)); mtd->mtd_num = mtd_num; if (!mtd_dev_present(desc, mtd_num)) { errno = ENODEV; return -1; } else if (!lib->sysfs_supported) return legacy_get_dev_info1(mtd_num, mtd); if (dev_get_major(lib, mtd_num, &mtd->major, &mtd->minor)) return -1; ret = dev_read_data(lib->mtd_name, mtd_num, &mtd->name, MTD_NAME_MAX + 1); if (ret < 0) return -1; ((char *)mtd->name)[ret - 1] = '\0'; ret = dev_read_data(lib->mtd_type, mtd_num, &mtd->type_str, MTD_TYPE_MAX + 1); if (ret < 0) return -1; ((char *)mtd->type_str)[ret - 1] = '\0'; if (dev_read_pos_int(lib->mtd_eb_size, mtd_num, &mtd->eb_size)) return -1; if (dev_read_pos_ll(lib->mtd_size, mtd_num, &mtd->size)) return -1; if (dev_read_pos_int(lib->mtd_min_io_size, mtd_num, &mtd->min_io_size)) return -1; if (dev_read_pos_int(lib->mtd_subpage_size, mtd_num, &mtd->subpage_size)) return -1; if (dev_read_pos_int(lib->mtd_oob_size, mtd_num, &mtd->oob_size)) return -1; if (dev_read_pos_int(lib->mtd_region_cnt, mtd_num, &mtd->region_cnt)) return -1; if (dev_read_hex_int(lib->mtd_flags, mtd_num, &ret)) return -1; mtd->writable = !!(ret & MTD_WRITEABLE); mtd->eb_cnt = mtd->size / mtd->eb_size; mtd->type = type_str2int(mtd->type_str); mtd->bb_allowed = !!(mtd->type == MTD_NANDFLASH); return 0; } int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd) { int mtd_num; struct libmtd *lib = (struct libmtd *)desc; if (!lib->sysfs_supported) return legacy_get_dev_info(node, mtd); if (dev_node2num(lib, node, &mtd_num)) return -1; return mtd_get_dev_info1(desc, mtd_num, mtd); } static inline int mtd_ioctl_error(const struct mtd_dev_info *mtd, int eb, const char *sreq) { return sys_errmsg("%s ioctl failed for eraseblock %d (mtd%d)", sreq, eb, mtd->mtd_num); } static int mtd_valid_erase_block(const struct mtd_dev_info *mtd, int eb) { if (eb < 0 || eb >= mtd->eb_cnt) { errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", eb, mtd->mtd_num, mtd->eb_cnt); errno = EINVAL; return -1; } return 0; } static int mtd_xlock(const struct mtd_dev_info *mtd, int fd, int eb, int req, const char *sreq) { int ret; struct erase_info_user ei; ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; ei.start = eb * mtd->eb_size; ei.length = mtd->eb_size; ret = ioctl(fd, req, &ei); if (ret < 0) return mtd_ioctl_error(mtd, eb, sreq); return 0; } #define mtd_xlock(mtd, fd, eb, req) mtd_xlock(mtd, fd, eb, req, #req) int mtd_lock(const struct mtd_dev_info *mtd, int fd, int eb) { return mtd_xlock(mtd, fd, eb, MEMLOCK); } int mtd_unlock(const struct mtd_dev_info *mtd, int fd, int eb) { return mtd_xlock(mtd, fd, eb, MEMUNLOCK); } int mtd_erase(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb) { int ret; struct libmtd *lib = (struct libmtd *)desc; struct erase_info_user64 ei64; struct erase_info_user ei; ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; ei64.start = (__u64)eb * mtd->eb_size; ei64.length = mtd->eb_size; if (lib->offs64_ioctls == OFFS64_IOCTLS_SUPPORTED || lib->offs64_ioctls == OFFS64_IOCTLS_UNKNOWN) { ret = ioctl(fd, MEMERASE64, &ei64); if (ret == 0) return ret; if (errno != ENOTTY || lib->offs64_ioctls != OFFS64_IOCTLS_UNKNOWN) return mtd_ioctl_error(mtd, eb, "MEMERASE64"); /* * MEMERASE64 support was added in kernel version 2.6.31, so * probably we are working with older kernel and this ioctl is * not supported. */ lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; } if (ei64.start + ei64.length > 0xFFFFFFFF) { errmsg("this system can address only %u eraseblocks", 0xFFFFFFFFU / mtd->eb_size); errno = EINVAL; return -1; } ei.start = ei64.start; ei.length = ei64.length; ret = ioctl(fd, MEMERASE, &ei); if (ret < 0) return mtd_ioctl_error(mtd, eb, "MEMERASE"); return 0; } int mtd_regioninfo(int fd, int regidx, struct region_info_user *reginfo) { int ret; if (regidx < 0) { errno = ENODEV; return -1; } ret = ioctl(fd, MEMGETREGIONINFO, reginfo); if (ret < 0) return sys_errmsg("%s ioctl failed for erase region %d", "MEMGETREGIONINFO", regidx); return 0; } int mtd_is_locked(const struct mtd_dev_info *mtd, int fd, int eb) { int ret; erase_info_t ei; ei.start = eb * mtd->eb_size; ei.length = mtd->eb_size; ret = ioctl(fd, MEMISLOCKED, &ei); if (ret < 0) { if (errno != ENOTTY && errno != EOPNOTSUPP) return mtd_ioctl_error(mtd, eb, "MEMISLOCKED"); else errno = EOPNOTSUPP; } return ret; } /* Patterns to write to a physical eraseblock when torturing it */ static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; /** * check_pattern - check if buffer contains only a certain byte pattern. * @buf: buffer to check * @patt: the pattern to check * @size: buffer size in bytes * * This function returns %1 in there are only @patt bytes in @buf, and %0 if * something else was also found. */ static int check_pattern(const void *buf, uint8_t patt, int size) { int i; for (i = 0; i < size; i++) if (((const uint8_t *)buf)[i] != patt) return 0; return 1; } int mtd_torture(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb) { int err, i, patt_count; void *buf; normsg("run torture test for PEB %d", eb); patt_count = ARRAY_SIZE(patterns); buf = xmalloc(mtd->eb_size); for (i = 0; i < patt_count; i++) { err = mtd_erase(desc, mtd, fd, eb); if (err) goto out; /* Make sure the PEB contains only 0xFF bytes */ err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size); if (err) goto out; err = check_pattern(buf, 0xFF, mtd->eb_size); if (err == 0) { errmsg("erased PEB %d, but a non-0xFF byte found", eb); errno = EIO; goto out; } /* Write a pattern and check it */ memset(buf, patterns[i], mtd->eb_size); err = mtd_write(desc, mtd, fd, eb, 0, buf, mtd->eb_size, NULL, 0, 0); if (err) goto out; memset(buf, ~patterns[i], mtd->eb_size); err = mtd_read(mtd, fd, eb, 0, buf, mtd->eb_size); if (err) goto out; err = check_pattern(buf, patterns[i], mtd->eb_size); if (err == 0) { errmsg("pattern %x checking failed for PEB %d", patterns[i], eb); errno = EIO; goto out; } } err = 0; normsg("PEB %d passed torture test, do not mark it a bad", eb); out: free(buf); return -1; } int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb) { int ret; loff_t seek; ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; if (!mtd->bb_allowed) return 0; seek = (loff_t)eb * mtd->eb_size; ret = ioctl(fd, MEMGETBADBLOCK, &seek); if (ret == -1) return mtd_ioctl_error(mtd, eb, "MEMGETBADBLOCK"); return ret; } int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb) { int ret; loff_t seek; if (!mtd->bb_allowed) { errno = EINVAL; return -1; } ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; seek = (loff_t)eb * mtd->eb_size; ret = ioctl(fd, MEMSETBADBLOCK, &seek); if (ret == -1) return mtd_ioctl_error(mtd, eb, "MEMSETBADBLOCK"); return 0; } int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs, void *buf, int len) { int ret, rd = 0; off_t seek; ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; if (offs < 0 || offs + len > mtd->eb_size) { errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", offs, len, mtd->mtd_num, mtd->eb_size); errno = EINVAL; return -1; } /* Seek to the beginning of the eraseblock */ seek = (off_t)eb * mtd->eb_size + offs; if (lseek(fd, seek, SEEK_SET) != seek) return sys_errmsg("cannot seek mtd%d to offset %llu", mtd->mtd_num, (unsigned long long)seek); while (rd < len) { ret = read(fd, buf, len); if (ret < 0) return sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)", len, mtd->mtd_num, eb, offs); rd += ret; } return 0; } static int legacy_auto_oob_layout(const struct mtd_dev_info *mtd, int fd, int ooblen, void *oob) { struct nand_oobinfo old_oobinfo; int start, len; uint8_t *tmp_buf; /* Read the current oob info */ if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo)) return sys_errmsg("MEMGETOOBSEL failed"); tmp_buf = malloc(ooblen); memcpy(tmp_buf, oob, ooblen); /* * We use autoplacement and have the oobinfo with the autoplacement * information from the kernel available */ if (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { int i, tags_pos = 0; 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(oob + start, tmp_buf + tags_pos, len); tags_pos += len; } } else { /* Set at least the ecc byte positions to 0xff */ start = old_oobinfo.eccbytes; len = mtd->oob_size - start; memcpy(oob + start, tmp_buf + start, len); } return 0; } int mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb, int offs, void *data, int len, void *oob, int ooblen, uint8_t mode) { int ret; off_t seek; struct mtd_write_req ops; ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; if (offs < 0 || offs + len > mtd->eb_size) { errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", offs, len, mtd->mtd_num, mtd->eb_size); errno = EINVAL; return -1; } if (offs % mtd->subpage_size) { errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", offs, mtd->mtd_num, mtd->subpage_size); errno = EINVAL; return -1; } if (len % mtd->subpage_size) { errmsg("write length %d is not aligned to mtd%d min. I/O size %d", len, mtd->mtd_num, mtd->subpage_size); errno = EINVAL; return -1; } /* Calculate seek address */ seek = (off_t)eb * mtd->eb_size + offs; if (oob) { ops.start = seek; ops.len = len; ops.ooblen = ooblen; ops.usr_data = (uint64_t)(unsigned long)data; ops.usr_oob = (uint64_t)(unsigned long)oob; ops.mode = mode; ret = ioctl(fd, MEMWRITE, &ops); if (ret == 0) return 0; else if (errno != ENOTTY && errno != EOPNOTSUPP) return mtd_ioctl_error(mtd, eb, "MEMWRITE"); /* Fall back to old OOB ioctl() if necessary */ if (mode == MTD_OPS_AUTO_OOB) if (legacy_auto_oob_layout(mtd, fd, ooblen, oob)) return -1; if (mtd_write_oob(desc, mtd, fd, seek, ooblen, oob) < 0) return sys_errmsg("cannot write to OOB"); } if (data) { /* Seek to the beginning of the eraseblock */ if (lseek(fd, seek, SEEK_SET) != seek) return sys_errmsg("cannot seek mtd%d to offset %llu", mtd->mtd_num, (unsigned long long)seek); ret = write(fd, data, len); if (ret != len) return sys_errmsg("cannot write %d bytes to mtd%d " "(eraseblock %d, offset %d)", len, mtd->mtd_num, eb, offs); } return 0; } int do_oob_op(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, uint64_t start, uint64_t length, void *data, unsigned int cmd64, unsigned int cmd) { int ret, oob_offs; struct mtd_oob_buf64 oob64; struct mtd_oob_buf oob; unsigned long long max_offs; const char *cmd64_str, *cmd_str; struct libmtd *lib = (struct libmtd *)desc; if (cmd64 == MEMREADOOB64) { cmd64_str = "MEMREADOOB64"; cmd_str = "MEMREADOOB"; } else { cmd64_str = "MEMWRITEOOB64"; cmd_str = "MEMWRITEOOB"; } max_offs = (unsigned long long)mtd->eb_cnt * mtd->eb_size; if (start >= max_offs) { errmsg("bad page address %" PRIu64 ", mtd%d has %d eraseblocks (%llu bytes)", start, mtd->mtd_num, mtd->eb_cnt, max_offs); errno = EINVAL; return -1; } oob_offs = start & (mtd->min_io_size - 1); if (oob_offs + length > mtd->oob_size || length == 0) { errmsg("Cannot write %" PRIu64 " OOB bytes to address %" PRIu64 " (OOB offset %u) - mtd%d OOB size is only %d bytes", length, start, oob_offs, mtd->mtd_num, mtd->oob_size); errno = EINVAL; return -1; } oob64.start = start; oob64.length = length; oob64.usr_ptr = (uint64_t)(unsigned long)data; if (lib->offs64_ioctls == OFFS64_IOCTLS_SUPPORTED || lib->offs64_ioctls == OFFS64_IOCTLS_UNKNOWN) { ret = ioctl(fd, cmd64, &oob64); if (ret == 0) return ret; if (errno != ENOTTY || lib->offs64_ioctls != OFFS64_IOCTLS_UNKNOWN) { sys_errmsg("%s ioctl failed for mtd%d, offset %" PRIu64 " (eraseblock %" PRIu64 ")", cmd64_str, mtd->mtd_num, start, start / mtd->eb_size); } /* * MEMREADOOB64/MEMWRITEOOB64 support was added in kernel * version 2.6.31, so probably we are working with older kernel * and these ioctls are not supported. */ lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; } if (oob64.start > 0xFFFFFFFFULL) { errmsg("this system can address only up to address %lu", 0xFFFFFFFFUL); errno = EINVAL; return -1; } oob.start = oob64.start; oob.length = oob64.length; oob.ptr = data; ret = ioctl(fd, cmd, &oob); if (ret < 0) sys_errmsg("%s ioctl failed for mtd%d, offset %" PRIu64 " (eraseblock %" PRIu64 ")", cmd_str, mtd->mtd_num, start, start / mtd->eb_size); return ret; } int mtd_read_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, uint64_t start, uint64_t length, void *data) { return do_oob_op(desc, mtd, fd, start, length, data, MEMREADOOB64, MEMREADOOB); } int mtd_write_oob(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, uint64_t start, uint64_t length, void *data) { return do_oob_op(desc, mtd, fd, start, length, data, MEMWRITEOOB64, MEMWRITEOOB); } int mtd_write_img(const struct mtd_dev_info *mtd, int fd, int eb, int offs, const char *img_name) { int tmp, ret, in_fd, len, written = 0; off_t seek; struct stat st; char *buf; ret = mtd_valid_erase_block(mtd, eb); if (ret) return ret; if (offs < 0 || offs >= mtd->eb_size) { errmsg("bad offset %d, mtd%d eraseblock size is %d", offs, mtd->mtd_num, mtd->eb_size); errno = EINVAL; return -1; } if (offs % mtd->subpage_size) { errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", offs, mtd->mtd_num, mtd->subpage_size); errno = EINVAL; return -1; } in_fd = open(img_name, O_RDONLY | O_CLOEXEC); if (in_fd == -1) return sys_errmsg("cannot open \"%s\"", img_name); if (fstat(in_fd, &st)) { sys_errmsg("cannot stat %s", img_name); goto out_close; } len = st.st_size; if (len % mtd->subpage_size) { errmsg("size of \"%s\" is %d byte, which is not aligned to " "mtd%d min. I/O size %d", img_name, len, mtd->mtd_num, mtd->subpage_size); errno = EINVAL; goto out_close; } tmp = (offs + len + mtd->eb_size - 1) / mtd->eb_size; if (eb + tmp > mtd->eb_cnt) { errmsg("\"%s\" image size is %d bytes, mtd%d size is %d " "eraseblocks, the image does not fit if we write it " "starting from eraseblock %d, offset %d", img_name, len, mtd->mtd_num, mtd->eb_cnt, eb, offs); errno = EINVAL; goto out_close; } /* Seek to the beginning of the eraseblock */ seek = (off_t)eb * mtd->eb_size + offs; if (lseek(fd, seek, SEEK_SET) != seek) { sys_errmsg("cannot seek mtd%d to offset %llu", mtd->mtd_num, (unsigned long long)seek); goto out_close; } buf = xmalloc(mtd->eb_size); while (written < len) { int rd = 0; do { ret = read(in_fd, buf, mtd->eb_size - offs - rd); if (ret == -1) { sys_errmsg("cannot read \"%s\"", img_name); goto out_free; } rd += ret; } while (ret && rd < mtd->eb_size - offs); ret = write(fd, buf, rd); if (ret != rd) { sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)", len, mtd->mtd_num, eb, offs); goto out_free; } offs = 0; eb += 1; written += rd; } free(buf); close(in_fd); return 0; out_free: free(buf); out_close: close(in_fd); return -1; } int mtd_probe_node(libmtd_t desc, const char *node) { struct stat st; struct mtd_info info; int i, mjr, mnr; struct libmtd *lib = (struct libmtd *)desc; if (stat(node, &st)) return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errmsg("\"%s\" is not a character device", node); errno = EINVAL; return -1; } mjr = major(st.st_rdev); mnr = minor(st.st_rdev); if (mtd_get_info((libmtd_t *)lib, &info)) return -1; if (!lib->sysfs_supported) return 0; for (i = info.lowest_mtd_num; i <= info.highest_mtd_num; i++) { int mjr1, mnr1, ret; ret = dev_get_major(lib, i, &mjr1, &mnr1); if (ret) { if (errno == ENOENT) continue; if (!errno) break; return -1; } if (mjr1 == mjr && mnr1 == mnr) return 1; } errno = 0; return -1; } mtd-utils-1.5.0/lib/libmtd_int.h000066400000000000000000000070551175167361300165240ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * MTD library. */ #ifndef __LIBMTD_INT_H__ #define __LIBMTD_INT_H__ #ifdef __cplusplus extern "C" { #endif #define PROGRAM_NAME "libmtd" #define SYSFS_MTD "class/mtd" #define MTD_NAME_PATT "mtd%d" #define MTD_DEV "dev" #define MTD_NAME "name" #define MTD_TYPE "type" #define MTD_EB_SIZE "erasesize" #define MTD_SIZE "size" #define MTD_MIN_IO_SIZE "writesize" #define MTD_SUBPAGE_SIZE "subpagesize" #define MTD_OOB_SIZE "oobsize" #define MTD_REGION_CNT "numeraseregions" #define MTD_FLAGS "flags" #define OFFS64_IOCTLS_UNKNOWN 0 #define OFFS64_IOCTLS_NOT_SUPPORTED 1 #define OFFS64_IOCTLS_SUPPORTED 2 /** * libmtd - MTD library description data structure. * @sysfs_mtd: MTD directory in sysfs * @mtd: MTD device sysfs directory pattern * @mtd_dev: MTD device major/minor numbers file pattern * @mtd_name: MTD device name file pattern * @mtd_type: MTD device type file pattern * @mtd_eb_size: MTD device eraseblock size file pattern * @mtd_size: MTD device size file pattern * @mtd_min_io_size: minimum I/O unit size file pattern * @mtd_subpage_size: sub-page size file pattern * @mtd_oob_size: MTD device OOB size file pattern * @mtd_region_cnt: count of additional erase regions file pattern * @mtd_flags: MTD device flags file pattern * @sysfs_supported: non-zero if sysfs is supported by MTD * @offs64_ioctls: %OFFS64_IOCTLS_SUPPORTED if 64-bit %MEMERASE64, * %MEMREADOOB64, %MEMWRITEOOB64 MTD device ioctls are * supported, %OFFS64_IOCTLS_NOT_SUPPORTED if not, and * %OFFS64_IOCTLS_UNKNOWN if it is not known yet; * * Note, we cannot find out whether 64-bit ioctls are supported by MTD when we * are initializing the library, because this requires an MTD device node. * Indeed, we have to actually call the ioctl and check for %ENOTTY to find * out whether it is supported or not. * * Thus, we leave %offs64_ioctls uninitialized in 'libmtd_open()', and * initialize it later, when corresponding libmtd function is used, and when * we actually have a device node and can invoke an ioctl command on it. */ struct libmtd { char *sysfs_mtd; char *mtd; char *mtd_dev; char *mtd_name; char *mtd_type; char *mtd_eb_size; char *mtd_size; char *mtd_min_io_size; char *mtd_subpage_size; char *mtd_oob_size; char *mtd_region_cnt; char *mtd_flags; unsigned int sysfs_supported:1; unsigned int offs64_ioctls:2; }; int legacy_libmtd_open(void); int legacy_dev_present(int mtd_num); int legacy_mtd_get_info(struct mtd_info *info); int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd); int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd); #ifdef __cplusplus } #endif #endif /* !__LIBMTD_INT_H__ */ mtd-utils-1.5.0/lib/libmtd_legacy.c000066400000000000000000000216571175167361300171750ustar00rootroot00000000000000/* * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * This file is part of the MTD library. Implements pre-2.6.30 kernels support, * where MTD did not have sysfs interface. The main limitation of the old * kernels was that the sub-page size was not exported to user-space, so it was * not possible to get sub-page size. */ #include #include #include #include #include #include #include #include #include #include #include "libmtd_int.h" #include "common.h" #define MTD_PROC_FILE "/proc/mtd" #define MTD_DEV_PATT "/dev/mtd%d" #define MTD_DEV_MAJOR 90 #define PROC_MTD_FIRST "dev: size erasesize name\n" #define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1) #define PROC_MTD_MAX_LEN 4096 #define PROC_MTD_PATT "mtd%d: %llx %x" /** * struct proc_parse_info - /proc/mtd parsing information. * @mtd_num: MTD device number * @size: device size * @eb_size: eraseblock size * @name: device name * @buf: contents of /proc/mtd * @data_size: how much data was read into @buf * @pos: next string in @buf to parse */ struct proc_parse_info { int mtd_num; long long size; char name[MTD_NAME_MAX + 1]; int eb_size; char *buf; int data_size; char *next; }; static int proc_parse_start(struct proc_parse_info *pi) { int fd, ret; fd = open(MTD_PROC_FILE, O_RDONLY); if (fd == -1) return -1; pi->buf = xmalloc(PROC_MTD_MAX_LEN); ret = read(fd, pi->buf, PROC_MTD_MAX_LEN); if (ret == -1) { sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE); goto out_free; } if (ret < PROC_MTD_FIRST_LEN || memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) { errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE, PROC_MTD_FIRST); goto out_free; } pi->data_size = ret; pi->next = pi->buf + PROC_MTD_FIRST_LEN; close(fd); return 0; out_free: free(pi->buf); close(fd); return -1; } static int proc_parse_next(struct proc_parse_info *pi) { int ret, len, pos = pi->next - pi->buf; char *p, *p1; if (pos >= pi->data_size) { free(pi->buf); return 0; } ret = sscanf(pi->next, PROC_MTD_PATT, &pi->mtd_num, &pi->size, &pi->eb_size); if (ret != 3) return errmsg("\"%s\" pattern not found", PROC_MTD_PATT); p = memchr(pi->next, '\"', pi->data_size - pos); if (!p) return errmsg("opening \" not found"); p += 1; pos = p - pi->buf; if (pos >= pi->data_size) return errmsg("opening \" not found"); p1 = memchr(p, '\"', pi->data_size - pos); if (!p1) return errmsg("closing \" not found"); pos = p1 - pi->buf; if (pos >= pi->data_size) return errmsg("closing \" not found"); len = p1 - p; if (len > MTD_NAME_MAX) return errmsg("too long mtd%d device name", pi->mtd_num); memcpy(pi->name, p, len); pi->name[len] = '\0'; if (p1[1] != '\n') return errmsg("opening \"\n\" not found"); pi->next = p1 + 2; return 1; } /** * legacy_libmtd_open - legacy version of 'libmtd_open()'. * * This function is just checks that MTD is present in the system. Returns * zero in case of success and %-1 in case of failure. In case of failure, * errno contains zero if MTD is not present in the system, or contains the * error code if a real error happened. This is similar to the 'libmtd_open()' * return conventions. */ int legacy_libmtd_open(void) { int fd; fd = open(MTD_PROC_FILE, O_RDONLY); if (fd == -1) { if (errno == ENOENT) errno = 0; return -1; } close(fd); return 0; } /** * legacy_dev_presentl - legacy version of 'mtd_dev_present()'. * @info: the MTD device information is returned here * * When the kernel does not provide sysfs files for the MTD subsystem, * fall-back to parsing the /proc/mtd file to determine whether an mtd device * number @mtd_num is present. */ int legacy_dev_present(int mtd_num) { int ret; struct proc_parse_info pi; ret = proc_parse_start(&pi); if (ret) return -1; while (proc_parse_next(&pi)) { if (pi.mtd_num == mtd_num) return 1; } return 0; } /** * legacy_mtd_get_info - legacy version of 'mtd_get_info()'. * @info: the MTD device information is returned here * * This function is similar to 'mtd_get_info()' and has the same conventions. */ int legacy_mtd_get_info(struct mtd_info *info) { int ret; struct proc_parse_info pi; ret = proc_parse_start(&pi); if (ret) return -1; info->lowest_mtd_num = INT_MAX; while (proc_parse_next(&pi)) { info->mtd_dev_cnt += 1; if (pi.mtd_num > info->highest_mtd_num) info->highest_mtd_num = pi.mtd_num; if (pi.mtd_num < info->lowest_mtd_num) info->lowest_mtd_num = pi.mtd_num; } return 0; } /** * legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'. * @node: name of the MTD device node * @mtd: the MTD device information is returned here * * This function is similar to 'mtd_get_dev_info()' and has the same * conventions. */ int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd) { struct stat st; struct mtd_info_user ui; int fd, ret; loff_t offs = 0; struct proc_parse_info pi; if (stat(node, &st)) { sys_errmsg("cannot open \"%s\"", node); if (errno == ENOENT) normsg("MTD subsystem is old and does not support " "sysfs, so MTD character device nodes have " "to exist"); } if (!S_ISCHR(st.st_mode)) { errno = EINVAL; return errmsg("\"%s\" is not a character device", node); } memset(mtd, '\0', sizeof(struct mtd_dev_info)); mtd->major = major(st.st_rdev); mtd->minor = minor(st.st_rdev); if (mtd->major != MTD_DEV_MAJOR) { errno = EINVAL; return errmsg("\"%s\" has major number %d, MTD devices have " "major %d", node, mtd->major, MTD_DEV_MAJOR); } mtd->mtd_num = mtd->minor / 2; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); if (ioctl(fd, MEMGETINFO, &ui)) { sys_errmsg("MEMGETINFO ioctl request failed"); goto out_close; } ret = ioctl(fd, MEMGETBADBLOCK, &offs); if (ret == -1) { if (errno != EOPNOTSUPP) { sys_errmsg("MEMGETBADBLOCK ioctl failed"); goto out_close; } errno = 0; mtd->bb_allowed = 0; } else mtd->bb_allowed = 1; mtd->type = ui.type; mtd->size = ui.size; mtd->eb_size = ui.erasesize; mtd->min_io_size = ui.writesize; mtd->oob_size = ui.oobsize; if (mtd->min_io_size <= 0) { errmsg("mtd%d (%s) has insane min. I/O unit size %d", mtd->mtd_num, node, mtd->min_io_size); goto out_close; } if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { errmsg("mtd%d (%s) has insane eraseblock size %d", mtd->mtd_num, node, mtd->eb_size); goto out_close; } if (mtd->size <= 0 || mtd->size < mtd->eb_size) { errmsg("mtd%d (%s) has insane size %lld", mtd->mtd_num, node, mtd->size); goto out_close; } mtd->eb_cnt = mtd->size / mtd->eb_size; switch(mtd->type) { case MTD_ABSENT: errmsg("mtd%d (%s) is removable and is not present", mtd->mtd_num, node); goto out_close; case MTD_RAM: strcpy((char *)mtd->type_str, "ram"); break; case MTD_ROM: strcpy((char *)mtd->type_str, "rom"); break; case MTD_NORFLASH: strcpy((char *)mtd->type_str, "nor"); break; case MTD_NANDFLASH: strcpy((char *)mtd->type_str, "nand"); break; case MTD_DATAFLASH: strcpy((char *)mtd->type_str, "dataflash"); break; case MTD_UBIVOLUME: strcpy((char *)mtd->type_str, "ubi"); break; default: goto out_close; } if (ui.flags & MTD_WRITEABLE) mtd->writable = 1; mtd->subpage_size = mtd->min_io_size; close(fd); /* * Unfortunately, the device name is not available via ioctl, and * we have to parse /proc/mtd to get it. */ ret = proc_parse_start(&pi); if (ret) return -1; while (proc_parse_next(&pi)) { if (pi.mtd_num == mtd->mtd_num) { strcpy((char *)mtd->name, pi.name); return 0; } } errmsg("mtd%d not found in \"%s\"", mtd->mtd_num, MTD_PROC_FILE); errno = ENOENT; return -1; out_close: close(fd); return -1; } /** * legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'. * @node: name of the MTD device node * @mtd: the MTD device information is returned here * * This function is similar to 'mtd_get_dev_info1()' and has the same * conventions. */ int legacy_get_dev_info1(int mtd_num, struct mtd_dev_info *mtd) { char node[sizeof(MTD_DEV_PATT) + 20]; sprintf(node, MTD_DEV_PATT, mtd_num); return legacy_get_dev_info(node, mtd); } mtd-utils-1.5.0/load_nandsim.sh000077500000000000000000000103251175167361300164410ustar00rootroot00000000000000#!/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 " echo " " 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 mtd-utils-1.5.0/make_a_release.sh000077500000000000000000000037631175167361300167360ustar00rootroot00000000000000#!/bin/sh -uef # A small helper script to release mtd-utils. Takes the new version # as a parameter. fatal() { printf "Error: %s\n" "$1" >&2 exit 1 } usage() { cat < - mtd utils version to create in X.Y.Z format - the output directory where to store the tarball with the gpg signature EOF exit 0 } [ $# -eq 0 ] && usage [ $# -eq 2 ] || fatal "Insufficient or too many argumetns" new_ver="$1"; shift outdir="$1"; shift release_name="mtd-utils-$new_ver" tag_name="v$new_ver" # Make sure the input is sane and the makefile contains sensible version echo "$new_ver" | egrep -q -x '[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+' || fatal "please, provide new version in X.Y.Z format" egrep -q -x 'VERSION = [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+' Makefile || fatal "Makefile does not contain \"Version = X.Y.Z\" line" # Make sure the git index is up-to-date [ -z "$(git status --porcelain)" ] || fatal "Git index is not up-to-date" # Make sure the tag does not exist [ -z "$(git tag -l "$tag_name")" ] || fatal "Tag $tag_name already exists" # Change the version in the Makefile sed -i -e "s/^VERSION = [[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+/VERSION = $new_ver/" Makefile # And commit the change git commit -s -m "Release $release_name" Makefile # Create new signed tag echo "Signing tag $tag_name" git tag -m "$release_name" -s "$tag_name" # Prepare signed tarball git archive --format=tar --prefix="$release_name/" "$tag_name" | \ bzip2 > "$outdir/$release_name.tar.bz2" echo "Signing the tarball" gpg -o "$outdir/$release_name.tar.bz2.asc" --detach-sign -a "$outdir/$release_name.tar.bz2" cat < #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); mtd-utils-1.5.0/mkfs.jffs2.1000066400000000000000000000151761175167361300155160ustar00rootroot00000000000000.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. Set according to target system's memory management page size (NOTE: this is NOT related to NAND page size). .TP .B -e, --eraseblock=SIZE Use erase block size SIZE. The default is 64 KiB. If you use a erase block size different than the erase block size of the target MTD device, JFFS2 may not perform optimally. If the SIZE specified is below 4096, the units are assumed to be KiB. .TP .B -c, --cleanmarker=SIZE Write \'CLEANMARKER\' nodes with the size specified. It is not normally appropriate to specify a size other than the default 12 bytes. .TP .B -n, --no-cleanmarkers Do not write \'CLEANMARKER\' nodes to the beginning of each erase block. This option can be useful for creating JFFS2 images for use on NAND flash, and for creating images which are to be used on a variety of hardware with differing eraseblock sizes. .TP .B -o, --output=FILE Write JFFS2 image to file FILE. Default is the standard output. .TP .B -l, --little-endian Create a little-endian JFFS2 image. Default is to make an image with the same endianness as the host. .TP .B -b, --big-endian Create a big-endian JFFS2 image. Default is to make an image with the same endianness as the host. .TP .B -D, --devtable=FILE Use the named FILE as a device table file, for including devices and changing permissions in the created image when the user does not have appropriate permissions to create them on the file system used as source. .TP .B -f, --faketime Change all file timestamps to \'0\' for regression testing. .TP .B -q, --squash Squash permissions and owners, making all files be owned by root and removing write permission for \'group\' and \'other\'. .TP .B -U, --squash-uids Squash owners making all files be owned by root. .TP .B -P, --squash-perms Squash permissions, removing write permission for \'group\' and \'other\'. .TP .B --with-xattr Enables xattr, stuff all xattr entries into jffs2 image file. .TP .B --with-selinux Enables xattr, stuff only SELinux Labels into jffs2 image file. .TP .B --with-posix-acl Enable xattr, stuff only POSIX ACL entries into jffs2 image file. .TP .B -m, --compression-mode=MODE Set the default compression mode. The default mode is .B priority which tries the compressors in a predefinied order and chooses the first successful one. The alternatives are: .B none (mkfs will not compress) and .B size (mkfs will try all compressor and chooses the one which have the smallest result). .TP .B -x, --disable-compressor=NAME Disable a compressor. Use .B -L to see the list of the avaiable compressors and their default states. .TP .B -X, --enable-compressor=NAME Enable a compressor. Use .B -L to see the list of the avaiable compressors and their default states. .TP .B -y, --compressor-priority=PRIORITY:NAME Set the priority of a compressor. Use .B -L to see the list of the avaiable compressors and their default priority. Priorities are used by priority compression mode. .TP .B -L, --list-compressors Show the list of the avaiable compressors and their states. .TP .B -t, --test-compression Call decompress after every compress - and compare the result with the original data -, and some other check. .TP .B -h, --help Display help text. .TP .B -v, --verbose Verbose operation. .TP .B -V, --version Display version information. .TP .B -i, --incremental=FILE Generate an appendage image for FILE. If FILE is written to flash and flash is appended with the output, then it seems as if it was one thing. .SH LIMITATIONS The format and grammar of the device table file does not allow it to create symbolic links when the symbolic links are not already present in the root working directory. However, symbolic links may be specified in the device table file using the \fIl\fR type for the purposes of setting their permissions and ownership. .SH BUGS JFFS2 limits device major and minor numbers to 8 bits each. Some consider this a bug. .B mkfs.jffs2 does not properly handle hard links in the input directory structure. Currently, hard linked files will be expanded to multiple identical files in the output image. .SH AUTHORS David Woodhouse .br Manual page written by David Schleef .SH SEE ALSO .BR mkfs (8), .BR mkfs.jffs (1), .BR fakeroot (1) mtd-utils-1.5.0/mkfs.jffs2.c000066400000000000000000001374071175167361300156020ustar00rootroot00000000000000/* vi: set sw=4 ts=4: */ /* * Build a JFFS2 image in a file, from a given directory tree. * * Copyright 2001, 2002 Red Hat, Inc. * 2001 David A. Schleef * 2002 Axis Communications AB * 2001, 2002 Erik Andersen * 2004 University of Szeged, Hungary * 2006 KaiGai Kohei * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Cross-endian support added by David Schleef . * * Major architectural rewrite by Erik Andersen * to allow support for making hard links (though hard links support is * not yet implemented), and for munging file permissions and ownership * on the fly using --faketime, --squash, --devtable. And I plugged a * few memory leaks, adjusted the error handling and fixed some little * nits here and there. * * I also added a sample device table file. See device_table.txt * -Erik, September 2001 * * Cleanmarkers support added by Axis Communications AB * * Rewritten again. Cleanly separated host and target filsystem * activities (mainly so I can reuse all the host handling stuff as I * rewrite other mkfs utils). Added a verbose option to list types * and attributes as files are added to the file system. Major cleanup * and scrubbing of the code so it can be read, understood, and * modified by mere mortals. * * -Erik, November 2002 */ #define PROGRAM_NAME "mkfs.jffs2" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WITHOUT_XATTR #include #include #endif #include #include #include #include "rbtree.h" #include "common.h" /* Do not use the weird XPG version of basename */ #undef basename //#define DMALLOC //#define mkfs_debug_msg errmsg #define mkfs_debug_msg(a...) { } #define PAD(x) (((x)+3)&~3) struct filesystem_entry { char *name; /* Name of this directory (think basename) */ char *path; /* Path of this directory (think dirname) */ char *fullname; /* Full name of this directory (i.e. path+name) */ char *hostname; /* Full path to this file on the host filesystem */ uint32_t ino; /* Inode number of this file in JFFS2 */ struct stat sb; /* Stores directory permissions and whatnot */ char *link; /* Target a symlink points to. */ struct filesystem_entry *parent; /* Parent directory */ struct filesystem_entry *prev; /* Only relevant to non-directories */ struct filesystem_entry *next; /* Only relevant to non-directories */ struct filesystem_entry *files; /* Only relevant to directories */ struct rb_node hardlink_rb; }; struct rb_root hardlinks; static int out_fd = -1; static int in_fd = -1; static char default_rootdir[] = "."; static char *rootdir = default_rootdir; static int verbose = 0; static int squash_uids = 0; static int squash_perms = 0; static int fake_times = 0; int target_endian = __BYTE_ORDER; uint32_t find_hardlink(struct filesystem_entry *e) { struct filesystem_entry *f; struct rb_node **n = &hardlinks.rb_node; struct rb_node *parent = NULL; while (*n) { parent = *n; f = rb_entry(parent, struct filesystem_entry, hardlink_rb); if ((f->sb.st_dev < e->sb.st_dev) || (f->sb.st_dev == e->sb.st_dev && f->sb.st_ino < e->sb.st_ino)) n = &parent->rb_left; else if ((f->sb.st_dev > e->sb.st_dev) || (f->sb.st_dev == e->sb.st_dev && f->sb.st_ino > e->sb.st_ino)) { n = &parent->rb_right; } else return f->ino; } rb_link_node(&e->hardlink_rb, parent, n); rb_insert_color(&e->hardlink_rb, &hardlinks); return 0; } extern char *xreadlink(const char *path) { static const int GROWBY = 80; /* how large we will grow strings by */ char *buf = NULL; int bufsize = 0, readsize = 0; do { buf = xrealloc(buf, bufsize += GROWBY); readsize = readlink(path, buf, bufsize); /* 1st try */ if (readsize == -1) { sys_errmsg("%s:%s", PROGRAM_NAME, path); return NULL; } } while (bufsize < readsize + 1); buf[readsize] = '\0'; return buf; } static FILE *xfopen(const char *path, const char *mode) { FILE *fp; if ((fp = fopen(path, mode)) == NULL) sys_errmsg_die("%s", path); return fp; } static struct filesystem_entry *find_filesystem_entry( struct filesystem_entry *dir, char *fullname, uint32_t type) { struct filesystem_entry *e = dir; if (S_ISDIR(dir->sb.st_mode)) { /* If this is the first call, and we actually want this * directory, then return it now */ if (strcmp(fullname, e->fullname) == 0) return e; e = dir->files; } while (e) { if (S_ISDIR(e->sb.st_mode)) { int len = strlen(e->fullname); /* Check if we are a parent of the correct path */ if (strncmp(e->fullname, fullname, len) == 0) { /* Is this an _exact_ match? */ if (strcmp(fullname, e->fullname) == 0) { return (e); } /* Looks like we found a parent of the correct path */ if (fullname[len] == '/') { if (e->files) { return (find_filesystem_entry (e, fullname, type)); } else { return NULL; } } } } else { if (strcmp(fullname, e->fullname) == 0) { return (e); } } e = e->next; } return (NULL); } static struct filesystem_entry *add_host_filesystem_entry(const char *name, const char *path, unsigned long uid, unsigned long gid, unsigned long mode, dev_t rdev, struct filesystem_entry *parent) { int status; char *tmp; struct stat sb; time_t timestamp = time(NULL); struct filesystem_entry *entry; memset(&sb, 0, sizeof(struct stat)); status = lstat(path, &sb); if (status >= 0) { /* It is ok for some types of files to not exit on disk (such as * device nodes), but if they _do_ exist the specified mode had * better match the actual file or strange things will happen.... */ if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) { errmsg_die ("%s: file type does not match specified type!", path); } timestamp = sb.st_mtime; } else { /* If this is a regular file, it _must_ exist on disk */ if ((mode & S_IFMT) == S_IFREG) { errmsg_die("%s: does not exist!", path); } } /* Squash all permissions so files are owned by root, all * timestamps are _right now_, and file permissions * have group and other write removed */ if (squash_uids) { uid = gid = 0; } if (squash_perms) { if (!S_ISLNK(mode)) { mode &= ~(S_IWGRP | S_IWOTH); mode &= ~(S_ISUID | S_ISGID); } } if (fake_times) { timestamp = 0; } entry = xcalloc(1, sizeof(struct filesystem_entry)); entry->hostname = xstrdup(path); entry->fullname = xstrdup(name); tmp = xstrdup(name); entry->name = xstrdup(basename(tmp)); free(tmp); tmp = xstrdup(name); entry->path = xstrdup(dirname(tmp)); free(tmp); entry->sb.st_ino = sb.st_ino; entry->sb.st_dev = sb.st_dev; entry->sb.st_nlink = sb.st_nlink; entry->sb.st_uid = uid; entry->sb.st_gid = gid; entry->sb.st_mode = mode; entry->sb.st_rdev = rdev; entry->sb.st_atime = entry->sb.st_ctime = entry->sb.st_mtime = timestamp; if (S_ISREG(mode)) { entry->sb.st_size = sb.st_size; } if (S_ISLNK(mode)) { entry->link = xreadlink(path); entry->sb.st_size = strlen(entry->link); } /* This happens only for root */ if (!parent) return (entry); /* Hook the file into the parent directory */ entry->parent = parent; if (!parent->files) { parent->files = entry; } else { struct filesystem_entry *prev; for (prev = parent->files; prev->next; prev = prev->next); prev->next = entry; entry->prev = prev; } return (entry); } static struct filesystem_entry *recursive_add_host_directory( struct filesystem_entry *parent, const char *targetpath, const char *hostpath) { int i, n; struct stat sb; char *hpath, *tpath; struct dirent *dp, **namelist; struct filesystem_entry *entry; if (lstat(hostpath, &sb)) { sys_errmsg_die("%s", hostpath); } entry = add_host_filesystem_entry(targetpath, hostpath, sb.st_uid, sb.st_gid, sb.st_mode, 0, parent); n = scandir(hostpath, &namelist, 0, alphasort); if (n < 0) { sys_errmsg_die("opening directory %s", hostpath); } for (i=0; id_name[0] == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0))) { free(dp); continue; } xasprintf(&hpath, "%s/%s", hostpath, dp->d_name); if (lstat(hpath, &sb)) { sys_errmsg_die("%s", hpath); } if (strcmp(targetpath, "/") == 0) { xasprintf(&tpath, "%s%s", targetpath, dp->d_name); } else { xasprintf(&tpath, "%s/%s", targetpath, dp->d_name); } switch (sb.st_mode & S_IFMT) { case S_IFDIR: recursive_add_host_directory(entry, tpath, hpath); break; case S_IFREG: case S_IFSOCK: case S_IFIFO: case S_IFLNK: case S_IFCHR: case S_IFBLK: add_host_filesystem_entry(tpath, hpath, sb.st_uid, sb.st_gid, sb.st_mode, sb.st_rdev, entry); break; default: errmsg("Unknown file type %o for %s", sb.st_mode, hpath); break; } free(dp); free(hpath); free(tpath); } free(namelist); return (entry); } /* the GNU C library has a wonderful scanf("%as", string) which will allocate the string with the right size, good to avoid buffer overruns. the following macros use it if available or use a hacky workaround... */ #ifdef __GNUC__ #define SCANF_PREFIX "a" #define SCANF_STRING(s) (&s) #define GETCWD_SIZE 0 #else #define SCANF_PREFIX "511" #define SCANF_STRING(s) (s = xmalloc(512)) #define GETCWD_SIZE -1 inline int snprintf(char *str, size_t n, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vsprintf(str, fmt, ap); va_end(ap); return ret; } #endif /* device table entries take the form of: /dev/mem c 640 0 0 1 1 0 0 - type can be one of: f A regular file d Directory c Character special device file b Block special device file p Fifo (named pipe) I don't bother with symlinks (permissions are irrelevant), hard links (special cases of regular files), or sockets (why bother). Regular files must exist in the target root directory. If a char, block, fifo, or directory does not exist, it will be created. */ static int interpret_table_entry(struct filesystem_entry *root, char *line) { char *hostpath; char type, *name = NULL, *tmp, *dir; unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; unsigned long start = 0, increment = 1, count = 0; struct filesystem_entry *parent, *entry; if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu", SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor, &start, &increment, &count) < 0) { return 1; } if (!strcmp(name, "/")) { errmsg_die("Device table entries require absolute paths"); } xasprintf(&hostpath, "%s%s", rootdir, name); /* Check if this file already exists... */ switch (type) { case 'd': mode |= S_IFDIR; break; case 'f': mode |= S_IFREG; break; case 'p': mode |= S_IFIFO; break; case 'c': mode |= S_IFCHR; break; case 'b': mode |= S_IFBLK; break; case 'l': mode |= S_IFLNK; break; default: errmsg_die("Unsupported file type '%c'", type); } entry = find_filesystem_entry(root, name, mode); if (entry && !(count > 0 && (type == 'c' || type == 'b'))) { /* Ok, we just need to fixup the existing entry * and we will be all done... */ entry->sb.st_uid = uid; entry->sb.st_gid = gid; entry->sb.st_mode = mode; if (major && minor) { entry->sb.st_rdev = makedev(major, minor); } } else { /* If parent is NULL (happens with device table entries), * try and find our parent now) */ tmp = strdup(name); dir = dirname(tmp); parent = find_filesystem_entry(root, dir, S_IFDIR); free(tmp); if (parent == NULL) { errmsg ("skipping device_table entry '%s': no parent directory!", name); free(name); free(hostpath); return 1; } switch (type) { case 'd': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'f': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'p': add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); break; case 'c': case 'b': if (count > 0) { dev_t rdev; unsigned long i; char *dname, *hpath; for (i = start; i < (start + count); i++) { xasprintf(&dname, "%s%lu", name, i); xasprintf(&hpath, "%s/%s%lu", rootdir, name, i); rdev = makedev(major, minor + (i - start) * increment); add_host_filesystem_entry(dname, hpath, uid, gid, mode, rdev, parent); free(dname); free(hpath); } } else { dev_t rdev = makedev(major, minor); add_host_filesystem_entry(name, hostpath, uid, gid, mode, rdev, parent); } break; default: errmsg_die("Unsupported file type '%c'", type); } } free(name); free(hostpath); return 0; } static int parse_device_table(struct filesystem_entry *root, FILE * file) { char *line; int status = 0; size_t length = 0; /* Turn off squash, since we must ensure that values * entered via the device table are not squashed */ squash_uids = 0; squash_perms = 0; /* Looks ok so far. The general plan now is to read in one * line at a time, check for leading comment delimiters ('#'), * then try and parse the line as a device table. If we fail * to parse things, try and help the poor fool to fix their * device table with a useful error msg... */ line = NULL; while (getline(&line, &length, file) != -1) { /* First trim off any whitespace */ int len = strlen(line); /* trim trailing whitespace */ while (len > 0 && isspace(line[len - 1])) line[--len] = '\0'; /* trim leading whitespace */ memmove(line, &line[strspn(line, " \n\r\t\v")], len); /* How long are we after trimming? */ len = strlen(line); /* If this is NOT a comment line, try to interpret it */ if (len && *line != '#') { if (interpret_table_entry(root, line)) status = 1; } free(line); line = NULL; } fclose(file); return status; } static void cleanup(struct filesystem_entry *dir) { struct filesystem_entry *e, *prev; e = dir->files; while (e) { if (e->name) free(e->name); if (e->path) free(e->path); if (e->fullname) free(e->fullname); e->next = NULL; e->name = NULL; e->path = NULL; e->fullname = NULL; e->prev = NULL; prev = e; if (S_ISDIR(e->sb.st_mode)) { cleanup(e); } e = e->next; free(prev); } } /* Here is where we do the actual creation of the file system */ #include "mtd/jffs2-user.h" #define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF #ifndef JFFS2_MAX_SYMLINK_LEN #define JFFS2_MAX_SYMLINK_LEN 254 #endif static uint32_t ino = 0; static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/ static int out_ofs = 0; static int erase_block_size = 65536; static int pad_fs_size = 0; static int add_cleanmarkers = 1; static struct jffs2_unknown_node cleanmarker; static int cleanmarker_size = sizeof(cleanmarker); static unsigned char ffbuf[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* We set this at start of main() using sysconf(), -1 means we don't know */ /* When building an fs for non-native systems, use --pagesize=SIZE option */ int page_size = -1; #include "compr.h" static void full_write(int fd, const void *buf, int len) { int ret; while (len > 0) { ret = write(fd, buf, len); if (ret < 0) sys_errmsg_die("write"); if (ret == 0) sys_errmsg_die("write returned zero"); len -= ret; buf += ret; out_ofs += ret; } } static void padblock(void) { while (out_ofs % erase_block_size) { full_write(out_fd, ffbuf, min(sizeof(ffbuf), erase_block_size - (out_ofs % erase_block_size))); } } static void pad(int req) { while (req) { if (req > sizeof(ffbuf)) { full_write(out_fd, ffbuf, sizeof(ffbuf)); req -= sizeof(ffbuf); } else { full_write(out_fd, ffbuf, req); req = 0; } } } static inline void padword(void) { if (out_ofs % 4) { full_write(out_fd, ffbuf, 4 - (out_ofs % 4)); } } static inline void pad_block_if_less_than(int req) { if (add_cleanmarkers) { if ((out_ofs % erase_block_size) == 0) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padword(); } } if ((out_ofs % erase_block_size) + req > erase_block_size) { padblock(); } if (add_cleanmarkers) { if ((out_ofs % erase_block_size) == 0) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padword(); } } } static void write_dirent(struct filesystem_entry *e) { char *name = e->name; struct jffs2_raw_dirent rd; struct stat *statbuf = &(e->sb); static uint32_t version = 0; memset(&rd, 0, sizeof(rd)); rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name)); rd.hdr_crc = cpu_to_je32(mtd_crc32(0, &rd, sizeof(struct jffs2_unknown_node) - 4)); rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); rd.version = cpu_to_je32(version++); rd.ino = cpu_to_je32(e->ino); rd.mctime = cpu_to_je32(statbuf->st_mtime); rd.nsize = strlen(name); rd.type = IFTODT(statbuf->st_mode); //rd.unused[0] = 0; //rd.unused[1] = 0; rd.node_crc = cpu_to_je32(mtd_crc32(0, &rd, sizeof(rd) - 8)); rd.name_crc = cpu_to_je32(mtd_crc32(0, name, strlen(name))); pad_block_if_less_than(sizeof(rd) + rd.nsize); full_write(out_fd, &rd, sizeof(rd)); full_write(out_fd, name, rd.nsize); padword(); } static unsigned int write_regular_file(struct filesystem_entry *e) { int fd, len; uint32_t ver; unsigned int offset; unsigned char *buf, *cbuf, *wbuf; struct jffs2_raw_inode ri; struct stat *statbuf; unsigned int totcomp = 0; statbuf = &(e->sb); if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { errmsg("Skipping file \"%s\" too large.", e->path); return -1; } fd = open(e->hostname, O_RDONLY); if (fd == -1) { sys_errmsg_die("%s: open file", e->hostname); } e->ino = ++ino; mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) e->ino, (unsigned long) e->parent->ino); write_dirent(e); buf = xmalloc(page_size); cbuf = NULL; ver = 0; offset = 0; memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); while ((len = read(fd, buf, page_size))) { unsigned char *tbuf = buf; if (len < 0) { sys_errmsg_die("read"); } while (len) { uint32_t dsize, space; uint16_t compression; pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN); dsize = len; space = erase_block_size - (out_ofs % erase_block_size) - sizeof(ri); if (space > dsize) space = dsize; compression = jffs2_compress(tbuf, &cbuf, &dsize, &space); ri.compr = compression & 0xff; ri.usercompr = (compression >> 8) & 0xff; if (ri.compr) { wbuf = cbuf; } else { wbuf = tbuf; dsize = space; } ri.totlen = cpu_to_je32(sizeof(ri) + space); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.version = cpu_to_je32(++ver); ri.offset = cpu_to_je32(offset); ri.csize = cpu_to_je32(space); ri.dsize = cpu_to_je32(dsize); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(mtd_crc32(0, wbuf, space)); full_write(out_fd, &ri, sizeof(ri)); totcomp += sizeof(ri); full_write(out_fd, wbuf, space); totcomp += space; padword(); if (tbuf != cbuf) { free(cbuf); cbuf = NULL; } tbuf += dsize; len -= dsize; offset += dsize; } } if (!je32_to_cpu(ri.version)) { /* Was empty file */ pad_block_if_less_than(sizeof(ri)); ri.version = cpu_to_je32(++ver); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.csize = cpu_to_je32(0); ri.dsize = cpu_to_je32(0); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); full_write(out_fd, &ri, sizeof(ri)); padword(); } free(buf); close(fd); return totcomp; } static void write_symlink(struct filesystem_entry *e) { int len; struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); e->ino = ++ino; mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) e->ino, (unsigned long) e->parent->ino); write_dirent(e); len = strlen(e->link); if (len > JFFS2_MAX_SYMLINK_LEN) { errmsg("symlink too large. Truncated to %d chars.", JFFS2_MAX_SYMLINK_LEN); len = JFFS2_MAX_SYMLINK_LEN; } memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + len); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(len); ri.dsize = cpu_to_je32(len); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(mtd_crc32(0, e->link, len)); pad_block_if_less_than(sizeof(ri) + len); full_write(out_fd, &ri, sizeof(ri)); full_write(out_fd, e->link, len); padword(); } static void write_pipe(struct filesystem_entry *e) { struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); e->ino = ++ino; if (S_ISDIR(statbuf->st_mode)) { mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) e->ino, (unsigned long) (e->parent) ? e->parent->ino : 1); } write_dirent(e); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(0); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(0); ri.dsize = cpu_to_je32(0); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(0); pad_block_if_less_than(sizeof(ri)); full_write(out_fd, &ri, sizeof(ri)); padword(); } static void write_special_file(struct filesystem_entry *e) { jint16_t kdev; struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); e->ino = ++ino; write_dirent(e); kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) + minor(statbuf->st_rdev)); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev)); ri.hdr_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(e->ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(sizeof(kdev)); ri.dsize = cpu_to_je32(sizeof(kdev)); ri.node_crc = cpu_to_je32(mtd_crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(mtd_crc32(0, &kdev, sizeof(kdev))); pad_block_if_less_than(sizeof(ri) + sizeof(kdev)); full_write(out_fd, &ri, sizeof(ri)); full_write(out_fd, &kdev, sizeof(kdev)); padword(); } #ifndef WITHOUT_XATTR typedef struct xattr_entry { struct xattr_entry *next; uint32_t xid; int xprefix; char *xname; char *xvalue; int name_len; int value_len; } xattr_entry_t; #define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ static uint32_t enable_xattr = 0; static uint32_t highest_xid = 0; static uint32_t highest_xseqno = 0; static struct { int xprefix; const char *string; int length; } xprefix_tbl[] = { { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, { 0, NULL, 0 } }; static void formalize_posix_acl(void *xvalue, int *value_len) { struct posix_acl_xattr_header *pacl_header; struct posix_acl_xattr_entry *pent, *plim; struct jffs2_acl_header *jacl_header; struct jffs2_acl_entry *jent; struct jffs2_acl_entry_short *jent_s; char buffer[XATTR_BUFFER_SIZE]; int offset = 0; pacl_header = xvalue;; pent = pacl_header->a_entries; plim = xvalue + *value_len; jacl_header = (struct jffs2_acl_header *)buffer; offset += sizeof(struct jffs2_acl_header); jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); while (pent < plim) { switch(le16_to_cpu(pent->e_tag)) { case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_MASK: case ACL_OTHER: jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); offset += sizeof(struct jffs2_acl_entry_short); jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); break; case ACL_USER: case ACL_GROUP: jent = (struct jffs2_acl_entry *)(buffer + offset); offset += sizeof(struct jffs2_acl_entry); jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); break; default: printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); exit(1); } pent++; } if (offset > *value_len) { printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", offset, *value_len); exit(1); } memcpy(xvalue, buffer, offset); *value_len = offset; } static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) { xattr_entry_t *xe; struct jffs2_raw_xattr rx; int name_len; /* create xattr entry */ name_len = strlen(xname); xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); xe->next = NULL; xe->xid = ++highest_xid; xe->xprefix = xprefix; xe->xname = ((char *)xe) + sizeof(xattr_entry_t); xe->xvalue = xe->xname + name_len + 1; xe->name_len = name_len; xe->value_len = value_len; strcpy(xe->xname, xname); memcpy(xe->xvalue, xvalue, value_len); /* write xattr node */ memset(&rx, 0, sizeof(rx)); rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); rx.hdr_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); rx.xid = cpu_to_je32(xe->xid); rx.version = cpu_to_je32(1); /* initial version */ rx.xprefix = xprefix; rx.name_len = xe->name_len; rx.value_len = cpu_to_je16(xe->value_len); rx.data_crc = cpu_to_je32(mtd_crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); rx.node_crc = cpu_to_je32(mtd_crc32(0, &rx, sizeof(rx) - 4)); pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); full_write(out_fd, &rx, sizeof(rx)); full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); padword(); return xe; } #define XATTRENTRY_HASHSIZE 57 static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) { static xattr_entry_t **xentry_hash = NULL; xattr_entry_t *xe; int index, name_len; /* create hash table */ if (!xentry_hash) xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); if (xprefix == JFFS2_XPREFIX_ACL_ACCESS || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) formalize_posix_acl(xvalue, &value_len); name_len = strlen(xname); index = (mtd_crc32(0, xname, name_len) ^ mtd_crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; for (xe = xentry_hash[index]; xe; xe = xe->next) { if (xe->xprefix == xprefix && xe->value_len == value_len && !strcmp(xe->xname, xname) && !memcmp(xe->xvalue, xvalue, value_len)) break; } if (!xe) { xe = create_xattr_entry(xprefix, xname, xvalue, value_len); xe->next = xentry_hash[index]; xentry_hash[index] = xe; } return xe; } static void write_xattr_entry(struct filesystem_entry *e) { struct jffs2_raw_xref ref; struct xattr_entry *xe; char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; char *xname; const char *prefix_str; int i, xprefix, prefix_len; int list_sz, offset, name_len, value_len; if (!enable_xattr) return; list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); if (list_sz < 0) { if (verbose) printf("llistxattr('%s') = %d : %s\n", e->hostname, errno, strerror(errno)); return; } for (offset = 0; offset < list_sz; offset += name_len) { xname = xlist + offset; name_len = strlen(xname) + 1; for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { prefix_str = xprefix_tbl[i].string; prefix_len = xprefix_tbl[i].length; if (prefix_str[prefix_len - 1] == '.') { if (!strncmp(xname, prefix_str, prefix_len - 1)) break; } else { if (!strcmp(xname, prefix_str)) break; } } if (!xprefix) { if (verbose) printf("%s: xattr '%s' is not supported.\n", e->hostname, xname); continue; } if ((enable_xattr & (1 << xprefix)) == 0) continue; value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); if (value_len < 0) { if (verbose) printf("lgetxattr('%s', '%s') = %d : %s\n", e->hostname, xname, errno, strerror(errno)); continue; } xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); if (!xe) { if (verbose) printf("%s : xattr '%s' was ignored.\n", e->hostname, xname); continue; } memset(&ref, 0, sizeof(ref)); ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); ref.totlen = cpu_to_je32(sizeof(ref)); ref.hdr_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); ref.ino = cpu_to_je32(e->ino); ref.xid = cpu_to_je32(xe->xid); ref.xseqno = cpu_to_je32(highest_xseqno += 2); ref.node_crc = cpu_to_je32(mtd_crc32(0, &ref, sizeof(ref) - 4)); pad_block_if_less_than(sizeof(ref)); full_write(out_fd, &ref, sizeof(ref)); padword(); } } #else /* WITHOUT_XATTR */ #define write_xattr_entry(x) #endif static void recursive_populate_directory(struct filesystem_entry *dir) { struct filesystem_entry *e; unsigned int wrote; if (verbose) { printf("%s\n", dir->fullname); } write_xattr_entry(dir); /* for '/' */ e = dir->files; while (e) { if (e->sb.st_nlink >= 1 && (e->ino = find_hardlink(e))) { write_dirent(e); if (verbose) { printf("\tL %04o %9lu %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, (int) (e->sb.st_uid), (int) (e->sb.st_gid), e->name); } } else switch (e->sb.st_mode & S_IFMT) { case S_IFDIR: if (verbose) { printf("\td %04o %9" PRIu64 " %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, e->sb.st_size, (int) (e->sb.st_uid), (int) (e->sb.st_gid), e->name); } write_pipe(e); write_xattr_entry(e); break; case S_IFSOCK: if (verbose) { printf("\ts %04o %9" PRIu64 " %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, e->sb.st_size, (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); } write_pipe(e); write_xattr_entry(e); break; case S_IFIFO: if (verbose) { printf("\tp %04o %9" PRIu64 " %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, e->sb.st_size, (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); } write_pipe(e); write_xattr_entry(e); break; case S_IFCHR: if (verbose) { printf("\tc %04o %4d,%4d %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), minor(e->sb.st_rdev), (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); } write_special_file(e); write_xattr_entry(e); break; case S_IFBLK: if (verbose) { printf("\tb %04o %4d,%4d %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), minor(e->sb.st_rdev), (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); } write_special_file(e); write_xattr_entry(e); break; case S_IFLNK: if (verbose) { printf("\tl %04o %9" PRIu64 " %5d:%-3d %s -> %s\n", e->sb.st_mode & ~S_IFMT, e->sb.st_size, (int) e->sb.st_uid, (int) e->sb.st_gid, e->name, e->link); } write_symlink(e); write_xattr_entry(e); break; case S_IFREG: wrote = write_regular_file(e); write_xattr_entry(e); if (verbose) { printf("\tf %04o %9" PRIu64 " (%9u) %5d:%-3d %s\n", e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); } break; default: errmsg("Unknown mode %o for %s", e->sb.st_mode, e->fullname); break; } e = e->next; } e = dir->files; while (e) { if (S_ISDIR(e->sb.st_mode)) { if (e->files) { recursive_populate_directory(e); } else if (verbose) { printf("%s\n", e->fullname); } } e = e->next; } } static void create_target_filesystem(struct filesystem_entry *root) { cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); cleanmarker.totlen = cpu_to_je32(cleanmarker_size); cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); if (ino == 0) ino = 1; root->ino = 1; recursive_populate_directory(root); if (pad_fs_size == -1) { padblock(); } else { if (pad_fs_size && add_cleanmarkers){ padblock(); while (out_ofs < pad_fs_size) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padblock(); } } else { while (out_ofs < pad_fs_size) { full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs)); } } } } static struct option long_options[] = { {"pad", 2, NULL, 'p'}, {"root", 1, NULL, 'r'}, {"pagesize", 1, NULL, 's'}, {"eraseblock", 1, NULL, 'e'}, {"output", 1, NULL, 'o'}, {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, 'V'}, {"big-endian", 0, NULL, 'b'}, {"little-endian", 0, NULL, 'l'}, {"no-cleanmarkers", 0, NULL, 'n'}, {"cleanmarker", 1, NULL, 'c'}, {"squash", 0, NULL, 'q'}, {"squash-uids", 0, NULL, 'U'}, {"squash-perms", 0, NULL, 'P'}, {"faketime", 0, NULL, 'f'}, {"devtable", 1, NULL, 'D'}, {"compression-mode", 1, NULL, 'm'}, {"disable-compressor", 1, NULL, 'x'}, {"enable-compressor", 1, NULL, 'X'}, {"test-compression", 0, NULL, 't'}, {"compressor-priority", 1, NULL, 'y'}, {"incremental", 1, NULL, 'i'}, #ifndef WITHOUT_XATTR {"with-xattr", 0, NULL, 1000 }, {"with-selinux", 0, NULL, 1001 }, {"with-posix-acl", 0, NULL, 1002 }, #endif {NULL, 0, NULL, 0} }; static const char helptext[] = "Usage: mkfs.jffs2 [OPTIONS]\n" "Make a JFFS2 file system image from an existing directory tree\n\n" "Options:\n" " -p, --pad[=SIZE] Pad output to SIZE bytes with 0xFF. If SIZE is\n" " not specified, the output is padded to the end of\n" " the final erase block\n" " -r, -d, --root=DIR Build file system from directory DIR (default: cwd)\n" " -s, --pagesize=SIZE Use page size (max data node size) SIZE.\n" " Set according to target system's memory management\n" " page size (default: 4KiB)\n" " -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" " -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" " -m, --compr-mode=MODE Select compression mode (default: priortiry)\n" " -x, --disable-compressor=COMPRESSOR_NAME\n" " Disable a compressor\n" " -X, --enable-compressor=COMPRESSOR_NAME\n" " Enable a compressor\n" " -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n" " Set the priority of a compressor\n" " -L, --list-compressors Show the list of the avaiable compressors\n" " -t, --test-compression Call decompress and compare with the original (for test)\n" " -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n" " -o, --output=FILE Output to FILE (default: stdout)\n" " -l, --little-endian Create a little-endian filesystem\n" " -b, --big-endian Create a big-endian filesystem\n" " -D, --devtable=FILE Use the named FILE as a device table file\n" " -f, --faketime Change all file times to '0' for regression testing\n" " -q, --squash Squash permissions and owners making all files be owned by root\n" " -U, --squash-uids Squash owners making all files be owned by root\n" " -P, --squash-perms Squash permissions on all files\n" #ifndef WITHOUT_XATTR " --with-xattr stuff all xattr entries into image\n" " --with-selinux stuff only SELinux Labels into jffs2 image\n" " --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" #endif " -h, --help Display this help text\n" " -v, --verbose Verbose operation\n" " -V, --version Display version information\n" " -i, --incremental=FILE Parse FILE and generate appendage output for it\n\n"; static const char revtext[] = "1.60"; int load_next_block() { int ret; ret = read(in_fd, file_buffer, erase_block_size); if(verbose) printf("Load next block : %d bytes read\n",ret); return ret; } void process_buffer(int inp_size) { uint8_t *p = file_buffer; union jffs2_node_union *node; uint16_t type; int bitchbitmask = 0; int obsolete; char name[256]; while ( p < (file_buffer + inp_size)) { node = (union jffs2_node_union *) p; /* Skip empty space */ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { p += 4; continue; } if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { if (!bitchbitmask++) printf ("Wrong bitmask at 0x%08zx, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); p += 4; continue; } bitchbitmask = 0; type = je16_to_cpu(node->u.nodetype); if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { obsolete = 1; type |= JFFS2_NODE_ACCURATE; } else obsolete = 0; node->u.nodetype = cpu_to_je16(type); switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: if(verbose) printf ("%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); if ( je32_to_cpu (node->i.ino) > ino ) ino = je32_to_cpu (node->i.ino); p += PAD(je32_to_cpu (node->i.totlen)); break; case JFFS2_NODETYPE_DIRENT: memcpy (name, node->d.name, node->d.nsize); name [node->d.nsize] = 0x0; if(verbose) printf ("%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), node->d.nsize, name); p += PAD(je32_to_cpu (node->d.totlen)); break; case JFFS2_NODETYPE_CLEANMARKER: if (verbose) { printf ("%8s Cleanmarker at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); break; case JFFS2_NODETYPE_PADDING: if (verbose) { printf ("%8s Padding node at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); break; case 0xffff: p += 4; break; default: if (verbose) { printf ("%8s Unknown node at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); } } } void parse_image(){ int ret; file_buffer = xmalloc(erase_block_size); while ((ret = load_next_block())) { process_buffer(ret); } if (file_buffer) free(file_buffer); close(in_fd); } int main(int argc, char **argv) { int c, opt; char *cwd; struct stat sb; FILE *devtable = NULL; struct filesystem_entry *root; char *compr_name = NULL; int compr_prior = -1; int warn_page_size = 0; page_size = sysconf(_SC_PAGESIZE); if (page_size < 0) /* System doesn't know so ... */ page_size = 4096; /* ... we make an educated guess */ if (page_size != 4096) warn_page_size = 1; /* warn user if page size not 4096 */ jffs2_compressors_init(); while ((opt = getopt_long(argc, argv, "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0) { switch (opt) { case 'D': devtable = xfopen(optarg, "r"); if (fstat(fileno(devtable), &sb) < 0) sys_errmsg_die("%s", optarg); if (sb.st_size < 10) errmsg_die("%s: not a proper device table file", optarg); break; case 'r': case 'd': /* for compatibility with mkfs.jffs, genext2fs, etc... */ if (rootdir != default_rootdir) { errmsg_die("root directory specified more than once"); } rootdir = xstrdup(optarg); break; case 's': page_size = strtol(optarg, NULL, 0); warn_page_size = 0; /* set by user, so don't need to warn */ break; case 'o': if (out_fd != -1) { errmsg_die("output filename specified more than once"); } out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); if (out_fd == -1) { sys_errmsg_die("open output file"); } break; case 'q': squash_uids = 1; squash_perms = 1; break; case 'U': squash_uids = 1; break; case 'P': squash_perms = 1; break; case 'f': fake_times = 1; break; case 'h': case '?': errmsg_die("%s", helptext); case 'v': verbose = 1; break; case 'V': errmsg_die("revision %s\n", revtext); case 'e': { char *next; unsigned units = 0; erase_block_size = strtol(optarg, &next, 0); if (!erase_block_size) errmsg_die("Unrecognisable erase size\n"); if (*next) { if (!strcmp(next, "KiB")) { units = 1024; } else if (!strcmp(next, "MiB")) { units = 1024 * 1024; } else { errmsg_die("Unknown units in erasesize\n"); } } else { if (erase_block_size < 0x1000) units = 1024; else units = 1; } erase_block_size *= units; /* If it's less than 8KiB, they're not allowed */ if (erase_block_size < 0x2000) { fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n", erase_block_size); erase_block_size = 0x2000; } break; } case 'l': target_endian = __LITTLE_ENDIAN; break; case 'b': target_endian = __BIG_ENDIAN; break; case 'p': if (optarg) pad_fs_size = strtol(optarg, NULL, 0); else pad_fs_size = -1; break; case 'n': add_cleanmarkers = 0; break; case 'c': cleanmarker_size = strtol(optarg, NULL, 0); if (cleanmarker_size < sizeof(cleanmarker)) { errmsg_die("cleanmarker size must be >= 12"); } if (cleanmarker_size >= erase_block_size) { errmsg_die("cleanmarker size must be < eraseblock size"); } break; case 'm': if (jffs2_set_compression_mode_name(optarg)) { errmsg_die("Unknown compression mode %s", optarg); } break; case 'x': if (jffs2_disable_compressor_name(optarg)) { errmsg_die("Unknown compressor name %s",optarg); } break; case 'X': if (jffs2_enable_compressor_name(optarg)) { errmsg_die("Unknown compressor name %s",optarg); } break; case 'L': errmsg_die("\n%s",jffs2_list_compressors()); break; case 't': jffs2_compression_check_set(1); break; case 'y': compr_name = xmalloc(strlen(optarg)); sscanf(optarg,"%d:%s",&compr_prior,compr_name); if ((compr_prior>=0)&&(compr_name)) { if (jffs2_set_compressor_priority(compr_name, compr_prior)) exit(EXIT_FAILURE); } else { errmsg_die("Cannot parse %s",optarg); } free(compr_name); break; case 'i': if (in_fd != -1) { errmsg_die("(incremental) filename specified more than once"); } in_fd = open(optarg, O_RDONLY); if (in_fd == -1) { sys_errmsg_die("cannot open (incremental) file"); } break; #ifndef WITHOUT_XATTR case 1000: /* --with-xattr */ enable_xattr |= (1 << JFFS2_XPREFIX_USER) | (1 << JFFS2_XPREFIX_SECURITY) | (1 << JFFS2_XPREFIX_ACL_ACCESS) | (1 << JFFS2_XPREFIX_ACL_DEFAULT) | (1 << JFFS2_XPREFIX_TRUSTED); break; case 1001: /* --with-selinux */ enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); break; case 1002: /* --with-posix-acl */ enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) | (1 << JFFS2_XPREFIX_ACL_DEFAULT); break; #endif } } if (warn_page_size) { errmsg("Page size for this system is by default %d", page_size); errmsg("Use the --pagesize=SIZE option if this is not what you want"); } if (out_fd == -1) { if (isatty(1)) { errmsg_die("%s", helptext); } out_fd = 1; } if (lstat(rootdir, &sb)) { sys_errmsg_die("%s", rootdir); } if (chdir(rootdir)) sys_errmsg_die("%s", rootdir); if (!(cwd = getcwd(0, GETCWD_SIZE))) sys_errmsg_die("getcwd failed"); if(in_fd != -1) parse_image(); root = recursive_add_host_directory(NULL, "/", cwd); if (devtable) parse_device_table(root, devtable); create_target_filesystem(root); cleanup(root); if (rootdir != default_rootdir) free(rootdir); close(out_fd); if (verbose) { char *s = jffs2_stats(); fprintf(stderr,"\n\n%s",s); free(s); } if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) { fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get()); } jffs2_compressors_exit(); return 0; } mtd-utils-1.5.0/mkfs.ubifs/000077500000000000000000000000001175167361300155205ustar00rootroot00000000000000mtd-utils-1.5.0/mkfs.ubifs/.gitignore000066400000000000000000000000141175167361300175030ustar00rootroot00000000000000/mkfs.ubifs mtd-utils-1.5.0/mkfs.ubifs/COPYING000066400000000000000000000431271175167361300165620ustar00rootroot00000000000000 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. Copyright (C) 19yy 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. , 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. mtd-utils-1.5.0/mkfs.ubifs/README000066400000000000000000000011401175167361300163740ustar00rootroot00000000000000UBIFS File System - Make File System program * crc16.h and crc16.c were copied from the linux kernel. * crc32.h and crc32.c were copied from mtd-utils and amended. * ubifs-media.h is fs/ubifs/ubifs-media.h from the linux kernel * ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. * key.h is copied from fs/ubifs/key.h from the linux kernel. * defs.h is a bunch of definitions to smooth things over. * lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. * hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ mtd-utils-1.5.0/mkfs.ubifs/compr.c000066400000000000000000000111101175167361300167760ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation. * Copyright (C) 2008 University of Szeged, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy * Adrian Hunter * Zoltan Sogor */ #include #include #include #include #include #include #define crc32 __zlib_crc32 #include #undef crc32 #include "compr.h" #include "ubifs-media.h" #include "mkfs.ubifs.h" static void *lzo_mem; static unsigned long long errcnt = 0; static struct ubifs_info *c = &info_; #define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION #define DEFLATE_DEF_WINBITS 11 #define DEFLATE_DEF_MEMLEVEL 8 static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, size_t *out_len) { z_stream strm; strm.zalloc = NULL; strm.zfree = NULL; /* * Match exactly the zlib parameters used by the Linux kernel crypto * API. */ if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, Z_DEFAULT_STRATEGY)) { errcnt += 1; return -1; } strm.next_in = in_buf; strm.avail_in = in_len; strm.total_in = 0; strm.next_out = out_buf; strm.avail_out = *out_len; strm.total_out = 0; if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { deflateEnd(&strm); errcnt += 1; return -1; } if (deflateEnd(&strm) != Z_OK) { errcnt += 1; return -1; } *out_len = strm.total_out; return 0; } static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, size_t *out_len) { lzo_uint len; int ret; len = *out_len; ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); *out_len = len; if (ret != LZO_E_OK) { errcnt += 1; return -1; } return 0; } static int no_compress(void *in_buf, size_t in_len, void *out_buf, size_t *out_len) { memcpy(out_buf, in_buf, in_len); *out_len = in_len; return 0; } static char *zlib_buf; static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, int *type) { int lzo_ret, zlib_ret; size_t lzo_len, zlib_len; lzo_len = zlib_len = *out_len; lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); if (lzo_ret && zlib_ret) /* Both compressors failed */ return -1; if (!lzo_ret && !zlib_ret) { double percent; /* Both compressors succeeded */ if (lzo_len <= zlib_len ) goto select_lzo; percent = (double)zlib_len / (double)lzo_len; percent *= 100; if (percent > 100 - c->favor_percent) goto select_lzo; goto select_zlib; } if (lzo_ret) /* Only zlib compressor succeeded */ goto select_zlib; /* Only LZO compressor succeeded */ select_lzo: *out_len = lzo_len; *type = MKFS_UBIFS_COMPR_LZO; return 0; select_zlib: *out_len = zlib_len; *type = MKFS_UBIFS_COMPR_ZLIB; memcpy(out_buf, zlib_buf, zlib_len); return 0; } int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, int type) { int ret; if (in_len < UBIFS_MIN_COMPR_LEN) { no_compress(in_buf, in_len, out_buf, out_len); return MKFS_UBIFS_COMPR_NONE; } if (c->favor_lzo) ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); else { switch (type) { case MKFS_UBIFS_COMPR_LZO: ret = lzo_compress(in_buf, in_len, out_buf, out_len); break; case MKFS_UBIFS_COMPR_ZLIB: ret = zlib_deflate(in_buf, in_len, out_buf, out_len); break; case MKFS_UBIFS_COMPR_NONE: ret = 1; break; default: errcnt += 1; ret = 1; break; } } if (ret || *out_len >= in_len) { no_compress(in_buf, in_len, out_buf, out_len); return MKFS_UBIFS_COMPR_NONE; } return type; } int init_compression(void) { lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); if (!lzo_mem) return -1; zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); if (!zlib_buf) { free(lzo_mem); return -1; } return 0; } void destroy_compression(void) { free(zlib_buf); free(lzo_mem); if (errcnt) fprintf(stderr, "%llu compression errors occurred\n", errcnt); } mtd-utils-1.5.0/mkfs.ubifs/compr.h000066400000000000000000000026351175167361300170170ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation. * Copyright (C) 2008 University of Szeged, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy * Adrian Hunter * Zoltan Sogor */ #ifndef __UBIFS_COMPRESS_H__ #define __UBIFS_COMPRESS_H__ /* * Compressors may end-up with more data in the output buffer than in the input * buffer. This constant defined the worst case factor, i.e. we assume that the * output buffer may be at max. WORST_COMPR_FACTOR times larger than input * buffer. */ #define WORST_COMPR_FACTOR 4 enum compression_type { MKFS_UBIFS_COMPR_NONE, MKFS_UBIFS_COMPR_LZO, MKFS_UBIFS_COMPR_ZLIB, }; int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, int type); int init_compression(void); void destroy_compression(void); #endif mtd-utils-1.5.0/mkfs.ubifs/crc16.c000066400000000000000000000050721175167361300166060ustar00rootroot00000000000000/* * This code was taken from the linux kernel. The license is GPL Version 2. */ #include "crc16.h" /** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ uint16_t const crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; /** * crc16 - compute the CRC-16 for the data buffer * @crc: previous CRC value * @buffer: data pointer * @len: number of bytes in the buffer * * Returns the updated CRC value. */ uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) { while (len--) crc = crc16_byte(crc, *buffer++); return crc; } mtd-utils-1.5.0/mkfs.ubifs/crc16.h000066400000000000000000000011271175167361300166100ustar00rootroot00000000000000/* * Implements the standard CRC-16: * Width 16 * Poly 0x8005 (x^16 + x^15 + x^2 + 1) * Init 0 * * Copyright (c) 2005 Ben Gardner * * This code was taken from the linux kernel. The license is GPL Version 2. */ #ifndef __CRC16_H__ #define __CRC16_H__ #include #include extern uint16_t const crc16_table[256]; extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len); static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data) { return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; } #endif /* __CRC16_H__ */ mtd-utils-1.5.0/mkfs.ubifs/defs.h000066400000000000000000000037061175167361300166200ustar00rootroot00000000000000/* * Greate deal of the code was taken from the kernel UBIFS implementation, and * this file contains some "glue" definitions. */ #ifndef __UBIFS_DEFS_H__ #define __UBIFS_DEFS_H__ #define t16(x) ({ \ uint16_t __b = (x); \ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ }) #define t32(x) ({ \ uint32_t __b = (x); \ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ }) #define t64(x) ({ \ uint64_t __b = (x); \ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ }) #define cpu_to_le16(x) ((__le16){t16(x)}) #define cpu_to_le32(x) ((__le32){t32(x)}) #define cpu_to_le64(x) ((__le64){t64(x)}) #define le16_to_cpu(x) (t16((x))) #define le32_to_cpu(x) (t32((x))) #define le64_to_cpu(x) (t64((x))) #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) #define min_t(t,x,y) ({ \ typeof((x)) _x = (x); \ typeof((y)) _y = (y); \ (_x < _y) ? _x : _y; \ }) #define max_t(t,x,y) ({ \ typeof((x)) _x = (x); \ typeof((y)) _y = (y); \ (_x > _y) ? _x : _y; \ }) #define unlikely(x) (x) #define ubifs_assert(x) ({}) struct qstr { char *name; size_t len; }; /** * fls - find last (most-significant) bit set * @x: the word to search * * This is defined the same way as ffs. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ static inline int fls(int x) { int r = 32; if (!x) return 0; if (!(x & 0xffff0000u)) { x <<= 16; r -= 16; } if (!(x & 0xff000000u)) { x <<= 8; r -= 8; } if (!(x & 0xf0000000u)) { x <<= 4; r -= 4; } if (!(x & 0xc0000000u)) { x <<= 2; r -= 2; } if (!(x & 0x80000000u)) { x <<= 1; r -= 1; } return r; } #define do_div(n,base) ({ \ int __res; \ __res = ((unsigned long) n) % (unsigned) base; \ n = ((unsigned long) n) / (unsigned) base; \ __res; }) #if INT_MAX != 0x7fffffff #error : sizeof(int) must be 4 for this program #endif #if (~0ULL) != 0xffffffffffffffffULL #error : sizeof(long long) must be 8 for this program #endif #endif mtd-utils-1.5.0/mkfs.ubifs/devtable.c000066400000000000000000000346141175167361300174620ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Author: Artem Bityutskiy * * Part of the device table parsing code was taken from the mkfs.jffs2 utility. * The original author of that code is Erik Andersen, hence: * Copyright (C) 2001, 2002 Erik Andersen */ /* * This file implemented device table support. Device table entries take the * form of: * * /dev/mem c 640 0 0 1 1 0 0 - * * Type can be one of: * f A regular file * d Directory * c Character special device file * b Block special device file * p Fifo (named pipe) * * Don't bother with symlinks (permissions are irrelevant), hard links (special * cases of regular files), or sockets (why bother). * * Regular files must exist in the target root directory. If a char, block, * fifo, or directory does not exist, it will be created. * * Please, refer the device_table.txt file which can be found at MTD utilities * for more information about what the device table is. */ #include "mkfs.ubifs.h" #include "hashtable/hashtable.h" #include "hashtable/hashtable_itr.h" /* * The hash table which contains paths to files/directories/device nodes * referred to in the device table. For example, if the device table refers * "/dev/loop0", the @path_htbl will contain "/dev" element. */ static struct hashtable *path_htbl; /* Hash function used for hash tables */ static unsigned int r5_hash(void *s) { unsigned int a = 0; const signed char *str = s; while (*str) { a += *str << 4; a += *str >> 4; a *= 11; str++; } return a; } /* * Check whether 2 keys of a hash table are equivalent. The keys are path/file * names, so we simply use 'strcmp()'. */ static int is_equivalent(void *k1, void *k2) { return !strcmp(k1, k2); } /** * separate_last - separate out the last path component * @buf: the path to split * @len: length of the @buf string * @path: the beginning of path is returned here * @name: the last path component is returned here * * This helper function separates out the the last component of the full path * string. For example, "/dev/loop" would be split on "/dev" and "loop". This * function allocates memory for @path and @name and return the result there. * Returns zero in case of success and a negative error code in case of * failure. */ static int separate_last(const char *buf, int len, char **path, char **name) { int path_len = len, name_len; const char *p = buf + len, *n; while (*--p != '/') path_len -= 1; /* Drop the final '/' unless this is the root directory */ name_len = len - path_len; n = buf + path_len; if (path_len > 1) path_len -= 1; *path = malloc(path_len + 1); if (!*path) return err_msg("cannot allocate %d bytes of memory", path_len + 1); memcpy(*path, buf, path_len); (*path)[path_len] = '\0'; *name = malloc(name_len + 1); if (!*name) { free(*path); return err_msg("cannot allocate %d bytes of memory", name_len + 1); } memcpy(*name, n, name_len + 1); return 0; } static int interpret_table_entry(const char *line) { char buf[1024], type, *path = NULL, *name = NULL; int len; struct path_htbl_element *ph_elt = NULL; struct name_htbl_element *nh_elt = NULL; unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; unsigned int start = 0, increment = 0, count = 0; if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u", buf, &type, &mode, &uid, &gid, &major, &minor, &start, &increment, &count) < 0) return sys_err_msg("sscanf failed"); dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, " "minor %u, start %u, inc %u, cnt %u", buf, type, mode, uid, gid, major, minor, start, increment, count); len = strnlen(buf, 1024); if (len == 1024) return err_msg("too long path"); if (!strcmp(buf, "/")) return err_msg("device table entries require absolute paths"); if (buf[1] == '\0') return err_msg("root directory cannot be created"); if (strstr(buf, "//")) return err_msg("'//' cannot be used in the path"); if (buf[len - 1] == '/') return err_msg("do not put '/' at the end"); if (strstr(buf, "/./") || strstr(buf, "/../") || !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/..")) return err_msg("'.' and '..' cannot be used in the path"); switch (type) { case 'd': mode |= S_IFDIR; break; case 'f': mode |= S_IFREG; break; case 'p': mode |= S_IFIFO; break; case 'c': mode |= S_IFCHR; break; case 'b': mode |= S_IFBLK; break; default: return err_msg("unsupported file type '%c'", type); } if (separate_last(buf, len, &path, &name)) return -1; /* * Check if this path already exist in the path hash table and add it * if it is not. */ ph_elt = hashtable_search(path_htbl, path); if (!ph_elt) { dbg_msg(3, "inserting '%s' into path hash table", path); ph_elt = malloc(sizeof(struct path_htbl_element)); if (!ph_elt) { err_msg("cannot allocate %zd bytes of memory", sizeof(struct path_htbl_element)); goto out_free; } if (!hashtable_insert(path_htbl, path, ph_elt)) { err_msg("cannot insert into path hash table"); goto out_free; } ph_elt->path = path; path = NULL; ph_elt->name_htbl = create_hashtable(128, &r5_hash, &is_equivalent); if (!ph_elt->name_htbl) { err_msg("cannot create name hash table"); goto out_free; } } if (increment != 0 && count == 0) return err_msg("count cannot be zero if increment is non-zero"); /* * Add the file/directory/device node (last component of the path) to * the name hashtable. The name hashtable resides in the corresponding * path hashtable element. */ if (count == 0) { /* This entry does not require any iterating */ nh_elt = malloc(sizeof(struct name_htbl_element)); if (!nh_elt) { err_msg("cannot allocate %zd bytes of memory", sizeof(struct name_htbl_element)); goto out_free; } nh_elt->mode = mode; nh_elt->uid = uid; nh_elt->gid = gid; nh_elt->dev = makedev(major, minor); dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", name, major(nh_elt->dev), minor(nh_elt->dev)); if (hashtable_search(ph_elt->name_htbl, name)) return err_msg("'%s' is referred twice", buf); nh_elt->name = name; if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) { err_msg("cannot insert into name hash table"); goto out_free; } } else { int i, num = start + count, len = strlen(name) + 20; char *nm; for (i = start; i < num; i++) { nh_elt = malloc(sizeof(struct name_htbl_element)); if (!nh_elt) { err_msg("cannot allocate %zd bytes of memory", sizeof(struct name_htbl_element)); goto out_free; } nh_elt->mode = mode; nh_elt->uid = uid; nh_elt->gid = gid; nh_elt->dev = makedev(major, minor + (i - start) * increment); nm = malloc(len); if (!nm) { err_msg("cannot allocate %d bytes of memory", len); goto out_free; } sprintf(nm, "%s%d", name, i); nh_elt->name = nm; dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", nm, major(nh_elt->dev), minor(nh_elt->dev)); if (hashtable_search(ph_elt->name_htbl, nm)) { err_msg("'%s' is referred twice", buf); free (nm); goto out_free; } if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { err_msg("cannot insert into name hash table"); free (nm); goto out_free; } } free(name); name = NULL; } return 0; out_free: free(ph_elt); free(nh_elt); free(path); free(name); return -1; } /** * parse_devtable - parse the device table. * @tbl_file: device table file name * * This function parses the device table and prepare the hash table which will * later be used by mkfs.ubifs to create the specified files/device nodes. * Returns zero in case of success and a negative error code in case of * failure. */ int parse_devtable(const char *tbl_file) { FILE *f; char *line = NULL; struct stat st; size_t len; dbg_msg(1, "parsing device table file '%s'", tbl_file); path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); if (!path_htbl) return err_msg("cannot create path hash table"); f = fopen(tbl_file, "r"); if (!f) return sys_err_msg("cannot open '%s'", tbl_file); if (fstat(fileno(f), &st) < 0) { sys_err_msg("cannot stat '%s'", tbl_file); goto out_close; } if (st.st_size < 10) { sys_err_msg("'%s' is too short", tbl_file); goto out_close; } /* * The general plan now is to read in one line at a time, check for * leading comment delimiters ('#'), then try and parse the line as a * device table */ while (getline(&line, &len, f) != -1) { /* First trim off any white-space */ len = strlen(line); /* Trim trailing white-space */ while (len > 0 && isspace(line[len - 1])) line[--len] = '\0'; /* Trim leading white-space */ memmove(line, &line[strspn(line, " \n\r\t\v")], len); /* How long are we after trimming? */ len = strlen(line); /* If this is not a comment line, try to interpret it */ if (len && *line != '#') { if (interpret_table_entry(line)) { err_msg("cannot parse '%s'", line); goto out_close; } } free(line); line = NULL; } dbg_msg(1, "finished parsing"); fclose(f); return 0; out_close: fclose(f); free_devtable_info(); return -1; } /** * devtbl_find_path - find a path in the path hash table. * @path: UBIFS path to find. * * This looks up the path hash table. Returns the path hash table element * reference if @path was found and %NULL if not. */ struct path_htbl_element *devtbl_find_path(const char *path) { if (!path_htbl) return NULL; return hashtable_search(path_htbl, (void *)path); } /** * devtbl_find_name - find a name in the name hash table. * @ph_etl: path hash table element to find at * @name: name to find * * This looks up the name hash table. Returns the name hash table element * reference if @name found and %NULL if not. */ struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, const char *name) { if (!path_htbl) return NULL; return hashtable_search(ph_elt->name_htbl, (void *)name); } /** * override_attributes - override inode attributes. * @st: struct stat object to containing the attributes to override * @ph_elt: path hash table element object * @nh_elt: name hash table element object containing the new values * * The device table file may override attributes like UID of files. For * example, the device table may contain a "/dev" entry, and the UBIFS FS on * the host may contain "/dev" directory. In this case the attributes of the * "/dev" directory inode has to be as the device table specifies. * * Note, the hash element is removed by this function as well. */ int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, struct name_htbl_element *nh_elt) { if (!path_htbl) return 0; if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || S_ISFIFO(st->st_mode)) return err_msg("%s/%s both exists at UBIFS root at host, " "and is referred from the device table", strcmp(ph_elt->path, "/") ? ph_elt->path : "", nh_elt->name); if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) return err_msg("%s/%s is referred from the device table also exists in " "the UBIFS root directory at host, but the file type is " "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", nh_elt->name); dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); st->st_uid = nh_elt->uid; st->st_gid = nh_elt->gid; st->st_mode = nh_elt->mode; hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); return 0; } /** * first_name_htbl_element - return first element of the name hash table. * @ph_elt: the path hash table the name hash table belongs to * @itr: double pointer to a 'struct hashtable_itr' object where the * information about further iterations is stored * * This function implements name hash table iteration together with * 'next_name_htbl_element()'. Returns the first name hash table element or * %NULL if the hash table is empty. */ struct name_htbl_element * first_name_htbl_element(struct path_htbl_element *ph_elt, struct hashtable_itr **itr) { if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) return NULL; *itr = hashtable_iterator(ph_elt->name_htbl); return hashtable_iterator_value(*itr); } /** * first_name_htbl_element - return next element of the name hash table. * @ph_elt: the path hash table the name hash table belongs to * @itr: double pointer to a 'struct hashtable_itr' object where the * information about further iterations is stored * * This function implements name hash table iteration together with * 'first_name_htbl_element()'. Returns the next name hash table element or * %NULL if there are no more elements. */ struct name_htbl_element * next_name_htbl_element(struct path_htbl_element *ph_elt, struct hashtable_itr **itr) { if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) return NULL; return hashtable_iterator_value(*itr); } /** * free_devtable_info - free device table information. * * This function frees the path hash table and the name hash tables. */ void free_devtable_info(void) { struct hashtable_itr *ph_itr; struct path_htbl_element *ph_elt; if (!path_htbl) return; if (hashtable_count(path_htbl) > 0) { ph_itr = hashtable_iterator(path_htbl); do { ph_elt = hashtable_iterator_value(ph_itr); /* * Note, since we use the same string for the key and * @name in the name hash table elements, we do not * have to iterate name hash table because @name memory * will be freed when freeing the key. */ hashtable_destroy(ph_elt->name_htbl, 1); } while (hashtable_iterator_advance(ph_itr)); } hashtable_destroy(path_htbl, 1); } mtd-utils-1.5.0/mkfs.ubifs/hashtable/000077500000000000000000000000001175167361300174535ustar00rootroot00000000000000mtd-utils-1.5.0/mkfs.ubifs/hashtable/hashtable.c000066400000000000000000000215501175167361300215550ustar00rootroot00000000000000/* Copyright (C) 2004 Christopher Clark */ #define PROGRAM_NAME "hashtable" #include "common.h" #include "hashtable.h" #include "hashtable_private.h" #include #include #include #include /* Credit for primes table: Aaron Krowne http://br.endernet.org/~akrowne/ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html */ static const unsigned int primes[] = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; const unsigned int prime_table_length = ARRAY_SIZE(primes); const float max_load_factor = 0.65; /*****************************************************************************/ struct hashtable * create_hashtable(unsigned int minsize, unsigned int (*hashf) (void*), int (*eqf) (void*,void*)) { struct hashtable *h; unsigned int pindex, size = primes[0]; /* Check requested hashtable isn't too large */ if (minsize > (1u << 30)) return NULL; /* Enforce size as prime */ for (pindex=0; pindex < prime_table_length; pindex++) { if (primes[pindex] > minsize) { size = primes[pindex]; break; } } h = (struct hashtable *)malloc(sizeof(struct hashtable)); if (NULL == h) return NULL; /*oom*/ h->table = (struct entry **)malloc(sizeof(struct entry*) * size); if (NULL == h->table) { free(h); return NULL; } /*oom*/ memset(h->table, 0, size * sizeof(struct entry *)); h->tablelength = size; h->primeindex = pindex; h->entrycount = 0; h->hashfn = hashf; h->eqfn = eqf; h->loadlimit = (unsigned int) ceil(size * max_load_factor); return h; } /*****************************************************************************/ unsigned int hash(struct hashtable *h, void *k) { /* Aim to protect against poor hash functions by adding logic here * - logic taken from java 1.4 hashtable source */ unsigned int i = h->hashfn(k); i += ~(i << 9); i ^= ((i >> 14) | (i << 18)); /* >>> */ i += (i << 4); i ^= ((i >> 10) | (i << 22)); /* >>> */ return i; } /*****************************************************************************/ static int hashtable_expand(struct hashtable *h) { /* Double the size of the table to accomodate more entries */ struct entry **newtable; struct entry *e; struct entry **pE; unsigned int newsize, i, index; /* Check we're not hitting max capacity */ if (h->primeindex == (prime_table_length - 1)) return 0; newsize = primes[++(h->primeindex)]; newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); if (NULL != newtable) { memset(newtable, 0, newsize * sizeof(struct entry *)); /* This algorithm is not 'stable'. ie. it reverses the list * when it transfers entries between the tables */ for (i = 0; i < h->tablelength; i++) { while (NULL != (e = h->table[i])) { h->table[i] = e->next; index = indexFor(newsize,e->h); e->next = newtable[index]; newtable[index] = e; } } free(h->table); h->table = newtable; } /* Plan B: realloc instead */ else { newtable = (struct entry **) realloc(h->table, newsize * sizeof(struct entry *)); if (NULL == newtable) { (h->primeindex)--; return 0; } h->table = newtable; memset(newtable[h->tablelength], 0, newsize - h->tablelength); for (i = 0; i < h->tablelength; i++) { for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { index = indexFor(newsize,e->h); if (index == i) { pE = &(e->next); } else { *pE = e->next; e->next = newtable[index]; newtable[index] = e; } } } } h->tablelength = newsize; h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); return -1; } /*****************************************************************************/ unsigned int hashtable_count(struct hashtable *h) { return h->entrycount; } /*****************************************************************************/ int hashtable_insert(struct hashtable *h, void *k, void *v) { /* This method allows duplicate keys - but they shouldn't be used */ unsigned int index; struct entry *e; if (++(h->entrycount) > h->loadlimit) { /* Ignore the return value. If expand fails, we should * still try cramming just this value into the existing table * -- we may not have memory for a larger table, but one more * element may be ok. Next time we insert, we'll try expanding again.*/ hashtable_expand(h); } e = (struct entry *)malloc(sizeof(struct entry)); if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ e->h = hash(h,k); index = indexFor(h->tablelength,e->h); e->k = k; e->v = v; e->next = h->table[index]; h->table[index] = e; return -1; } /*****************************************************************************/ void * /* returns value associated with key */ hashtable_search(struct hashtable *h, void *k) { struct entry *e; unsigned int hashvalue, index; hashvalue = hash(h,k); index = indexFor(h->tablelength,hashvalue); e = h->table[index]; while (NULL != e) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; e = e->next; } return NULL; } /*****************************************************************************/ void * /* returns value associated with key */ hashtable_remove(struct hashtable *h, void *k) { /* TODO: consider compacting the table when the load factor drops enough, * or provide a 'compact' method. */ struct entry *e; struct entry **pE; void *v; unsigned int hashvalue, index; hashvalue = hash(h,k); index = indexFor(h->tablelength,hash(h,k)); pE = &(h->table[index]); e = *pE; while (NULL != e) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { *pE = e->next; h->entrycount--; v = e->v; freekey(e->k); free(e); return v; } pE = &(e->next); e = e->next; } return NULL; } /*****************************************************************************/ /* destroy */ void hashtable_destroy(struct hashtable *h, int free_values) { unsigned int i; struct entry *e, *f; struct entry **table = h->table; if (free_values) { for (i = 0; i < h->tablelength; i++) { e = table[i]; while (NULL != e) { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } } } else { for (i = 0; i < h->tablelength; i++) { e = table[i]; while (NULL != e) { f = e; e = e->next; freekey(f->k); free(f); } } } free(h->table); free(h); } /* * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ mtd-utils-1.5.0/mkfs.ubifs/hashtable/hashtable.h000066400000000000000000000163161175167361300215660ustar00rootroot00000000000000/* Copyright (C) 2002 Christopher Clark */ #ifndef __HASHTABLE_CWC22_H__ #define __HASHTABLE_CWC22_H__ struct hashtable; /* Example of use: * * struct hashtable *h; * struct some_key *k; * struct some_value *v; * * static unsigned int hash_from_key_fn( void *k ); * static int keys_equal_fn ( void *key1, void *key2 ); * * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); * k = (struct some_key *) malloc(sizeof(struct some_key)); * v = (struct some_value *) malloc(sizeof(struct some_value)); * * (initialise k and v to suitable values) * * if (! hashtable_insert(h,k,v) ) * { exit(-1); } * * if (NULL == (found = hashtable_search(h,k) )) * { printf("not found!"); } * * if (NULL == (found = hashtable_remove(h,k) )) * { printf("Not found\n"); } * */ /* Macros may be used to define type-safe(r) hashtable access functions, with * methods specialized to take known key and value types as parameters. * * Example: * * Insert this at the start of your file: * * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); * * This defines the functions 'insert_some', 'search_some' and 'remove_some'. * These operate just like hashtable_insert etc., with the same parameters, * but their function signatures have 'struct some_key *' rather than * 'void *', and hence can generate compile time errors if your program is * supplying incorrect data as a key (and similarly for value). * * Note that the hash and key equality functions passed to create_hashtable * still take 'void *' parameters instead of 'some key *'. This shouldn't be * a difficult issue as they're only defined and passed once, and the other * functions will ensure that only valid keys are supplied to them. * * The cost for this checking is increased code size and runtime overhead * - if performance is important, it may be worth switching back to the * unsafe methods once your program has been debugged with the safe methods. * This just requires switching to some simple alternative defines - eg: * #define insert_some hashtable_insert * */ /***************************************************************************** * create_hashtable * @name create_hashtable * @param minsize minimum initial size of hashtable * @param hashfunction function for hashing keys * @param key_eq_fn function for determining key equality * @return newly created hashtable or NULL on failure */ struct hashtable * create_hashtable(unsigned int minsize, unsigned int (*hashfunction) (void*), int (*key_eq_fn) (void*,void*)); /***************************************************************************** * hashtable_insert * @name hashtable_insert * @param h the hashtable to insert into * @param k the key - hashtable claims ownership and will free on removal * @param v the value - does not claim ownership * @return non-zero for successful insertion * * This function will cause the table to expand if the insertion would take * the ratio of entries to table size over the maximum load factor. * * This function does not check for repeated insertions with a duplicate key. * The value returned when using a duplicate key is undefined -- when * the hashtable changes size, the order of retrieval of duplicate key * entries is reversed. * If in doubt, remove before insert. */ int hashtable_insert(struct hashtable *h, void *k, void *v); #define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ int fnname (struct hashtable *h, keytype *k, valuetype *v) \ { \ return hashtable_insert(h,k,v); \ } /***************************************************************************** * hashtable_search * @name hashtable_search * @param h the hashtable to search * @param k the key to search for - does not claim ownership * @return the value associated with the key, or NULL if none found */ void * hashtable_search(struct hashtable *h, void *k); #define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ valuetype * fnname (struct hashtable *h, keytype *k) \ { \ return (valuetype *) (hashtable_search(h,k)); \ } /***************************************************************************** * hashtable_remove * @name hashtable_remove * @param h the hashtable to remove the item from * @param k the key to search for - does not claim ownership * @return the value associated with the key, or NULL if none found */ void * /* returns value */ hashtable_remove(struct hashtable *h, void *k); #define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ valuetype * fnname (struct hashtable *h, keytype *k) \ { \ return (valuetype *) (hashtable_remove(h,k)); \ } /***************************************************************************** * hashtable_count * @name hashtable_count * @param h the hashtable * @return the number of items stored in the hashtable */ unsigned int hashtable_count(struct hashtable *h); /***************************************************************************** * hashtable_destroy * @name hashtable_destroy * @param h the hashtable * @param free_values whether to call 'free' on the remaining values */ void hashtable_destroy(struct hashtable *h, int free_values); #endif /* __HASHTABLE_CWC22_H__ */ /* * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ mtd-utils-1.5.0/mkfs.ubifs/hashtable/hashtable_itr.c000066400000000000000000000132271175167361300224350ustar00rootroot00000000000000/* Copyright (C) 2002, 2004 Christopher Clark */ #include "hashtable.h" #include "hashtable_private.h" #include "hashtable_itr.h" #include /* defines NULL */ /*****************************************************************************/ /* hashtable_iterator - iterator constructor */ struct hashtable_itr * hashtable_iterator(struct hashtable *h) { unsigned int i, tablelength; struct hashtable_itr *itr = (struct hashtable_itr *) malloc(sizeof(struct hashtable_itr)); if (NULL == itr) return NULL; itr->h = h; itr->e = NULL; itr->parent = NULL; tablelength = h->tablelength; itr->index = tablelength; if (0 == h->entrycount) return itr; for (i = 0; i < tablelength; i++) { if (NULL != h->table[i]) { itr->e = h->table[i]; itr->index = i; break; } } return itr; } /*****************************************************************************/ /* key - return the key of the (key,value) pair at the current position */ /* value - return the value of the (key,value) pair at the current position */ void * hashtable_iterator_key(struct hashtable_itr *i) { return i->e->k; } void * hashtable_iterator_value(struct hashtable_itr *i) { return i->e->v; } /*****************************************************************************/ /* advance - advance the iterator to the next element * returns zero if advanced to end of table */ int hashtable_iterator_advance(struct hashtable_itr *itr) { unsigned int j,tablelength; struct entry **table; struct entry *next; if (NULL == itr->e) return 0; /* stupidity check */ next = itr->e->next; if (NULL != next) { itr->parent = itr->e; itr->e = next; return -1; } tablelength = itr->h->tablelength; itr->parent = NULL; if (tablelength <= (j = ++(itr->index))) { itr->e = NULL; return 0; } table = itr->h->table; while (NULL == (next = table[j])) { if (++j >= tablelength) { itr->index = tablelength; itr->e = NULL; return 0; } } itr->index = j; itr->e = next; return -1; } /*****************************************************************************/ /* remove - remove the entry at the current iterator position * and advance the iterator, if there is a successive * element. * If you want the value, read it before you remove: * beware memory leaks if you don't. * Returns zero if end of iteration. */ int hashtable_iterator_remove(struct hashtable_itr *itr) { struct entry *remember_e, *remember_parent; int ret; /* Do the removal */ if (NULL == (itr->parent)) { /* element is head of a chain */ itr->h->table[itr->index] = itr->e->next; } else { /* element is mid-chain */ itr->parent->next = itr->e->next; } /* itr->e is now outside the hashtable */ remember_e = itr->e; itr->h->entrycount--; freekey(remember_e->k); /* Advance the iterator, correcting the parent */ remember_parent = itr->parent; ret = hashtable_iterator_advance(itr); if (itr->parent == remember_e) { itr->parent = remember_parent; } free(remember_e); return ret; } /*****************************************************************************/ int /* returns zero if not found */ hashtable_iterator_search(struct hashtable_itr *itr, struct hashtable *h, void *k) { struct entry *e, *parent; unsigned int hashvalue, index; hashvalue = hash(h,k); index = indexFor(h->tablelength,hashvalue); e = h->table[index]; parent = NULL; while (NULL != e) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { itr->index = index; itr->e = e; itr->parent = parent; itr->h = h; return -1; } parent = e; e = e->next; } return 0; } /* * Copyright (c) 2002, 2004, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ mtd-utils-1.5.0/mkfs.ubifs/hashtable/hashtable_itr.h000066400000000000000000000077661175167361300224550ustar00rootroot00000000000000/* Copyright (C) 2002, 2004 Christopher Clark */ #ifndef __HASHTABLE_ITR_CWC22__ #define __HASHTABLE_ITR_CWC22__ #include "hashtable.h" #include "hashtable_private.h" /* needed to enable inlining */ /*****************************************************************************/ /* This struct is only concrete here to allow the inlining of two of the * accessor functions. */ struct hashtable_itr { struct hashtable *h; struct entry *e; struct entry *parent; unsigned int index; }; /*****************************************************************************/ /* hashtable_iterator */ struct hashtable_itr * hashtable_iterator(struct hashtable *h); /*****************************************************************************/ /* hashtable_iterator_key * - return the value of the (key,value) pair at the current position */ extern inline void * hashtable_iterator_key(struct hashtable_itr *i) { return i->e->k; } /*****************************************************************************/ /* value - return the value of the (key,value) pair at the current position */ extern inline void * hashtable_iterator_value(struct hashtable_itr *i) { return i->e->v; } /*****************************************************************************/ /* advance - advance the iterator to the next element * returns zero if advanced to end of table */ int hashtable_iterator_advance(struct hashtable_itr *itr); /*****************************************************************************/ /* remove - remove current element and advance the iterator to the next element * NB: if you need the value to free it, read it before * removing. ie: beware memory leaks! * returns zero if advanced to end of table */ int hashtable_iterator_remove(struct hashtable_itr *itr); /*****************************************************************************/ /* search - overwrite the supplied iterator, to point to the entry * matching the supplied key. h points to the hashtable to be searched. * returns zero if not found. */ int hashtable_iterator_search(struct hashtable_itr *itr, struct hashtable *h, void *k); #define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ { \ return (hashtable_iterator_search(i,h,k)); \ } #endif /* __HASHTABLE_ITR_CWC22__*/ /* * Copyright (c) 2002, 2004, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ mtd-utils-1.5.0/mkfs.ubifs/hashtable/hashtable_private.h000066400000000000000000000056131175167361300233160ustar00rootroot00000000000000/* Copyright (C) 2002, 2004 Christopher Clark */ #ifndef __HASHTABLE_PRIVATE_CWC22_H__ #define __HASHTABLE_PRIVATE_CWC22_H__ #include "hashtable.h" /*****************************************************************************/ struct entry { void *k, *v; unsigned int h; struct entry *next; }; struct hashtable { unsigned int tablelength; struct entry **table; unsigned int entrycount; unsigned int loadlimit; unsigned int primeindex; unsigned int (*hashfn) (void *k); int (*eqfn) (void *k1, void *k2); }; /*****************************************************************************/ unsigned int hash(struct hashtable *h, void *k); /*****************************************************************************/ /* indexFor */ static inline unsigned int indexFor(unsigned int tablelength, unsigned int hashvalue) { return (hashvalue % tablelength); }; /* Only works if tablelength == 2^N */ /*static inline unsigned int indexFor(unsigned int tablelength, unsigned int hashvalue) { return (hashvalue & (tablelength - 1u)); } */ /*****************************************************************************/ #define freekey(X) free(X) /*define freekey(X) ; */ /*****************************************************************************/ #endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ /* * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ mtd-utils-1.5.0/mkfs.ubifs/key.h000066400000000000000000000117261175167361300164700ustar00rootroot00000000000000/* * This file is part of UBIFS. * * Copyright (C) 2006-2008 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter */ /* * This header contains various key-related definitions and helper function. * UBIFS allows several key schemes, so we access key fields only via these * helpers. At the moment only one key scheme is supported. * * Simple key scheme * ~~~~~~~~~~~~~~~~~ * * Keys are 64-bits long. First 32-bits are inode number (parent inode number * in case of direntry key). Next 3 bits are node type. The last 29 bits are * 4KiB offset in case of inode node, and direntry hash in case of a direntry * node. We use "r5" hash borrowed from reiserfs. */ #ifndef __UBIFS_KEY_H__ #define __UBIFS_KEY_H__ /** * key_mask_hash - mask a valid hash value. * @val: value to be masked * * We use hash values as offset in directories, so values %0 and %1 are * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This * function makes sure the reserved values are not used. */ static inline uint32_t key_mask_hash(uint32_t hash) { hash &= UBIFS_S_KEY_HASH_MASK; if (unlikely(hash <= 2)) hash += 3; return hash; } /** * key_r5_hash - R5 hash function (borrowed from reiserfs). * @s: direntry name * @len: name length */ static inline uint32_t key_r5_hash(const char *s, int len) { uint32_t a = 0; const signed char *str = (const signed char *)s; len = len; while (*str) { a += *str << 4; a += *str >> 4; a *= 11; str++; } return key_mask_hash(a); } /** * key_test_hash - testing hash function. * @str: direntry name * @len: name length */ static inline uint32_t key_test_hash(const char *str, int len) { uint32_t a = 0; len = min_t(uint32_t, len, 4); memcpy(&a, str, len); return key_mask_hash(a); } /** * ino_key_init - initialize inode key. * @c: UBIFS file-system description object * @key: key to initialize * @inum: inode number */ static inline void ino_key_init(union ubifs_key *key, ino_t inum) { key->u32[0] = inum; key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; } /** * dent_key_init - initialize directory entry key. * @c: UBIFS file-system description object * @key: key to initialize * @inum: parent inode number * @nm: direntry name and length */ static inline void dent_key_init(const struct ubifs_info *c, union ubifs_key *key, ino_t inum, const struct qstr *nm) { uint32_t hash = c->key_hash(nm->name, nm->len); ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); key->u32[0] = inum; key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); } /** * data_key_init - initialize data key. * @c: UBIFS file-system description object * @key: key to initialize * @inum: inode number * @block: block number */ static inline void data_key_init(union ubifs_key *key, ino_t inum, unsigned int block) { ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); key->u32[0] = inum; key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); } /** * key_write - transform a key from in-memory format. * @c: UBIFS file-system description object * @from: the key to transform * @to: the key to store the result */ static inline void key_write(const union ubifs_key *from, void *to) { union ubifs_key *t = to; t->j32[0] = cpu_to_le32(from->u32[0]); t->j32[1] = cpu_to_le32(from->u32[1]); memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); } /** * key_write_idx - transform a key from in-memory format for the index. * @c: UBIFS file-system description object * @from: the key to transform * @to: the key to store the result */ static inline void key_write_idx(const union ubifs_key *from, void *to) { union ubifs_key *t = to; t->j32[0] = cpu_to_le32(from->u32[0]); t->j32[1] = cpu_to_le32(from->u32[1]); } /** * keys_cmp - compare keys. * @c: UBIFS file-system description object * @key1: the first key to compare * @key2: the second key to compare * * This function compares 2 keys and returns %-1 if @key1 is less than * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. */ static inline int keys_cmp(const union ubifs_key *key1, const union ubifs_key *key2) { if (key1->u32[0] < key2->u32[0]) return -1; if (key1->u32[0] > key2->u32[0]) return 1; if (key1->u32[1] < key2->u32[1]) return -1; if (key1->u32[1] > key2->u32[1]) return 1; return 0; } #endif /* !__UBIFS_KEY_H__ */ mtd-utils-1.5.0/mkfs.ubifs/lpt.c000066400000000000000000000367421175167361300164770ustar00rootroot00000000000000/* * This file is part of UBIFS. * * Copyright (C) 2006, 2007 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Adrian Hunter * Artem Bityutskiy */ #include "mkfs.ubifs.h" /** * do_calc_lpt_geom - calculate sizes for the LPT area. * @c: the UBIFS file-system description object * * Calculate the sizes of LPT bit fields, nodes, and tree, based on the * properties of the flash and whether LPT is "big" (c->big_lpt). */ static void do_calc_lpt_geom(struct ubifs_info *c) { int n, bits, per_leb_wastage; long long sz, tot_wastage; c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; c->nnode_cnt = n; while (n > 1) { n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; c->nnode_cnt += n; } c->lpt_hght = 1; n = UBIFS_LPT_FANOUT; while (n < c->pnode_cnt) { c->lpt_hght += 1; n <<= UBIFS_LPT_FANOUT_SHIFT; } c->space_bits = fls(c->leb_size) - 3; c->lpt_lnum_bits = fls(c->lpt_lebs); c->lpt_offs_bits = fls(c->leb_size - 1); c->lpt_spc_bits = fls(c->leb_size); n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; c->pcnt_bits = fls(n - 1); c->lnum_bits = fls(c->max_leb_cnt - 1); bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + (c->big_lpt ? c->pcnt_bits : 0) + (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; c->pnode_sz = (bits + 7) / 8; bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + (c->big_lpt ? c->pcnt_bits : 0) + (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; c->nnode_sz = (bits + 7) / 8; bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + c->lpt_lebs * c->lpt_spc_bits * 2; c->ltab_sz = (bits + 7) / 8; bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + c->lnum_bits * c->lsave_cnt; c->lsave_sz = (bits + 7) / 8; /* Calculate the minimum LPT size */ c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; c->lpt_sz += c->ltab_sz; c->lpt_sz += c->lsave_sz; /* Add wastage */ sz = c->lpt_sz; per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); sz += per_leb_wastage; tot_wastage = per_leb_wastage; while (sz > c->leb_size) { sz += per_leb_wastage; sz -= c->leb_size; tot_wastage += per_leb_wastage; } tot_wastage += ALIGN(sz, c->min_io_size) - sz; c->lpt_sz += tot_wastage; } /** * calc_dflt_lpt_geom - calculate default LPT geometry. * @c: the UBIFS file-system description object * @main_lebs: number of main area LEBs is passed and returned here * @big_lpt: whether the LPT area is "big" is returned here * * The size of the LPT area depends on parameters that themselves are dependent * on the size of the LPT area. This function, successively recalculates the LPT * area geometry until the parameters and resultant geometry are consistent. * * This function returns %0 on success and a negative error code on failure. */ int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) { int i, lebs_needed; long long sz; /* Start by assuming the minimum number of LPT LEBs */ c->lpt_lebs = UBIFS_MIN_LPT_LEBS; c->main_lebs = *main_lebs - c->lpt_lebs; if (c->main_lebs <= 0) return -EINVAL; /* And assume we will use the small LPT model */ c->big_lpt = 0; /* * Calculate the geometry based on assumptions above and then see if it * makes sense */ do_calc_lpt_geom(c); /* Small LPT model must have lpt_sz < leb_size */ if (c->lpt_sz > c->leb_size) { /* Nope, so try again using big LPT model */ c->big_lpt = 1; do_calc_lpt_geom(c); } /* Now check there are enough LPT LEBs */ for (i = 0; i < 64 ; i++) { sz = c->lpt_sz * 4; /* Allow 4 times the size */ sz += c->leb_size - 1; do_div(sz, c->leb_size); lebs_needed = sz; if (lebs_needed > c->lpt_lebs) { /* Not enough LPT LEBs so try again with more */ c->lpt_lebs = lebs_needed; c->main_lebs = *main_lebs - c->lpt_lebs; if (c->main_lebs <= 0) return -EINVAL; do_calc_lpt_geom(c); continue; } if (c->ltab_sz > c->leb_size) { err_msg("LPT ltab too big"); return -EINVAL; } *main_lebs = c->main_lebs; *big_lpt = c->big_lpt; return 0; } return -EINVAL; } /** * pack_bits - pack bit fields end-to-end. * @addr: address at which to pack (passed and next address returned) * @pos: bit position at which to pack (passed and next position returned) * @val: value to pack * @nrbits: number of bits of value to pack (1-32) */ static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) { uint8_t *p = *addr; int b = *pos; if (b) { *p |= ((uint8_t)val) << b; nrbits += b; if (nrbits > 8) { *++p = (uint8_t)(val >>= (8 - b)); if (nrbits > 16) { *++p = (uint8_t)(val >>= 8); if (nrbits > 24) { *++p = (uint8_t)(val >>= 8); if (nrbits > 32) *++p = (uint8_t)(val >>= 8); } } } } else { *p = (uint8_t)val; if (nrbits > 8) { *++p = (uint8_t)(val >>= 8); if (nrbits > 16) { *++p = (uint8_t)(val >>= 8); if (nrbits > 24) *++p = (uint8_t)(val >>= 8); } } } b = nrbits & 7; if (b == 0) p++; *addr = p; *pos = b; } /** * pack_pnode - pack all the bit fields of a pnode. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @pnode: pnode to pack */ static void pack_pnode(struct ubifs_info *c, void *buf, struct ubifs_pnode *pnode) { uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0; uint16_t crc; pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); if (c->big_lpt) pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, c->space_bits); pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, c->space_bits); if (pnode->lprops[i].flags & LPROPS_INDEX) pack_bits(&addr, &pos, 1, 1); else pack_bits(&addr, &pos, 0, 1); } crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->pnode_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * pack_nnode - pack all the bit fields of a nnode. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @nnode: nnode to pack */ static void pack_nnode(struct ubifs_info *c, void *buf, struct ubifs_nnode *nnode) { uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0; uint16_t crc; pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); if (c->big_lpt) pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); for (i = 0; i < UBIFS_LPT_FANOUT; i++) { int lnum = nnode->nbranch[i].lnum; if (lnum == 0) lnum = c->lpt_last + 1; pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); pack_bits(&addr, &pos, nnode->nbranch[i].offs, c->lpt_offs_bits); } crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->nnode_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * pack_ltab - pack the LPT's own lprops table. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @ltab: LPT's own lprops table to pack */ static void pack_ltab(struct ubifs_info *c, void *buf, struct ubifs_lpt_lprops *ltab) { uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0; uint16_t crc; pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); for (i = 0; i < c->lpt_lebs; i++) { pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); } crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->ltab_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * pack_lsave - pack the LPT's save table. * @c: UBIFS file-system description object * @buf: buffer into which to pack * @lsave: LPT's save table to pack */ static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) { uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0; uint16_t crc; pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); for (i = 0; i < c->lsave_cnt; i++) pack_bits(&addr, &pos, lsave[i], c->lnum_bits); crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, c->lsave_sz - UBIFS_LPT_CRC_BYTES); addr = buf; pos = 0; pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); } /** * set_ltab - set LPT LEB properties. * @c: UBIFS file-system description object * @lnum: LEB number * @free: amount of free space * @dirty: amount of dirty space */ static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) { dbg_msg(3, "LEB %d free %d dirty %d to %d %d", lnum, c->ltab[lnum - c->lpt_first].free, c->ltab[lnum - c->lpt_first].dirty, free, dirty); c->ltab[lnum - c->lpt_first].free = free; c->ltab[lnum - c->lpt_first].dirty = dirty; } /** * calc_nnode_num - calculate nnode number. * @row: the row in the tree (root is zero) * @col: the column in the row (leftmost is zero) * * The nnode number is a number that uniquely identifies a nnode and can be used * easily to traverse the tree from the root to that nnode. * * This function calculates and returns the nnode number for the nnode at @row * and @col. */ static int calc_nnode_num(int row, int col) { int num, bits; num = 1; while (row--) { bits = (col & (UBIFS_LPT_FANOUT - 1)); col >>= UBIFS_LPT_FANOUT_SHIFT; num <<= UBIFS_LPT_FANOUT_SHIFT; num |= bits; } return num; } /** * create_lpt - create LPT. * @c: UBIFS file-system description object * * This function returns %0 on success and a negative error code on failure. */ int create_lpt(struct ubifs_info *c) { int lnum, err = 0, i, j, cnt, len, alen, row; int blnum, boffs, bsz, bcnt; struct ubifs_pnode *pnode = NULL; struct ubifs_nnode *nnode = NULL; void *buf = NULL, *p; int *lsave = NULL; pnode = malloc(sizeof(struct ubifs_pnode)); nnode = malloc(sizeof(struct ubifs_nnode)); buf = malloc(c->leb_size); lsave = malloc(sizeof(int) * c->lsave_cnt); if (!pnode || !nnode || !buf || !lsave) { err = -ENOMEM; goto out; } memset(pnode, 0 , sizeof(struct ubifs_pnode)); memset(nnode, 0 , sizeof(struct ubifs_pnode)); c->lscan_lnum = c->main_first; lnum = c->lpt_first; p = buf; len = 0; /* Number of leaf nodes (pnodes) */ cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; //printf("pnode_cnt=%d\n",cnt); /* * To calculate the internal node branches, we keep information about * the level below. */ blnum = lnum; /* LEB number of level below */ boffs = 0; /* Offset of level below */ bcnt = cnt; /* Number of nodes in level below */ bsz = c->pnode_sz; /* Size of nodes in level below */ /* Add pnodes */ for (i = 0; i < cnt; i++) { if (len + c->pnode_sz > c->leb_size) { alen = ALIGN(len, c->min_io_size); set_ltab(c, lnum, c->leb_size - alen, alen - len); memset(p, 0xff, alen - len); err = write_leb(lnum++, alen, buf, UBI_SHORTTERM); if (err) goto out; p = buf; len = 0; } /* Fill in the pnode */ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; if (k < c->main_lebs) pnode->lprops[j] = c->lpt[k]; else { pnode->lprops[j].free = c->leb_size; pnode->lprops[j].dirty = 0; pnode->lprops[j].flags = 0; } } pack_pnode(c, p, pnode); p += c->pnode_sz; len += c->pnode_sz; /* * pnodes are simply numbered left to right starting at zero, * which means the pnode number can be used easily to traverse * down the tree to the corresponding pnode. */ pnode->num += 1; } row = c->lpt_hght - 1; /* Add all nnodes, one level at a time */ while (1) { /* Number of internal nodes (nnodes) at next level */ cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; if (cnt == 0) cnt = 1; for (i = 0; i < cnt; i++) { if (len + c->nnode_sz > c->leb_size) { alen = ALIGN(len, c->min_io_size); set_ltab(c, lnum, c->leb_size - alen, alen - len); memset(p, 0xff, alen - len); err = write_leb(lnum++, alen, buf, UBI_SHORTTERM); if (err) goto out; p = buf; len = 0; } /* The root is on row zero */ if (row == 0) { c->lpt_lnum = lnum; c->lpt_offs = len; } /* Set branches to the level below */ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { if (bcnt) { if (boffs + bsz > c->leb_size) { blnum += 1; boffs = 0; } nnode->nbranch[j].lnum = blnum; nnode->nbranch[j].offs = boffs; boffs += bsz; bcnt--; } else { nnode->nbranch[j].lnum = 0; nnode->nbranch[j].offs = 0; } } nnode->num = calc_nnode_num(row, i); pack_nnode(c, p, nnode); p += c->nnode_sz; len += c->nnode_sz; } /* Row zero is the top row */ if (row == 0) break; /* Update the information about the level below */ bcnt = cnt; bsz = c->nnode_sz; row -= 1; } if (c->big_lpt) { /* Need to add LPT's save table */ if (len + c->lsave_sz > c->leb_size) { alen = ALIGN(len, c->min_io_size); set_ltab(c, lnum, c->leb_size - alen, alen - len); memset(p, 0xff, alen - len); err = write_leb(lnum++, alen, buf, UBI_SHORTTERM); if (err) goto out; p = buf; len = 0; } c->lsave_lnum = lnum; c->lsave_offs = len; for (i = 0; i < c->lsave_cnt; i++) lsave[i] = c->main_first + i; pack_lsave(c, p, lsave); p += c->lsave_sz; len += c->lsave_sz; } /* Need to add LPT's own LEB properties table */ if (len + c->ltab_sz > c->leb_size) { alen = ALIGN(len, c->min_io_size); set_ltab(c, lnum, c->leb_size - alen, alen - len); memset(p, 0xff, alen - len); err = write_leb(lnum++, alen, buf, UBI_SHORTTERM); if (err) goto out; p = buf; len = 0; } c->ltab_lnum = lnum; c->ltab_offs = len; /* Update ltab before packing it */ len += c->ltab_sz; alen = ALIGN(len, c->min_io_size); set_ltab(c, lnum, c->leb_size - alen, alen - len); pack_ltab(c, p, c->ltab); p += c->ltab_sz; /* Write remaining buffer */ memset(p, 0xff, alen - len); err = write_leb(lnum, alen, buf, UBI_SHORTTERM); if (err) goto out; c->nhead_lnum = lnum; c->nhead_offs = ALIGN(len, c->min_io_size); dbg_msg(1, "lpt_sz: %lld", c->lpt_sz); dbg_msg(1, "space_bits: %d", c->space_bits); dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); dbg_msg(1, "lnum_bits: %d", c->lnum_bits); dbg_msg(1, "pnode_sz: %d", c->pnode_sz); dbg_msg(1, "nnode_sz: %d", c->nnode_sz); dbg_msg(1, "ltab_sz: %d", c->ltab_sz); dbg_msg(1, "lsave_sz: %d", c->lsave_sz); dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); dbg_msg(1, "lpt_hght: %d", c->lpt_hght); dbg_msg(1, "big_lpt: %d", c->big_lpt); dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); if (c->big_lpt) dbg_msg(1, "LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); out: free(lsave); free(buf); free(nnode); free(pnode); return err; } mtd-utils-1.5.0/mkfs.ubifs/lpt.h000066400000000000000000000017241175167361300164740ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation. * Copyright (C) 2008 University of Szeged, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy * Adrian Hunter */ #ifndef __UBIFS_LPT_H__ #define __UBIFS_LPT_H__ int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); int create_lpt(struct ubifs_info *c); #endif mtd-utils-1.5.0/mkfs.ubifs/mkfs.ubifs.c000066400000000000000000001721161175167361300177430ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation. * Copyright (C) 2008 University of Szeged, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Adrian Hunter * Artem Bityutskiy * Zoltan Sogor */ #define PROGRAM_NAME "mkfs.ubifs" #include "mkfs.ubifs.h" #include #include "common.h" /* Size (prime number) of hash table for link counting */ #define HASH_TABLE_SIZE 10099 /* The node buffer must allow for worst case compression */ #define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + \ UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) /* Default time granularity in nanoseconds */ #define DEFAULT_TIME_GRAN 1000000000 /** * struct idx_entry - index entry. * @next: next index entry (NULL at end of list) * @prev: previous index entry (NULL at beginning of list) * @key: key * @name: directory entry name used for sorting colliding keys by name * @lnum: LEB number * @offs: offset * @len: length * * The index is recorded as a linked list which is sorted and used to create * the bottom level of the on-flash index tree. The remaining levels of the * index tree are each built from the level below. */ struct idx_entry { struct idx_entry *next; struct idx_entry *prev; union ubifs_key key; char *name; int lnum; int offs; int len; }; /** * struct inum_mapping - inode number mapping for link counting. * @next: next inum_mapping (NULL at end of list) * @prev: previous inum_mapping (NULL at beginning of list) * @dev: source device on which the source inode number resides * @inum: source inode number of the file * @use_inum: target inode number of the file * @use_nlink: number of links * @path_name: a path name of the file * @st: struct stat object containing inode attributes which have to be used * when the inode is being created (actually only UID, GID, access * mode, major and minor device numbers) * * If a file has more than one hard link, then the number of hard links that * exist in the source directory hierarchy must be counted to exclude the * possibility that the file is linked from outside the source directory * hierarchy. * * The inum_mappings are stored in a hash_table of linked lists. */ struct inum_mapping { struct inum_mapping *next; struct inum_mapping *prev; dev_t dev; ino_t inum; ino_t use_inum; unsigned int use_nlink; char *path_name; struct stat st; }; /* * Because we copy functions from the kernel, we use a subset of the UBIFS * file-system description object struct ubifs_info. */ struct ubifs_info info_; static struct ubifs_info *c = &info_; static libubi_t ubi; /* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ int debug_level; int verbose; static char *root; static int root_len; static struct stat root_st; static char *output; static int out_fd; static int out_ubi; static int squash_owner; /* The 'head' (position) which nodes are written */ static int head_lnum; static int head_offs; static int head_flags; /* The index list */ static struct idx_entry *idx_list_first; static struct idx_entry *idx_list_last; static size_t idx_cnt; /* Global buffers */ static void *leb_buf; static void *node_buf; static void *block_buf; /* Hash table for inode link counting */ static struct inum_mapping **hash_table; /* Inode creation sequence number */ static unsigned long long creat_sqnum; static const char *optstring = "d:r:m:o:D:h?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq"; static const struct option longopts[] = { {"root", 1, NULL, 'r'}, {"min-io-size", 1, NULL, 'm'}, {"leb-size", 1, NULL, 'e'}, {"max-leb-cnt", 1, NULL, 'c'}, {"output", 1, NULL, 'o'}, {"devtable", 1, NULL, 'D'}, {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, 'V'}, {"debug-level", 1, NULL, 'g'}, {"jrn-size", 1, NULL, 'j'}, {"reserved", 1, NULL, 'R'}, {"compr", 1, NULL, 'x'}, {"favor-percent", 1, NULL, 'X'}, {"fanout", 1, NULL, 'f'}, {"space-fixup", 0, NULL, 'F'}, {"keyhash", 1, NULL, 'k'}, {"log-lebs", 1, NULL, 'l'}, {"orph-lebs", 1, NULL, 'p'}, {"squash-uids" , 0, NULL, 'U'}, {NULL, 0, NULL, 0} }; static const char *helptext = "Usage: mkfs.ubifs [OPTIONS] target\n" "Make a UBIFS file system image from an existing directory tree\n\n" "Examples:\n" "Build file system from directory /opt/img, writting the result in the ubifs.img file\n" "\tmkfs.ubifs -m 512 -e 128KiB -c 100 -r /opt/img ubifs.img\n" "The same, but writting directly to an UBI volume\n" "\tmkfs.ubifs -r /opt/img /dev/ubi0_0\n" "Creating an empty UBIFS filesystem on an UBI volume\n" "\tmkfs.ubifs /dev/ubi0_0\n\n" "Options:\n" "-r, -d, --root=DIR build file system from directory DIR\n" "-m, --min-io-size=SIZE minimum I/O unit size\n" "-e, --leb-size=SIZE logical erase block size\n" "-c, --max-leb-cnt=COUNT maximum logical erase block count\n" "-o, --output=FILE output to FILE\n" "-j, --jrn-size=SIZE journal size\n" "-R, --reserved=SIZE how much space should be reserved for the super-user\n" "-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" " \"none\" (default: \"lzo\")\n" "-X, --favor-percent may only be used with favor LZO compression and defines\n" " how many percent better zlib should compress to make\n" " mkfs.ubifs use zlib instead of LZO (default 20%)\n" "-f, --fanout=NUM fanout NUM (default: 8)\n" "-F, --space-fixup file-system free space has to be fixed up on first mount\n" " (requires kernel version 3.0 or greater)\n" "-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" "-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" "-D, --devtable=FILE use device table FILE\n" "-U, --squash-uids squash owners making all files owned by root\n" "-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" " debugging)\n" "-v, --verbose verbose operation\n" "-V, --version display version information\n" "-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" " 2 - files, 3 - more details)\n" "-h, --help display this help text\n\n" "Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" "Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" "If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" "for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" "really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" "compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" "compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" "or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" "\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" "default 20%.\n\n" "The -F parameter is used to set the \"fix up free space\" flag in the superblock,\n" "which forces UBIFS to \"fixup\" all the free space which it is going to use. This\n" "option is useful to work-around the problem of double free space programming: if the\n" "flasher program which flashes the UBI image is unable to skip NAND pages containing\n" "only 0xFF bytes, the effect is that some NAND pages are written to twice - first time\n" "when flashing the image and the second time when UBIFS is mounted and writes useful\n" "data there. A proper UBI-aware flasher should skip such NAND pages, though. Note, this\n" "flag may make the first mount very slow, because the \"free space fixup\" procedure\n" "takes time. This feature is supported by the Linux kernel starting from version 3.0.\n"; /** * make_path - make a path name from a directory and a name. * @dir: directory path name * @name: name */ static char *make_path(const char *dir, const char *name) { char *s; s = malloc(strlen(dir) + strlen(name) + 2); if (!s) return NULL; strcpy(s, dir); if (dir[strlen(dir) - 1] != '/') strcat(s, "/"); strcat(s, name); return s; } /** * same_dir - determine if two file descriptors refer to the same directory. * @fd1: file descriptor 1 * @fd2: file descriptor 2 */ static int same_dir(int fd1, int fd2) { struct stat stat1, stat2; if (fstat(fd1, &stat1) == -1) return -1; if (fstat(fd2, &stat2) == -1) return -1; return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino; } /** * do_openat - open a file in a directory. * @fd: file descriptor of open directory * @path: path relative to directory * @flags: open flags * * This function is provided because the library function openat is sometimes * not available. */ static int do_openat(int fd, const char *path, int flags) { int ret; char *cwd; cwd = getcwd(NULL, 0); if (!cwd) return -1; ret = fchdir(fd); if (ret != -1) ret = open(path, flags); if (chdir(cwd) && !ret) ret = -1; free(cwd); return ret; } /** * in_path - determine if a file is beneath a directory. * @dir_name: directory path name * @file_name: file path name */ static int in_path(const char *dir_name, const char *file_name) { char *fn = strdup(file_name); char *dn; int fd1, fd2, fd3, ret = -1, top_fd; if (!fn) return -1; top_fd = open("/", O_RDONLY); if (top_fd != -1) { dn = dirname(fn); fd1 = open(dir_name, O_RDONLY); if (fd1 != -1) { fd2 = open(dn, O_RDONLY); if (fd2 != -1) { while (1) { int same; same = same_dir(fd1, fd2); if (same) { ret = same; break; } if (same_dir(fd2, top_fd)) { ret = 0; break; } fd3 = do_openat(fd2, "..", O_RDONLY); if (fd3 == -1) break; close(fd2); fd2 = fd3; } close(fd2); } close(fd1); } close(top_fd); } free(fn); return ret; } /** * calc_min_log_lebs - calculate the minimum number of log LEBs needed. * @max_bud_bytes: journal size (buds only) */ static int calc_min_log_lebs(unsigned long long max_bud_bytes) { int buds, log_lebs; unsigned long long log_size; buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); log_size *= buds; log_size += ALIGN(UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), c->min_io_size); log_lebs = (log_size + c->leb_size - 1) / c->leb_size; log_lebs += 1; return log_lebs; } /** * add_space_overhead - add UBIFS overhead. * @size: flash space which should be visible to the user * * UBIFS has overhead, and if we need to reserve @size bytes for the user data, * we have to reserve more flash space, to compensate the overhead. This * function calculates and returns the amount of physical flash space which * should be reserved to provide @size bytes for the user. */ static long long add_space_overhead(long long size) { int divisor, factor, f, max_idx_node_sz; /* * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS * function does. */ max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); f = c->fanout > 3 ? c->fanout >> 1 : 2; divisor = UBIFS_BLOCK_SIZE; factor = UBIFS_MAX_DATA_NODE_SZ; factor += (max_idx_node_sz * 3) / (f - 1); size *= factor; return size / divisor; } static int validate_options(void) { int tmp; if (!output) return err_msg("no output file or UBI volume specified"); if (root && in_path(root, output)) return err_msg("output file cannot be in the UBIFS root " "directory"); if (!is_power_of_2(c->min_io_size)) return err_msg("min. I/O unit size should be power of 2"); if (c->leb_size < c->min_io_size) return err_msg("min. I/O unit cannot be larger than LEB size"); if (c->leb_size < UBIFS_MIN_LEB_SZ) return err_msg("too small LEB size %d, minimum is %d", c->min_io_size, UBIFS_MIN_LEB_SZ); if (c->leb_size % c->min_io_size) return err_msg("LEB should be multiple of min. I/O units"); if (c->leb_size % 8) return err_msg("LEB size has to be multiple of 8"); if (c->leb_size > UBIFS_MAX_LEB_SZ) return err_msg("too large LEB size %d", c->leb_size); if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) return err_msg("too low max. count of LEBs, minimum is %d", UBIFS_MIN_LEB_CNT); if (c->fanout < UBIFS_MIN_FANOUT) return err_msg("too low fanout, minimum is %d", UBIFS_MIN_FANOUT); tmp = c->leb_size - UBIFS_IDX_NODE_SZ; tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; if (c->fanout > tmp) return err_msg("too high fanout, maximum is %d", tmp); if (c->log_lebs < UBIFS_MIN_LOG_LEBS) return err_msg("too few log LEBs, minimum is %d", UBIFS_MIN_LOG_LEBS); if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) return err_msg("too many log LEBs, maximum is %d", c->max_leb_cnt - UBIFS_MIN_LEB_CNT); if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) return err_msg("too few orphan LEBs, minimum is %d", UBIFS_MIN_ORPH_LEBS); if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) return err_msg("too many orphan LEBs, maximum is %d", c->max_leb_cnt - UBIFS_MIN_LEB_CNT); tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; tmp += c->orph_lebs + 4; if (tmp > c->max_leb_cnt) return err_msg("too low max. count of LEBs, expected at " "least %d", tmp); tmp = calc_min_log_lebs(c->max_bud_bytes); if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) return err_msg("too few log LEBs, expected at least %d", tmp); if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) return err_msg("too much reserved space %lld", c->rp_size); return 0; } /** * get_multiplier - convert size specifier to an integer multiplier. * @str: the size specifier string * * This function parses the @str size specifier, which may be one of * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive * size multiplier in case of success and %-1 in case of failure. */ static int get_multiplier(const char *str) { if (!str) return 1; /* Remove spaces before the specifier */ while (*str == ' ' || *str == '\t') str += 1; if (!strcmp(str, "KiB")) return 1024; if (!strcmp(str, "MiB")) return 1024 * 1024; if (!strcmp(str, "GiB")) return 1024 * 1024 * 1024; return -1; } /** * get_bytes - convert a string containing amount of bytes into an * integer. * @str: string to convert * * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size * specifiers. Returns positive amount of bytes in case of success and %-1 in * case of failure. */ static long long get_bytes(const char *str) { char *endp; long long bytes = strtoull(str, &endp, 0); if (endp == str || bytes < 0) return err_msg("incorrect amount of bytes: \"%s\"", str); if (*endp != '\0') { int mult = get_multiplier(endp); if (mult == -1) return err_msg("bad size specifier: \"%s\" - " "should be 'KiB', 'MiB' or 'GiB'", endp); bytes *= mult; } return bytes; } /** * open_ubi - open the UBI volume. * @node: name of the UBI volume character device to fetch information about * * Returns %0 in case of success and %-1 in case of failure */ static int open_ubi(const char *node) { struct stat st; if (stat(node, &st) || !S_ISCHR(st.st_mode)) return -1; ubi = libubi_open(); if (!ubi) return -1; if (ubi_get_vol_info(ubi, node, &c->vi)) return -1; if (ubi_get_dev_info1(ubi, c->vi.dev_num, &c->di)) return -1; return 0; } static int get_options(int argc, char**argv) { int opt, i; const char *tbl_file = NULL; struct stat st; char *endp; c->fanout = 8; c->orph_lebs = 1; c->key_hash = key_r5_hash; c->key_len = UBIFS_SK_LEN; c->default_compr = UBIFS_COMPR_LZO; c->favor_percent = 20; c->lsave_cnt = 256; c->leb_size = -1; c->min_io_size = -1; c->max_leb_cnt = -1; c->max_bud_bytes = -1; c->log_lebs = -1; while (1) { opt = getopt_long(argc, argv, optstring, longopts, &i); if (opt == -1) break; switch (opt) { case 'r': case 'd': root_len = strlen(optarg); root = malloc(root_len + 2); if (!root) return err_msg("cannot allocate memory"); /* * The further code expects '/' at the end of the root * UBIFS directory on the host. */ memcpy(root, optarg, root_len); if (root[root_len - 1] != '/') root[root_len++] = '/'; root[root_len] = 0; /* Make sure the root directory exists */ if (stat(root, &st)) return sys_err_msg("bad root directory '%s'", root); break; case 'm': c->min_io_size = get_bytes(optarg); if (c->min_io_size <= 0) return err_msg("bad min. I/O size"); break; case 'e': c->leb_size = get_bytes(optarg); if (c->leb_size <= 0) return err_msg("bad LEB size"); break; case 'c': c->max_leb_cnt = get_bytes(optarg); if (c->max_leb_cnt <= 0) return err_msg("bad maximum LEB count"); break; case 'o': output = strdup(optarg); break; case 'D': tbl_file = optarg; if (stat(tbl_file, &st) < 0) return sys_err_msg("bad device table file '%s'", tbl_file); break; case 'h': case '?': printf("%s", helptext); exit(0); case 'v': verbose = 1; break; case 'V': common_print_version(); exit(0); case 'g': debug_level = strtol(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || debug_level < 0 || debug_level > 3) return err_msg("bad debugging level '%s'", optarg); break; case 'f': c->fanout = strtol(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || c->fanout <= 0) return err_msg("bad fanout %s", optarg); break; case 'F': c->space_fixup = 1; break; case 'l': c->log_lebs = strtol(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) return err_msg("bad count of log LEBs '%s'", optarg); break; case 'p': c->orph_lebs = strtol(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || c->orph_lebs <= 0) return err_msg("bad orphan LEB count '%s'", optarg); break; case 'k': if (strcmp(optarg, "r5") == 0) { c->key_hash = key_r5_hash; c->key_hash_type = UBIFS_KEY_HASH_R5; } else if (strcmp(optarg, "test") == 0) { c->key_hash = key_test_hash; c->key_hash_type = UBIFS_KEY_HASH_TEST; } else return err_msg("bad key hash"); break; case 'x': if (strcmp(optarg, "favor_lzo") == 0) c->favor_lzo = 1; else if (strcmp(optarg, "zlib") == 0) c->default_compr = UBIFS_COMPR_ZLIB; else if (strcmp(optarg, "none") == 0) c->default_compr = UBIFS_COMPR_NONE; else if (strcmp(optarg, "lzo") != 0) return err_msg("bad compressor name"); break; case 'X': c->favor_percent = strtol(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || c->favor_percent <= 0 || c->favor_percent >= 100) return err_msg("bad favor LZO percent '%s'", optarg); break; case 'j': c->max_bud_bytes = get_bytes(optarg); if (c->max_bud_bytes <= 0) return err_msg("bad maximum amount of buds"); break; case 'R': c->rp_size = get_bytes(optarg); if (c->rp_size < 0) return err_msg("bad reserved bytes count"); break; case 'U': squash_owner = 1; break; } } if (optind != argc && !output) output = strdup(argv[optind]); if (!output) return err_msg("not output device or file specified"); out_ubi = !open_ubi(output); if (out_ubi) { c->min_io_size = c->di.min_io_size; c->leb_size = c->vi.leb_size; if (c->max_leb_cnt == -1) c->max_leb_cnt = c->vi.rsvd_lebs; } if (c->min_io_size == -1) return err_msg("min. I/O unit was not specified " "(use -h for help)"); if (c->leb_size == -1) return err_msg("LEB size was not specified (use -h for help)"); if (c->max_leb_cnt == -1) return err_msg("Maximum count of LEBs was not specified " "(use -h for help)"); if (c->max_bud_bytes == -1) { int lebs; lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; lebs -= c->orph_lebs; if (c->log_lebs != -1) lebs -= c->log_lebs; else lebs -= UBIFS_MIN_LOG_LEBS; /* * We do not know lprops geometry so far, so assume minimum * count of lprops LEBs. */ lebs -= UBIFS_MIN_LPT_LEBS; /* Make the journal about 12.5% of main area lebs */ c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; /* Make the max journal size 8MiB */ if (c->max_bud_bytes > 8 * 1024 * 1024) c->max_bud_bytes = 8 * 1024 * 1024; if (c->max_bud_bytes < 4 * c->leb_size) c->max_bud_bytes = 4 * c->leb_size; } if (c->log_lebs == -1) { c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); c->log_lebs += 2; } if (c->min_io_size < 8) c->min_io_size = 8; c->rp_size = add_space_overhead(c->rp_size); if (verbose) { printf("mkfs.ubifs\n"); printf("\troot: %s\n", root); printf("\tmin_io_size: %d\n", c->min_io_size); printf("\tleb_size: %d\n", c->leb_size); printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); printf("\toutput: %s\n", output); printf("\tjrn_size: %llu\n", c->max_bud_bytes); printf("\treserved: %llu\n", c->rp_size); switch (c->default_compr) { case UBIFS_COMPR_LZO: printf("\tcompr: lzo\n"); break; case UBIFS_COMPR_ZLIB: printf("\tcompr: zlib\n"); break; case UBIFS_COMPR_NONE: printf("\tcompr: none\n"); break; } printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? "r5" : "test"); printf("\tfanout: %d\n", c->fanout); printf("\torph_lebs: %d\n", c->orph_lebs); printf("\tspace_fixup: %d\n", c->space_fixup); } if (validate_options()) return -1; if (tbl_file && parse_devtable(tbl_file)) return err_msg("cannot parse device table file '%s'", tbl_file); return 0; } /** * prepare_node - fill in the common header. * @node: node * @len: node length */ static void prepare_node(void *node, int len) { uint32_t crc; struct ubifs_ch *ch = node; ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ch->len = cpu_to_le32(len); ch->group_type = UBIFS_NO_NODE_GROUP; ch->sqnum = cpu_to_le64(++c->max_sqnum); ch->padding[0] = ch->padding[1] = 0; crc = mtd_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); ch->crc = cpu_to_le32(crc); } /** * write_leb - copy the image of a LEB to the output target. * @lnum: LEB number * @len: length of data in the buffer * @buf: buffer (must be at least c->leb_size bytes) * @dtype: expected data type */ int write_leb(int lnum, int len, void *buf, int dtype) { off64_t pos = (off64_t)lnum * c->leb_size; dbg_msg(3, "LEB %d len %d", lnum, len); memset(buf + len, 0xff, c->leb_size - len); if (out_ubi) if (ubi_leb_change_start(ubi, out_fd, lnum, c->leb_size, dtype)) return sys_err_msg("ubi_leb_change_start failed"); if (lseek64(out_fd, pos, SEEK_SET) != pos) return sys_err_msg("lseek64 failed seeking %lld", (long long)pos); if (write(out_fd, buf, c->leb_size) != c->leb_size) return sys_err_msg("write failed writing %d bytes at pos %lld", c->leb_size, (long long)pos); return 0; } /** * write_empty_leb - copy the image of an empty LEB to the output target. * @lnum: LEB number * @dtype: expected data type */ static int write_empty_leb(int lnum, int dtype) { return write_leb(lnum, 0, leb_buf, dtype); } /** * do_pad - pad a buffer to the minimum I/O size. * @buf: buffer * @len: buffer length */ static int do_pad(void *buf, int len) { int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); uint32_t crc; memset(buf + len, 0xff, alen - len); pad_len = wlen - alen; dbg_msg(3, "len %d pad_len %d", len, pad_len); buf += alen; if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { struct ubifs_ch *ch = buf; struct ubifs_pad_node *pad_node = buf; ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ch->node_type = UBIFS_PAD_NODE; ch->group_type = UBIFS_NO_NODE_GROUP; ch->padding[0] = ch->padding[1] = 0; ch->sqnum = cpu_to_le64(0); ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); pad_len -= UBIFS_PAD_NODE_SZ; pad_node->pad_len = cpu_to_le32(pad_len); crc = mtd_crc32(UBIFS_CRC32_INIT, buf + 8, UBIFS_PAD_NODE_SZ - 8); ch->crc = cpu_to_le32(crc); memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); } else if (pad_len > 0) memset(buf, UBIFS_PADDING_BYTE, pad_len); return wlen; } /** * write_node - write a node to a LEB. * @node: node * @len: node length * @lnum: LEB number * @dtype: expected data type */ static int write_node(void *node, int len, int lnum, int dtype) { prepare_node(node, len); memcpy(leb_buf, node, len); len = do_pad(leb_buf, len); return write_leb(lnum, len, leb_buf, dtype); } /** * calc_dark - calculate LEB dark space size. * @c: the UBIFS file-system description object * @spc: amount of free and dirty space in the LEB * * This function calculates amount of dark space in an LEB which has @spc bytes * of free and dirty space. Returns the calculations result. * * Dark space is the space which is not always usable - it depends on which * nodes are written in which order. E.g., if an LEB has only 512 free bytes, * it is dark space, because it cannot fit a large data node. So UBIFS cannot * count on this LEB and treat these 512 bytes as usable because it is not true * if, for example, only big chunks of uncompressible data will be written to * the FS. */ static int calc_dark(struct ubifs_info *c, int spc) { if (spc < c->dark_wm) return spc; /* * If we have slightly more space then the dark space watermark, we can * anyway safely assume it we'll be able to write a node of the * smallest size there. */ if (spc - c->dark_wm < (int)MIN_WRITE_SZ) return spc - MIN_WRITE_SZ; return c->dark_wm; } /** * set_lprops - set the LEB property values for a LEB. * @lnum: LEB number * @offs: end offset of data in the LEB * @flags: LEB property flags */ static void set_lprops(int lnum, int offs, int flags) { int i = lnum - c->main_first, free, dirty; int a = max_t(int, c->min_io_size, 8); free = c->leb_size - ALIGN(offs, a); dirty = c->leb_size - free - ALIGN(offs, 8); dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, flags); if (i < c->main_lebs) { c->lpt[i].free = free; c->lpt[i].dirty = dirty; c->lpt[i].flags = flags; } c->lst.total_free += free; c->lst.total_dirty += dirty; if (flags & LPROPS_INDEX) c->lst.idx_lebs += 1; else { int spc; spc = free + dirty; if (spc < c->dead_wm) c->lst.total_dead += spc; else c->lst.total_dark += calc_dark(c, spc); c->lst.total_used += c->leb_size - spc; } } /** * add_to_index - add a node key and position to the index. * @key: node key * @lnum: node LEB number * @offs: node offset * @len: node length */ static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, int len) { struct idx_entry *e; dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); e = malloc(sizeof(struct idx_entry)); if (!e) return err_msg("out of memory"); e->next = NULL; e->prev = idx_list_last; e->key = *key; e->name = name; e->lnum = lnum; e->offs = offs; e->len = len; if (!idx_list_first) idx_list_first = e; if (idx_list_last) idx_list_last->next = e; idx_list_last = e; idx_cnt += 1; return 0; } /** * flush_nodes - write the current head and move the head to the next LEB. */ static int flush_nodes(void) { int len, err; if (!head_offs) return 0; len = do_pad(leb_buf, head_offs); err = write_leb(head_lnum, len, leb_buf, UBI_UNKNOWN); if (err) return err; set_lprops(head_lnum, head_offs, head_flags); head_lnum += 1; head_offs = 0; return 0; } /** * reserve_space - reserve space for a node on the head. * @len: node length * @lnum: LEB number is returned here * @offs: offset is returned here */ static int reserve_space(int len, int *lnum, int *offs) { int err; if (len > c->leb_size - head_offs) { err = flush_nodes(); if (err) return err; } *lnum = head_lnum; *offs = head_offs; head_offs += ALIGN(len, 8); return 0; } /** * add_node - write a node to the head. * @key: node key * @node: node * @len: node length */ static int add_node(union ubifs_key *key, char *name, void *node, int len) { int err, lnum, offs; prepare_node(node, len); err = reserve_space(len, &lnum, &offs); if (err) return err; memcpy(leb_buf + offs, node, len); memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); add_to_index(key, name, lnum, offs, len); return 0; } /** * add_inode_with_data - write an inode. * @st: stat information of source inode * @inum: target inode number * @data: inode data (for special inodes e.g. symlink path etc) * @data_len: inode data length * @flags: source inode flags */ static int add_inode_with_data(struct stat *st, ino_t inum, void *data, unsigned int data_len, int flags) { struct ubifs_ino_node *ino = node_buf; union ubifs_key key; int len, use_flags = 0; if (c->default_compr != UBIFS_COMPR_NONE) use_flags |= UBIFS_COMPR_FL; if (flags & FS_COMPR_FL) use_flags |= UBIFS_COMPR_FL; if (flags & FS_SYNC_FL) use_flags |= UBIFS_SYNC_FL; if (flags & FS_IMMUTABLE_FL) use_flags |= UBIFS_IMMUTABLE_FL; if (flags & FS_APPEND_FL) use_flags |= UBIFS_APPEND_FL; if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) use_flags |= UBIFS_DIRSYNC_FL; memset(ino, 0, UBIFS_INO_NODE_SZ); ino_key_init(&key, inum); ino->ch.node_type = UBIFS_INO_NODE; key_write(&key, &ino->key); ino->creat_sqnum = cpu_to_le64(creat_sqnum); ino->size = cpu_to_le64(st->st_size); ino->nlink = cpu_to_le32(st->st_nlink); /* * The time fields are updated assuming the default time granularity * of 1 second. To support finer granularities, utime() would be needed. */ ino->atime_sec = cpu_to_le64(st->st_atime); ino->ctime_sec = cpu_to_le64(st->st_ctime); ino->mtime_sec = cpu_to_le64(st->st_mtime); ino->atime_nsec = 0; ino->ctime_nsec = 0; ino->mtime_nsec = 0; ino->uid = cpu_to_le32(st->st_uid); ino->gid = cpu_to_le32(st->st_gid); ino->mode = cpu_to_le32(st->st_mode); ino->flags = cpu_to_le32(use_flags); ino->data_len = cpu_to_le32(data_len); ino->compr_type = cpu_to_le16(c->default_compr); if (data_len) memcpy(&ino->data, data, data_len); len = UBIFS_INO_NODE_SZ + data_len; return add_node(&key, NULL, ino, len); } /** * add_inode - write an inode. * @st: stat information of source inode * @inum: target inode number * @flags: source inode flags */ static int add_inode(struct stat *st, ino_t inum, int flags) { return add_inode_with_data(st, inum, NULL, 0, flags); } /** * add_dir_inode - write an inode for a directory. * @dir: source directory * @inum: target inode number * @size: target directory size * @nlink: target directory link count * @st: struct stat object describing attributes (except size and nlink) of the * target inode to create * * Note, this function may be called with %NULL @dir, when the directory which * is being created does not exist at the host file system, but is defined by * the device table. */ static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, struct stat *st) { int fd, flags = 0; st->st_size = size; st->st_nlink = nlink; if (dir) { fd = dirfd(dir); if (fd == -1) return sys_err_msg("dirfd failed"); if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) flags = 0; } return add_inode(st, inum, flags); } /** * add_dev_inode - write an inode for a character or block device. * @st: stat information of source inode * @inum: target inode number * @flags: source inode flags */ static int add_dev_inode(struct stat *st, ino_t inum, int flags) { union ubifs_dev_desc dev; dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); return add_inode_with_data(st, inum, &dev, 8, flags); } /** * add_symlink_inode - write an inode for a symbolic link. * @path_name: path name of symbolic link inode itself (not the link target) * @st: stat information of source inode * @inum: target inode number * @flags: source inode flags */ static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, int flags) { char buf[UBIFS_MAX_INO_DATA + 2]; ssize_t len; /* Take the symlink as is */ len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); if (len <= 0) return sys_err_msg("readlink failed for %s", path_name); if (len > UBIFS_MAX_INO_DATA) return err_msg("symlink too long for %s", path_name); return add_inode_with_data(st, inum, buf, len, flags); } /** * add_dent_node - write a directory entry node. * @dir_inum: target inode number of directory * @name: directory entry name * @inum: target inode number of the directory entry * @type: type of the target inode */ static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, unsigned char type) { struct ubifs_dent_node *dent = node_buf; union ubifs_key key; struct qstr dname; char *kname; int len; dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, (unsigned long)inum, (unsigned int)type, (unsigned long)dir_inum); memset(dent, 0, UBIFS_DENT_NODE_SZ); dname.name = (void *)name; dname.len = strlen(name); dent->ch.node_type = UBIFS_DENT_NODE; dent_key_init(c, &key, dir_inum, &dname); key_write(&key, dent->key); dent->inum = cpu_to_le64(inum); dent->padding1 = 0; dent->type = type; dent->nlen = cpu_to_le16(dname.len); memcpy(dent->name, dname.name, dname.len); dent->name[dname.len] = '\0'; len = UBIFS_DENT_NODE_SZ + dname.len + 1; kname = strdup(name); if (!kname) return err_msg("cannot allocate memory"); return add_node(&key, kname, dent, len); } /** * lookup_inum_mapping - add an inode mapping for link counting. * @dev: source device on which source inode number resides * @inum: source inode number */ static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) { struct inum_mapping *im; unsigned int k; k = inum % HASH_TABLE_SIZE; im = hash_table[k]; while (im) { if (im->dev == dev && im->inum == inum) return im; im = im->next; } im = malloc(sizeof(struct inum_mapping)); if (!im) return NULL; im->next = hash_table[k]; im->prev = NULL; im->dev = dev; im->inum = inum; im->use_inum = 0; im->use_nlink = 0; if (hash_table[k]) hash_table[k]->prev = im; hash_table[k] = im; return im; } /** * all_zero - does a buffer contain only zero bytes. * @buf: buffer * @len: buffer length */ static int all_zero(void *buf, int len) { unsigned char *p = buf; while (len--) if (*p++ != 0) return 0; return 1; } /** * add_file - write the data of a file and its inode to the output file. * @path_name: source path name * @st: source inode stat information * @inum: target inode number * @flags: source inode flags */ static int add_file(const char *path_name, struct stat *st, ino_t inum, int flags) { struct ubifs_data_node *dn = node_buf; void *buf = block_buf; loff_t file_size = 0; ssize_t ret, bytes_read; union ubifs_key key; int fd, dn_len, err, compr_type, use_compr; unsigned int block_no = 0; size_t out_len; fd = open(path_name, O_RDONLY | O_LARGEFILE); if (fd == -1) return sys_err_msg("failed to open file '%s'", path_name); do { /* Read next block */ bytes_read = 0; do { ret = read(fd, buf + bytes_read, UBIFS_BLOCK_SIZE - bytes_read); if (ret == -1) { sys_err_msg("failed to read file '%s'", path_name); close(fd); return 1; } bytes_read += ret; } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); if (bytes_read == 0) break; file_size += bytes_read; /* Skip holes */ if (all_zero(buf, bytes_read)) { block_no += 1; continue; } /* Make data node */ memset(dn, 0, UBIFS_DATA_NODE_SZ); data_key_init(&key, inum, block_no++); dn->ch.node_type = UBIFS_DATA_NODE; key_write(&key, &dn->key); dn->size = cpu_to_le32(bytes_read); out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; if (c->default_compr == UBIFS_COMPR_NONE && (flags & FS_COMPR_FL)) use_compr = UBIFS_COMPR_LZO; else use_compr = c->default_compr; compr_type = compress_data(buf, bytes_read, &dn->data, &out_len, use_compr); dn->compr_type = cpu_to_le16(compr_type); dn_len = UBIFS_DATA_NODE_SZ + out_len; /* Add data node to file system */ err = add_node(&key, NULL, dn, dn_len); if (err) { close(fd); return err; } } while (ret != 0); if (close(fd) == -1) return sys_err_msg("failed to close file '%s'", path_name); if (file_size != st->st_size) return err_msg("file size changed during writing file '%s'", path_name); return add_inode(st, inum, flags); } /** * add_non_dir - write a non-directory to the output file. * @path_name: source path name * @inum: target inode number is passed and returned here (due to link counting) * @nlink: number of links if known otherwise zero * @type: UBIFS inode type is returned here * @st: struct stat object containing inode attributes which should be use when * creating the UBIFS inode */ static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, unsigned char *type, struct stat *st) { int fd, flags = 0; dbg_msg(2, "%s", path_name); if (S_ISREG(st->st_mode)) { fd = open(path_name, O_RDONLY); if (fd == -1) return sys_err_msg("failed to open file '%s'", path_name); if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) flags = 0; if (close(fd) == -1) return sys_err_msg("failed to close file '%s'", path_name); *type = UBIFS_ITYPE_REG; } else if (S_ISCHR(st->st_mode)) *type = UBIFS_ITYPE_CHR; else if (S_ISBLK(st->st_mode)) *type = UBIFS_ITYPE_BLK; else if (S_ISLNK(st->st_mode)) *type = UBIFS_ITYPE_LNK; else if (S_ISSOCK(st->st_mode)) *type = UBIFS_ITYPE_SOCK; else if (S_ISFIFO(st->st_mode)) *type = UBIFS_ITYPE_FIFO; else return err_msg("file '%s' has unknown inode type", path_name); if (nlink) st->st_nlink = nlink; else if (st->st_nlink > 1) { /* * If the number of links is greater than 1, then add this file * later when we know the number of links that we actually have. * For now, we just put the inode mapping in the hash table. */ struct inum_mapping *im; im = lookup_inum_mapping(st->st_dev, st->st_ino); if (!im) return err_msg("out of memory"); if (im->use_nlink == 0) { /* New entry */ im->use_inum = *inum; im->use_nlink = 1; im->path_name = malloc(strlen(path_name) + 1); if (!im->path_name) return err_msg("out of memory"); strcpy(im->path_name, path_name); } else { /* Existing entry */ *inum = im->use_inum; im->use_nlink += 1; /* Return unused inode number */ c->highest_inum -= 1; } memcpy(&im->st, st, sizeof(struct stat)); return 0; } else st->st_nlink = 1; creat_sqnum = ++c->max_sqnum; if (S_ISREG(st->st_mode)) return add_file(path_name, st, *inum, flags); if (S_ISCHR(st->st_mode)) return add_dev_inode(st, *inum, flags); if (S_ISBLK(st->st_mode)) return add_dev_inode(st, *inum, flags); if (S_ISLNK(st->st_mode)) return add_symlink_inode(path_name, st, *inum, flags); if (S_ISSOCK(st->st_mode)) return add_inode(st, *inum, flags); if (S_ISFIFO(st->st_mode)) return add_inode(st, *inum, flags); return err_msg("file '%s' has unknown inode type", path_name); } /** * add_directory - write a directory tree to the output file. * @dir_name: directory path name * @dir_inum: UBIFS inode number of directory * @st: directory inode statistics * @non_existing: non-zero if this function is called for a directory which * does not exist on the host file-system and it is being * created because it is defined in the device table file. */ static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, int non_existing) { struct dirent *entry; DIR *dir = NULL; int err = 0; loff_t size = UBIFS_INO_NODE_SZ; char *name = NULL; unsigned int nlink = 2; struct path_htbl_element *ph_elt; struct name_htbl_element *nh_elt = NULL; struct hashtable_itr *itr; ino_t inum; unsigned char type; unsigned long long dir_creat_sqnum = ++c->max_sqnum; dbg_msg(2, "%s", dir_name); if (!non_existing) { dir = opendir(dir_name); if (dir == NULL) return sys_err_msg("cannot open directory '%s'", dir_name); } /* * Check whether this directory contains files which should be * added/changed because they were specified in the device table. * @ph_elt will be non-zero if yes. */ ph_elt = devtbl_find_path(dir_name + root_len - 1); /* * Before adding the directory itself, we have to iterate over all the * entries the device table adds to this directory and create them. */ for (; !non_existing;) { struct stat dent_st; errno = 0; entry = readdir(dir); if (!entry) { if (errno == 0) break; sys_err_msg("error reading directory '%s'", dir_name); err = -1; break; } if (strcmp(".", entry->d_name) == 0) continue; if (strcmp("..", entry->d_name) == 0) continue; if (ph_elt) /* * This directory was referred to at the device table * file. Check if this directory entry is referred at * too. */ nh_elt = devtbl_find_name(ph_elt, entry->d_name); /* * We are going to create the file corresponding to this * directory entry (@entry->d_name). We use 'struct stat' * object to pass information about file attributes (actually * only about UID, GID, mode, major, and minor). Get attributes * for this file from the UBIFS rootfs on the host. */ free(name); name = make_path(dir_name, entry->d_name); if (lstat(name, &dent_st) == -1) { sys_err_msg("lstat failed for file '%s'", name); goto out_free; } if (squash_owner) /* * Squash UID/GID. But the device table may override * this. */ dent_st.st_uid = dent_st.st_gid = 0; /* * And if the device table describes the same file, override * the attributes. However, this is not allowed for device node * files. */ if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) goto out_free; inum = ++c->highest_inum; if (S_ISDIR(dent_st.st_mode)) { err = add_directory(name, inum, &dent_st, 0); if (err) goto out_free; nlink += 1; type = UBIFS_ITYPE_DIR; } else { err = add_non_dir(name, &inum, 0, &type, &dent_st); if (err) goto out_free; } err = add_dent_node(dir_inum, entry->d_name, inum, type); if (err) goto out_free; size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, 8); } /* * OK, we have created all files in this directory (recursively), let's * also create all files described in the device table. All t */ nh_elt = first_name_htbl_element(ph_elt, &itr); while (nh_elt) { struct stat fake_st; /* * We prohibit creating regular files using the device table, * the device table may only re-define attributes of regular * files. */ if (S_ISREG(nh_elt->mode)) { err_msg("Bad device table entry %s/%s - it is " "prohibited to create regular files " "via device table", strcmp(ph_elt->path, "/") ? ph_elt->path : "", nh_elt->name); goto out_free; } memcpy(&fake_st, &root_st, sizeof(struct stat)); fake_st.st_uid = nh_elt->uid; fake_st.st_uid = nh_elt->uid; fake_st.st_mode = nh_elt->mode; fake_st.st_rdev = nh_elt->dev; fake_st.st_nlink = 1; free(name); name = make_path(dir_name, nh_elt->name); inum = ++c->highest_inum; if (S_ISDIR(nh_elt->mode)) { err = add_directory(name, inum, &fake_st, 1); if (err) goto out_free; nlink += 1; type = UBIFS_ITYPE_DIR; } else { err = add_non_dir(name, &inum, 0, &type, &fake_st); if (err) goto out_free; } err = add_dent_node(dir_inum, nh_elt->name, inum, type); if (err) goto out_free; size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); nh_elt = next_name_htbl_element(ph_elt, &itr); } creat_sqnum = dir_creat_sqnum; err = add_dir_inode(dir, dir_inum, size, nlink, st); if (err) goto out_free; free(name); if (!non_existing && closedir(dir) == -1) return sys_err_msg("error closing directory '%s'", dir_name); return 0; out_free: free(name); if (!non_existing) closedir(dir); return -1; } /** * add_multi_linked_files - write all the files for which we counted links. */ static int add_multi_linked_files(void) { int i, err; for (i = 0; i < HASH_TABLE_SIZE; i++) { struct inum_mapping *im; unsigned char type = 0; for (im = hash_table[i]; im; im = im->next) { dbg_msg(2, "%s", im->path_name); err = add_non_dir(im->path_name, &im->use_inum, im->use_nlink, &type, &im->st); if (err) return err; } } return 0; } /** * write_data - write the files and directories. */ static int write_data(void) { int err; mode_t mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; if (root) { err = stat(root, &root_st); if (err) return sys_err_msg("bad root file-system directory '%s'", root); } else { root_st.st_mtime = time(NULL); root_st.st_atime = root_st.st_ctime = root_st.st_mtime; root_st.st_mode = mode; } head_flags = 0; err = add_directory(root, UBIFS_ROOT_INO, &root_st, !root); if (err) return err; err = add_multi_linked_files(); if (err) return err; return flush_nodes(); } static int namecmp(const char *name1, const char *name2) { size_t len1 = strlen(name1), len2 = strlen(name2); size_t clen = (len1 < len2) ? len1 : len2; int cmp; cmp = memcmp(name1, name2, clen); if (cmp) return cmp; return (len1 < len2) ? -1 : 1; } static int cmp_idx(const void *a, const void *b) { const struct idx_entry *e1 = *(const struct idx_entry **)a; const struct idx_entry *e2 = *(const struct idx_entry **)b; int cmp; cmp = keys_cmp(&e1->key, &e2->key); if (cmp) return cmp; return namecmp(e1->name, e2->name); } /** * add_idx_node - write an index node to the head. * @node: index node * @child_cnt: number of children of this index node */ static int add_idx_node(void *node, int child_cnt) { int err, lnum, offs, len; len = ubifs_idx_node_sz(c, child_cnt); prepare_node(node, len); err = reserve_space(len, &lnum, &offs); if (err) return err; memcpy(leb_buf + offs, node, len); memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); c->old_idx_sz += ALIGN(len, 8); dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, c->old_idx_sz); /* The last index node written will be the root */ c->zroot.lnum = lnum; c->zroot.offs = offs; c->zroot.len = len; return 0; } /** * write_index - write out the index. */ static int write_index(void) { size_t sz, i, cnt, idx_sz, pstep, bcnt; struct idx_entry **idx_ptr, **p; struct ubifs_idx_node *idx; struct ubifs_branch *br; int child_cnt = 0, j, level, blnum, boffs, blen, blast_len, err; dbg_msg(1, "leaf node count: %zd", idx_cnt); /* Reset the head for the index */ head_flags = LPROPS_INDEX; /* Allocate index node */ idx_sz = ubifs_idx_node_sz(c, c->fanout); idx = malloc(idx_sz); if (!idx) return err_msg("out of memory"); /* Make an array of pointers to sort the index list */ sz = idx_cnt * sizeof(struct idx_entry *); if (sz / sizeof(struct idx_entry *) != idx_cnt) { free(idx); return err_msg("index is too big (%zu entries)", idx_cnt); } idx_ptr = malloc(sz); if (!idx_ptr) { free(idx); return err_msg("out of memory - needed %zu bytes for index", sz); } idx_ptr[0] = idx_list_first; for (i = 1; i < idx_cnt; i++) idx_ptr[i] = idx_ptr[i - 1]->next; qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); /* Write level 0 index nodes */ cnt = idx_cnt / c->fanout; if (idx_cnt % c->fanout) cnt += 1; p = idx_ptr; blnum = head_lnum; boffs = head_offs; for (i = 0; i < cnt; i++) { /* * Calculate the child count. All index nodes are created full * except for the last index node on each row. */ if (i == cnt - 1) { child_cnt = idx_cnt % c->fanout; if (child_cnt == 0) child_cnt = c->fanout; } else child_cnt = c->fanout; memset(idx, 0, idx_sz); idx->ch.node_type = UBIFS_IDX_NODE; idx->child_cnt = cpu_to_le16(child_cnt); idx->level = cpu_to_le16(0); for (j = 0; j < child_cnt; j++, p++) { br = ubifs_idx_branch(c, idx, j); key_write_idx(&(*p)->key, &br->key); br->lnum = cpu_to_le32((*p)->lnum); br->offs = cpu_to_le32((*p)->offs); br->len = cpu_to_le32((*p)->len); } add_idx_node(idx, child_cnt); } /* Write level 1 index nodes and above */ level = 0; pstep = 1; while (cnt > 1) { /* * 'blast_len' is the length of the last index node in the level * below. */ blast_len = ubifs_idx_node_sz(c, child_cnt); /* 'bcnt' is the number of index nodes in the level below */ bcnt = cnt; /* 'cnt' is the number of index nodes in this level */ cnt = (cnt + c->fanout - 1) / c->fanout; if (cnt == 0) cnt = 1; level += 1; /* * The key of an index node is the same as the key of its first * child. Thus we can get the key by stepping along the bottom * level 'p' with an increasing large step 'pstep'. */ p = idx_ptr; pstep *= c->fanout; for (i = 0; i < cnt; i++) { /* * Calculate the child count. All index nodes are * created full except for the last index node on each * row. */ if (i == cnt - 1) { child_cnt = bcnt % c->fanout; if (child_cnt == 0) child_cnt = c->fanout; } else child_cnt = c->fanout; memset(idx, 0, idx_sz); idx->ch.node_type = UBIFS_IDX_NODE; idx->child_cnt = cpu_to_le16(child_cnt); idx->level = cpu_to_le16(level); for (j = 0; j < child_cnt; j++) { size_t bn = i * c->fanout + j; /* * The length of the index node in the level * below is 'idx_sz' except when it is the last * node on the row. i.e. all the others on the * row are full. */ if (bn == bcnt - 1) blen = blast_len; else blen = idx_sz; /* * 'blnum' and 'boffs' hold the position of the * index node on the level below. */ if (boffs + blen > c->leb_size) { blnum += 1; boffs = 0; } /* * Fill in the branch with the key and position * of the index node from the level below. */ br = ubifs_idx_branch(c, idx, j); key_write_idx(&(*p)->key, &br->key); br->lnum = cpu_to_le32(blnum); br->offs = cpu_to_le32(boffs); br->len = cpu_to_le32(blen); /* * Step to the next index node on the level * below. */ boffs += ALIGN(blen, 8); p += pstep; } add_idx_node(idx, child_cnt); } } /* Free stuff */ for (i = 0; i < idx_cnt; i++) free(idx_ptr[i]); free(idx_ptr); free(idx); dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, c->zroot.len); /* Set the index head */ c->ihead_lnum = head_lnum; c->ihead_offs = ALIGN(head_offs, c->min_io_size); dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); /* Flush the last index LEB */ err = flush_nodes(); if (err) return err; return 0; } /** * set_gc_lnum - set the LEB number reserved for the garbage collector. */ static int set_gc_lnum(void) { int err; c->gc_lnum = head_lnum++; err = write_empty_leb(c->gc_lnum, UBI_LONGTERM); if (err) return err; set_lprops(c->gc_lnum, 0, 0); c->lst.empty_lebs += 1; return 0; } /** * finalize_leb_cnt - now that we know how many LEBs we used. */ static int finalize_leb_cnt(void) { c->leb_cnt = head_lnum; if (c->leb_cnt > c->max_leb_cnt) return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); c->main_lebs = c->leb_cnt - c->main_first; if (verbose) { printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); printf("\tlog_lebs: %d\n", c->log_lebs); printf("\tlpt_lebs: %d\n", c->lpt_lebs); printf("\torph_lebs: %d\n", c->orph_lebs); printf("\tmain_lebs: %d\n", c->main_lebs); printf("\tgc lebs: %d\n", 1); printf("\tindex lebs: %d\n", c->lst.idx_lebs); printf("\tleb_cnt: %d\n", c->leb_cnt); } dbg_msg(1, "total_free: %llu", c->lst.total_free); dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); dbg_msg(1, "total_used: %llu", c->lst.total_used); dbg_msg(1, "total_dead: %llu", c->lst.total_dead); dbg_msg(1, "total_dark: %llu", c->lst.total_dark); dbg_msg(1, "index size: %llu", c->old_idx_sz); dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); return 0; } /** * write_super - write the super block. */ static int write_super(void) { struct ubifs_sb_node sup; memset(&sup, 0, UBIFS_SB_NODE_SZ); sup.ch.node_type = UBIFS_SB_NODE; sup.key_hash = c->key_hash_type; sup.min_io_size = cpu_to_le32(c->min_io_size); sup.leb_size = cpu_to_le32(c->leb_size); sup.leb_cnt = cpu_to_le32(c->leb_cnt); sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); sup.log_lebs = cpu_to_le32(c->log_lebs); sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); sup.orph_lebs = cpu_to_le32(c->orph_lebs); sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); sup.fanout = cpu_to_le32(c->fanout); sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); sup.default_compr = cpu_to_le16(c->default_compr); sup.rp_size = cpu_to_le64(c->rp_size); sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); uuid_generate_random(sup.uuid); if (verbose) { char s[40]; uuid_unparse_upper(sup.uuid, s); printf("\tUUID: %s\n", s); } if (c->big_lpt) sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); if (c->space_fixup) sup.flags |= cpu_to_le32(UBIFS_FLG_SPACE_FIXUP); return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM, UBI_LONGTERM); } /** * write_master - write the master node. */ static int write_master(void) { struct ubifs_mst_node mst; int err; memset(&mst, 0, UBIFS_MST_NODE_SZ); mst.ch.node_type = UBIFS_MST_NODE; mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); mst.highest_inum = cpu_to_le64(c->highest_inum); mst.cmt_no = cpu_to_le64(0); mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); mst.root_lnum = cpu_to_le32(c->zroot.lnum); mst.root_offs = cpu_to_le32(c->zroot.offs); mst.root_len = cpu_to_le32(c->zroot.len); mst.gc_lnum = cpu_to_le32(c->gc_lnum); mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); mst.ihead_offs = cpu_to_le32(c->ihead_offs); mst.index_size = cpu_to_le64(c->old_idx_sz); mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); mst.lpt_offs = cpu_to_le32(c->lpt_offs); mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); mst.nhead_offs = cpu_to_le32(c->nhead_offs); mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); mst.ltab_offs = cpu_to_le32(c->ltab_offs); mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); mst.lsave_offs = cpu_to_le32(c->lsave_offs); mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); mst.total_free = cpu_to_le64(c->lst.total_free); mst.total_dirty = cpu_to_le64(c->lst.total_dirty); mst.total_used = cpu_to_le64(c->lst.total_used); mst.total_dead = cpu_to_le64(c->lst.total_dead); mst.total_dark = cpu_to_le64(c->lst.total_dark); mst.leb_cnt = cpu_to_le32(c->leb_cnt); err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, UBI_SHORTTERM); if (err) return err; err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1, UBI_SHORTTERM); if (err) return err; return 0; } /** * write_log - write an empty log. */ static int write_log(void) { struct ubifs_cs_node cs; int err, i, lnum; lnum = UBIFS_LOG_LNUM; cs.ch.node_type = UBIFS_CS_NODE; cs.cmt_no = cpu_to_le64(0); err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum, UBI_UNKNOWN); if (err) return err; lnum += 1; for (i = 1; i < c->log_lebs; i++, lnum++) { err = write_empty_leb(lnum, UBI_UNKNOWN); if (err) return err; } return 0; } /** * write_lpt - write the LEB properties tree. */ static int write_lpt(void) { int err, lnum; err = create_lpt(c); if (err) return err; lnum = c->nhead_lnum + 1; while (lnum <= c->lpt_last) { err = write_empty_leb(lnum++, UBI_SHORTTERM); if (err) return err; } return 0; } /** * write_orphan_area - write an empty orphan area. */ static int write_orphan_area(void) { int err, i, lnum; lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; for (i = 0; i < c->orph_lebs; i++, lnum++) { err = write_empty_leb(lnum, UBI_SHORTTERM); if (err) return err; } return 0; } /** * check_volume_empty - check if the UBI volume is empty. * * This function checks if the UBI volume is empty by looking if its LEBs are * mapped or not. * * Returns %0 in case of success, %1 is the volume is not empty, * and a negative error code in case of failure. */ static int check_volume_empty(void) { int lnum, err; for (lnum = 0; lnum < c->vi.rsvd_lebs; lnum++) { err = ubi_is_mapped(out_fd, lnum); if (err < 0) return err; if (err == 1) return 1; } return 0; } /** * open_target - open the output target. * * Open the output target. The target can be an UBI volume * or a file. * * Returns %0 in case of success and %-1 in case of failure. */ static int open_target(void) { if (out_ubi) { out_fd = open(output, O_RDWR | O_EXCL); if (out_fd == -1) return sys_err_msg("cannot open the UBI volume '%s'", output); if (ubi_set_property(out_fd, UBI_PROP_DIRECT_WRITE, 1)) return sys_err_msg("ubi_set_property failed"); if (check_volume_empty()) return err_msg("UBI volume is not empty"); } else { out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if (out_fd == -1) return sys_err_msg("cannot create output file '%s'", output); } return 0; } /** * close_target - close the output target. * * Close the output target. If the target was an UBI * volume, also close libubi. * * Returns %0 in case of success and %-1 in case of failure. */ static int close_target(void) { if (ubi) libubi_close(ubi); if (out_fd >= 0 && close(out_fd) == -1) return sys_err_msg("cannot close the target '%s'", output); if (output) free(output); return 0; } /** * init - initialize things. */ static int init(void) { int err, i, main_lebs, big_lpt = 0, sz; c->highest_inum = UBIFS_FIRST_INO; c->jhead_cnt = 1; main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; main_lebs -= c->log_lebs + c->orph_lebs; err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); if (err) return err; c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + c->orph_lebs; head_lnum = c->main_first; head_offs = 0; c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; c->lpt_last = c->lpt_first + c->lpt_lebs - 1; c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); if (!c->lpt) return err_msg("unable to allocate LPT"); c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); if (!c->ltab) return err_msg("unable to allocate LPT ltab"); /* Initialize LPT's own lprops */ for (i = 0; i < c->lpt_lebs; i++) { c->ltab[i].free = c->leb_size; c->ltab[i].dirty = 0; } c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); leb_buf = malloc(c->leb_size); if (!leb_buf) return err_msg("out of memory"); node_buf = malloc(NODE_BUFFER_SIZE); if (!node_buf) return err_msg("out of memory"); block_buf = malloc(UBIFS_BLOCK_SIZE); if (!block_buf) return err_msg("out of memory"); sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; hash_table = malloc(sz); if (!hash_table) return err_msg("out of memory"); memset(hash_table, 0, sz); err = init_compression(); if (err) return err; return 0; } static void destroy_hash_table(void) { int i; for (i = 0; i < HASH_TABLE_SIZE; i++) { struct inum_mapping *im, *q; for (im = hash_table[i]; im; ) { q = im; im = im->next; free(q->path_name); free(q); } } } /** * deinit - deinitialize things. */ static void deinit(void) { free(c->lpt); free(c->ltab); free(leb_buf); free(node_buf); free(block_buf); destroy_hash_table(); free(hash_table); destroy_compression(); free_devtable_info(); } /** * mkfs - make the file system. * * Each on-flash area has a corresponding function to create it. The order of * the functions reflects what information must be known to complete each stage. * As a consequence the output file is not written sequentially. No effort has * been made to make efficient use of memory or to allow for the possibility of * incremental updates to the output file. */ static int mkfs(void) { int err = 0; err = init(); if (err) goto out; err = write_data(); if (err) goto out; err = set_gc_lnum(); if (err) goto out; err = write_index(); if (err) goto out; err = finalize_leb_cnt(); if (err) goto out; err = write_lpt(); if (err) goto out; err = write_super(); if (err) goto out; err = write_master(); if (err) goto out; err = write_log(); if (err) goto out; err = write_orphan_area(); out: deinit(); return err; } int main(int argc, char *argv[]) { int err; err = get_options(argc, argv); if (err) return err; err = open_target(); if (err) return err; err = mkfs(); if (err) { close_target(); return err; } err = close_target(); if (err) return err; if (verbose) printf("Success!\n"); return 0; } mtd-utils-1.5.0/mkfs.ubifs/mkfs.ubifs.h000066400000000000000000000105231175167361300177410ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation. * Copyright (C) 2008 University of Szeged, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy * Adrian Hunter * Zoltan Sogor */ #ifndef __MKFS_UBIFS_H__ #define __MKFS_UBIFS_H__ #define _GNU_SOURCE #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libubi.h" #include "defs.h" #include "crc16.h" #include "ubifs-media.h" #include "ubifs.h" #include "key.h" #include "lpt.h" #include "compr.h" /* * Compression flags are duplicated so that compr.c can compile without ubifs.h. * Here we make sure they are the same. */ #if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE #error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE #endif #if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO #error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO #endif #if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB #error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB #endif extern int verbose; extern int debug_level; #define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ } while(0) #define err_msg(fmt, ...) ({ \ fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ -1; \ }) #define sys_err_msg(fmt, ...) ({ \ int err_ = errno; \ fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ -1; \ }) /** * struct path_htbl_element - an element of the path hash table. * @path: the UBIFS path the element describes (the key of the element) * @name_htbl: one more (nested) hash table containing names of all * files/directories/device nodes which should be created at this * path * * See device table handling for more information. */ struct path_htbl_element { const char *path; struct hashtable *name_htbl; }; /** * struct name_htbl_element - an element in the name hash table * @name: name of the file/directory/device node (the key of the element) * @mode: accsess rights and file type * @uid: user ID * @gid: group ID * @major: device node major number * @minor: device node minor number * * This is an element of the name hash table. Name hash table sits in the path * hash table elements and describes file names which should be created/changed * at this path. */ struct name_htbl_element { const char *name; unsigned int mode; unsigned int uid; unsigned int gid; dev_t dev; }; extern struct ubifs_info info_; struct hashtable_itr; int write_leb(int lnum, int len, void *buf, int dtype); int parse_devtable(const char *tbl_file); struct path_htbl_element *devtbl_find_path(const char *path); struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, const char *name); int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, struct name_htbl_element *nh_elt); struct name_htbl_element * first_name_htbl_element(struct path_htbl_element *ph_elt, struct hashtable_itr **itr); struct name_htbl_element * next_name_htbl_element(struct path_htbl_element *ph_elt, struct hashtable_itr **itr); void free_devtable_info(void); #endif mtd-utils-1.5.0/mkfs.ubifs/ubifs-media.h000066400000000000000000000530341175167361300200630ustar00rootroot00000000000000/* * This file is part of UBIFS. * * Copyright (C) 2006-2008 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter */ /* * This file describes UBIFS on-flash format and contains definitions of all the * relevant data structures and constants. * * All UBIFS on-flash objects are stored in the form of nodes. All nodes start * with the UBIFS node magic number and have the same common header. Nodes * always sit at 8-byte aligned positions on the media and node header sizes are * also 8-byte aligned (except for the indexing node and the padding node). */ #ifndef __UBIFS_MEDIA_H__ #define __UBIFS_MEDIA_H__ /* UBIFS node magic number (must not have the padding byte first or last) */ #define UBIFS_NODE_MAGIC 0x06101831 /* UBIFS on-flash format version */ #define UBIFS_FORMAT_VERSION 4 /* Minimum logical eraseblock size in bytes */ #define UBIFS_MIN_LEB_SZ (15*1024) /* Initial CRC32 value used when calculating CRC checksums */ #define UBIFS_CRC32_INIT 0xFFFFFFFFU /* * UBIFS does not try to compress data if its length is less than the below * constant. */ #define UBIFS_MIN_COMPR_LEN 128 /* Root inode number */ #define UBIFS_ROOT_INO 1 /* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */ #define UBIFS_FIRST_INO 64 /* * Maximum file name and extended attribute length (must be a multiple of 8, * minus 1). */ #define UBIFS_MAX_NLEN 255 /* Maximum number of data journal heads */ #define UBIFS_MAX_JHEADS 1 /* * Size of UBIFS data block. Note, UBIFS is not a block oriented file-system, * which means that it does not treat the underlying media as consisting of * blocks like in case of hard drives. Do not be confused. UBIFS block is just * the maximum amount of data which one data node can have or which can be * attached to an inode node. */ #define UBIFS_BLOCK_SIZE 4096 #define UBIFS_BLOCK_SHIFT 12 #define UBIFS_BLOCK_MASK 0x00000FFF /* UBIFS padding byte pattern (must not be first or last byte of node magic) */ #define UBIFS_PADDING_BYTE 0xCE /* Maximum possible key length */ #define UBIFS_MAX_KEY_LEN 16 /* Key length ("simple" format) */ #define UBIFS_SK_LEN 8 /* Minimum index tree fanout */ #define UBIFS_MIN_FANOUT 3 /* Maximum number of levels in UBIFS indexing B-tree */ #define UBIFS_MAX_LEVELS 512 /* Maximum amount of data attached to an inode in bytes */ #define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE /* LEB Properties Tree fanout (must be power of 2) and fanout shift */ #define UBIFS_LPT_FANOUT 4 #define UBIFS_LPT_FANOUT_SHIFT 2 /* LEB Properties Tree bit field sizes */ #define UBIFS_LPT_CRC_BITS 16 #define UBIFS_LPT_CRC_BYTES 2 #define UBIFS_LPT_TYPE_BITS 4 /* The key is always at the same position in all keyed nodes */ #define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key) /* * LEB Properties Tree node types. * * UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties) * UBIFS_LPT_NNODE: LPT internal node * UBIFS_LPT_LTAB: LPT's own lprops table * UBIFS_LPT_LSAVE: LPT's save table (big model only) * UBIFS_LPT_NODE_CNT: count of LPT node types * UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type */ enum { UBIFS_LPT_PNODE, UBIFS_LPT_NNODE, UBIFS_LPT_LTAB, UBIFS_LPT_LSAVE, UBIFS_LPT_NODE_CNT, UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1, }; /* * UBIFS inode types. * * UBIFS_ITYPE_REG: regular file * UBIFS_ITYPE_DIR: directory * UBIFS_ITYPE_LNK: soft link * UBIFS_ITYPE_BLK: block device node * UBIFS_ITYPE_CHR: character device node * UBIFS_ITYPE_FIFO: fifo * UBIFS_ITYPE_SOCK: socket * UBIFS_ITYPES_CNT: count of supported file types */ enum { UBIFS_ITYPE_REG, UBIFS_ITYPE_DIR, UBIFS_ITYPE_LNK, UBIFS_ITYPE_BLK, UBIFS_ITYPE_CHR, UBIFS_ITYPE_FIFO, UBIFS_ITYPE_SOCK, UBIFS_ITYPES_CNT, }; /* * Supported key hash functions. * * UBIFS_KEY_HASH_R5: R5 hash * UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name */ enum { UBIFS_KEY_HASH_R5, UBIFS_KEY_HASH_TEST, }; /* * Supported key formats. * * UBIFS_SIMPLE_KEY_FMT: simple key format */ enum { UBIFS_SIMPLE_KEY_FMT, }; /* * The simple key format uses 29 bits for storing UBIFS block number and hash * value. */ #define UBIFS_S_KEY_BLOCK_BITS 29 #define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF #define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS #define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK /* * Key types. * * UBIFS_INO_KEY: inode node key * UBIFS_DATA_KEY: data node key * UBIFS_DENT_KEY: directory entry node key * UBIFS_XENT_KEY: extended attribute entry key * UBIFS_KEY_TYPES_CNT: number of supported key types */ enum { UBIFS_INO_KEY, UBIFS_DATA_KEY, UBIFS_DENT_KEY, UBIFS_XENT_KEY, UBIFS_KEY_TYPES_CNT, }; /* Count of LEBs reserved for the superblock area */ #define UBIFS_SB_LEBS 1 /* Count of LEBs reserved for the master area */ #define UBIFS_MST_LEBS 2 /* First LEB of the superblock area */ #define UBIFS_SB_LNUM 0 /* First LEB of the master area */ #define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS) /* First LEB of the log area */ #define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS) /* * The below constants define the absolute minimum values for various UBIFS * media areas. Many of them actually depend of flash geometry and the FS * configuration (number of journal heads, orphan LEBs, etc). This means that * the smallest volume size which can be used for UBIFS cannot be pre-defined * by these constants. The file-system that meets the below limitation will not * necessarily mount. UBIFS does run-time calculations and validates the FS * size. */ /* Minimum number of logical eraseblocks in the log */ #define UBIFS_MIN_LOG_LEBS 2 /* Minimum number of bud logical eraseblocks (one for each head) */ #define UBIFS_MIN_BUD_LEBS 3 /* Minimum number of journal logical eraseblocks */ #define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS) /* Minimum number of LPT area logical eraseblocks */ #define UBIFS_MIN_LPT_LEBS 2 /* Minimum number of orphan area logical eraseblocks */ #define UBIFS_MIN_ORPH_LEBS 1 /* * Minimum number of main area logical eraseblocks (buds, 3 for the index, 1 * for GC, 1 for deletions, and at least 1 for committed data). */ #define UBIFS_MIN_MAIN_LEBS (UBIFS_MIN_BUD_LEBS + 6) /* Minimum number of logical eraseblocks */ #define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \ UBIFS_MIN_LOG_LEBS + UBIFS_MIN_LPT_LEBS + \ UBIFS_MIN_ORPH_LEBS + UBIFS_MIN_MAIN_LEBS) /* Node sizes (N.B. these are guaranteed to be multiples of 8) */ #define UBIFS_CH_SZ sizeof(struct ubifs_ch) #define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node) #define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node) #define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node) #define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node) #define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node) #define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node) #define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node) #define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node) #define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node) #define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node) #define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node) /* Extended attribute entry nodes are identical to directory entry nodes */ #define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ /* Only this does not have to be multiple of 8 bytes */ #define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch) /* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */ #define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE) #define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA) #define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1) #define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ /* The largest UBIFS node */ #define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ /* * On-flash inode flags. * * UBIFS_COMPR_FL: use compression for this inode * UBIFS_SYNC_FL: I/O on this inode has to be synchronous * UBIFS_IMMUTABLE_FL: inode is immutable * UBIFS_APPEND_FL: writes to the inode may only append data * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous * UBIFS_XATTR_FL: this inode is the inode for an extended attribute value * * Note, these are on-flash flags which correspond to ioctl flags * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not * have to be the same. */ enum { UBIFS_COMPR_FL = 0x01, UBIFS_SYNC_FL = 0x02, UBIFS_IMMUTABLE_FL = 0x04, UBIFS_APPEND_FL = 0x08, UBIFS_DIRSYNC_FL = 0x10, UBIFS_XATTR_FL = 0x20, }; /* Inode flag bits used by UBIFS */ #define UBIFS_FL_MASK 0x0000001F /* * UBIFS compression algorithms. * * UBIFS_COMPR_NONE: no compression * UBIFS_COMPR_LZO: LZO compression * UBIFS_COMPR_ZLIB: ZLIB compression * UBIFS_COMPR_TYPES_CNT: count of supported compression types */ enum { UBIFS_COMPR_NONE, UBIFS_COMPR_LZO, UBIFS_COMPR_ZLIB, UBIFS_COMPR_TYPES_CNT, }; /* * UBIFS node types. * * UBIFS_INO_NODE: inode node * UBIFS_DATA_NODE: data node * UBIFS_DENT_NODE: directory entry node * UBIFS_XENT_NODE: extended attribute node * UBIFS_TRUN_NODE: truncation node * UBIFS_PAD_NODE: padding node * UBIFS_SB_NODE: superblock node * UBIFS_MST_NODE: master node * UBIFS_REF_NODE: LEB reference node * UBIFS_IDX_NODE: index node * UBIFS_CS_NODE: commit start node * UBIFS_ORPH_NODE: orphan node * UBIFS_NODE_TYPES_CNT: count of supported node types * * Note, we index arrays by these numbers, so keep them low and contiguous. * Node type constants for inodes, direntries and so on have to be the same as * corresponding key type constants. */ enum { UBIFS_INO_NODE, UBIFS_DATA_NODE, UBIFS_DENT_NODE, UBIFS_XENT_NODE, UBIFS_TRUN_NODE, UBIFS_PAD_NODE, UBIFS_SB_NODE, UBIFS_MST_NODE, UBIFS_REF_NODE, UBIFS_IDX_NODE, UBIFS_CS_NODE, UBIFS_ORPH_NODE, UBIFS_NODE_TYPES_CNT, }; /* * Master node flags. * * UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty * UBIFS_MST_NO_ORPHS: no orphan inodes present * UBIFS_MST_RCVRY: written by recovery */ enum { UBIFS_MST_DIRTY = 1, UBIFS_MST_NO_ORPHS = 2, UBIFS_MST_RCVRY = 4, }; /* * Node group type (used by recovery to recover whole group or none). * * UBIFS_NO_NODE_GROUP: this node is not part of a group * UBIFS_IN_NODE_GROUP: this node is a part of a group * UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group */ enum { UBIFS_NO_NODE_GROUP = 0, UBIFS_IN_NODE_GROUP, UBIFS_LAST_OF_NODE_GROUP, }; /* * Superblock flags. * * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set * UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed */ enum { UBIFS_FLG_BIGLPT = 0x02, UBIFS_FLG_SPACE_FIXUP = 0x04, }; /** * struct ubifs_ch - common header node. * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) * @crc: CRC-32 checksum of the node header * @sqnum: sequence number * @len: full node length * @node_type: node type * @group_type: node group type * @padding: reserved for future, zeroes * * Every UBIFS node starts with this common part. If the node has a key, the * key always goes next. */ struct ubifs_ch { __le32 magic; __le32 crc; __le64 sqnum; __le32 len; __u8 node_type; __u8 group_type; __u8 padding[2]; } __attribute__ ((packed)); /** * union ubifs_dev_desc - device node descriptor. * @new: new type device descriptor * @huge: huge type device descriptor * * This data structure describes major/minor numbers of a device node. In an * inode is a device node then its data contains an object of this type. UBIFS * uses standard Linux "new" and "huge" device node encodings. */ union ubifs_dev_desc { __le32 new; __le64 huge; } __attribute__ ((packed)); /** * struct ubifs_ino_node - inode node. * @ch: common header * @key: node key * @creat_sqnum: sequence number at time of creation * @size: inode size in bytes (amount of uncompressed data) * @atime_sec: access time seconds * @ctime_sec: creation time seconds * @mtime_sec: modification time seconds * @atime_nsec: access time nanoseconds * @ctime_nsec: creation time nanoseconds * @mtime_nsec: modification time nanoseconds * @nlink: number of hard links * @uid: owner ID * @gid: group ID * @mode: access flags * @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc) * @data_len: inode data length * @xattr_cnt: count of extended attributes this inode has * @xattr_size: summarized size of all extended attributes in bytes * @padding1: reserved for future, zeroes * @xattr_names: sum of lengths of all extended attribute names belonging to * this inode * @compr_type: compression type used for this inode * @padding2: reserved for future, zeroes * @data: data attached to the inode * * Note, even though inode compression type is defined by @compr_type, some * nodes of this inode may be compressed with different compressor - this * happens if compression type is changed while the inode already has data * nodes. But @compr_type will be use for further writes to the inode. * * Note, do not forget to amend 'zero_ino_node_unused()' function when changing * the padding fields. */ struct ubifs_ino_node { struct ubifs_ch ch; __u8 key[UBIFS_MAX_KEY_LEN]; __le64 creat_sqnum; __le64 size; __le64 atime_sec; __le64 ctime_sec; __le64 mtime_sec; __le32 atime_nsec; __le32 ctime_nsec; __le32 mtime_nsec; __le32 nlink; __le32 uid; __le32 gid; __le32 mode; __le32 flags; __le32 data_len; __le32 xattr_cnt; __le32 xattr_size; __u8 padding1[4]; /* Watch 'zero_ino_node_unused()' if changing! */ __le32 xattr_names; __le16 compr_type; __u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */ __u8 data[]; } __attribute__ ((packed)); /** * struct ubifs_dent_node - directory entry node. * @ch: common header * @key: node key * @inum: target inode number * @padding1: reserved for future, zeroes * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc) * @nlen: name length * @padding2: reserved for future, zeroes * @name: zero-terminated name * * Note, do not forget to amend 'zero_dent_node_unused()' function when * changing the padding fields. */ struct ubifs_dent_node { struct ubifs_ch ch; __u8 key[UBIFS_MAX_KEY_LEN]; __le64 inum; __u8 padding1; __u8 type; __le16 nlen; __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */ __u8 name[]; } __attribute__ ((packed)); /** * struct ubifs_data_node - data node. * @ch: common header * @key: node key * @size: uncompressed data size in bytes * @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc) * @padding: reserved for future, zeroes * @data: data * * Note, do not forget to amend 'zero_data_node_unused()' function when * changing the padding fields. */ struct ubifs_data_node { struct ubifs_ch ch; __u8 key[UBIFS_MAX_KEY_LEN]; __le32 size; __le16 compr_type; __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */ __u8 data[]; } __attribute__ ((packed)); /** * struct ubifs_trun_node - truncation node. * @ch: common header * @inum: truncated inode number * @padding: reserved for future, zeroes * @old_size: size before truncation * @new_size: size after truncation * * This node exists only in the journal and never goes to the main area. Note, * do not forget to amend 'zero_trun_node_unused()' function when changing the * padding fields. */ struct ubifs_trun_node { struct ubifs_ch ch; __le32 inum; __u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */ __le64 old_size; __le64 new_size; } __attribute__ ((packed)); /** * struct ubifs_pad_node - padding node. * @ch: common header * @pad_len: how many bytes after this node are unused (because padded) * @padding: reserved for future, zeroes */ struct ubifs_pad_node { struct ubifs_ch ch; __le32 pad_len; } __attribute__ ((packed)); /** * struct ubifs_sb_node - superblock node. * @ch: common header * @padding: reserved for future, zeroes * @key_hash: type of hash function used in keys * @key_fmt: format of the key * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) * @min_io_size: minimal input/output unit size * @leb_size: logical eraseblock size in bytes * @leb_cnt: count of LEBs used by file-system * @max_leb_cnt: maximum count of LEBs used by file-system * @max_bud_bytes: maximum amount of data stored in buds * @log_lebs: log size in logical eraseblocks * @lpt_lebs: number of LEBs used for lprops table * @orph_lebs: number of LEBs used for recording orphans * @jhead_cnt: count of journal heads * @fanout: tree fanout (max. number of links per indexing node) * @lsave_cnt: number of LEB numbers in LPT's save table * @fmt_version: UBIFS on-flash format version * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc) * @padding1: reserved for future, zeroes * @rp_uid: reserve pool UID * @rp_gid: reserve pool GID * @rp_size: size of the reserved pool in bytes * @padding2: reserved for future, zeroes * @time_gran: time granularity in nanoseconds * @uuid: UUID generated when the file system image was created */ struct ubifs_sb_node { struct ubifs_ch ch; __u8 padding[2]; __u8 key_hash; __u8 key_fmt; __le32 flags; __le32 min_io_size; __le32 leb_size; __le32 leb_cnt; __le32 max_leb_cnt; __le64 max_bud_bytes; __le32 log_lebs; __le32 lpt_lebs; __le32 orph_lebs; __le32 jhead_cnt; __le32 fanout; __le32 lsave_cnt; __le32 fmt_version; __le16 default_compr; __u8 padding1[2]; __le32 rp_uid; __le32 rp_gid; __le64 rp_size; __le32 time_gran; __u8 uuid[16]; __u8 padding2[3972]; } __attribute__ ((packed)); /** * struct ubifs_mst_node - master node. * @ch: common header * @highest_inum: highest inode number in the committed index * @cmt_no: commit number * @flags: various flags (%UBIFS_MST_DIRTY, etc) * @log_lnum: start of the log * @root_lnum: LEB number of the root indexing node * @root_offs: offset within @root_lnum * @root_len: root indexing node length * @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was * not reserved and should be reserved on mount) * @ihead_lnum: LEB number of index head * @ihead_offs: offset of index head * @index_size: size of index on flash * @total_free: total free space in bytes * @total_dirty: total dirty space in bytes * @total_used: total used space in bytes (includes only data LEBs) * @total_dead: total dead space in bytes (includes only data LEBs) * @total_dark: total dark space in bytes (includes only data LEBs) * @lpt_lnum: LEB number of LPT root nnode * @lpt_offs: offset of LPT root nnode * @nhead_lnum: LEB number of LPT head * @nhead_offs: offset of LPT head * @ltab_lnum: LEB number of LPT's own lprops table * @ltab_offs: offset of LPT's own lprops table * @lsave_lnum: LEB number of LPT's save table (big model only) * @lsave_offs: offset of LPT's save table (big model only) * @lscan_lnum: LEB number of last LPT scan * @empty_lebs: number of empty logical eraseblocks * @idx_lebs: number of indexing logical eraseblocks * @leb_cnt: count of LEBs used by file-system * @padding: reserved for future, zeroes */ struct ubifs_mst_node { struct ubifs_ch ch; __le64 highest_inum; __le64 cmt_no; __le32 flags; __le32 log_lnum; __le32 root_lnum; __le32 root_offs; __le32 root_len; __le32 gc_lnum; __le32 ihead_lnum; __le32 ihead_offs; __le64 index_size; __le64 total_free; __le64 total_dirty; __le64 total_used; __le64 total_dead; __le64 total_dark; __le32 lpt_lnum; __le32 lpt_offs; __le32 nhead_lnum; __le32 nhead_offs; __le32 ltab_lnum; __le32 ltab_offs; __le32 lsave_lnum; __le32 lsave_offs; __le32 lscan_lnum; __le32 empty_lebs; __le32 idx_lebs; __le32 leb_cnt; __u8 padding[344]; } __attribute__ ((packed)); /** * struct ubifs_ref_node - logical eraseblock reference node. * @ch: common header * @lnum: the referred logical eraseblock number * @offs: start offset in the referred LEB * @jhead: journal head number * @padding: reserved for future, zeroes */ struct ubifs_ref_node { struct ubifs_ch ch; __le32 lnum; __le32 offs; __le32 jhead; __u8 padding[28]; } __attribute__ ((packed)); /** * struct ubifs_branch - key/reference/length branch * @lnum: LEB number of the target node * @offs: offset within @lnum * @len: target node length * @key: key */ struct ubifs_branch { __le32 lnum; __le32 offs; __le32 len; __u8 key[]; } __attribute__ ((packed)); /** * struct ubifs_idx_node - indexing node. * @ch: common header * @child_cnt: number of child index nodes * @level: tree level * @branches: LEB number / offset / length / key branches */ struct ubifs_idx_node { struct ubifs_ch ch; __le16 child_cnt; __le16 level; __u8 branches[]; } __attribute__ ((packed)); /** * struct ubifs_cs_node - commit start node. * @ch: common header * @cmt_no: commit number */ struct ubifs_cs_node { struct ubifs_ch ch; __le64 cmt_no; } __attribute__ ((packed)); /** * struct ubifs_orph_node - orphan node. * @ch: common header * @cmt_no: commit number (also top bit is set on the last node of the commit) * @inos: inode numbers of orphans */ struct ubifs_orph_node { struct ubifs_ch ch; __le64 cmt_no; __le64 inos[]; } __attribute__ ((packed)); #endif /* __UBIFS_MEDIA_H__ */ mtd-utils-1.5.0/mkfs.ubifs/ubifs.h000066400000000000000000000265721175167361300170150ustar00rootroot00000000000000/* * This file is part of UBIFS. * * Copyright (C) 2008 Nokia Corporation. * Copyright (C) 2008 University of Szeged, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Artem Bityutskiy * Adrian Hunter * Zoltan Sogor */ #ifndef __UBIFS_H__ #define __UBIFS_H__ /* Maximum logical eraseblock size in bytes */ #define UBIFS_MAX_LEB_SZ (2*1024*1024) /* Minimum amount of data UBIFS writes to the flash */ #define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) /* Largest key size supported in this implementation */ #define CUR_MAX_KEY_LEN UBIFS_SK_LEN /* * There is no notion of truncation key because truncation nodes do not exist * in TNC. However, when replaying, it is handy to introduce fake "truncation" * keys for truncation nodes because the code becomes simpler. So we define * %UBIFS_TRUN_KEY type. */ #define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT /* The below union makes it easier to deal with keys */ union ubifs_key { uint8_t u8[CUR_MAX_KEY_LEN]; uint32_t u32[CUR_MAX_KEY_LEN/4]; uint64_t u64[CUR_MAX_KEY_LEN/8]; __le32 j32[CUR_MAX_KEY_LEN/4]; }; /* * LEB properties flags. * * LPROPS_UNCAT: not categorized * LPROPS_DIRTY: dirty > 0, not index * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index * LPROPS_FREE: free > 0, not empty, not index * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs * LPROPS_EMPTY: LEB is empty, not taken * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken * LPROPS_CAT_MASK: mask for the LEB categories above * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) */ enum { LPROPS_UNCAT = 0, LPROPS_DIRTY = 1, LPROPS_DIRTY_IDX = 2, LPROPS_FREE = 3, LPROPS_HEAP_CNT = 3, LPROPS_EMPTY = 4, LPROPS_FREEABLE = 5, LPROPS_FRDI_IDX = 6, LPROPS_CAT_MASK = 15, LPROPS_TAKEN = 16, LPROPS_INDEX = 32, }; /** * struct ubifs_lprops - logical eraseblock properties. * @free: amount of free space in bytes * @dirty: amount of dirty space in bytes * @flags: LEB properties flags (see above) */ struct ubifs_lprops { int free; int dirty; int flags; }; /** * struct ubifs_lpt_lprops - LPT logical eraseblock properties. * @free: amount of free space in bytes * @dirty: amount of dirty space in bytes */ struct ubifs_lpt_lprops { int free; int dirty; }; struct ubifs_nnode; /** * struct ubifs_cnode - LEB Properties Tree common node. * @parent: parent nnode * @cnext: next cnode to commit * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) * @iip: index in parent * @level: level in the tree (zero for pnodes, greater than zero for nnodes) * @num: node number */ struct ubifs_cnode { struct ubifs_nnode *parent; struct ubifs_cnode *cnext; unsigned long flags; int iip; int level; int num; }; /** * struct ubifs_pnode - LEB Properties Tree leaf node. * @parent: parent nnode * @cnext: next cnode to commit * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) * @iip: index in parent * @level: level in the tree (always zero for pnodes) * @num: node number * @lprops: LEB properties array */ struct ubifs_pnode { struct ubifs_nnode *parent; struct ubifs_cnode *cnext; unsigned long flags; int iip; int level; int num; struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; }; /** * struct ubifs_nbranch - LEB Properties Tree internal node branch. * @lnum: LEB number of child * @offs: offset of child * @nnode: nnode child * @pnode: pnode child * @cnode: cnode child */ struct ubifs_nbranch { int lnum; int offs; union { struct ubifs_nnode *nnode; struct ubifs_pnode *pnode; struct ubifs_cnode *cnode; }; }; /** * struct ubifs_nnode - LEB Properties Tree internal node. * @parent: parent nnode * @cnext: next cnode to commit * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) * @iip: index in parent * @level: level in the tree (always greater than zero for nnodes) * @num: node number * @nbranch: branches to child nodes */ struct ubifs_nnode { struct ubifs_nnode *parent; struct ubifs_cnode *cnext; unsigned long flags; int iip; int level; int num; struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; }; /** * struct ubifs_lp_stats - statistics of eraseblocks in the main area. * @empty_lebs: number of empty LEBs * @taken_empty_lebs: number of taken LEBs * @idx_lebs: number of indexing LEBs * @total_free: total free space in bytes * @total_dirty: total dirty space in bytes * @total_used: total used space in bytes (includes only data LEBs) * @total_dead: total dead space in bytes (includes only data LEBs) * @total_dark: total dark space in bytes (includes only data LEBs) */ struct ubifs_lp_stats { int empty_lebs; int taken_empty_lebs; int idx_lebs; long long total_free; long long total_dirty; long long total_used; long long total_dead; long long total_dark; }; /** * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. * @key: key * @znode: znode address in memory * @lnum: LEB number of the indexing node * @offs: offset of the indexing node within @lnum * @len: target node length */ struct ubifs_zbranch { union ubifs_key key; struct ubifs_znode *znode; int lnum; int offs; int len; }; /** * struct ubifs_znode - in-memory representation of an indexing node. * @parent: parent znode or NULL if it is the root * @cnext: next znode to commit * @flags: flags * @time: last access time (seconds) * @level: level of the entry in the TNC tree * @child_cnt: count of child znodes * @iip: index in parent's zbranch array * @alt: lower bound of key range has altered i.e. child inserted at slot 0 * @zbranch: array of znode branches (@c->fanout elements) */ struct ubifs_znode { struct ubifs_znode *parent; struct ubifs_znode *cnext; unsigned long flags; unsigned long time; int level; int child_cnt; int iip; int alt; #ifdef CONFIG_UBIFS_FS_DEBUG int lnum, offs, len; #endif struct ubifs_zbranch zbranch[]; }; /** * struct ubifs_info - UBIFS file-system description data structure * (per-superblock). * * @highest_inum: highest used inode number * @max_sqnum: current global sequence number * * @jhead_cnt: count of journal heads * @max_bud_bytes: maximum number of bytes allowed in buds * * @zroot: zbranch which points to the root index node and znode * @ihead_lnum: LEB number of index head * @ihead_offs: offset of index head * * @log_lebs: number of logical eraseblocks in the log * @lpt_lebs: number of LEBs used for lprops table * @lpt_first: first LEB of the lprops table area * @lpt_last: last LEB of the lprops table area * @main_lebs: count of LEBs in the main area * @main_first: first LEB of the main area * @default_compr: default compression type * @favor_lzo: favor LZO compression method * @favor_percent: lzo vs. zlib threshold used in case favor LZO * * @key_hash_type: type of the key hash * @key_hash: direntry key hash function * @key_fmt: key format * @key_len: key length * @fanout: fanout of the index tree (number of links per indexing node) * * @min_io_size: minimal input/output unit size * @leb_size: logical eraseblock size in bytes * @leb_cnt: count of logical eraseblocks * @max_leb_cnt: maximum count of logical eraseblocks * * @old_idx_sz: size of index on flash * @lst: lprops statistics * * @dead_wm: LEB dead space watermark * @dark_wm: LEB dark space watermark * * @di: UBI device information * @vi: UBI volume information * * @gc_lnum: LEB number used for garbage collection * @rp_size: reserved pool size * * @space_bits: number of bits needed to record free or dirty space * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT * @lpt_offs_bits: number of bits needed to record an offset in the LPT * @lpt_spc_bits: number of bits needed to space in the LPT * @pcnt_bits: number of bits needed to record pnode or nnode number * @lnum_bits: number of bits needed to record LEB number * @nnode_sz: size of on-flash nnode * @pnode_sz: size of on-flash pnode * @ltab_sz: size of on-flash LPT lprops table * @lsave_sz: size of on-flash LPT save table * @pnode_cnt: number of pnodes * @nnode_cnt: number of nnodes * @lpt_hght: height of the LPT * * @lpt_lnum: LEB number of the root nnode of the LPT * @lpt_offs: offset of the root nnode of the LPT * @nhead_lnum: LEB number of LPT head * @nhead_offs: offset of LPT head * @big_lpt: flag that LPT is too big to write whole during commit * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up * @lpt_sz: LPT size * * @ltab_lnum: LEB number of LPT's own lprops table * @ltab_offs: offset of LPT's own lprops table * @lpt: lprops table * @ltab: LPT's own lprops table * @lsave_cnt: number of LEB numbers in LPT's save table * @lsave_lnum: LEB number of LPT's save table * @lsave_offs: offset of LPT's save table * @lsave: LPT's save table * @lscan_lnum: LEB number of last LPT scan */ struct ubifs_info { ino_t highest_inum; unsigned long long max_sqnum; int jhead_cnt; long long max_bud_bytes; struct ubifs_zbranch zroot; int ihead_lnum; int ihead_offs; int log_lebs; int lpt_lebs; int lpt_first; int lpt_last; int orph_lebs; int main_lebs; int main_first; int default_compr; int favor_lzo; int favor_percent; uint8_t key_hash_type; uint32_t (*key_hash)(const char *str, int len); int key_fmt; int key_len; int fanout; int min_io_size; int leb_size; int leb_cnt; int max_leb_cnt; unsigned long long old_idx_sz; struct ubifs_lp_stats lst; int dead_wm; int dark_wm; struct ubi_dev_info di; struct ubi_vol_info vi; int gc_lnum; long long rp_size; int space_bits; int lpt_lnum_bits; int lpt_offs_bits; int lpt_spc_bits; int pcnt_bits; int lnum_bits; int nnode_sz; int pnode_sz; int ltab_sz; int lsave_sz; int pnode_cnt; int nnode_cnt; int lpt_hght; int lpt_lnum; int lpt_offs; int nhead_lnum; int nhead_offs; int big_lpt; int space_fixup; long long lpt_sz; int ltab_lnum; int ltab_offs; struct ubifs_lprops *lpt; struct ubifs_lpt_lprops *ltab; int lsave_cnt; int lsave_lnum; int lsave_offs; int *lsave; int lscan_lnum; }; /** * ubifs_idx_node_sz - return index node size. * @c: the UBIFS file-system description object * @child_cnt: number of children of this index node */ static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) { return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; } /** * ubifs_idx_branch - return pointer to an index branch. * @c: the UBIFS file-system description object * @idx: index node * @bnum: branch number */ static inline struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, const struct ubifs_idx_node *idx, int bnum) { return (struct ubifs_branch *)((void *)idx->branches + (UBIFS_BRANCH_SZ + c->key_len) * bnum); } #endif /* __UBIFS_H__ */ mtd-utils-1.5.0/mtd-utils.spec000066400000000000000000000013271175167361300162520ustar00rootroot00000000000000Summary: 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 - 1.0 - Initial build. mtd-utils-1.5.0/mtd_debug.c000066400000000000000000000221201175167361300155440ustar00rootroot00000000000000/* * Copyright (c) 2d3D, Inc. * Written by Abraham vd Merwe * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define PROGRAM_NAME "mtd_debug" #include #include #include #include #include #include #include #include #include #include #include "common.h" /* * MEMGETINFO */ static int getmeminfo(int fd, struct mtd_info_user *mtd) { return ioctl(fd, MEMGETINFO, mtd); } /* * MEMERASE */ static int memerase(int fd, struct erase_info_user *erase) { return ioctl(fd, MEMERASE, erase); } /* * MEMGETREGIONCOUNT * MEMGETREGIONINFO */ static int getregions(int fd, struct region_info_user *regions, int *n) { int i, err; err = ioctl(fd, MEMGETREGIONCOUNT, n); if (err) return err; for (i = 0; i < *n; i++) { regions[i].regionindex = i; err = ioctl(fd, MEMGETREGIONINFO, ®ions[i]); if (err) return err; } return 0; } int erase_flash(int fd, u_int32_t offset, u_int32_t bytes) { int err; struct erase_info_user erase; erase.start = offset; erase.length = bytes; err = memerase(fd, &erase); if (err < 0) { perror("MEMERASE"); return 1; } fprintf(stderr, "Erased %d bytes from address 0x%.8x in flash\n", bytes, offset); return 0; } void printsize(u_int32_t x) { int i; static const char *flags = "KMGT"; printf("%u ", x); for (i = 0; x >= 1024 && flags[i] != '\0'; i++) x /= 1024; i--; if (i >= 0) printf("(%u%c)", x, flags[i]); } int flash_to_file(int fd, 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, 0666); if (outfd < 0) { perror("creat()"); goto err1; } retry: if ((buf = (u_int8_t *) malloc(size)) == NULL) { #define BUF_SIZE (64 * 1024 * sizeof(u_int8_t)) fprintf(stderr, "%s: malloc(%#x)\n", __func__, size); if (size != BUF_SIZE) { size = BUF_SIZE; fprintf(stderr, "%s: trying buffer size %#x\n", __func__, size); goto retry; } perror("malloc()"); goto err0; } do { if (n <= size) size = n; err = read(fd, buf, size); if (err < 0) { fprintf(stderr, "%s: read, size %#x, n %#x\n", __func__, size, n); perror("read()"); goto err2; } err = write(outfd, buf, size); if (err < 0) { fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, size, n); perror("write()"); goto err2; } if (err != size) { fprintf(stderr, "Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n", filename, err, size); goto err2; } n -= size; } while (n > 0); if (buf != NULL) free(buf); close(outfd); printf("Copied %zu bytes from address 0x%.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", __func__, size); if (size != BUF_SIZE) { size = BUF_SIZE; fprintf(stderr, "%s: trying buffer size %#x\n", __func__, size); goto retry; } perror("malloc()"); fclose(fp); return 1; } do { if (n <= size) size = n; if (fread(buf, size, 1, fp) != 1 || ferror(fp)) { fprintf(stderr, "%s: fread, size %#x, n %#x\n", __func__, size, n); perror("fread()"); free(buf); fclose(fp); return 1; } err = write(fd, buf, size); if (err < 0) { fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, size, n); perror("write()"); free(buf); fclose(fp); return 1; } n -= size; } while (n > 0); if (buf != NULL) free(buf); fclose(fp); printf("Copied %d bytes from %s to address 0x%.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_POWERUP_LOCK", MTD_POWERUP_LOCK }, { NULL, -1 } }; for (i = 0; flags[i].name != NULL; i++) { if (mtd.flags & flags[i].value) { if (first) { printf("%s", flags[i].name); first = 0; } else { printf(" | %s", flags[i].name); } } } } printf("\nmtd.size = "); printsize(mtd.size); printf("\nmtd.erasesize = "); printsize(mtd.erasesize); printf("\nmtd.writesize = "); printsize(mtd.writesize); printf("\nmtd.oobsize = "); printsize(mtd.oobsize); printf("\nregions = %d\n\n", n); for (i = 0; i < n; i++) { printf("region[%d].offset = 0x%.8x\n" "region[%d].erasesize = ", i, region[i].offset, i); printsize(region[i].erasesize); printf("\nregion[%d].numblocks = %d\n" "region[%d].regionindex = %d\n", i, region[i].numblocks, i, region[i].regionindex); } return 0; } void showusage(void) { fprintf(stderr, "usage: %1$s info \n" " %1$s read \n" " %1$s write \n" " %1$s erase \n", PROGRAM_NAME); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int err = 0, fd; int open_flag; enum { OPT_INFO, OPT_READ, OPT_WRITE, OPT_ERASE } option = OPT_INFO; /* parse command-line options */ if (argc == 3 && !strcmp(argv[1], "info")) option = OPT_INFO; else if (argc == 6 && !strcmp(argv[1], "read")) option = OPT_READ; else if (argc == 6 && !strcmp(argv[1], "write")) option = OPT_WRITE; else if (argc == 5 && !strcmp(argv[1], "erase")) option = OPT_ERASE; else showusage(); /* open device */ open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR; if ((fd = open(argv[2], O_SYNC | open_flag)) < 0) errmsg_die("open()"); switch (option) { case OPT_INFO: showinfo(fd); break; case OPT_READ: err = flash_to_file(fd, 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) errmsg_die("close()"); return err; } mtd-utils-1.5.0/nanddump.c000066400000000000000000000324351175167361300154320ustar00rootroot00000000000000/* * nanddump.c * * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) * Steven J. Hill (sjhill@realitydiluted.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Overview: * This utility dumps the contents of raw NAND chips or NAND * chips contained in DoC devices. */ #define PROGRAM_NAME "nanddump" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include static void display_help(void) { printf( "Usage: %s [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" " --bb=METHOD Choose bad block handling method (see below).\n" "-a --forcebinary Force printing of binary data to tty\n" "-c --canonicalprint Print canonical Hex+ASCII dump\n" "-f file --file=file Dump to file\n" "-l length --length=length Length\n" "-n --noecc Read without error correction\n" " --omitoob Omit OOB data (default)\n" "-o --oob Dump OOB data\n" "-p --prettyprint Print nice (hexdump)\n" "-q --quiet Don't display progress and status messages\n" "-s addr --startaddress=addr Start address\n" "\n" "--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':\n" " padbad: dump flash data, substituting 0xFF for any bad blocks\n" " dumpbad: dump flash data, including any bad blocks\n" " skipbad: dump good data, completely skipping any bad blocks (default)\n", PROGRAM_NAME); exit(EXIT_SUCCESS); } static void display_version(void) { printf("%1$s " VERSION "\n" "\n" "%1$s comes with NO WARRANTY\n" "to the extent permitted by law.\n" "\n" "You may redistribute copies of %1$s\n" "under the terms of the GNU General Public Licence.\n" "See the file `COPYING' for more information.\n", PROGRAM_NAME); exit(EXIT_SUCCESS); } // Option variables static bool pretty_print = false; // print nice static bool noecc = false; // don't error correct static bool omitoob = true; // omit oob data static long long start_addr; // start address static long long length; // dump length static const char *mtddev; // mtd device name static const char *dumpfile; // dump file name static bool quiet = false; // suppress diagnostic output static bool canonical = false; // print nice + ascii static bool forcebinary = false; // force printing binary to tty static enum { padbad, // dump flash data, substituting 0xFF for any bad blocks dumpbad, // dump flash data, including any bad blocks skipbad, // dump good data, completely skipping any bad blocks } bb_method = skipbad; static void process_options(int argc, char * const argv[]) { int error = 0; bool oob_default = true; for (;;) { int option_index = 0; static const char *short_options = "s:f:l:opqnca"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"bb", required_argument, 0, 0}, {"omitoob", no_argument, 0, 0}, {"forcebinary", no_argument, 0, 'a'}, {"canonicalprint", no_argument, 0, 'c'}, {"file", required_argument, 0, 'f'}, {"oob", no_argument, 0, 'o'}, {"prettyprint", no_argument, 0, 'p'}, {"startaddress", required_argument, 0, 's'}, {"length", required_argument, 0, 'l'}, {"noecc", no_argument, 0, 'n'}, {"quiet", no_argument, 0, 'q'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) { break; } switch (c) { case 0: switch (option_index) { case 0: display_help(); break; case 1: display_version(); break; case 2: /* Handle --bb=METHOD */ if (!strcmp(optarg, "padbad")) bb_method = padbad; else if (!strcmp(optarg, "dumpbad")) bb_method = dumpbad; else if (!strcmp(optarg, "skipbad")) bb_method = skipbad; else error++; break; case 3: /* --omitoob */ if (oob_default) { oob_default = false; omitoob = true; } else { errmsg_die("--oob and --oomitoob are mutually exclusive"); } break; } break; case 's': start_addr = simple_strtoll(optarg, &error); break; case 'f': if (!(dumpfile = strdup(optarg))) { perror("stddup"); exit(EXIT_FAILURE); } break; case 'l': length = simple_strtoll(optarg, &error); break; case 'o': if (oob_default) { oob_default = false; omitoob = false; } else { errmsg_die("--oob and --oomitoob are mutually exclusive"); } break; case 'a': forcebinary = true; break; case 'c': canonical = true; case 'p': pretty_print = true; break; case 'q': quiet = true; break; case 'n': noecc = true; break; case '?': error++; break; } } if (start_addr < 0) errmsg_die("Can't specify negative offset with option -s: %lld", start_addr); if (length < 0) errmsg_die("Can't specify negative length with option -l: %lld", length); if (quiet && pretty_print) { fprintf(stderr, "The quiet and pretty print options are mutually-\n" "exclusive. Choose one or the other.\n"); exit(EXIT_FAILURE); } if (forcebinary && pretty_print) { fprintf(stderr, "The forcebinary and pretty print options are\n" "mutually-exclusive. Choose one or the " "other.\n"); exit(EXIT_FAILURE); } if ((argc - optind) != 1 || error) display_help(); mtddev = argv[optind]; } #define PRETTY_ROW_SIZE 16 #define PRETTY_BUF_LEN 80 /** * pretty_dump_to_buffer - formats a blob of data to "hex ASCII" in memory * @buf: data blob to dump * @len: number of bytes in the @buf * @linebuf: where to put the converted data * @linebuflen: total size of @linebuf, including space for terminating NULL * @pagedump: true - dumping as page format; false - dumping as OOB format * @ascii: dump ascii formatted data next to hexdump * @prefix: address to print before line in a page dump, ignored if !pagedump * * pretty_dump_to_buffer() works on one "line" of output at a time, i.e., * PRETTY_ROW_SIZE bytes of input data converted to hex + ASCII output. * * Given a buffer of unsigned char data, pretty_dump_to_buffer() converts the * input data to a hex/ASCII dump at the supplied memory location. A prefix * is included based on whether we are dumping page or OOB data. The converted * output is always NULL-terminated. * * e.g. * pretty_dump_to_buffer(data, data_len, prettybuf, linelen, true, * false, 256); * produces: * 0x00000100: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f * NOTE: This function was adapted from linux kernel, "lib/hexdump.c" */ static void pretty_dump_to_buffer(const unsigned char *buf, size_t len, char *linebuf, size_t linebuflen, bool pagedump, bool ascii, unsigned long long prefix) { static const char hex_asc[] = "0123456789abcdef"; unsigned char ch; unsigned int j, lx = 0, ascii_column; if (pagedump) lx += sprintf(linebuf, "0x%.8llx: ", prefix); else lx += sprintf(linebuf, " OOB Data: "); if (!len) goto nil; if (len > PRETTY_ROW_SIZE) /* limit to one line at a time */ len = PRETTY_ROW_SIZE; for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { ch = buf[j]; linebuf[lx++] = hex_asc[(ch & 0xf0) >> 4]; linebuf[lx++] = hex_asc[ch & 0x0f]; linebuf[lx++] = ' '; } if (j) lx--; ascii_column = 3 * PRETTY_ROW_SIZE + 14; if (!ascii) goto nil; /* Spacing between hex and ASCII - ensure at least one space */ lx += sprintf(linebuf + lx, "%*s", MAX((int)MIN(linebuflen, ascii_column) - 1 - lx, 1), " "); linebuf[lx++] = '|'; for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) linebuf[lx++] = (isascii(buf[j]) && isprint(buf[j])) ? buf[j] : '.'; linebuf[lx++] = '|'; nil: linebuf[lx++] = '\n'; linebuf[lx++] = '\0'; } /* * Main program */ int main(int argc, char * const argv[]) { long long ofs, end_addr = 0; long long blockstart = 1; int i, fd, ofd = 0, bs, badblock = 0; struct mtd_dev_info mtd; char pretty_buf[PRETTY_BUF_LEN]; int firstblock = 1; struct mtd_ecc_stats stat1, stat2; bool eccstats = false; unsigned char *readbuf = NULL, *oobbuf = NULL; libmtd_t mtd_desc; process_options(argc, argv); /* Initialize libmtd */ mtd_desc = libmtd_open(); if (!mtd_desc) return errmsg("can't initialize libmtd"); /* Open MTD device */ if ((fd = open(mtddev, O_RDONLY)) == -1) { perror(mtddev); exit(EXIT_FAILURE); } /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) return errmsg("mtd_get_dev_info failed"); /* Allocate buffers */ oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); if (noecc) { if (ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW) != 0) { perror("MTDFILEMODE"); goto closeall; } } else { /* check if we can read ecc stats */ if (!ioctl(fd, ECCGETSTATS, &stat1)) { eccstats = true; if (!quiet) { fprintf(stderr, "ECC failed: %d\n", stat1.failed); fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); } } else perror("No ECC status information available"); } /* Open output file for writing. If file name is "-", write to standard * output. */ if (!dumpfile) { ofd = STDOUT_FILENO; } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { perror(dumpfile); goto closeall; } if (!pretty_print && !forcebinary && isatty(ofd)) { fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" "or '--forcebinary' to override.\n"); goto closeall; } /* Initialize start/end addresses and block size */ if (start_addr & (mtd.min_io_size - 1)) { fprintf(stderr, "the start address (-s parameter) is not page-aligned!\n" "The pagesize of this NAND Flash is 0x%x.\n", mtd.min_io_size); goto closeall; } if (length) end_addr = start_addr + length; if (!length || end_addr > mtd.size) end_addr = mtd.size; bs = mtd.min_io_size; /* Print informative message */ if (!quiet) { fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", mtd.eb_size, mtd.min_io_size, mtd.oob_size); fprintf(stderr, "Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", start_addr, end_addr); } /* Dump the flash contents */ for (ofs = start_addr; ofs < end_addr; ofs += bs) { /* Check for bad block */ if (bb_method == dumpbad) { badblock = 0; } else if (blockstart != (ofs & (~mtd.eb_size + 1)) || firstblock) { blockstart = ofs & (~mtd.eb_size + 1); firstblock = 0; if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { errmsg("libmtd: mtd_is_bad"); goto closeall; } } if (badblock) { /* skip bad block, increase end_addr */ if (bb_method == skipbad) { end_addr += mtd.eb_size; ofs += mtd.eb_size - bs; if (end_addr > mtd.size) end_addr = mtd.size; continue; } memset(readbuf, 0xff, bs); } else { /* Read page data and exit on failure */ if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { errmsg("mtd_read"); goto closeall; } } /* ECC stats available ? */ if (eccstats) { if (ioctl(fd, ECCGETSTATS, &stat2)) { perror("ioctl(ECCGETSTATS)"); goto closeall; } if (stat1.failed != stat2.failed) fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" " at offset 0x%08llx\n", stat2.failed - stat1.failed, ofs); if (stat1.corrected != stat2.corrected) fprintf(stderr, "ECC: %d corrected bitflip(s) at" " offset 0x%08llx\n", stat2.corrected - stat1.corrected, ofs); stat1 = stat2; } /* Write out page data */ if (pretty_print) { for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); write(ofd, pretty_buf, strlen(pretty_buf)); } } else write(ofd, readbuf, bs); if (omitoob) continue; if (badblock) { memset(oobbuf, 0xff, mtd.oob_size); } else { /* Read OOB data and exit on failure */ if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { errmsg("libmtd: mtd_read_oob"); goto closeall; } } /* Write out OOB data */ if (pretty_print) { for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); write(ofd, pretty_buf, strlen(pretty_buf)); } } else write(ofd, oobbuf, mtd.oob_size); } /* Close the output file and MTD device, free memory */ close(fd); close(ofd); free(oobbuf); free(readbuf); /* Exit happy */ return EXIT_SUCCESS; closeall: close(fd); close(ofd); free(oobbuf); free(readbuf); exit(EXIT_FAILURE); } mtd-utils-1.5.0/nandtest.c000066400000000000000000000140761175167361300154450ustar00rootroot00000000000000#define PROGRAM_NAME "nandtest" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtd/mtd-user.h" void usage(void) { fprintf(stderr, "usage: %s [OPTIONS] \n\n" " -h, --help Display this help output\n" " -m, --markbad Mark blocks bad if they appear so\n" " -s, --seed Supply random seed\n" " -p, --passes Number of passes\n" " -o, --offset Start offset on flash\n" " -l, --length Length of flash to test\n" " -k, --keep Restore existing contents after test\n", PROGRAM_NAME); exit(1); } struct mtd_info_user meminfo; struct mtd_ecc_stats oldstats, newstats; int fd; int markbad=0; int seed; int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf) { struct erase_info_user er; ssize_t len; int i; printf("\r%08x: erasing... ", (unsigned)ofs); fflush(stdout); er.start = ofs; er.length = meminfo.erasesize; if (ioctl(fd, MEMERASE, &er)) { perror("MEMERASE"); if (markbad) { printf("Mark block bad at %08lx\n", (long)ofs); ioctl(fd, MEMSETBADBLOCK, &ofs); } return 1; } printf("\r%08x: writing...", (unsigned)ofs); fflush(stdout); len = pwrite(fd, data, meminfo.erasesize, ofs); if (len < 0) { printf("\n"); perror("write"); if (markbad) { printf("Mark block bad at %08lx\n", (long)ofs); ioctl(fd, MEMSETBADBLOCK, &ofs); } return 1; } if (len < meminfo.erasesize) { printf("\n"); fprintf(stderr, "Short write (%zd bytes)\n", len); exit(1); } printf("\r%08x: reading...", (unsigned)ofs); fflush(stdout); len = pread(fd, rbuf, meminfo.erasesize, ofs); if (len < meminfo.erasesize) { printf("\n"); if (len) fprintf(stderr, "Short read (%zd bytes)\n", len); else perror("read"); exit(1); } if (ioctl(fd, ECCGETSTATS, &newstats)) { printf("\n"); perror("ECCGETSTATS"); close(fd); exit(1); } if (newstats.corrected > oldstats.corrected) { printf("\n %d bit(s) ECC corrected at %08x\n", newstats.corrected - oldstats.corrected, (unsigned) ofs); oldstats.corrected = newstats.corrected; } if (newstats.failed > oldstats.failed) { printf("\nECC failed at %08x\n", (unsigned) ofs); oldstats.failed = newstats.failed; } if (len < meminfo.erasesize) exit(1); printf("\r%08x: checking...", (unsigned)ofs); fflush(stdout); if (memcmp(data, rbuf, meminfo.erasesize)) { printf("\n"); fprintf(stderr, "compare failed. seed %d\n", seed); for (i=0; i meminfo.size) { fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", length, offset, meminfo.size); exit(1); } wbuf = malloc(meminfo.erasesize * 3); if (!wbuf) { fprintf(stderr, "Could not allocate %d bytes for buffer\n", meminfo.erasesize * 2); exit(1); } rbuf = wbuf + meminfo.erasesize; kbuf = rbuf + meminfo.erasesize; if (ioctl(fd, ECCGETSTATS, &oldstats)) { perror("ECCGETSTATS"); close(fd); exit(1); } printf("ECC corrections: %d\n", oldstats.corrected); printf("ECC failures : %d\n", oldstats.failed); printf("Bad blocks : %d\n", oldstats.badblocks); printf("BBT blocks : %d\n", oldstats.bbtblocks); srand(seed); for (pass = 0; pass < nr_passes; pass++) { loff_t test_ofs; for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { ssize_t len; seed = rand(); srand(seed); if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); continue; } for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtd/mtd-user.h" #include "common.h" #include static void display_help(void) { printf( "Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" "Writes to the specified MTD device.\n" "\n" " -a, --autoplace Use auto OOB layout\n" " -m, --markbad Mark blocks bad if write fails\n" " -n, --noecc Write without ecc\n" " -N, --noskipbad Write without bad block skipping\n" " -o, --oob Image contains oob data\n" " -O, --onlyoob Image contains oob data and only write the oob part\n" " -s addr, --start=addr Set start address (default is 0)\n" " -p, --pad Pad to page size\n" " -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n" " -q, --quiet Don't display progress messages\n" " --help Display this help and exit\n" " --version Output version information and exit\n" ); exit(EXIT_SUCCESS); } static void display_version(void) { printf("%1$s " VERSION "\n" "\n" "Copyright (C) 2003 Thomas Gleixner \n" "\n" "%1$s comes with NO WARRANTY\n" "to the extent permitted by law.\n" "\n" "You may redistribute copies of %1$s\n" "under the terms of the GNU General Public Licence.\n" "See the file `COPYING' for more information.\n", PROGRAM_NAME); exit(EXIT_SUCCESS); } static const char *standard_input = "-"; static const char *mtd_device, *img; static long long mtdoffset = 0; static bool quiet = false; static bool writeoob = false; static bool onlyoob = false; static bool markbad = false; static bool noecc = false; static bool autoplace = false; static bool noskipbad = false; static bool pad = false; static int blockalign = 1; /* default to using actual block size */ static void process_options(int argc, char * const argv[]) { int error = 0; for (;;) { int option_index = 0; static const char *short_options = "b:mnNoOpqs:a"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"blockalign", required_argument, 0, 'b'}, {"markbad", no_argument, 0, 'm'}, {"noecc", no_argument, 0, 'n'}, {"noskipbad", no_argument, 0, 'N'}, {"oob", no_argument, 0, 'o'}, {"onlyoob", no_argument, 0, 'O'}, {"pad", no_argument, 0, 'p'}, {"quiet", no_argument, 0, 'q'}, {"start", required_argument, 0, 's'}, {"autoplace", no_argument, 0, 'a'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) { break; } switch (c) { case 0: switch (option_index) { case 0: display_help(); break; case 1: display_version(); break; } break; case 'q': quiet = true; break; case 'n': noecc = true; break; case 'N': noskipbad = true; break; case 'm': markbad = true; break; case 'o': writeoob = true; break; case 'O': writeoob = true; onlyoob = true; break; case 'p': pad = true; break; case 's': mtdoffset = simple_strtoll(optarg, &error); break; case 'b': blockalign = atoi(optarg); break; case 'a': autoplace = true; break; case '?': error++; break; } } if (mtdoffset < 0) errmsg_die("Can't specify negative device offset with option" " -s: %lld", mtdoffset); if (blockalign < 0) errmsg_die("Can't specify negative blockalign with option -b:" " %d", blockalign); if (autoplace && noecc) errmsg_die("Autoplacement and no-ECC are mutually exclusive"); if (!onlyoob && (pad && writeoob)) errmsg_die("Can't pad when oob data is present"); argc -= optind; argv += optind; /* * There must be at least the MTD device node positional * argument remaining and, optionally, the input file. */ if (argc < 1 || argc > 2 || error) display_help(); mtd_device = argv[0]; /* * Standard input may be specified either explictly as "-" or * implicity by simply omitting the second of the two * positional arguments. */ img = ((argc == 2) ? argv[1] : standard_input); } static void erase_buffer(void *buffer, size_t size) { const uint8_t kEraseByte = 0xff; if (buffer != NULL && size > 0) { memset(buffer, kEraseByte, size); } } /* * Main program */ int main(int argc, char * const argv[]) { int cnt = 0; int fd = -1; int ifd = -1; int imglen = 0, pagelen; bool baderaseblock = false; long long blockstart = -1; struct mtd_dev_info mtd; long long offs; int ret; bool failed = true; /* contains all the data read from the file so far for the current eraseblock */ unsigned char *filebuf = NULL; size_t filebuf_max = 0; size_t filebuf_len = 0; /* points to the current page inside filebuf */ unsigned char *writebuf = NULL; /* points to the OOB for the current page in filebuf */ unsigned char *oobbuf = NULL; libmtd_t mtd_desc; int ebsize_aligned; uint8_t write_mode; process_options(argc, argv); /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) == -1) sys_errmsg_die("%s", mtd_device); mtd_desc = libmtd_open(); if (!mtd_desc) errmsg_die("can't initialize libmtd"); /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) errmsg_die("mtd_get_dev_info failed"); /* * Pretend erasesize is specified number of blocks - to match jffs2 * (virtual) block size * Use this value throughout unless otherwise necessary */ ebsize_aligned = mtd.eb_size * blockalign; if (mtdoffset & (mtd.min_io_size - 1)) errmsg_die("The start address is not page-aligned !\n" "The pagesize of this NAND Flash is 0x%x.\n", mtd.min_io_size); /* Select OOB write mode */ if (noecc) write_mode = MTD_OPS_RAW; else if (autoplace) write_mode = MTD_OPS_AUTO_OOB; else write_mode = MTD_OPS_PLACE_OOB; if (noecc) { ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); if (ret) { switch (errno) { case ENOTTY: errmsg_die("ioctl MTDFILEMODE is missing"); default: sys_errmsg_die("MTDFILEMODE"); } } } /* Determine if we are reading from standard input or from a file. */ if (strcmp(img, standard_input) == 0) { ifd = STDIN_FILENO; } else { ifd = open(img, O_RDONLY); } if (ifd == -1) { perror(img); goto closeall; } pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); /* * For the standard input case, the input size is merely an * invariant placeholder and is set to the write page * size. Otherwise, just use the input file size. * * TODO: Add support for the -l,--length=length option (see * previous discussion by Tommi Airikka at * */ if (ifd == STDIN_FILENO) { imglen = pagelen; } else { imglen = lseek(ifd, 0, SEEK_END); lseek(ifd, 0, SEEK_SET); } /* Check, if file is page-aligned */ if ((!pad) && ((imglen % pagelen) != 0)) { fprintf(stderr, "Input file is not page-aligned. Use the padding " "option.\n"); goto closeall; } /* Check, if length fits into device */ if (((imglen / pagelen) * mtd.min_io_size) > (mtd.size - mtdoffset)) { fprintf(stderr, "Image %d bytes, NAND page %d bytes, OOB area %d" " bytes, device size %lld bytes\n", imglen, pagelen, mtd.oob_size, mtd.size); sys_errmsg("Input file does not fit into device"); goto closeall; } /* * Allocate a buffer big enough to contain all the data (OOB included) * for one eraseblock. The order of operations here matters; if ebsize * and pagelen are large enough, then "ebsize_aligned * pagelen" could * overflow a 32-bit data type. */ filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; filebuf = xmalloc(filebuf_max); erase_buffer(filebuf, filebuf_max); /* * Get data from input and write to the device while there is * still input to read and we are still within the device * bounds. Note that in the case of standard input, the input * length is simply a quasi-boolean flag whose values are page * length or zero. */ while (((imglen > 0) || (writebuf < (filebuf + filebuf_len))) && (mtdoffset < mtd.size)) { /* * New eraseblock, check for bad block(s) * Stay in the loop to be sure that, if mtdoffset changes because * of a bad block, the next block that will be written to * is also checked. Thus, we avoid errors if the block(s) after the * skipped block(s) is also bad (number of blocks depending on * the blockalign). */ while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { blockstart = mtdoffset & (~ebsize_aligned + 1); offs = blockstart; /* * if writebuf == filebuf, we are rewinding so we must * not reset the buffer but just replay it */ if (writebuf != filebuf) { erase_buffer(filebuf, filebuf_len); filebuf_len = 0; writebuf = filebuf; } baderaseblock = false; if (!quiet) fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", blockstart / ebsize_aligned, blockstart); /* Check all the blocks in an erase block for bad blocks */ if (noskipbad) continue; do { if ((ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned)) < 0) { sys_errmsg("%s: MTD get bad block failed", mtd_device); goto closeall; } else if (ret == 1) { baderaseblock = true; if (!quiet) fprintf(stderr, "Bad block at %llx, %u block(s) " "from %llx will be skipped\n", offs, blockalign, blockstart); } if (baderaseblock) { mtdoffset = blockstart + ebsize_aligned; } offs += ebsize_aligned / blockalign; } while (offs < blockstart + ebsize_aligned); } /* Read more data from the input if there isn't enough in the buffer */ if ((writebuf + mtd.min_io_size) > (filebuf + filebuf_len)) { int readlen = mtd.min_io_size; int alreadyread = (filebuf + filebuf_len) - writebuf; int tinycnt = alreadyread; while (tinycnt < readlen) { cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); if (cnt == 0) { /* EOF */ break; } else if (cnt < 0) { perror("File I/O error on input"); goto closeall; } tinycnt += cnt; } /* No padding needed - we are done */ if (tinycnt == 0) { /* * For standard input, set imglen to 0 to signal * the end of the "file". For nonstandard input, * leave it as-is to detect an early EOF. */ if (ifd == STDIN_FILENO) { imglen = 0; } break; } /* Padding */ if (tinycnt < readlen) { if (!pad) { fprintf(stderr, "Unexpected EOF. Expecting at least " "%d more bytes. Use the padding option.\n", readlen - tinycnt); goto closeall; } erase_buffer(writebuf + tinycnt, readlen - tinycnt); } filebuf_len += readlen - alreadyread; if (ifd != STDIN_FILENO) { imglen -= tinycnt - alreadyread; } else if (cnt == 0) { /* No more bytes - we are done after writing the remaining bytes */ imglen = 0; } } if (writeoob) { oobbuf = writebuf + mtd.min_io_size; /* Read more data for the OOB from the input if there isn't enough in the buffer */ if ((oobbuf + mtd.oob_size) > (filebuf + filebuf_len)) { int readlen = mtd.oob_size; int alreadyread = (filebuf + filebuf_len) - oobbuf; int tinycnt = alreadyread; while (tinycnt < readlen) { cnt = read(ifd, oobbuf + tinycnt, readlen - tinycnt); if (cnt == 0) { /* EOF */ break; } else if (cnt < 0) { perror("File I/O error on input"); goto closeall; } tinycnt += cnt; } if (tinycnt < readlen) { fprintf(stderr, "Unexpected EOF. Expecting at least " "%d more bytes for OOB\n", readlen - tinycnt); goto closeall; } filebuf_len += readlen - alreadyread; if (ifd != STDIN_FILENO) { imglen -= tinycnt - alreadyread; } else if (cnt == 0) { /* No more bytes - we are done after writing the remaining bytes */ imglen = 0; } } } /* Write out data */ ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size, onlyoob ? NULL : writebuf, onlyoob ? 0 : mtd.min_io_size, writeoob ? oobbuf : NULL, writeoob ? mtd.oob_size : 0, write_mode); if (ret) { int i; if (errno != EIO) { sys_errmsg("%s: MTD write failure", mtd_device); goto closeall; } /* Must rewind to blockstart if we can */ writebuf = filebuf; fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", blockstart, blockstart + ebsize_aligned - 1); for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { int errno_tmp = errno; sys_errmsg("%s: MTD Erase failure", mtd_device); if (errno_tmp != EIO) { goto closeall; } } } if (markbad) { fprintf(stderr, "Marking block at %08llx bad\n", mtdoffset & (~mtd.eb_size + 1)); if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { sys_errmsg("%s: MTD Mark bad block failure", mtd_device); goto closeall; } } mtdoffset = blockstart + ebsize_aligned; continue; } mtdoffset += mtd.min_io_size; writebuf += pagelen; } failed = false; closeall: close(ifd); libmtd_close(mtd_desc); free(filebuf); close(fd); if (failed || ((ifd != STDIN_FILENO) && (imglen > 0)) || (writebuf < (filebuf + filebuf_len))) sys_errmsg_die("Data was only partially written due to error"); /* Return happy */ return EXIT_SUCCESS; } mtd-utils-1.5.0/nftl_format.c000066400000000000000000000302541175167361300161340ustar00rootroot00000000000000/* * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ToDo: * 1. UnitSizeFactor != 0xFF cases * 2. test, test, and test !!! */ #define PROGRAM_NAME "nftl_format" #define _XOPEN_SOURCE 500 /* for pread/pwrite */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned char BadUnitTable[MAX_ERASE_ZONES]; unsigned char *readbuf; unsigned char *writebuf[4]; mtd_info_t meminfo; erase_info_t erase; int fd; struct NFTLMediaHeader *NFTLhdr; struct INFTLMediaHeader *INFTLhdr; static int do_oobcheck = 1; static int do_rwecheck = 1; static unsigned char check_block_1(unsigned long block) { unsigned char oobbuf[16]; struct mtd_oob_buf oob = { 0, 16, oobbuf }; oob.start = block * meminfo.erasesize; if (ioctl(fd, MEMREADOOB, &oob)) return ZONE_BAD_ORIGINAL; if(oobbuf[5] == 0) return ZONE_BAD_ORIGINAL; oob.start = block * meminfo.erasesize + 512 /* FIXME */; if (ioctl(fd, MEMREADOOB, &oob)) return ZONE_BAD_ORIGINAL; if(oobbuf[5] == 0) return ZONE_BAD_ORIGINAL; return ZONE_GOOD; } static unsigned char check_block_2(unsigned long block) { unsigned long ofs = block * meminfo.erasesize; unsigned long blockofs; /* Erase test */ erase.start = ofs; for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { pread(fd, readbuf, 512, ofs + blockofs); if (memcmp(readbuf, writebuf[0], 512)) { /* Block wasn't 0xff after erase */ printf(": Block not 0xff after erase\n"); return ZONE_BAD_ORIGINAL; } pwrite(fd, writebuf[1], 512, blockofs + ofs); pread(fd, readbuf, 512, blockofs + ofs); if (memcmp(readbuf, writebuf[1], 512)) { printf(": Block not zero after clearing\n"); return ZONE_BAD_ORIGINAL; } } /* Write test */ if (ioctl(fd, MEMERASE, &erase) != 0) { printf(": Second erase failed (%s)\n", strerror(errno)); return ZONE_BAD_ORIGINAL; } for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { pwrite(fd, writebuf[2], 512, blockofs + ofs); pread(fd, readbuf, 512, blockofs + ofs); if (memcmp(readbuf, writebuf[2], 512)) { printf(": Block not 0x5a after writing\n"); return ZONE_BAD_ORIGINAL; } } if (ioctl(fd, MEMERASE, &erase) != 0) { printf(": Third erase failed (%s)\n", strerror(errno)); return ZONE_BAD_ORIGINAL; } for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { pwrite(fd, writebuf[3], 512, blockofs + ofs); pread(fd, readbuf, 512, blockofs + ofs); if (memcmp(readbuf, writebuf[3], 512)) { printf(": Block not 0xa5 after writing\n"); return ZONE_BAD_ORIGINAL; } } if (ioctl(fd, MEMERASE, &erase) != 0) { printf(": Fourth erase failed (%s)\n", strerror(errno)); return ZONE_BAD_ORIGINAL; } return ZONE_GOOD; } static unsigned char erase_block(unsigned long block) { unsigned char status; int ret; status = (do_oobcheck) ? check_block_1(block) : ZONE_GOOD; erase.start = block * meminfo.erasesize; if (status != ZONE_GOOD) { printf("\rSkipping bad zone (factory marked) #%ld @ 0x%x\n", block, erase.start); fflush(stdout); return status; } printf("\r\t Erasing Zone #%ld @ 0x%x", block, erase.start); fflush(stdout); if ((ret=ioctl(fd, MEMERASE, &erase)) != 0) { printf(": Erase failed (%s)\n", strerror(errno)); return ZONE_BAD_ORIGINAL; } if (do_rwecheck) { printf("\r\tChecking Zone #%ld @ 0x%x", block, erase.start); fflush(stdout); status = check_block_2(block); if (status != ZONE_GOOD) { printf("\rSkipping bad zone (RWE test failed) #%ld @ 0x%x\n", block, erase.start); fflush(stdout); } } return status; } static int checkbbt(void) { unsigned char bbt[512]; unsigned char bits; int i, addr; if (pread(fd, bbt, 512, 0x800) < 0) { printf("%s: failed to read BBT, errno=%d\n", PROGRAM_NAME, errno); return (-1); } for (i = 0; (i < 512); i++) { addr = i / 4; bits = 0x3 << ((i % 4) * 2); if ((bbt[addr] & bits) == 0) { BadUnitTable[i] = ZONE_BAD_ORIGINAL; } } return (0); } void usage(int rc) { fprintf(stderr, "Usage: %s [-ib] [ []]\n", PROGRAM_NAME); exit(rc); } int main(int argc, char **argv) { unsigned long startofs = 0, part_size = 0; unsigned long ezones = 0, ezone = 0, bad_zones = 0; unsigned char unit_factor = 0xFF; long MediaUnit1 = -1, MediaUnit2 = -1; long MediaUnitOff1 = 0, MediaUnitOff2 = 0; unsigned char oobbuf[16]; struct mtd_oob_buf oob = {0, 16, oobbuf}; char *mtddevice; const char *nftl; int c, do_inftl = 0, do_bbt = 0; printf("version 1.24 2005/11/07 11:15:13 gleixner\n"); if (argc < 2) usage(1); nftl = "NFTL"; while ((c = getopt(argc, argv, "?hib")) > 0) { switch (c) { case 'i': nftl = "INFTL"; do_inftl = 1; break; case 'b': do_bbt = 1; break; case 'h': case '?': usage(0); break; default: usage(1); break; } } mtddevice = argv[optind++]; if (argc > optind) { startofs = strtoul(argv[optind++], NULL, 0); } if (argc > optind) { part_size = strtoul(argv[optind++], NULL, 0); } // Open and size the device if ((fd = open(mtddevice, O_RDWR)) < 0) { perror("Open flash device"); return 1; } if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { perror("ioctl(MEMGETINFO)"); close(fd); return 1; } switch (meminfo.erasesize) { case 0x1000: case 0x2000: case 0x4000: case 0x8000: break; default: printf("Unrecognized Erase size, 0x%x - I'm confused\n", meminfo.erasesize); close(fd); return 1; } writebuf[0] = malloc(meminfo.erasesize * 5); if (!writebuf[0]) { printf("Malloc failed\n"); close(fd); return 1; } writebuf[1] = writebuf[0] + meminfo.erasesize; writebuf[2] = writebuf[1] + meminfo.erasesize; writebuf[3] = writebuf[2] + meminfo.erasesize; readbuf = writebuf[3] + meminfo.erasesize; memset(writebuf[0], 0xff, meminfo.erasesize); memset(writebuf[1], 0x00, meminfo.erasesize); memset(writebuf[2], 0x5a, meminfo.erasesize); memset(writebuf[3], 0xa5, meminfo.erasesize); memset(BadUnitTable, ZONE_GOOD, MAX_ERASE_ZONES); if (part_size == 0 || (part_size > meminfo.size - startofs)) /* the user doest not or incorrectly specify NFTL partition size */ part_size = meminfo.size - startofs; erase.length = meminfo.erasesize; ezones = part_size / meminfo.erasesize; if (ezones > MAX_ERASE_ZONES) { /* Ought to change the UnitSizeFactor. But later. */ part_size = meminfo.erasesize * MAX_ERASE_ZONES; ezones = MAX_ERASE_ZONES; unit_factor = 0xFF; } /* If using device BBT then parse that now */ if (do_bbt) { checkbbt(); do_oobcheck = 0; do_rwecheck = 0; } /* Phase 1. Erasing and checking each erase zones in the NFTL partition. N.B. Erase Zones not used by the NFTL partition are untouched and marked ZONE_GOOD */ printf("Phase 1. Checking and erasing Erase Zones from 0x%08lx to 0x%08lx\n", startofs, startofs + part_size); for (ezone = startofs / meminfo.erasesize; ezone < (ezones + startofs / meminfo.erasesize); ezone++) { if (BadUnitTable[ezone] != ZONE_GOOD) continue; if ((BadUnitTable[ezone] = erase_block(ezone)) == ZONE_GOOD) { if (MediaUnit1 == -1) { MediaUnit1 = ezone; } else if (MediaUnit2 == -1) { MediaUnit2 = ezone; } } else { bad_zones++; } } printf("\n"); /* N.B. from dump of M-System original chips, NumEraseUnits counts the 2 Erase Unit used by MediaHeader and the FirstPhysicalEUN starts from the MediaHeader */ if (do_inftl) { unsigned long maxzones, pezstart, pezend, numvunits; INFTLhdr = (struct INFTLMediaHeader *) (writebuf[0]); strcpy(INFTLhdr->bootRecordID, "BNAND"); INFTLhdr->NoOfBootImageBlocks = cpu_to_le32(0); INFTLhdr->NoOfBinaryPartitions = cpu_to_le32(0); INFTLhdr->NoOfBDTLPartitions = cpu_to_le32(1); INFTLhdr->BlockMultiplierBits = cpu_to_le32(0); INFTLhdr->FormatFlags = cpu_to_le32(0); INFTLhdr->OsakVersion = cpu_to_le32(OSAK_VERSION); INFTLhdr->PercentUsed = cpu_to_le32(PERCENTUSED); /* * Calculate number of virtual units we will have to work * with. I am calculating out the known bad units here, not * sure if that is what M-Systems do... */ MediaUnit2 = MediaUnit1; MediaUnitOff2 = 4096; maxzones = meminfo.size / meminfo.erasesize; pezstart = startofs / meminfo.erasesize + 1; pezend = startofs / meminfo.erasesize + ezones - 1; numvunits = (ezones - 2) * PERCENTUSED / 100; for (ezone = pezstart; ezone < maxzones; ezone++) { if (BadUnitTable[ezone] != ZONE_GOOD) { if (numvunits > 1) numvunits--; } } INFTLhdr->Partitions[0].virtualUnits = cpu_to_le32(numvunits); INFTLhdr->Partitions[0].firstUnit = cpu_to_le32(pezstart); INFTLhdr->Partitions[0].lastUnit = cpu_to_le32(pezend); INFTLhdr->Partitions[0].flags = cpu_to_le32(INFTL_BDTL); INFTLhdr->Partitions[0].spareUnits = cpu_to_le32(0); INFTLhdr->Partitions[0].Reserved0 = INFTLhdr->Partitions[0].firstUnit; INFTLhdr->Partitions[0].Reserved1 = cpu_to_le32(0); } else { NFTLhdr = (struct NFTLMediaHeader *) (writebuf[0]); strcpy(NFTLhdr->DataOrgID, "ANAND"); NFTLhdr->NumEraseUnits = cpu_to_le16(part_size / meminfo.erasesize); NFTLhdr->FirstPhysicalEUN = cpu_to_le16(MediaUnit1); /* N.B. we reserve 2 more Erase Units for "folding" of Virtual Unit Chain */ NFTLhdr->FormattedSize = cpu_to_le32(part_size - ( (5+bad_zones) * meminfo.erasesize)); NFTLhdr->UnitSizeFactor = unit_factor; } /* Phase 2. Writing NFTL Media Headers and Bad Unit Table */ printf("Phase 2.a Writing %s Media Header and Bad Unit Table\n", nftl); pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize + MediaUnitOff1); for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { pwrite(fd, BadUnitTable + ezone, 512, (MediaUnit1 * meminfo.erasesize) + 512 * (1 + ezone / 512)); } #if 0 printf(" MediaHeader contents:\n"); printf(" NumEraseUnits: %d\n", le16_to_cpu(NFTLhdr->NumEraseUnits)); printf(" FirstPhysicalEUN: %d\n", le16_to_cpu(NFTLhdr->FirstPhysicalEUN)); printf(" FormattedSize: %d (%d sectors)\n", le32_to_cpu(NFTLhdr->FormattedSize), le32_to_cpu(NFTLhdr->FormattedSize)/512); #endif printf("Phase 2.b Writing Spare %s Media Header and Spare Bad Unit Table\n", nftl); pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize + MediaUnitOff2); for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { pwrite(fd, BadUnitTable + ezone, 512, (MediaUnit2 * meminfo.erasesize + MediaUnitOff2) + 512 * (1 + ezone / 512)); } /* UCI #1 for newly erased Erase Unit */ memset(oobbuf, 0xff, 16); oobbuf[11] = oobbuf[10] = oobbuf[9] = 0; oobbuf[8] = (do_inftl) ? 0x00 : 0x03; oobbuf[12] = oobbuf[14] = 0x69; oobbuf[13] = oobbuf[15] = 0x3c; /* N.B. The Media Header and Bad Erase Unit Table are considered as Free Erase Unit by M-System i.e. their Virtual Unit Number == 0xFFFF in the Unit Control Information #0, but their Block Status is BLOCK_USED (0x5555) in their Block Control Information */ /* Phase 3. Writing Unit Control Information for each Erase Unit */ printf("Phase 3. Writing Unit Control Information to each Erase Unit\n"); for (ezone = MediaUnit1; ezone < (ezones + startofs / meminfo.erasesize); ezone++) { /* write UCI #1 to each Erase Unit */ if (BadUnitTable[ezone] != ZONE_GOOD) continue; oob.start = (ezone * meminfo.erasesize) + 512 + (do_inftl * 512); if (ioctl(fd, MEMWRITEOOB, &oob)) printf("MEMWRITEOOB at %lx: %s\n", (unsigned long)oob.start, strerror(errno)); } exit(0); } mtd-utils-1.5.0/nftldump.c000066400000000000000000000172221175167361300154520ustar00rootroot00000000000000/* * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ToDo: * 1. UnitSizeFactor != 0xFF cases * 2. test, test, and test !!! */ #define PROGRAM_NAME "nftldump" #define _XOPEN_SOURCE 500 /* For pread */ #include #include #include #include #include #include #include #include #include #include #include #include #include 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 []\n", PROGRAM_NAME); exit(1); } fd = open(argv[1], O_RDONLY); if (fd == -1) { perror("open flash"); exit (1); } if (argc > 2) { ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); if (ofd == -1) perror ("open outfile"); } /* get size information of the MTD device */ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { perror("ioctl(MEMGETINFO)"); close(fd); return 1; } while (find_media_headers() != 0) { dump_erase_units(); dump_virtual_units(); free(VUCtable); } exit(0); } mtd-utils-1.5.0/rbtree.c000066400000000000000000000206541175167361300151070ustar00rootroot00000000000000/* Red Black Trees (C) 1999 Andrea Arcangeli (C) 2002 David Woodhouse 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 #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; } mtd-utils-1.5.0/rbtree.h000066400000000000000000000121771175167361300151150ustar00rootroot00000000000000/* Red Black Trees (C) 1999 Andrea Arcangeli 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 #include struct rb_node { unsigned long rb_parent_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); /* The alignment might seem pointless, but allegedly CRIS needs it */ struct rb_root { struct rb_node *rb_node; }; #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) #define rb_color(r) ((r)->rb_parent_color & 1) #define rb_is_red(r) (!rb_color(r)) #define rb_is_black(r) rb_color(r) #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) { rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; } static inline void rb_set_color(struct rb_node *rb, int color) { rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; } #define RB_ROOT (struct rb_root) { NULL, } /* Newer gcc versions take care of exporting this */ #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define rb_entry(ptr, type, member) container_of(ptr, type, member) #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) #define RB_EMPTY_NODE(node) (rb_parent(node) == node) #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(struct rb_node *); extern struct rb_node *rb_prev(struct rb_node *); extern struct rb_node *rb_first(struct rb_root *); extern struct rb_node *rb_last(struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { node->rb_parent_color = (unsigned long )parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } #endif /* _LINUX_RBTREE_H */ mtd-utils-1.5.0/recv_image.c000066400000000000000000000347651175167361300157350ustar00rootroot00000000000000 #define PROGRAM_NAME "recv_image" #define _XOPEN_SOURCE 500 #define _BSD_SOURCE /* struct ip_mreq */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtd/mtd-user.h" #include "mcast_image.h" #include "common.h" #define WBUF_SIZE 4096 struct eraseblock { uint32_t flash_offset; unsigned char wbuf[WBUF_SIZE]; int wbuf_ofs; int nr_pkts; int *pkt_indices; uint32_t crc; }; int main(int argc, char **argv) { struct addrinfo *ai; struct addrinfo hints; struct addrinfo *runp; int ret; int sock; ssize_t len; int flfd; struct mtd_info_user meminfo; unsigned char *eb_buf, *decode_buf, **src_pkts; int nr_blocks = 0; int pkts_per_block; int block_nr = -1; uint32_t image_crc = 0; int total_pkts = 0; int ignored_pkts = 0; loff_t mtdoffset = 0; int badcrcs = 0; int duplicates = 0; int file_mode = 0; struct fec_parms *fec = NULL; int i; struct eraseblock *eraseblocks = NULL; uint32_t start_seq = 0; struct timeval start, now; unsigned long fec_time = 0, flash_time = 0, crc_time = 0, rflash_time = 0, erase_time = 0, net_time = 0; if (argc != 4) { fprintf(stderr, "usage: %s \n", PROGRAM_NAME); exit(1); } /* Open the device */ flfd = open(argv[3], O_RDWR); if (flfd >= 0) { /* Fill in MTD device capability structure */ if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { perror("MEMGETINFO"); close(flfd); flfd = -1; } else { printf("Receive to MTD device %s with erasesize %d\n", argv[3], meminfo.erasesize); } } if (flfd == -1) { /* Try again, as if it's a file */ flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); if (flfd < 0) { perror("open"); exit(1); } meminfo.erasesize = 131072; file_mode = 1; printf("Receive to file %s with (assumed) erasesize %d\n", argv[3], meminfo.erasesize); } pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; eb_buf = malloc(pkts_per_block * PKT_SIZE); decode_buf = malloc(pkts_per_block * PKT_SIZE); if (!eb_buf && !decode_buf) { fprintf(stderr, "No memory for eraseblock buffer\n"); exit(1); } src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); if (!src_pkts) { fprintf(stderr, "No memory for decode packet pointers\n"); exit(1); } memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_DGRAM; ret = getaddrinfo(argv[1], argv[2], &hints, &ai); if (ret) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); exit(1); } runp = ai; for (runp = ai; runp; runp = runp->ai_next) { sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == -1) { perror("socket"); continue; } if (runp->ai_family == AF_INET && IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { struct ip_mreq rq; rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; rq.imr_interface.s_addr = INADDR_ANY; if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { perror("IP_ADD_MEMBERSHIP"); close(sock); continue; } } else if (runp->ai_family == AF_INET6 && ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { struct ipv6_mreq rq; rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; rq.ipv6mr_interface = 0; if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { perror("IPV6_ADD_MEMBERSHIP"); close(sock); continue; } } if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { perror("bind"); close(sock); continue; } break; } if (!runp) exit(1); while (1) { struct image_pkt thispkt; len = read(sock, &thispkt, sizeof(thispkt)); if (len < 0) { perror("read socket"); break; } if (len < sizeof(thispkt)) { fprintf(stderr, "Wrong length %zd bytes (expected %zu)\n", len, sizeof(thispkt)); continue; } if (!eraseblocks) { image_crc = thispkt.hdr.totcrc; start_seq = ntohl(thispkt.hdr.pkt_sequence); if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", ntohl(thispkt.hdr.blocksize), meminfo.erasesize); exit(1); } nr_blocks = ntohl(thispkt.hdr.nr_blocks); fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); if (!eraseblocks) { fprintf(stderr, "No memory for block map\n"); exit(1); } for (i = 0; i < nr_blocks; i++) { eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); if (!eraseblocks[i].pkt_indices) { fprintf(stderr, "Failed to allocate packet indices\n"); exit(1); } eraseblocks[i].nr_pkts = 0; if (!file_mode) { if (mtdoffset >= meminfo.size) { fprintf(stderr, "Run out of space on flash\n"); exit(1); } #if 1 /* Deliberately use bad blocks... test write failures */ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); mtdoffset += meminfo.erasesize; } #endif } eraseblocks[i].flash_offset = mtdoffset; mtdoffset += meminfo.erasesize; eraseblocks[i].wbuf_ofs = 0; } gettimeofday(&start, NULL); } if (image_crc != thispkt.hdr.totcrc) { fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); exit(1); } block_nr = ntohl(thispkt.hdr.block_nr); if (block_nr >= nr_blocks) { fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", block_nr, nr_blocks); exit(1); } for (i=0; i= pkts_per_block) { /* We have a block which we didn't really need */ eraseblocks[block_nr].nr_pkts++; ignored_pkts++; continue; } if (mtd_crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), mtd_crc32(-1, thispkt.data, PKT_SIZE), ntohl(thispkt.hdr.thiscrc)); badcrcs++; continue; } pkt_again: eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = ntohs(thispkt.hdr.pkt_nr); total_pkts++; if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; long time_msec; gettimeofday(&now, NULL); time_msec = ((now.tv_usec - start.tv_usec) / 1000) + (now.tv_sec - start.tv_sec) * 1000; printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", total_pkts, nr_blocks * pkts_per_block, total_pkts * 100 / nr_blocks / pkts_per_block, time_msec / 1000, total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, pkts_sent - total_pkts - duplicates - ignored_pkts, (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, duplicates + ignored_pkts); fflush(stdout); } if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { /* New packet doesn't full the wbuf */ memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, thispkt.data, PKT_SIZE); eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; } else { int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; ssize_t wrotelen; static int faked = 1; memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, thispkt.data, fits); wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, eraseblocks[block_nr].flash_offset); if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { faked = 1; if (wrotelen < 0) perror("\npacket write"); else fprintf(stderr, "\nshort write of packet wbuf\n"); if (!file_mode) { struct erase_info_user erase; /* FIXME: Perhaps we should store pkt crcs and try to recover data from the offending eraseblock */ /* We have increased nr_pkts but not yet flash_offset */ erase.start = eraseblocks[block_nr].flash_offset & ~(meminfo.erasesize - 1); erase.length = meminfo.erasesize; printf("Will erase at %08x len %08x (bad write was at %08x)\n", erase.start, erase.length, eraseblocks[block_nr].flash_offset); if (ioctl(flfd, MEMERASE, &erase)) { perror("MEMERASE"); exit(1); } if (mtdoffset >= meminfo.size) { fprintf(stderr, "Run out of space on flash\n"); exit(1); } while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); mtdoffset += meminfo.erasesize; if (mtdoffset >= meminfo.size) { fprintf(stderr, "Run out of space on flash\n"); exit(1); } } eraseblocks[block_nr].flash_offset = mtdoffset; printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); total_pkts -= eraseblocks[block_nr].nr_pkts; eraseblocks[block_nr].nr_pkts = 0; eraseblocks[block_nr].wbuf_ofs = 0; mtdoffset += meminfo.erasesize; goto pkt_again; } else /* Usually nothing we can do in file mode */ exit(1); } eraseblocks[block_nr].flash_offset += WBUF_SIZE; /* Copy the remainder into the wbuf */ memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; } if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); if (total_pkts == nr_blocks * pkts_per_block) break; } } printf("\n"); gettimeofday(&now, NULL); net_time = (now.tv_usec - start.tv_usec) / 1000; net_time += (now.tv_sec - start.tv_sec) * 1000; close(sock); for (block_nr = 0; block_nr < nr_blocks; block_nr++) { ssize_t rwlen; gettimeofday(&start, NULL); eraseblocks[block_nr].flash_offset -= meminfo.erasesize; rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); gettimeofday(&now, NULL); rflash_time += (now.tv_usec - start.tv_usec) / 1000; rflash_time += (now.tv_sec - start.tv_sec) * 1000; if (rwlen < 0) { perror("read"); /* Argh. Perhaps we could go back and try again, but if the flash is going to fail to read back what we write to it, and the whole point in this program is to write to it, what's the point? */ fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); exit(1); } memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, eraseblocks[block_nr].wbuf_ofs); for (i=0; i < pkts_per_block; i++) src_pkts[i] = &eb_buf[i * PKT_SIZE]; gettimeofday(&start, NULL); if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { /* Eep. This cannot happen */ printf("The world is broken. fec_decode() returned error\n"); exit(1); } gettimeofday(&now, NULL); fec_time += (now.tv_usec - start.tv_usec) / 1000; fec_time += (now.tv_sec - start.tv_sec) * 1000; for (i=0; i < pkts_per_block; i++) memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); /* Paranoia */ gettimeofday(&start, NULL); if (mtd_crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { printf("\nCRC mismatch for block #%d: want %08x got %08x\n", block_nr, eraseblocks[block_nr].crc, mtd_crc32(-1, decode_buf, meminfo.erasesize)); exit(1); } gettimeofday(&now, NULL); crc_time += (now.tv_usec - start.tv_usec) / 1000; crc_time += (now.tv_sec - start.tv_sec) * 1000; start = now; if (!file_mode) { struct erase_info_user erase; erase.start = eraseblocks[block_nr].flash_offset; erase.length = meminfo.erasesize; printf("\rErasing block at %08x...", erase.start); if (ioctl(flfd, MEMERASE, &erase)) { perror("MEMERASE"); /* This block has dirty data on it. If the erase failed, we're screwed */ fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); exit(1); } gettimeofday(&now, NULL); erase_time += (now.tv_usec - start.tv_usec) / 1000; erase_time += (now.tv_sec - start.tv_sec) * 1000; start = now; } else printf("\r"); write_again: rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); if (rwlen < meminfo.erasesize) { if (rwlen < 0) { perror("\ndecoded data write"); } else fprintf(stderr, "\nshort write of decoded data\n"); if (!file_mode) { struct erase_info_user erase; erase.start = eraseblocks[block_nr].flash_offset; erase.length = meminfo.erasesize; printf("Erasing failed block at %08x\n", eraseblocks[block_nr].flash_offset); if (ioctl(flfd, MEMERASE, &erase)) { perror("MEMERASE"); exit(1); } if (mtdoffset >= meminfo.size) { fprintf(stderr, "Run out of space on flash\n"); exit(1); } while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); mtdoffset += meminfo.erasesize; if (mtdoffset >= meminfo.size) { fprintf(stderr, "Run out of space on flash\n"); exit(1); } } printf("Will try again at %08lx...", (long)mtdoffset); eraseblocks[block_nr].flash_offset = mtdoffset; goto write_again; } else /* Usually nothing we can do in file mode */ exit(1); } gettimeofday(&now, NULL); flash_time += (now.tv_usec - start.tv_usec) / 1000; flash_time += (now.tv_sec - start.tv_sec) * 1000; printf("wrote image block %08x (%d pkts) ", block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); fflush(stdout); } close(flfd); printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); return 0; } mtd-utils-1.5.0/rfddump.c000066400000000000000000000162231175167361300152620ustar00rootroot00000000000000/* * rfddump.c * * Copyright (C) 2005 Sean Young * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #define PROGRAM_NAME "rfddump" #define VERSION "$Revision 1.0 $" #define _XOPEN_SOURCE 500 /* For pread */ #include #include #include #include #include #include #include #include #include #include #include #include /* next is an array of mapping for each corresponding sector */ #define RFD_MAGIC 0x9193 #define HEADER_MAP_OFFSET 3 #define SECTOR_DELETED 0x0000 #define SECTOR_ZERO 0xfffe #define SECTOR_FREE 0xffff #define SECTOR_SIZE 512 #define SECTORS_PER_TRACK 63 struct rfd { int block_size; int block_count; int header_sectors; int data_sectors; int header_size; uint16_t *header; int sector_count; int *sector_map; const char *mtd_filename; const char *out_filename; int verbose; }; void display_help(void) { printf("Usage: %s [OPTIONS] MTD-device filename\n" "Dumps the contents of a resident flash disk\n" "\n" "-h --help display this help and exit\n" "-V --version output version information and exit\n" "-v --verbose Be verbose\n" "-b size --blocksize Block size (defaults to erase unit)\n", PROGRAM_NAME); exit(0); } void display_version(void) { printf("%s " VERSION "\n" "\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", PROGRAM_NAME); exit(0); } void process_options(int argc, char *argv[], struct rfd *rfd) { int error = 0; rfd->block_size = 0; rfd->verbose = 0; for (;;) { int option_index = 0; static const char *short_options = "hvVb:"; static const struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V', }, { "blocksize", required_argument, 0, 'b' }, { "verbose", no_argument, 0, 'v' }, { NULL, 0, 0, 0 } }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) break; switch (c) { case 'h': display_help(); break; case 'V': display_version(); break; case 'v': rfd->verbose = 1; break; case 'b': rfd->block_size = atoi(optarg); break; case '?': error = 1; break; } } if ((argc - optind) != 2 || error) display_help(); rfd->mtd_filename = argv[optind]; rfd->out_filename = argv[optind + 1]; } int build_block_map(struct rfd *rfd, int fd, int block) { int i; int sectors; if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size) != rfd->header_size) { return -1; } if (le16_to_cpu(rfd->header[0]) != RFD_MAGIC) { if (rfd->verbose) printf("Block #%02d: Magic missing\n", block); return 0; } sectors = 0; for (i=0; idata_sectors; i++) { uint16_t entry = le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]); if (entry == SECTOR_FREE || entry == SECTOR_DELETED) continue; if (entry == SECTOR_ZERO) entry = 0; if (entry >= rfd->sector_count) { fprintf(stderr, "%s: warning: sector %d out of range\n", rfd->mtd_filename, entry); continue; } if (rfd->sector_map[entry] != -1) { fprintf(stderr, "%s: warning: more than one entry " "for sector %d\n", rfd->mtd_filename, entry); continue; } rfd->sector_map[entry] = rfd->block_size * block + (i + rfd->header_sectors) * SECTOR_SIZE; sectors++; } if (rfd->verbose) printf("Block #%02d: %d sectors\n", block, sectors); return 1; } int main(int argc, char *argv[]) { int fd, sectors_per_block; mtd_info_t mtd_info; struct rfd rfd; int i, blocks_found; int out_fd = 0; uint8_t sector[512]; int blank, rc, cylinders; process_options(argc, argv, &rfd); fd = open(rfd.mtd_filename, O_RDONLY); if (fd == -1) { perror(rfd.mtd_filename); return 1; } if (rfd.block_size == 0) { if (ioctl(fd, MEMGETINFO, &mtd_info)) { perror(rfd.mtd_filename); close(fd); return 1; } if (mtd_info.type != MTD_NORFLASH) { fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename); close(fd); return 2; } sectors_per_block = mtd_info.erasesize / SECTOR_SIZE; rfd.block_size = mtd_info.erasesize; rfd.block_count = mtd_info.size / mtd_info.erasesize; } else { struct stat st; if (fstat(fd, &st) == -1) { perror(rfd.mtd_filename); close(fd); return 1; } if (st.st_size % SECTOR_SIZE) fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename); sectors_per_block = rfd.block_size / SECTOR_SIZE; if (st.st_size % rfd.block_size) fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename); rfd.block_count = st.st_size / rfd.block_size; if (!rfd.block_count) { fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename); close(fd); return 2; } } rfd.header_sectors = ((HEADER_MAP_OFFSET + sectors_per_block) * sizeof(uint16_t) + SECTOR_SIZE - 1) / SECTOR_SIZE; rfd.data_sectors = sectors_per_block - rfd.header_sectors; cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1) / SECTORS_PER_TRACK; rfd.sector_count = cylinders * SECTORS_PER_TRACK; rfd.header_size = (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(uint16_t); rfd.header = malloc(rfd.header_size); if (!rfd.header) { perror(PROGRAM_NAME); close(fd); return 2; } rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); if (!rfd.sector_map) { perror(PROGRAM_NAME); close(fd); free(rfd.sector_map); return 2; } rfd.mtd_filename = rfd.mtd_filename; for (i=0; i 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 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is very easy: just erase all the blocks and put the magic at * the beginning of each block. */ #define PROGRAM_NAME "rfdformat" #define VERSION "$Revision 1.0 $" #define _XOPEN_SOURCE 500 /* For pread/pwrite */ #include #include #include #include #include #include #include #include #include #include void display_help(void) { printf("Usage: %s [OPTIONS] MTD-device\n" "Formats NOR flash for resident flash disk\n" "\n" "-h --help display this help and exit\n" "-V --version output version information and exit\n", PROGRAM_NAME); exit(0); } void display_version(void) { printf("%s " VERSION "\n" "\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", PROGRAM_NAME); exit(0); } void process_options(int argc, char *argv[], const char **mtd_filename) { int error = 0; for (;;) { int option_index = 0; static const char *short_options = "hV"; static const struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V', }, { NULL, 0, 0, 0 } }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) break; switch (c) { case 'h': display_help(); break; case 'V': display_version(); break; case '?': error = 1; break; } } if ((argc - optind) != 1 || error) display_help(); *mtd_filename = argv[optind]; } int main(int argc, char *argv[]) { static const uint8_t magic[] = { 0x93, 0x91 }; int fd, block_count, i; struct mtd_info_user mtd_info; char buf[512]; const char *mtd_filename; process_options(argc, argv, &mtd_filename); fd = open(mtd_filename, O_RDWR); if (fd == -1) { perror(mtd_filename); return 1; } if (ioctl(fd, MEMGETINFO, &mtd_info)) { perror(mtd_filename); close(fd); return 1; } if (mtd_info.type != MTD_NORFLASH) { fprintf(stderr, "%s: not NOR flash\n", mtd_filename); close(fd); return 2; } if (mtd_info.size > 32*1024*1024) { fprintf(stderr, "%s: flash larger than 32MiB not supported\n", mtd_filename); close(fd); return 2; } block_count = mtd_info.size / mtd_info.erasesize; if (block_count < 2) { fprintf(stderr, "%s: at least two erase units required\n", mtd_filename); close(fd); return 2; } for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 []\n", PROGRAM_NAME); exit(1); } pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; printf("Inter-packet delay (avg): %dµs\n", pkt_delay); printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); erasesize = atol(argv[4]); if (!erasesize) { fprintf(stderr, "erasesize cannot be zero\n"); exit(1); } pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; total_pkts_per_block = pkts_per_block * 3 / 2; /* We have to pad it with zeroes, so can't use it in-place */ last_block = malloc(pkts_per_block * PKT_SIZE); if (!last_block) { fprintf(stderr, "Failed to allocate last-block buffer\n"); exit(1); } fec = fec_new(pkts_per_block, total_pkts_per_block); if (!fec) { fprintf(stderr, "Error initialising FEC\n"); exit(1); } memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_DGRAM; ret = getaddrinfo(argv[1], argv[2], &hints, &ai); if (ret) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); exit(1); } runp = ai; for (runp = ai; runp; runp = runp->ai_next) { sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == -1) { perror("socket"); continue; } if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) break; perror("connect"); close(sock); } if (!runp) exit(1); rfd = open(argv[3], O_RDONLY); if (rfd < 0) { perror("open"); exit(1); } if (fstat(rfd, &st)) { perror("fstat"); exit(1); } if (st.st_size % erasesize) { fprintf(stderr, "Image size %" PRIu64 " bytes is not a multiple of erasesize %d bytes\n", st.st_size, erasesize); exit(1); } image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); if (image == MAP_FAILED) { perror("mmap"); exit(1); } nr_blocks = st.st_size / erasesize; block_crcs = malloc(nr_blocks * sizeof(uint32_t)); if (!block_crcs) { fprintf(stderr, "Failed to allocate memory for CRCs\n"); exit(1); } memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); printf("Checking CRC...."); fflush(stdout); pktbuf.hdr.resend = 0; pktbuf.hdr.totcrc = htonl(mtd_crc32(-1, image, st.st_size)); pktbuf.hdr.nr_blocks = htonl(nr_blocks); pktbuf.hdr.blocksize = htonl(erasesize); pktbuf.hdr.thislen = htonl(PKT_SIZE); pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); printf("Checking block CRCs...."); fflush(stdout); for (block_nr=0; block_nr < nr_blocks; block_nr++) { printf("\rChecking block CRCS.... %d/%d", block_nr + 1, nr_blocks); fflush(stdout); block_crcs[block_nr] = mtd_crc32(-1, image + (block_nr * erasesize), erasesize); } printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" "Estimated transmit time per cycle: %ds\n", (long)st.st_size / 1024, (long) st.st_size, nr_blocks, pkts_per_block, nr_blocks * pkts_per_block * pkt_delay / 1000000); gettimeofday(&then, NULL); nextpkt = then; #ifdef RANDOMDROP srand((unsigned)then.tv_usec); printf("Random seed %u\n", (unsigned)then.tv_usec); #endif while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { if (blockptr && pkt_nr == 0) { unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); gettimeofday(&now, NULL); time_msecs = (now.tv_sec - then.tv_sec) * 1000; time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", amt_sent / 1024, time_msecs, amt_sent / 1024 * 1000 / time_msecs); then = now; } for (block_nr = 0; block_nr < nr_blocks; block_nr++) { int actualpkt; /* Calculating the redundant FEC blocks is expensive; the first $pkts_per_block are cheap enough though because they're just copies. So alternate between simple and complex stuff, so that we don't start to choke and fail to keep up with the expected bitrate in the second half of the sequence */ if (block_nr & 1) actualpkt = pkt_nr; else actualpkt = total_pkts_per_block - 1 - pkt_nr; blockptr = image + (erasesize * block_nr); if (block_nr == nr_blocks - 1) blockptr = last_block; fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); pktbuf.hdr.thiscrc = htonl(mtd_crc32(-1, pktbuf.data, PKT_SIZE)); pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); pktbuf.hdr.block_nr = htonl(block_nr); pktbuf.hdr.pkt_nr = htons(actualpkt); pktbuf.hdr.pkt_sequence = htonl(sequence++); printf("\rSending data block %08x packet %3d/%d", block_nr * erasesize, pkt_nr, total_pkts_per_block); if (pkt_nr && !block_nr) { unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); gettimeofday(&now, NULL); time_msecs = (now.tv_sec - then.tv_sec) * 1000; time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; printf(" (%ld KiB/s) ", amt_sent / 1024 * 1000 / time_msecs); } fflush(stdout); #ifdef RANDOMDROP if ((rand() % 1000) < 20) { printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); continue; } #endif gettimeofday(&now, NULL); #if 1 tosleep = nextpkt.tv_usec - now.tv_usec + (1000000 * (nextpkt.tv_sec - now.tv_sec)); /* We need hrtimers for this to actually work */ if (tosleep > 0) { struct timespec req; req.tv_nsec = (tosleep % 1000000) * 1000; req.tv_sec = tosleep / 1000000; nanosleep(&req, NULL); } #else while (now.tv_sec < nextpkt.tv_sec || (now.tv_sec == nextpkt.tv_sec && now.tv_usec < nextpkt.tv_usec)) { gettimeofday(&now, NULL); } #endif nextpkt.tv_usec += pkt_delay; if (nextpkt.tv_usec >= 1000000) { nextpkt.tv_sec += nextpkt.tv_usec / 1000000; nextpkt.tv_usec %= 1000000; } /* If the time for the next packet has already passed (by some margin), then we've lost time Adjust our expected timings accordingly. If we're only a little way behind, don't slip yet */ if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + 1000000 * (nextpkt.tv_sec - now.tv_sec))) { nextpkt = now; } if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { perror("write"); writeerrors++; if (writeerrors > 10) { fprintf(stderr, "Too many consecutive write errors\n"); exit(1); } } else writeerrors = 0; } } munmap(image, st.st_size); close(rfd); close(sock); return 0; } mtd-utils-1.5.0/summary.h000066400000000000000000000113741175167361300153250ustar00rootroot00000000000000/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2004 Ferenc Havasi , * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary * * For licensing information, see the file 'LICENCE' in this directory. */ #ifndef JFFS2_SUMMARY_H #define JFFS2_SUMMARY_H #include #include #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 mtd-utils-1.5.0/sumtool.c000066400000000000000000000614641175167361300153320ustar00rootroot00000000000000/* * sumtool.c * * Copyright (C) 2004 Zoltan Sogor , * Ferenc Havasi * University of Szeged, Hungary * 2006 KaiGai Kohei * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Overview: * This is a utility insert summary information into JFFS2 image for * faster mount time * */ #define PROGRAM_NAME "sumtool" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "summary.h" #include "common.h" #define PAD(x) (((x)+3)&~3) static struct jffs2_summary *sum_collected = NULL; static int verbose = 0; static int padto = 0; /* pad the output with 0xFF to the end of the final eraseblock */ static int add_cleanmarkers = 1; /* add cleanmarker to output */ static int use_input_cleanmarker_size = 1; /* use input file's cleanmarker size (default) */ static int found_cleanmarkers = 0; /* cleanmarker found in input file */ static struct jffs2_unknown_node cleanmarker; static int cleanmarker_size = sizeof(cleanmarker); static const char *short_options = "o:i:e:hvVblnc:p"; static int erase_block_size = 65536; static int out_fd = -1; static int in_fd = -1; static uint8_t *data_buffer = NULL; /* buffer for inodes */ static unsigned int data_ofs = 0; /* inode buffer offset */ static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/ static unsigned int file_ofs = 0; /* position in the buffer */ int target_endian = __BYTE_ORDER; static struct option long_options[] = { {"output", 1, NULL, 'o'}, {"input", 1, NULL, 'i'}, {"eraseblock", 1, NULL, 'e'}, {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, 'V'}, {"bigendian", 0, NULL, 'b'}, {"littleendian", 0, NULL, 'l'}, {"no-cleanmarkers", 0, NULL, 'n'}, {"cleanmarker", 1, NULL, 'c'}, {"pad", 0, NULL, 'p'}, {NULL, 0, NULL, 0} }; static const char helptext[] = "Usage: sumtool [OPTIONS] -i inputfile -o outputfile\n\n" "Convert the input JFFS2 image to a summarized JFFS2 image\n" "Summary makes mounting faster - if summary support enabled in your kernel\n\n" "Options:\n" " -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" " (usually 16KiB on NAND)\n" " -c, --cleanmarker=SIZE Size of cleanmarker (default 12).\n" " (usually 16 bytes on NAND, and will be set to\n" " this value if left at the default 12). Will be\n" " stored in OOB after each physical page composing\n" " a physical eraseblock.\n" " -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n" " -o, --output=FILE Output to FILE \n" " -i, --input=FILE Input from FILE \n" " -b, --bigendian Image is big endian\n" " -l --littleendian Image is little endian\n" " -h, --help Display this help text\n" " -v, --verbose Verbose operation\n" " -V, --version Display version information\n" " -p, --pad Pad the OUTPUT with 0xFF to the end of the final\n" " eraseblock\n\n"; static const char revtext[] = "$Revision: 1.9 $"; static unsigned char ffbuf[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static void full_write(void *target_buff, const void *buf, int len); void setup_cleanmarker(void) { cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); cleanmarker.totlen = cpu_to_je32(cleanmarker_size); cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); } void process_options (int argc, char **argv) { int opt,c; while ((opt = getopt_long(argc, argv, short_options, long_options, &c)) >= 0) { switch (opt) { case 'o': if (out_fd != -1) errmsg_die("output filename specified more than once"); out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); if (out_fd == -1) sys_errmsg_die("open output file"); break; case 'i': if (in_fd != -1) errmsg_die("input filename specified more than once"); in_fd = open(optarg, O_RDONLY); if (in_fd == -1) sys_errmsg_die("open input file"); break; case 'b': target_endian = __BIG_ENDIAN; break; case 'l': target_endian = __LITTLE_ENDIAN; break; case 'h': case '?': errmsg_die("%s", helptext); case 'v': verbose = 1; break; case 'V': errmsg_die("revision %.*s\n", (int) strlen(revtext) - 13, revtext + 11); case 'e': { char *next; unsigned units = 0; erase_block_size = strtol(optarg, &next, 0); if (!erase_block_size) errmsg_die("Unrecognisable erase size\n"); if (*next) { if (!strcmp(next, "KiB")) { units = 1024; } else if (!strcmp(next, "MiB")) { units = 1024 * 1024; } else { errmsg_die("Unknown units in erasesize\n"); } } else { if (erase_block_size < 0x1000) units = 1024; else units = 1; } erase_block_size *= units; /* If it's less than 8KiB, they're not allowed */ if (erase_block_size < 0x2000) { warnmsg("Erase size 0x%x too small. Increasing to 8KiB minimum\n", erase_block_size); erase_block_size = 0x2000; } break; } case 'n': add_cleanmarkers = 0; break; case 'c': cleanmarker_size = strtol(optarg, NULL, 0); if (cleanmarker_size < sizeof(cleanmarker)) { errmsg_die("cleanmarker size must be >= 12"); } if (cleanmarker_size >= erase_block_size) { errmsg_die("cleanmarker size must be < eraseblock size"); } use_input_cleanmarker_size = 0; found_cleanmarkers = 1; setup_cleanmarker(); break; case 'p': padto = 1; break; } } } void init_buffers(void) { data_buffer = xmalloc(erase_block_size); file_buffer = xmalloc(erase_block_size); } void init_sumlist(void) { sum_collected = xzalloc(sizeof(*sum_collected)); } void clean_buffers(void) { free(data_buffer); free(file_buffer); } void clean_sumlist(void) { union jffs2_sum_mem *temp; if (sum_collected) { while (sum_collected->sum_list_head) { temp = sum_collected->sum_list_head; sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; free(temp); sum_collected->sum_num--; } if (sum_collected->sum_num != 0) warnmsg("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); free(sum_collected); } } int load_next_block(void) { int ret; ret = read(in_fd, file_buffer, erase_block_size); file_ofs = 0; bareverbose(verbose, "Load next block : %d bytes read\n", ret); return ret; } void write_buff_to_file(void) { int ret; int len = data_ofs; uint8_t *buf = NULL; buf = data_buffer; while (len > 0) { ret = write(out_fd, buf, len); if (ret < 0) sys_errmsg_die("write"); if (ret == 0) sys_errmsg_die("write returned zero"); len -= ret; buf += ret; } data_ofs = 0; } void dump_sum_records(void) { struct jffs2_raw_summary isum; struct jffs2_sum_marker *sm; union jffs2_sum_mem *temp; jint32_t offset; jint32_t *tpage; void *wpage; int datasize, infosize, padsize; jint32_t magic = cpu_to_je32(JFFS2_SUM_MAGIC); if (!sum_collected->sum_num || !sum_collected->sum_list_head) return; datasize = sum_collected->sum_size + sizeof(struct jffs2_sum_marker); infosize = sizeof(struct jffs2_raw_summary) + datasize; padsize = erase_block_size - data_ofs - infosize; infosize += padsize; datasize += padsize; offset = cpu_to_je32(data_ofs); tpage = xmalloc(datasize); memset(tpage, 0xff, datasize); memset(&isum, 0, sizeof(isum)); isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); isum.totlen = cpu_to_je32(infosize); isum.hdr_crc = cpu_to_je32(mtd_crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); isum.padded = cpu_to_je32(0); if (add_cleanmarkers && found_cleanmarkers) { isum.cln_mkr = cpu_to_je32(cleanmarker_size); } else { isum.cln_mkr = cpu_to_je32(0); } isum.sum_num = cpu_to_je32(sum_collected->sum_num); wpage = tpage; while (sum_collected->sum_num) { switch(je16_to_cpu(sum_collected->sum_list_head->u.nodetype)) { case JFFS2_NODETYPE_INODE : { struct jffs2_sum_inode_flash *sino_ptr = wpage; sino_ptr->nodetype = sum_collected->sum_list_head->i.nodetype; sino_ptr->inode = sum_collected->sum_list_head->i.inode; sino_ptr->version = sum_collected->sum_list_head->i.version; sino_ptr->offset = sum_collected->sum_list_head->i.offset; sino_ptr->totlen = sum_collected->sum_list_head->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT : { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; sdrnt_ptr->nodetype = sum_collected->sum_list_head->d.nodetype; sdrnt_ptr->totlen = sum_collected->sum_list_head->d.totlen; sdrnt_ptr->offset = sum_collected->sum_list_head->d.offset; sdrnt_ptr->pino = sum_collected->sum_list_head->d.pino; sdrnt_ptr->version = sum_collected->sum_list_head->d.version; sdrnt_ptr->ino = sum_collected->sum_list_head->d.ino; sdrnt_ptr->nsize = sum_collected->sum_list_head->d.nsize; sdrnt_ptr->type = sum_collected->sum_list_head->d.type; memcpy(sdrnt_ptr->name, sum_collected->sum_list_head->d.name, sum_collected->sum_list_head->d.nsize); wpage += JFFS2_SUMMARY_DIRENT_SIZE(sum_collected->sum_list_head->d.nsize); break; } case JFFS2_NODETYPE_XATTR: { struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; sxattr_ptr->version = sum_collected->sum_list_head->x.version; sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; wpage += JFFS2_SUMMARY_XATTR_SIZE; break; } case JFFS2_NODETYPE_XREF: { struct jffs2_sum_xref_flash *sxref_ptr = wpage; sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; sxref_ptr->offset = sum_collected->sum_list_head->r.offset; wpage += JFFS2_SUMMARY_XREF_SIZE; break; } default : { warnmsg("Unknown node type!\n"); } } temp = sum_collected->sum_list_head; sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; free(temp); sum_collected->sum_num--; } sum_collected->sum_size = 0; sum_collected->sum_num = 0; sum_collected->sum_list_tail = NULL; wpage += padsize; sm = wpage; sm->offset = offset; sm->magic = magic; isum.sum_crc = cpu_to_je32(mtd_crc32(0, tpage, datasize)); isum.node_crc = cpu_to_je32(mtd_crc32(0, &isum, sizeof(isum) - 8)); full_write(data_buffer + data_ofs, &isum, sizeof(isum)); full_write(data_buffer + data_ofs, tpage, datasize); free(tpage); } static void full_write(void *target_buff, const void *buf, int len) { memcpy(target_buff, buf, len); data_ofs += len; } static void pad(int req) { while (req) { if (req > sizeof(ffbuf)) { full_write(data_buffer + data_ofs, ffbuf, sizeof(ffbuf)); req -= sizeof(ffbuf); } else { full_write(data_buffer + data_ofs, ffbuf, req); req = 0; } } } static inline void padword(void) { if (data_ofs % 4) full_write(data_buffer + data_ofs, ffbuf, 4 - (data_ofs % 4)); } static inline void pad_block_if_less_than(int req,int plus) { int datasize = req + plus + sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; datasize += (4 - (datasize % 4)) % 4; if (data_ofs + req > erase_block_size - datasize) { dump_sum_records(); write_buff_to_file(); } if (add_cleanmarkers && found_cleanmarkers) { if (!data_ofs) { full_write(data_buffer, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padword(); } } } void flush_buffers(void) { if ((add_cleanmarkers == 1) && (found_cleanmarkers == 1)) { /* CLEANMARKER */ if (data_ofs != cleanmarker_size) { /* INODE BUFFER */ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; datasize += (4 - (datasize % 4)) % 4; /* If we have a full inode buffer, then write out inode and summary data */ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { dump_sum_records(); write_buff_to_file(); } else { /* else just write out inode data */ if (padto) pad(erase_block_size - data_ofs); write_buff_to_file(); } } } else { /* NO CLEANMARKER */ if (data_ofs != 0) { /* INODE BUFFER */ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; datasize += (4 - (datasize % 4)) % 4; /* If we have a full inode buffer, then write out inode and summary data */ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { dump_sum_records(); write_buff_to_file(); } else { /* Else just write out inode data */ if(padto) pad(erase_block_size - data_ofs); write_buff_to_file(); } } } } int add_sum_mem(union jffs2_sum_mem *item) { if (!sum_collected->sum_list_head) sum_collected->sum_list_head = (union jffs2_sum_mem *) item; if (sum_collected->sum_list_tail) sum_collected->sum_list_tail->u.next = (union jffs2_sum_mem *) item; sum_collected->sum_list_tail = (union jffs2_sum_mem *) item; switch (je16_to_cpu(item->u.nodetype)) { case JFFS2_NODETYPE_INODE: sum_collected->sum_size += JFFS2_SUMMARY_INODE_SIZE; sum_collected->sum_num++; break; case JFFS2_NODETYPE_DIRENT: sum_collected->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize); sum_collected->sum_num++; break; case JFFS2_NODETYPE_XATTR: sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; sum_collected->sum_num++; break; case JFFS2_NODETYPE_XREF: sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; sum_collected->sum_num++; break; default: errmsg_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype)); } return 0; } void add_sum_inode_mem(union jffs2_node_union *node) { struct jffs2_sum_inode_mem *temp = xmalloc(sizeof(*temp)); temp->nodetype = node->i.nodetype; temp->inode = node->i.ino; temp->version = node->i.version; temp->offset = cpu_to_je32(data_ofs); temp->totlen = node->i.totlen; temp->next = NULL; add_sum_mem((union jffs2_sum_mem *) temp); } void add_sum_dirent_mem(union jffs2_node_union *node) { struct jffs2_sum_dirent_mem *temp = xmalloc(sizeof(*temp) + node->d.nsize); temp->nodetype = node->d.nodetype; temp->totlen = node->d.totlen; temp->offset = cpu_to_je32(data_ofs); temp->pino = node->d.pino; temp->version = node->d.version; temp->ino = node->d.ino; temp->nsize = node->d.nsize; temp->type = node->d.type; temp->next = NULL; memcpy(temp->name,node->d.name,node->d.nsize); add_sum_mem((union jffs2_sum_mem *) temp); } void add_sum_xattr_mem(union jffs2_node_union *node) { struct jffs2_sum_xattr_mem *temp = xmalloc(sizeof(*temp)); temp->nodetype = node->x.nodetype; temp->xid = node->x.xid; temp->version = node->x.version; temp->offset = cpu_to_je32(data_ofs); temp->totlen = node->x.totlen; temp->next = NULL; add_sum_mem((union jffs2_sum_mem *) temp); } void add_sum_xref_mem(union jffs2_node_union *node) { struct jffs2_sum_xref_mem *temp = xmalloc(sizeof(*temp)); temp->nodetype = node->r.nodetype; temp->offset = cpu_to_je32(data_ofs); temp->next = NULL; add_sum_mem((union jffs2_sum_mem *) temp); } void write_dirent_to_buff(union jffs2_node_union *node) { pad_block_if_less_than(je32_to_cpu (node->d.totlen),JFFS2_SUMMARY_DIRENT_SIZE(node->d.nsize)); add_sum_dirent_mem(node); full_write(data_buffer + data_ofs, &(node->d), je32_to_cpu (node->d.totlen)); padword(); } void write_inode_to_buff(union jffs2_node_union *node) { pad_block_if_less_than(je32_to_cpu (node->i.totlen),JFFS2_SUMMARY_INODE_SIZE); add_sum_inode_mem(node); /* Add inode summary mem to summary list */ full_write(data_buffer + data_ofs, &(node->i), je32_to_cpu (node->i.totlen)); /* Write out the inode to inode_buffer */ padword(); } void write_xattr_to_buff(union jffs2_node_union *node) { pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); padword(); } void write_xref_to_buff(union jffs2_node_union *node) { pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); add_sum_xref_mem(node); /* Add xref summary mem to summary list */ full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); padword(); } void create_summed_image(int inp_size) { uint8_t *p = file_buffer; union jffs2_node_union *node; uint32_t crc, length; uint16_t type; int bitchbitmask = 0; int obsolete; char name[256]; while ( p < (file_buffer + inp_size)) { node = (union jffs2_node_union *) p; /* Skip empty space */ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { p += 4; continue; } if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { if (!bitchbitmask++) warnmsg("Wrong bitmask at 0x%08zx, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); p += 4; continue; } bitchbitmask = 0; type = je16_to_cpu(node->u.nodetype); if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { obsolete = 1; type |= JFFS2_NODE_ACCURATE; } else { obsolete = 0; } node->u.nodetype = cpu_to_je16(type); crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); if (crc != je32_to_cpu (node->u.hdr_crc)) { warnmsg("Wrong hdr_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->u.hdr_crc), crc); p += 4; continue; } switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: bareverbose(verbose, "%8s Inode node at 0x%08zx, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), je32_to_cpu (node->i.version), je32_to_cpu (node->i.isize), je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); if (crc != je32_to_cpu (node->i.node_crc)) { warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.node_crc), crc); p += PAD(je32_to_cpu (node->i.totlen)); continue; } crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); if (crc != je32_to_cpu(node->i.data_crc)) { warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.data_crc), crc); p += PAD(je32_to_cpu (node->i.totlen)); continue; } write_inode_to_buff(node); p += PAD(je32_to_cpu (node->i.totlen)); break; case JFFS2_NODETYPE_DIRENT: memcpy (name, node->d.name, node->d.nsize); name [node->d.nsize] = 0x0; bareverbose(verbose, "%8s Dirent node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), je32_to_cpu (node->d.version), je32_to_cpu (node->d.ino), node->d.nsize, name); crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); if (crc != je32_to_cpu (node->d.node_crc)) { warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.node_crc), crc); p += PAD(je32_to_cpu (node->d.totlen)); continue; } crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); if (crc != je32_to_cpu(node->d.name_crc)) { warnmsg("Wrong name_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.name_crc), crc); p += PAD(je32_to_cpu (node->d.totlen)); continue; } write_dirent_to_buff(node); p += PAD(je32_to_cpu (node->d.totlen)); break; case JFFS2_NODETYPE_XATTR: if (je32_to_cpu(node->x.node_crc) == 0xffffffff) obsolete = 1; bareverbose(verbose, "%8s Xdatum node at 0x%08zx, totlen 0x%08x, #xid %5u, version %5u\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->x.totlen), je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); if (crc != je32_to_cpu(node->x.node_crc)) { warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu(node->x.node_crc), crc); p += PAD(je32_to_cpu (node->x.totlen)); continue; } length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); crc = mtd_crc32(0, node->x.data, length); if (crc != je32_to_cpu(node->x.data_crc)) { warnmsg("Wrong data_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu(node->x.data_crc), crc); p += PAD(je32_to_cpu (node->x.totlen)); continue; } write_xattr_to_buff(node); p += PAD(je32_to_cpu (node->x.totlen)); break; case JFFS2_NODETYPE_XREF: if (je32_to_cpu(node->r.node_crc) == 0xffffffff) obsolete = 1; bareverbose(verbose, "%8s Xref node at 0x%08zx, totlen 0x%08x, #ino %5u, xid %5u\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu(node->r.totlen), je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); crc = mtd_crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); if (crc != je32_to_cpu(node->r.node_crc)) { warnmsg("Wrong node_crc at 0x%08zx, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu(node->r.node_crc), crc); p += PAD(je32_to_cpu (node->r.totlen)); continue; } write_xref_to_buff(node); p += PAD(je32_to_cpu (node->r.totlen)); break; case JFFS2_NODETYPE_CLEANMARKER: bareverbose(verbose, "%8s Cleanmarker at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->u.totlen)); if (!found_cleanmarkers) { found_cleanmarkers = 1; if (add_cleanmarkers == 1 && use_input_cleanmarker_size == 1){ cleanmarker_size = je32_to_cpu (node->u.totlen); setup_cleanmarker(); } } p += PAD(je32_to_cpu (node->u.totlen)); break; case JFFS2_NODETYPE_PADDING: bareverbose(verbose, "%8s Padding node at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->u.totlen)); p += PAD(je32_to_cpu (node->u.totlen)); break; case 0xffff: p += 4; break; default: bareverbose(verbose, "%8s Unknown node at 0x%08zx, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - file_buffer, je32_to_cpu (node->u.totlen)); p += PAD(je32_to_cpu (node->u.totlen)); } } } int main(int argc, char **argv) { int ret; process_options(argc,argv); if ((in_fd == -1) || (out_fd == -1)) { if(in_fd != -1) close(in_fd); if(out_fd != -1) close(out_fd); fprintf(stderr, "%s", helptext); errmsg_die("You must specify input and output files!\n"); } init_buffers(); init_sumlist(); while ((ret = load_next_block())) { create_summed_image(ret); } flush_buffers(); clean_buffers(); clean_sumlist(); if (in_fd != -1) close(in_fd); if (out_fd != -1) close(out_fd); return 0; } mtd-utils-1.5.0/tests/000077500000000000000000000000001175167361300146135ustar00rootroot00000000000000mtd-utils-1.5.0/tests/Makefile000066400000000000000000000002151175167361300162510ustar00rootroot00000000000000 SUBDIRS = checkfs fs-tests jittertest ubi-tests all clean tests: $(SUBDIRS) .PHONY: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ $(MAKECMDGOALS) mtd-utils-1.5.0/tests/checkfs/000077500000000000000000000000001175167361300162215ustar00rootroot00000000000000mtd-utils-1.5.0/tests/checkfs/.gitignore000066400000000000000000000000241175167361300202050ustar00rootroot00000000000000/checkfs /makefiles mtd-utils-1.5.0/tests/checkfs/Makefile000066400000000000000000000001451175167361300176610ustar00rootroot00000000000000TARGETS = checkfs makefiles include ../../common.mk $(TARGETS): $(addprefix $(BUILDDIR)/, comm.o) mtd-utils-1.5.0/tests/checkfs/README000066400000000000000000000155151175167361300171100ustar00rootroot00000000000000$Id: README,v 1.2 2001/06/21 23:07:06 dwmw2 Exp $ $Log: README,v $ Revision 1.2 2001/06/21 23:07:06 dwmw2 Initial import to MTD CVS Revision 1.1 2001/06/11 19:34:40 vipin Added README file to dir. This is the README file for the "checkfs" power fail test program. By: Vipin Malik NOTE: This program requires an external "power cycling box" connected to one of the com ports of the system under test. This power cycling box should wait for a random amount of time after it receives a "ok to power me down" message over the serial port, and then yank power to the system under test. (The box that I rigged up tested with waits anywhere from 0 to ~40 seconds). It should then restore power after a few seconds and wait for the message again. ABOUT: This program's primary purpose it to test the reliiability of various file systems under Linux. SETUP: You need to setup the file system you want to test and run the "makefiles" program ONCE. This creates a set of files that are required by the "checkfs" program. Also copy the "checkfs" executable program to the same dir. Then you need to make sure that the program "checkfs" is called automatically on startup. You can customise the operation of the "checkfs" program by passing it various cmd line arguments. run "checkfs -?" for more details. ****NOTE******* Make sure that you call the checkfs program only after you have mounted the file system you want to test (this is obvious), but also after you have run any "scan" utilities to check for and fix any file systems errors. The e2fsck is one utility for the ext2 file system. For an automated setup you of course need to provide these scan programs to run in standalone mode (-f -y flags for e2fsck for example). File systems like JFFS and JFFS2 do not have any such external utilities and you may call "checkfs" right after you have mounted the respective file system under test. There are two ways you can mount the file system under test: 1. Mount your root fs on a "standard" fs like ext2 and then mount the file system under test (which may be ext2 on another partition or device) and then run "checkfs" on this mounted partition OR 2. Make your fs AND device that you have put this fs as your root fs and run "checkfs" on the root device (i.e. "/"). You can of course still run checkfs under a separate dir under your "/" root dir. I have found the second method to be a particularly stringent arrangement (and thus preferred when you are trying to break something). Using this arrangement I was able to find that JFFS clobbered some "sister" files on the root fs even though "checkfs" would run fine through all its own check files. (I found this out when one of the clobbered sister file happened to be /bin/bash. The system refused to run rc.local thus preventing my "checkfs" program from being launched :) "checkfs": The "formatting" reliability of the fs as well as the file data integrity of files on the fs can be checked using this program. "formatiing" reliability can only be checked via an indirect method. If there is severe formatting reliability issues with the file system, it will most likely cause other system failures that will prevent this program from running successfully on a power up. This will prevent a "ok to power me down" message from going out to the power cycling black box and prevent power being turned off again. File data reliability is checked more directly. A fixed number of files are created in the current dir (using the program "makefiles"). Each file has a random number of bytes in it (set by using the -s cmd line flag). The number of "ints" in the file is stored as the first "int" in it (note: 0 length files are not allowed). Each file is then filled with random data and a 16 bit CRC appended at the end. When "checkfs" is run, it runs through all files (with predetermined file names)- one at a time- and checks for the number of "int's" in it as well as the ending CRC. The program exits if the numbers of files that are corrupt are greater that a user specified parameter (set by using the -e cmd line flag). If the number of corrupt files is less than this parameter, the corrupt files are repaired and operation resumes as explained below. The idea behind allowing a user specified amount of corrupt files is as follows: If you are testing for "formatting" reliability of a fs, and for the data reliability of "other" files present of the fs, use -e 1. "other" files are defined as sister files on the fs, not being written to by the "checkfs" test program. As mentioned, in this case you would set -e 1, or allow at most 1 file to be corrupt each time after a power fail. This would be the file that was probably being written to when power failed (and CRC was not updated to reflect the new data being written). You would check file systems like ext2 etc. with such a configuration. (As you have no hope that these file systems provide for either your new data or old data to be present in the file if power failed during the write. This is called "roll back and recover".) With JFFS2 I tested for such "roll back and recover" file data reliability by setting -e 0 and making sure that all writes to the file being updated are done in a *single* write(). This is how I found that JFFS2 (yet) does NOT support this functionality. (There was a great debate if this was a bug or a feature that was lacking or even an issue at all. See the mtd archives for more details). In other words, JFFS2 will partially update a file on FLASH even before the write() command has completed, thus leaving part old data part new data in your file if power failed in the middle of a write(). This is bad functionality if you are updating a binary structure or a CRC protected file (as in our case). If All Files Check Out OK: On the startup scan, if there are less errors than specified by the "-e flag" a "ok to power me down message" is sent via the specified com port. The actual format of this message will depend on the format expected by the power cycling box that will receive this message. One may customise the actual message that goes out in the "do_pwr_dn)" routine in "comm.c". This file is called with an open file descriptor to the comm port that this message needs to go out over and the count of the current power cycle (in case your power cycling box can display/log this count). After this message has been sent out, the checkfs program goes into a while(1) loop of writing new data (with CRC), one at a time, into all the "check files" in the dir. Its life comes to a sudden end when power is asynchronously pulled from under its feet (by your external power cycling box). It comes back to life when power is restored and the system boots and checkfs is called from the rc.local script file. The cycle then repeats till a problem is detected, at which point the "ok to power me down" message is not sent and the cycle stops waiting for the user to examine the system. mtd-utils-1.5.0/tests/checkfs/checkfs.c000066400000000000000000000522311175167361300177760ustar00rootroot00000000000000/* * 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 in a endless loop and checksum each before closing them. STRUCTURE OF THE FILES: The fist int is the size of the file in bytes. The last 2 bytes are the CRC for the entire file. There is random data in between. The files are opened in the current dir. $Id: checkfs.c,v 1.8 2005/11/07 11:15:17 gleixner Exp $ $Log: checkfs.c,v $ Revision 1.8 2005/11/07 11:15:17 gleixner [MTD / JFFS2] Clean up trailing white spaces Revision 1.7 2001/06/21 23:04:17 dwmw2 Initial import to MTD CVS Revision 1.6 2001/06/08 22:26:05 vipin Split the modbus comm part of the program (that sends the ok to pwr me down message) into another file "comm.c" Revision 1.5 2001/06/08 21:29:56 vipin fixed small issue with write() checking for < 0 instead of < (bytes to be written). Now it does the latter (as it should). Revision 1.4 2001/05/11 22:29:40 vipin Added a test to check and err out if the first int in file (which tells us how many bytes there are in the file) is zero. This will prevent a corrupt file with zero's in it from passing the crc test. Revision 1.3 2001/05/11 21:33:54 vipin Changed to use write() rather than fwrite() when creating new file. Additionally, and more important, it now does a single write() for the entire data. This will enable us to use this program to test for power fail *data* reliability when writing over an existing file, specially on powr fail "safe" file systems as jffs/jffs2. Also added a new cmdline parameter "-e" that specifies the max # of errors that can be tolerated. This should be set to ZERO to test for the above, as old data should be reliabily maintained if the newer write never "took" before power failed. If the write did succeed, then the newer data will have its own CRC in place when it gets checked => hence no error. In theory at least! Revision 1.2 2001/05/11 19:27:33 vipin Added cmd line args to change serial port, and specify max size of random files created. Some cleanup. Added -Wall to Makefile. Revision 1.1 2001/05/11 16:06:28 vipin Importing checkfs (the power fail test program) into CVS. This was originally done for NEWS. NEWS had a lot of version, this is based off the last one done for NEWS. The "makefiles" program is run once initially to create the files in the current dir. "checkfs" is then run on every powerup to check consistancy of the files. See checkfs.c for more details. */ #include #include #include #include #include #include #include #include #include #include #include #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, strerror(errno)); exit(1); } if (tcgetattr(fd, &tios) < 0) { fprintf(stderr, "Could not get terminal attributes: %s", strerror(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", strerror(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, S_IRWXU)) <= 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 \n", argv[0]); printf("%s : Set com port to send ok to pwr dn msg on\n", CMDLINE_PORT); printf("%s : Set Max size in bytes of each file to be created.\n", CMDLINE_MAXFILEBYTES); printf("%s : 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 allowed is %zu.\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; short errorCnt = 0; time_t timep; char * time_string; unsigned int seed; 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 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 #include #include #include /* 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; } mtd-utils-1.5.0/tests/checkfs/common.h000066400000000000000000000003301175167361300176560ustar00rootroot00000000000000/* $Id: common.h,v 1.1 2001/06/21 23:07:56 dwmw2 Exp $ */ //this .h file is common to both the file creation utility and //the file checking utility. #define TRUE 1 #define FALSE 0 #define MAX_NUM_FILES 100 mtd-utils-1.5.0/tests/checkfs/makefiles.c000066400000000000000000000205101175167361300203230ustar00rootroot00000000000000/* * Copyright Daniel Industries. * Created by: Vipin Malik (vipin.malik@daniel.com) * * This is GPL code. See the file COPYING for more details * * Software distributed under the Licence is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. * See the Licence for the specific language governing rights and * limitations under the Licence. * $Id: makefiles.c,v 1.2 2005/11/07 11:15:17 gleixner Exp $ This program creates MAX_NUM_FILES files (file00001, file00002 etc) and fills them with random numbers till they are a random length. Then it checksums the files (with the checksum as the last two bytes) and closes the file. The fist int is the size of the file in bytes. It then opens another file and the process continues. The files are opened in the current dir. */ #include #include #include #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROGRAM_VERSION "1.1" #define PROGRAM_NAME "integck" #include "common.h" #include "libubi.h" /* * WARNING! This is a dirty hack! The symbols for static functions are not * printed in the stack backtrace. So we remove ths 'static' keyword using the * pre-processor. This is really error-prone because this won't work if, e.g., * local static variables were used. */ #ifdef INTEGCK_DEBUG #define static #endif #define MAX_RANDOM_SEED 10000000 /* The pattern for the top directory where we run the test */ #define TEST_DIR_PATTERN "integck_test_dir_%u" /* Maximum buffer size for a single read/write operation */ #define IO_BUFFER_SIZE 32768 /* * Check if a condition is true and die if not. */ #define stringify1(x) #x #define stringify(x) stringify1(x) #define CHECK(cond) do { \ if (!(cond)) \ check_failed(stringify(cond), __func__, __FILE__, __LINE__); \ } while(0) /* * In case of emulated power cut failures the FS has to return EROFS. But * unfortunately, the Linux kernel sometimes returns EIO to user-space anyway * (when write-back fails the return code is awayse EIO). */ #define pcv(fmt, ...) do { \ int __err = 1; \ if (args.power_cut_mode && (errno == EROFS || errno == EIO)) \ __err = 0; \ if (!args.power_cut_mode || args.verbose || __err) \ normsg(fmt " (line %d, error %d (%s))", \ ##__VA_ARGS__, __LINE__, errno, strerror(errno)); \ CHECK(!__err); \ } while(0) #define v(fmt, ...) do { \ if (args.verbose) \ normsg(fmt " (line %d)", ##__VA_ARGS__, __LINE__); \ } while(0) /* The variables below are set by command line arguments */ static struct { long repeat_cnt; int power_cut_mode; int verify_ops; int reattach; int mtdn; int verbose; const char *mount_point; } args = { .repeat_cnt = 1, }; /* * The below data structure describes the tested file-system. * * max_name_len: maximum file name length * page_size: memory page size to use with 'mmap()' * log10_initial_free: logarighm base 10 of the initial amount of free space in * the tested file-system * nospc_size_ok: file size is updated even if the write operation failed with * ENOSPC error * can_mmap: file-system supports share writable 'mmap()' operation * can_remount: is it possible to re-mount the tested file-system? * fstype: file-system type (e.g., "ubifs") * fsdev: the underlying device mounted by the tested file-system * mount_opts: non-standard mount options of the tested file-system (non-standard * options are stored in string form as a comma-separated list) * mount_flags: standard mount options of the tested file-system (standard * options as stored as a set of flags) * mount_point: tested file-system mount point path * test_dir: the directory on the tested file-system where we test */ static struct { int max_name_len; int page_size; unsigned int log10_initial_free; unsigned int nospc_size_ok:1; unsigned int can_mmap:1; unsigned int can_remount:1; char *fstype; char *fsdev; char *mount_opts; unsigned long mount_flags; char *mount_point; char *test_dir; } fsinfo = { .nospc_size_ok = 1, .can_mmap = 1, }; /* Structures to store data written to the test file system, so that we can check whether the file system is correct. */ struct write_info /* Record of random data written into a file */ { struct write_info *next; off_t offset; /* Where in the file the data was written */ union { off_t random_offset; /* Call rand_r() this number of times first */ off_t new_length; /* For truncation records new file length */ }; size_t size; /* Number of bytes written */ unsigned int random_seed; /* Seed for rand_r() to create random data. If greater than MAX_RANDOM_SEED then this is a truncation record (raw_writes only) */ }; struct dir_entry_info; struct file_info /* Each file has one of these */ { struct write_info *writes; /* Record accumulated writes to the file */ struct write_info *raw_writes; /* Record in order all writes to the file */ struct fd_info *fds; /* All open file descriptors for this file */ struct dir_entry_info *links; off_t length; int link_count; unsigned int check_run_no; /* Run number used when checking */ unsigned int no_space_error:1; /* File has incurred a ENOSPC error */ unsigned int clean:1; /* Non-zero if the file is synchronized */ }; struct symlink_info /* Each symlink has one of these */ { char *target_pathname; struct dir_entry_info *entry; /* dir entry of this symlink */ }; struct dir_info /* Each directory has one of these */ { struct dir_info *parent; /* Parent directory or null for our top directory */ unsigned int number_of_entries; struct dir_entry_info *first; struct dir_entry_info *entry; /* Dir entry of this dir */ unsigned int clean:1; /* Non-zero if the directory is synchronized */ }; struct dir_entry_info /* Each entry in a directory has one of these */ { struct dir_entry_info *next; /* List of entries in directory */ struct dir_entry_info *prev; /* List of entries in directory */ struct dir_entry_info *next_link; /* List of hard links for same file */ struct dir_entry_info *prev_link; /* List of hard links for same file */ char *name; struct dir_info *parent; /* Parent directory */ union { struct file_info *file; struct dir_info *dir; struct symlink_info *symlink; void *target; }; char type; /* f => file, d => dir, s => symlink */ char checked; /* Temporary flag used when checking */ }; struct fd_info /* We keep a number of files open */ { struct fd_info *next; struct file_info *file; int fd; }; struct open_file_info /* We keep a list of open files */ { struct open_file_info *next; struct fd_info *fdi; }; static struct dir_info *top_dir = NULL; /* Our top directory */ static struct open_file_info *open_files = NULL; /* We keep a list of open files */ static size_t open_files_count = 0; static int grow = 1; /* Should we try to grow files and directories */ static int shrink = 0; /* Should we try to shrink files and directories */ static int full = 0; /* Flag that the file system is full */ static uint64_t operation_count = 0; /* Number of operations used to fill up the file system */ static unsigned int check_run_no; static unsigned int random_seed; /* * A buffer which is used by 'make_name()' to return the generated random name. */ static char *random_name_buf; /* * This is a helper for the 'CHECK()' macro - prints a scary error message and * terminates the program. */ static void check_failed(const char *cond, const char *func, const char *file, int line) { int error = errno, count; void *addresses[128]; fflush(stdout); fflush(stderr); errmsg("condition '%s' failed in %s() at %s:%d", cond, func, file, line); normsg("error %d (%s)", error, strerror(error)); /* * Note, to make this work well you need: * 1. Make all functions non-static - add "#define static' * 2. Compile with -rdynamic and -g gcc options * 3. Preferrably compile with -O0 to avoid inlining */ count = backtrace(addresses, 128); backtrace_symbols_fd(addresses, count, fileno(stdout)); exit(EXIT_FAILURE); } /* * Is this 'struct write_info' actually holds information about a truncation? */ static int is_truncation(struct write_info *w) { return w->random_seed > MAX_RANDOM_SEED; } /* * Return a random number between 0 and max - 1. */ static unsigned int random_no(unsigned int max) { assert(max < RAND_MAX); if (max == 0) return 0; return rand_r(&random_seed) % max; } /* * Allocate a buffer of 'size' bytes and fill it with zeroes. */ static void *zalloc(size_t size) { void *buf = malloc(size); CHECK(buf != NULL); memset(buf, 0, size); return buf; } /* * Duplicate a string. */ static char *dup_string(const char *s) { char *str; assert(s != NULL); str = strdup(s); CHECK(str != NULL); return str; } static char *cat_strings(const char *a, const char *b) { char *str; size_t sz; if (a && !b) return dup_string(a); if (b && !a) return dup_string(b); if (!a && !b) return NULL; sz = strlen(a) + strlen(b) + 1; str = malloc(sz); CHECK(str != NULL); strcpy(str, a); strcat(str, b); return str; } static char *cat_paths(const char *a, const char *b) { char *str; size_t sz, na, nb; int as = 0, bs = 0; assert(a != NULL); assert(b != NULL); na = strlen(a); nb = strlen(b); if (na && a[na - 1] == '/') as = 1; if (nb && b[0] == '/') bs = 1; if ((as && !bs) || (!as && bs)) return cat_strings(a, b); if (as && bs) return cat_strings(a, b + 1); sz = na + nb + 2; str = malloc(sz); CHECK(str != NULL); strcpy(str, a); strcat(str, "/"); strcat(str, b); return str; } /* * Get the free space for the tested file system. */ static void get_fs_space(uint64_t *total, uint64_t *free) { struct statvfs st; CHECK(statvfs(fsinfo.mount_point, &st) == 0); if (total) *total = (uint64_t)st.f_blocks * (uint64_t)st.f_frsize; if (free) *free = (uint64_t)st.f_bavail * (uint64_t)st.f_frsize; } static char *dir_path(struct dir_info *parent, const char *name) { char *parent_path, *path; if (!parent) return cat_paths(fsinfo.mount_point, name); parent_path = dir_path(parent->parent, parent->entry->name); path = cat_paths(parent_path, name); free(parent_path); return path; } static void open_file_add(struct fd_info *fdi) { struct open_file_info *ofi; ofi = zalloc(sizeof(struct open_file_info)); ofi->next = open_files; ofi->fdi = fdi; open_files = ofi; open_files_count += 1; } static void open_file_remove(struct fd_info *fdi) { struct open_file_info *ofi; struct open_file_info **prev; prev = &open_files; for (ofi = open_files; ofi; ofi = ofi->next) { if (ofi->fdi == fdi) { *prev = ofi->next; free(ofi); open_files_count -= 1; return; } prev = &ofi->next; } CHECK(0); /* We are trying to remove something that is not there */ } static struct fd_info *add_fd(struct file_info *file, int fd) { struct fd_info *fdi; fdi = zalloc(sizeof(struct fd_info)); fdi->next = file->fds; fdi->file = file; fdi->fd = fd; file->fds = fdi; open_file_add(fdi); return fdi; } /* * Free all the information about writes to a file. */ static void free_writes_info(struct file_info *file) { struct write_info *w, *next; w = file->writes; while (w) { next = w->next; free(w); w = next; } w = file->raw_writes; while (w) { next = w->next; free(w); w = next; } } static void *add_dir_entry(struct dir_info *parent, char type, const char *name, void *target) { struct dir_entry_info *entry; entry = zalloc(sizeof(struct dir_entry_info)); entry->type = type; entry->name = dup_string(name); entry->parent = parent; entry->next = parent->first; if (parent->first) parent->first->prev = entry; parent->first = entry; parent->number_of_entries += 1; parent->clean = 0; if (entry->type == 'f') { struct file_info *file = target; if (!file) file = zalloc(sizeof(struct file_info)); entry->file = file; entry->next_link = file->links; if (file->links) file->links->prev_link = entry; file->links = entry; file->link_count += 1; return file; } else if (entry->type == 'd') { struct dir_info *dir = target; if (!dir) dir = zalloc(sizeof(struct dir_info)); entry->dir = dir; dir->entry = entry; dir->parent = parent; return dir; } else if (entry->type == 's') { struct symlink_info *symlink = target; if (!symlink) symlink = zalloc(sizeof(struct symlink_info)); entry->symlink = symlink; symlink->entry = entry; return symlink; } else assert(0); } static void remove_dir_entry(struct dir_entry_info *entry, int free_target) { entry->parent->clean = 0; entry->parent->number_of_entries -= 1; if (entry->parent->first == entry) entry->parent->first = entry->next; if (entry->prev) entry->prev->next = entry->next; if (entry->next) entry->next->prev = entry->prev; if (entry->type == 'f') { struct file_info *file = entry->file; if (entry->prev_link) entry->prev_link->next_link = entry->next_link; if (entry->next_link) entry->next_link->prev_link = entry->prev_link; if (file->links == entry) file->links = entry->next_link; file->link_count -= 1; if (file->link_count == 0) assert(file->links == NULL); /* Free struct file_info if file is not open and not linked */ if (free_target && !file->fds && !file->links) { free_writes_info(file); free(file); } } if (free_target) { if (entry->type == 'd') { free(entry->dir); } else if (entry->type == 's') { free(entry->symlink->target_pathname); free(entry->symlink); } } free(entry->name); free(entry); } /* * Create a new directory "name" in the parent directory described by "parent" * and add it to the in-memory list of directories. Returns zero in case of * success and -1 in case of failure. */ static int dir_new(struct dir_info *parent, const char *name) { char *path; assert(parent); path = dir_path(parent, name); v("creating dir %s", path); if (mkdir(path, 0777) != 0) { if (errno == ENOSPC) { full = 1; free(path); return 0; } pcv("cannot create directory %s", path); free(path); return -1; } if (args.verify_ops) { struct stat st; CHECK(lstat(path, &st) == 0); CHECK(S_ISDIR(st.st_mode)); } free(path); add_dir_entry(parent, 'd', name, NULL); return 0; } static int file_delete(struct file_info *file); static int file_unlink(struct dir_entry_info *entry); static int symlink_remove(struct symlink_info *symlink); static int dir_remove(struct dir_info *dir) { char *path; /* Remove directory contents */ while (dir->first) { struct dir_entry_info *entry; int ret = 0; entry = dir->first; if (entry->type == 'd') ret = dir_remove(entry->dir); else if (entry->type == 'f') ret = file_unlink(entry); else if (entry->type == 's') ret = symlink_remove(entry->symlink); else CHECK(0); /* Invalid struct dir_entry_info */ if (ret) return -1; } /* Remove directory form the file-system */ path = dir_path(dir->parent, dir->entry->name); if (rmdir(path) != 0) { pcv("cannot remove directory entry %s", path); free(path); return -1; } if (args.verify_ops) { struct stat st; CHECK(lstat(path, &st) == -1); CHECK(errno == ENOENT); } /* Remove entry from parent directory */ remove_dir_entry(dir->entry, 1); free(path); return 0; } static int file_new(struct dir_info *parent, const char *name) { char *path; mode_t mode; int fd; assert(parent != NULL); path = dir_path(parent, name); mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; v("creating file %s", path); fd = open(path, O_CREAT | O_EXCL | O_RDWR, mode); if (fd == -1) { if (errno == ENOSPC) { full = 1; free(path); return 0; } pcv("cannot create file %s", path); free(path); return -1; } if (args.verify_ops) { struct stat st; CHECK(lstat(path, &st) == 0); CHECK(S_ISREG(st.st_mode)); } add_dir_entry(parent, 'f', name, NULL); close(fd); free(path); return 0; } static int link_new(struct dir_info *parent, const char *name, struct file_info *file) { struct dir_entry_info *entry; char *path, *target; int ret; struct stat st1, st2; entry = file->links; if (!entry) return 0; path = dir_path(parent, name); target = dir_path(entry->parent, entry->name); if (args.verify_ops) CHECK(lstat(target, &st1) == 0); v("creating hardlink %s ---> %s", path, target); ret = link(target, path); if (ret != 0) { if (errno == ENOSPC) { ret = 0; full = 1; } else pcv("cannot create hardlink %s in directory %s to file %s", path, parent->entry->name, target); free(target); free(path); return ret; } if (args.verify_ops) { CHECK(lstat(path, &st2) == 0); CHECK(S_ISREG(st2.st_mode)); CHECK(st1.st_ino == st2.st_ino); CHECK(st2.st_nlink > 1); CHECK(st2.st_nlink == st1.st_nlink + 1); } add_dir_entry(parent, 'f', name, file); free(target); free(path); return 0; } static void file_close(struct fd_info *fdi); static void file_close_all(struct file_info *file) { struct fd_info *fdi = file->fds; while (fdi) { struct fd_info *next = fdi->next; file_close(fdi); fdi = next; } } /* * Unlink a directory entry for a file. */ static int file_unlink(struct dir_entry_info *entry) { char *path; int ret; path = dir_path(entry->parent, entry->name); /* Unlink the file */ ret = unlink(path); if (ret) { pcv("cannot unlink file %s", path); free(path); return -1; } if (args.verify_ops) { struct stat st; CHECK(lstat(path, &st) == -1); CHECK(errno == ENOENT); } /* Remove file entry from parent directory */ remove_dir_entry(entry, 1); free(path); return 0; } static struct dir_entry_info *pick_entry(struct file_info *file) { struct dir_entry_info *entry; unsigned int r; if (!file->link_count) return NULL; r = random_no(file->link_count); entry = file->links; while (entry && r--) entry = entry->next_link; return entry; } static int file_unlink_file(struct file_info *file) { struct dir_entry_info *entry; entry = pick_entry(file); if (!entry) return 0; return file_unlink(entry); } /* * Close all open descriptors for a file described by 'file' and delete it by * unlinking all its hardlinks. */ static int file_delete(struct file_info *file) { struct dir_entry_info *entry = file->links; file_close_all(file); while (entry) { struct dir_entry_info *next = entry->next_link; if (file_unlink(entry)) return -1; entry = next; } return 0; } static void file_info_display(struct file_info *file) { struct dir_entry_info *entry; struct write_info *w; unsigned int wcnt; normsg("File Info:"); normsg(" Link count: %d", file->link_count); normsg(" Links:"); entry = file->links; while (entry) { normsg(" Name: %s", entry->name); normsg(" Directory: %s", entry->parent->entry->name); entry = entry->next_link; } normsg(" Length: %llu", (unsigned long long)file->length); normsg(" File was open: %s", (file->fds == NULL) ? "false" : "true"); normsg(" File was deleted: %s", (file->link_count == 0) ? "true" : "false"); normsg(" File was out of space: %s", (file->no_space_error == 0) ? "false" : "true"); normsg(" File Data:"); wcnt = 0; w = file->writes; while (w) { normsg(" Offset: %llu Size: %zu Seed: %llu R.Off: %llu", (unsigned long long)w->offset, w->size, (unsigned long long)w->random_seed, (unsigned long long)w->random_offset); wcnt += 1; w = w->next; } normsg(" %u writes", wcnt); normsg(" ============================================"); normsg(" Write Info:"); wcnt = 0; w = file->raw_writes; while (w) { if (is_truncation(w)) normsg(" Trunc from %llu to %llu", (unsigned long long)w->offset, (unsigned long long)w->new_length); else normsg(" Offset: %llu Size: %zu Seed: %llu R.Off: %llu", (unsigned long long)w->offset, w->size, (unsigned long long)w->random_seed, (unsigned long long)w->random_offset); wcnt += 1; w = w->next; } normsg(" %u writes or truncations", wcnt); normsg(" ============================================"); } static int file_open(struct file_info *file) { int fd, flags = O_RDWR; char *path; assert(file->links); path = dir_path(file->links->parent, file->links->name); if (random_no(100) == 1) flags |= O_SYNC; fd = open(path, flags); if (fd == -1) { pcv("cannot open file %s", path); free(path); return -1; } free(path); add_fd(file, fd); return 0; } static const char *get_file_name(struct file_info *file) { if (file->links) return file->links->name; return "(unlinked file, no names)"; } /* * Write random 'size' bytes of random data to offset 'offset'. Seed the random * gererator with 'seed'. Return amount of written data on success and -1 on * failure. */ static ssize_t file_write_data(struct file_info *file, int fd, off_t offset, size_t size, unsigned int seed) { size_t remains, actual, block; ssize_t written; char buf[IO_BUFFER_SIZE]; CHECK(lseek(fd, offset, SEEK_SET) != (off_t)-1); remains = size; actual = 0; written = IO_BUFFER_SIZE; v("write %zd bytes, offset %llu, file %s", size, (unsigned long long)offset, get_file_name(file)); while (remains) { /* Fill up buffer with random data */ if (written < IO_BUFFER_SIZE) { memmove(buf, buf + written, IO_BUFFER_SIZE - written); written = IO_BUFFER_SIZE - written; } else written = 0; for (; written < IO_BUFFER_SIZE; ++written) buf[written] = rand_r(&seed); /* Write a block of data */ if (remains > IO_BUFFER_SIZE) block = IO_BUFFER_SIZE; else block = remains; written = write(fd, buf, block); if (written < 0) { if (errno == ENOSPC) { full = 1; file->no_space_error = 1; break; } pcv("failed to write %zu bytes to offset %llu of file %s", block, (unsigned long long)(offset + actual), get_file_name(file)); return -1; } remains -= written; actual += written; } return actual; } static void file_check_data(struct file_info *file, int fd, struct write_info *w); /* * Save the information about a file write operation and verify the write if * necessary. */ static void file_write_info(struct file_info *file, int fd, off_t offset, size_t size, unsigned int seed) { struct write_info *new_write, *w, **prev, *tmp; int inserted; off_t end, chg; /* Create struct write_info */ new_write = zalloc(sizeof(struct write_info)); new_write->offset = offset; new_write->size = size; new_write->random_seed = seed; w = zalloc(sizeof(struct write_info)); w->next = file->raw_writes; w->offset = offset; w->size = size; w->random_seed = seed; file->raw_writes = w; if (args.verify_ops && !args.power_cut_mode) file_check_data(file, fd, new_write); /* Insert it into file->writes */ inserted = 0; end = offset + size; w = file->writes; prev = &file->writes; while (w) { if (w->offset >= end) { /* w comes after new_write, so insert before it */ new_write->next = w; *prev = new_write; inserted = 1; break; } /* w does not come after new_write */ if (w->offset + w->size > offset) { /* w overlaps new_write */ if (w->offset < offset) { /* w begins before new_write begins */ if (w->offset + w->size <= end) /* w ends before new_write ends */ w->size = offset - w->offset; else { /* w ends after new_write ends */ /* Split w */ tmp = malloc(sizeof(struct write_info)); CHECK(tmp != NULL); *tmp = *w; chg = end - tmp->offset; tmp->offset += chg; tmp->random_offset += chg; tmp->size -= chg; w->size = offset - w->offset; /* Insert new struct write_info */ w->next = new_write; new_write->next = tmp; inserted = 1; break; } } else { /* w begins after new_write begins */ if (w->offset + w->size <= end) { /* w is completely overlapped, so remove it */ *prev = w->next; tmp = w; w = w->next; free(tmp); continue; } /* w ends after new_write ends */ chg = end - w->offset; w->offset += chg; w->random_offset += chg; w->size -= chg; continue; } } prev = &w->next; w = w->next; } if (!inserted) *prev = new_write; /* Update file length */ if (end > file->length) file->length = end; } /* Randomly select offset and and size to write in a file */ static void get_offset_and_size(struct file_info *file, off_t *offset, size_t *size) { unsigned int r, n; r = random_no(100); if (r == 0 && grow) /* 1 time in 100, when growing, write off the end of the file */ *offset = file->length + random_no(10000000); else if (r < 4) /* 3 (or 4) times in 100, write at the beginning of file */ *offset = 0; else if (r < 52 || !grow) /* 48 times in 100, write into the file */ *offset = random_no(file->length); else /* 48 times in 100, write at the end of the file */ *offset = file->length; /* Distribute the size logarithmically */ if (random_no(1000) == 0) r = random_no(fsinfo.log10_initial_free + 2); else r = random_no(fsinfo.log10_initial_free); n = 1; while (r--) n *= 10; *size = random_no(n); if (!grow && *offset + *size > file->length) *size = file->length - *offset; if (*size == 0) *size = 1; } static void file_check_hole(struct file_info *file, int fd, off_t offset, size_t size); static void file_truncate_info(struct file_info *file, int fd, size_t new_length) { struct write_info *w, **prev, *tmp; /* Remove / truncate file->writes */ w = file->writes; prev = &file->writes; while (w) { if (w->offset >= new_length) { /* w comes after eof, so remove it */ *prev = w->next; tmp = w; w = w->next; free(tmp); continue; } if (w->offset + w->size > new_length) w->size = new_length - w->offset; prev = &w->next; w = w->next; } /* Add an entry in raw_writes for the truncation */ w = zalloc(sizeof(struct write_info)); w->next = file->raw_writes; w->offset = file->length; w->new_length = new_length; w->random_seed = MAX_RANDOM_SEED + 1; file->raw_writes = w; if (args.verify_ops && !args.power_cut_mode && new_length > file->length) file_check_hole(file, fd, file->length, new_length - file->length); /* Update file length */ file->length = new_length; } /* * Truncate a file to length 'new_length'. If there is no enough space to * peform the operation, this function returns 1. Returns 0 on success and -1 * on failure. */ static int file_ftruncate(struct file_info *file, int fd, off_t new_length) { if (ftruncate(fd, new_length) != 0) { if (errno == ENOSPC) { file->no_space_error = 1; /* Delete errored files */ if (!fsinfo.nospc_size_ok) if (file_delete(file)) return -1; return 1; } else pcv("cannot truncate file %s to %llu", get_file_name(file), (unsigned long long)new_length); return -1; } if (args.verify_ops) CHECK(lseek(fd, 0, SEEK_END) == new_length); return 0; } /* * 'mmap()' a file and randomly select where to write data. */ static int file_mmap_write(struct file_info *file) { size_t write_cnt = 0, r, i, len, size; struct write_info *w = file->writes; void *addr; char *waddr, *path; off_t offs, offset; unsigned int seed, seed_tmp; uint64_t free_space; int fd; assert(!args.power_cut_mode); if (!file->links) return 0; get_fs_space(NULL, &free_space); if (!free_space) return 0; /* Randomly pick a written area of the file */ if (!w) return 0; while (w) { write_cnt += 1; w = w->next; } r = random_no(write_cnt); w = file->writes; for (i = 0; w && w->next && i < r; i++) w = w->next; offs = (w->offset / fsinfo.page_size) * fsinfo.page_size; len = w->size + (w->offset - offs); if (len > 1 << 24) len = 1 << 24; /* Open it */ assert(file->links); path = dir_path(file->links->parent, file->links->name); fd = open(path, O_RDWR); if (fd == -1) { pcv("cannot open file %s to do mmap", path); goto out_error; } /* mmap it */ addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offs); if (addr == MAP_FAILED) { pcv("cannot mmap file %s", path); goto out_error; } /* Randomly select a part of the mmapped area to write */ size = random_no(w->size); if (size > free_space) size = free_space; if (size == 0) size = 1; offset = w->offset + random_no(w->size - size); /* Write it */ seed_tmp = seed = random_no(MAX_RANDOM_SEED); waddr = addr + (offset - offs); for (i = 0; i < size; i++) waddr[i] = rand_r(&seed_tmp); /* Unmap it */ if (munmap(addr, len)) { pcv("cannot unmap file %s", path); goto out_error; } /* Record what was written */ file_write_info(file, fd, offset, size, seed); free(path); CHECK(close(fd) == 0); return 0; out_error: free(path); CHECK(close(fd) == 0); return -1; } /* * Write random amount of data to a random offset in an open file or randomly * choose to truncate it. */ static int file_write(struct file_info *file, int fd) { int ret; file->clean = 0; if (!args.power_cut_mode && fsinfo.can_mmap && !full && file->link_count && random_no(100) == 1) { /* * Do not do 'mmap()' operations if: * 1. we are in power cut testing mode, because an emulated * power cut failure may cause SIGBUS when we are writing to * the 'mmap()'ed area, and SIGBUS is not easy to ignore. * 2. When the file-system is full, because again, writing to the * 'mmap()'ed area may cause SIGBUS when the space allocation * fails. This is not enough to guarantee we never get * SIGBUS, though. For example, if we write a lot to a hole, * this might require a lot of additional space, and we may * fail here. But I do not know why we never observed this, * probably this is just very unlikely. */ ret = file_mmap_write(file); } else { int truncate = 0; off_t offset; size_t size; ssize_t actual; unsigned int seed; get_offset_and_size(file, &offset, &size); seed = random_no(MAX_RANDOM_SEED); actual = file_write_data(file, fd, offset, size, seed); if (actual < 0) return -1; if (actual != 0) file_write_info(file, fd, offset, actual, seed); if (offset + actual <= file->length && shrink) { /* * 1 time in 100, when shrinking truncate after the * write. */ if (random_no(100) == 0) truncate = 1; } if (truncate) { size_t new_length = offset + actual; ret = file_ftruncate(file, fd, new_length); if (ret == -1) return -1; if (!ret) file_truncate_info(file, fd, new_length); } /* Delete errored files */ if (!fsinfo.nospc_size_ok && file->no_space_error) return file_delete(file); } /* Sync sometimes */ if (random_no(100) >= 99) { if (random_no(100) >= 50) { v("fsyncing file %s", get_file_name(file)); ret = fsync(fd); if (ret) pcv("fsync failed for %s", get_file_name(file)); } else { v("fdatasyncing file %s", get_file_name(file)); ret = fdatasync(fd); if (ret) pcv("fdatasync failed for %s", get_file_name(file)); } if (ret) return -1; file->clean = 1; } return 0; } /* * Write random amount of data to a random offset in a file or randomly * choose to truncate it. */ static int file_write_file(struct file_info *file) { int fd, ret; char *path; assert(file->links); path = dir_path(file->links->parent, file->links->name); fd = open(path, O_RDWR); if (fd == -1) { pcv("cannot open file %s for writing", path); free(path); return -1; } ret = file_write(file, fd); CHECK(close(fd) == 0); free(path); return ret; } /* * Truncate an open file randomly. */ static int file_truncate(struct file_info *file, int fd) { int ret; size_t new_length = random_no(file->length); file->clean = 0; ret = file_ftruncate(file, fd, new_length); if (ret == -1) return -1; if (!ret) file_truncate_info(file, fd, new_length); return 0; } static int file_truncate_file(struct file_info *file) { int fd; char *path; int ret; assert(file->links); path = dir_path(file->links->parent, file->links->name); fd = open(path, O_WRONLY); if (fd == -1) { pcv("cannot open file %s to truncate", path); free(path); return -1; } free(path); ret = file_truncate(file, fd); CHECK(close(fd) == 0); return ret; } static void file_close(struct fd_info *fdi) { struct file_info *file; struct fd_info *fdp; struct fd_info **prev; /* Close file */ CHECK(close(fdi->fd) == 0); /* Remove struct fd_info */ open_file_remove(fdi); file = fdi->file; prev = &file->fds; for (fdp = file->fds; fdp; fdp = fdp->next) { if (fdp == fdi) { *prev = fdi->next; free(fdi); if (!file->link_count && !file->fds) { free_writes_info(file); free(file); } return; } prev = &fdp->next; } CHECK(0); /* Didn't find struct fd_info */ } static void file_rewrite_data(int fd, struct write_info *w, char *buf) { size_t remains, block; ssize_t written; off_t r; unsigned int seed = w->random_seed; for (r = 0; r < w->random_offset; ++r) rand_r(&seed); CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t)-1); remains = w->size; written = IO_BUFFER_SIZE; while (remains) { /* Fill up buffer with random data */ if (written < IO_BUFFER_SIZE) memmove(buf, buf + written, IO_BUFFER_SIZE - written); else written = 0; for (; written < IO_BUFFER_SIZE; ++written) buf[written] = rand_r(&seed); /* Write a block of data */ if (remains > IO_BUFFER_SIZE) block = IO_BUFFER_SIZE; else block = remains; written = write(fd, buf, block); CHECK(written == block); remains -= written; } } static void save_file(int fd, struct file_info *file) { int w_fd; struct write_info *w; char buf[IO_BUFFER_SIZE]; char name[256]; /* Open file to save contents to */ strcpy(name, "/tmp/"); strcat(name, get_file_name(file)); strcat(name, ".integ.sav.read"); normsg("Saving %sn", name); w_fd = open(name, O_CREAT | O_WRONLY, 0777); CHECK(w_fd != -1); /* Start at the beginning */ CHECK(lseek(fd, 0, SEEK_SET) != (off_t)-1); for (;;) { ssize_t r = read(fd, buf, IO_BUFFER_SIZE); CHECK(r != -1); if (!r) break; CHECK(write(w_fd, buf, r) == r); } CHECK(close(w_fd) == 0); /* Open file to save contents to */ strcpy(name, "/tmp/"); strcat(name, get_file_name(file)); strcat(name, ".integ.sav.written"); normsg("Saving %s", name); w_fd = open(name, O_CREAT | O_WRONLY, 0777); CHECK(w_fd != -1); for (w = file->writes; w; w = w->next) file_rewrite_data(w_fd, w, buf); CHECK(close(w_fd) == 0); } static void file_check_hole(struct file_info *file, int fd, off_t offset, size_t size) { size_t remains, block, i; char buf[IO_BUFFER_SIZE]; CHECK(lseek(fd, offset, SEEK_SET) != (off_t)-1); remains = size; while (remains) { if (remains > IO_BUFFER_SIZE) block = IO_BUFFER_SIZE; else block = remains; CHECK(read(fd, buf, block) == block); for (i = 0; i < block; ++i) { if (buf[i] != 0) { errmsg("file_check_hole failed at %zu checking " "hole at %llu size %zu", size - remains + i, (unsigned long long)offset, size); file_info_display(file); save_file(fd, file); } CHECK(buf[i] == 0); } remains -= block; } } static void file_check_data(struct file_info *file, int fd, struct write_info *w) { size_t remains, block, i; off_t r; char buf[IO_BUFFER_SIZE]; unsigned int seed = w->random_seed; if (args.power_cut_mode && !file->clean) return; for (r = 0; r < w->random_offset; ++r) rand_r(&seed); CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t)-1); remains = w->size; while (remains) { if (remains > IO_BUFFER_SIZE) block = IO_BUFFER_SIZE; else block = remains; CHECK(read(fd, buf, block) == block); for (i = 0; i < block; ++i) { char c = (char)rand_r(&seed); if (buf[i] != c) { errmsg("file_check_data failed at %zu checking " "data at %llu size %zu", w->size - remains + i, (unsigned long long)w->offset, w->size); file_info_display(file); save_file(fd, file); } CHECK(buf[i] == c); } remains -= block; } } static void file_check(struct file_info *file, int fd) { int open_and_close = 0, link_count = 0; char *path = NULL; off_t pos; struct write_info *w; struct dir_entry_info *entry; struct stat st; /* * In case of power cut emulation testing check only clean files, i.e. * the files which have not been modified since last 'fsync()'. */ if (args.power_cut_mode && !file->clean) return; /* Do not check files that have errored */ if (!fsinfo.nospc_size_ok && file->no_space_error) return; /* Do not check the same file twice */ if (file->check_run_no == check_run_no) return; file->check_run_no = check_run_no; if (fd == -1) { /* Open file */ open_and_close = 1; assert(file->links); path = dir_path(file->links->parent, get_file_name(file)); v("checking file %s", path); fd = open(path, O_RDONLY); if (fd == -1) { sys_errmsg("cannot open file %s", path); CHECK(0); } } else v("checking file %s", get_file_name(file)); /* Check length */ pos = lseek(fd, 0, SEEK_END); if (pos != file->length) { errmsg("file_check failed checking length expected %llu actual %llu\n", (unsigned long long)file->length, (unsigned long long)pos); file_info_display(file); save_file(fd, file); } CHECK(pos == file->length); /* Check each write */ pos = 0; for (w = file->writes; w; w = w->next) { if (w->offset > pos) file_check_hole(file, fd, pos, w->offset - pos); file_check_data(file, fd, w); pos = w->offset + w->size; } if (file->length > pos) file_check_hole(file, fd, pos, file->length - pos); CHECK(fstat(fd, &st) == 0); CHECK(file->link_count == st.st_nlink); if (open_and_close) { CHECK(close(fd) == 0); free(path); } entry = file->links; while (entry) { link_count += 1; entry = entry->next_link; } CHECK(link_count == file->link_count); } static char *symlink_path(const char *path, const char *target_pathname) { char *p; size_t len, totlen, tarlen; if (target_pathname[0] == '/') return dup_string(target_pathname); p = strrchr(path, '/'); len = p - path; len += 1; tarlen = strlen(target_pathname); totlen = len + tarlen + 1; p = malloc(totlen); CHECK(p != NULL); strncpy(p, path, len); p[len] = '\0'; strcat(p, target_pathname); return p; } void symlink_check(const struct symlink_info *symlink) { char *path, buf[8192], *target; struct stat st1, st2; ssize_t len; int ret1, ret2; if (args.power_cut_mode) return; path = dir_path(symlink->entry->parent, symlink->entry->name); v("checking symlink %s", path); CHECK(lstat(path, &st1) == 0); CHECK(S_ISLNK(st1.st_mode)); CHECK(st1.st_nlink == 1); len = readlink(path, buf, 8192); CHECK(len > 0 && len < 8192); buf[len] = '\0'; CHECK(strlen(symlink->target_pathname) == len); CHECK(strncmp(symlink->target_pathname, buf, len) == 0); /* Check symlink points where it should */ ret1 = stat(path, &st1); target = symlink_path(path, symlink->target_pathname); ret2 = stat(target, &st2); CHECK(ret1 == ret2); if (ret1 == 0) { CHECK(st1.st_dev == st2.st_dev); CHECK(st1.st_ino == st2.st_ino); } free(target); free(path); } static int search_comp(const void *pa, const void *pb) { const struct dirent *a = (const struct dirent *) pa; const struct dir_entry_info *b = * (const struct dir_entry_info **) pb; return strcmp(a->d_name, b->name); } static void dir_entry_check(struct dir_entry_info **entry_array, size_t number_of_entries, struct dirent *ent) { struct dir_entry_info **found; struct dir_entry_info *entry; size_t sz; sz = sizeof(struct dir_entry_info *); found = bsearch(ent, entry_array, number_of_entries, sz, search_comp); CHECK(found != NULL); entry = *found; CHECK(!entry->checked); entry->checked = 1; } static int sort_comp(const void *pa, const void *pb) { const struct dir_entry_info *a = * (const struct dir_entry_info **) pa; const struct dir_entry_info *b = * (const struct dir_entry_info **) pb; return strcmp(a->name, b->name); } static void dir_check(struct dir_info *dir) { struct dir_entry_info *entry, **entry_array, **p; size_t sz, n; DIR *d; struct dirent *ent; unsigned int checked = 0; char *path; int link_count = 2; /* Parent and dot */ struct stat st; path = dir_path(dir->parent, dir->entry->name); if (!args.power_cut_mode || dir->clean) { v("checking dir %s", path); /* Create an array of entries */ sz = sizeof(struct dir_entry_info *); n = dir->number_of_entries; entry_array = malloc(sz * n); CHECK(entry_array != NULL); entry = dir->first; p = entry_array; while (entry) { *p++ = entry; entry->checked = 0; entry = entry->next; } /* Sort it by name */ qsort(entry_array, n, sz, sort_comp); /* Go through directory on file system checking entries match */ d = opendir(path); if (!d) { sys_errmsg("cannot open directory %s", path); CHECK(0); } for (;;) { errno = 0; ent = readdir(d); if (ent) { if (strcmp(".",ent->d_name) != 0 && strcmp("..",ent->d_name) != 0) { dir_entry_check(entry_array, n, ent); checked += 1; } } else { CHECK(errno == 0); break; } } free(entry_array); CHECK(closedir(d) == 0); CHECK(checked == dir->number_of_entries); } /* Now check each entry */ entry = dir->first; while (entry) { if (entry->type == 'd') { dir_check(entry->dir); link_count += 1; /* /.. */ } else if (entry->type == 'f') file_check(entry->file, -1); else if (entry->type == 's') symlink_check(entry->symlink); else CHECK(0); entry = entry->next; } if (!args.power_cut_mode || dir->clean) { CHECK(stat(path, &st) == 0); if (link_count != st.st_nlink) { errmsg("calculated link count %d, FS reports %d for dir %s", link_count, (int)st.st_nlink, path); CHECK(0); } } free(path); } static void check_deleted_files(void) { struct open_file_info *ofi; for (ofi = open_files; ofi; ofi = ofi->next) if (!ofi->fdi->file->link_count) file_check(ofi->fdi->file, ofi->fdi->fd); } static void close_open_files(void) { struct open_file_info *ofi; for (ofi = open_files; ofi; ofi = open_files) file_close(ofi->fdi); } static char *make_name(struct dir_info *dir) { struct dir_entry_info *entry; int found; do { found = 0; if (random_no(5) == 1) { int i, n = random_no(fsinfo.max_name_len) + 1; for (i = 0; i < n; i++) random_name_buf[i] = 'a' + random_no(26); random_name_buf[i] = '\0'; } else sprintf(random_name_buf, "%u", random_no(1000000)); for (entry = dir->first; entry; entry = entry->next) { if (strcmp(entry->name, random_name_buf) == 0) { found = 1; break; } } } while (found); return random_name_buf; } static struct file_info *pick_file(void) { struct dir_info *dir = top_dir; for (;;) { struct dir_entry_info *entry; unsigned int r; r = random_no(dir->number_of_entries); entry = dir->first; while (entry && r) { entry = entry->next; --r; } for (;;) { if (!entry) return NULL; if (entry->type == 'f') return entry->file; if (entry->type == 'd') if (entry->dir->number_of_entries != 0) break; entry = entry->next; } dir = entry->dir; } } static struct dir_info *pick_dir(void) { struct dir_info *dir = top_dir; if (random_no(40) >= 30) return dir; for (;;) { struct dir_entry_info *entry; size_t r; r = random_no(dir->number_of_entries); entry = dir->first; while (entry && r) { entry = entry->next; --r; } for (;;) { if (!entry) break; if (entry->type == 'd') break; entry = entry->next; } if (!entry) { entry = dir->first; for (;;) { if (!entry) break; if (entry->type == 'd') break; entry = entry->next; } } if (!entry) return dir; dir = entry->dir; if (random_no(40) >= 30) return dir; } } static char *pick_rename_name(struct dir_info **parent, struct dir_entry_info **rename_entry, int isdir) { struct dir_info *dir = pick_dir(); struct dir_entry_info *entry; unsigned int r; *parent = dir; *rename_entry = NULL; if (grow || random_no(20) < 10) return dup_string(make_name(dir)); r = random_no(dir->number_of_entries); entry = dir->first; while (entry && r) { entry = entry->next; --r; } if (!entry) entry = dir->first; if (!entry || (entry->type == 'd' && entry->dir->number_of_entries != 0)) return dup_string(make_name(dir)); if ((isdir && entry->type != 'd') || (!isdir && entry->type == 'd')) return dup_string(make_name(dir)); *rename_entry = entry; return dup_string(entry->name); } static int rename_entry(struct dir_entry_info *entry) { struct dir_entry_info *rename_entry = NULL; struct dir_info *parent; char *path, *to, *name; int ret, isdir, retry; struct stat st1, st2; if (!entry->parent) return 0; for (retry = 0; retry < 3; retry++) { path = dir_path(entry->parent, entry->name); isdir = entry->type == 'd' ? 1 : 0; name = pick_rename_name(&parent, &rename_entry, isdir); to = dir_path(parent, name); /* * Check we are not trying to move a directory to a subdirectory * of itself. */ if (isdir) { struct dir_info *p; for (p = parent; p; p = p->parent) if (p == entry->dir) break; if (p == entry->dir) { free(path); free(name); free(to); path = NULL; continue; } } break; } if (!path) return 0; if (args.verify_ops) CHECK(lstat(path, &st1) == 0); if (rename_entry) v("moving %s (type %c) inoto %s (type %c)", path, entry->type, to, rename_entry->type); else v("renaming %s (type %c) to %s", path, entry->type, to); ret = rename(path, to); if (ret != 0) { ret = 0; if (errno == ENOSPC) full = 1; else if (errno != EBUSY) { pcv("failed to rename %s to %s", path, to); ret = -1; } free(path); free(name); free(to); return ret; } if (args.verify_ops) { CHECK(lstat(to, &st2) == 0); CHECK(st1.st_ino == st2.st_ino); } free(path); free(to); if (rename_entry && rename_entry->type == entry->type && rename_entry->target == entry->target) { free(name); return 0; } add_dir_entry(parent, entry->type, name, entry->target); if (rename_entry) remove_dir_entry(rename_entry, 1); remove_dir_entry(entry, 0); free(name); return 0; } static size_t str_count(const char *s, char c) { size_t count = 0; char cc; while ((cc = *s++) != '\0') if (cc == c) count += 1; return count; } static char *relative_path(const char *path1, const char *path2) { const char *p1, *p2; char *rel; size_t up, len, len2, i; p1 = path1; p2 = path2; while (*p1 == *p2 && *p1) { p1 += 1; p2 += 1; } len2 = strlen(p2); up = str_count(p1, '/'); if (up == 0 && len2 != 0) return dup_string(p2); if (up == 0 && len2 == 0) { p2 = strrchr(path2, '/'); return dup_string(p2); } if (up == 1 && len2 == 0) return dup_string("."); if (len2 == 0) up -= 1; len = up * 3 + len2 + 1; rel = malloc(len); CHECK(rel != NULL); rel[0] = '\0'; if (up) { strcat(rel, ".."); for (i = 1; i < up; i++) strcat(rel, "/.."); if (len2) strcat(rel, "/"); } if (len2) strcat(rel, p2); return rel; } static char *pick_symlink_target(const char *symlink_path) { struct dir_info *dir; struct dir_entry_info *entry; char *path, *rel_path; unsigned int r; dir = pick_dir(); if (random_no(100) < 10) return dir_path(dir, make_name(dir)); r = random_no(dir->number_of_entries); entry = dir->first; while (entry && r) { entry = entry->next; --r; } if (!entry) entry = dir->first; if (!entry) return dir_path(dir, make_name(dir)); path = dir_path(dir, entry->name); if (random_no(20) < 10) return path; rel_path = relative_path(symlink_path, path); free(path); return rel_path; } static void verify_symlink(const char *target, const char *path) { int bytes; char buf[PATH_MAX + 1]; bytes = readlink(path, buf, PATH_MAX); CHECK(bytes >= 0); CHECK(bytes < PATH_MAX); buf[bytes] = '\0'; CHECK(!strcmp(buf, target)); } static int symlink_new(struct dir_info *dir, const char *nm) { struct symlink_info *s; char *path, *target, *name = dup_string(nm); /* * Note, we need to duplicate the input 'name' string because of the * shared random_name_buf. */ path = dir_path(dir, name); target = pick_symlink_target(path); v("creating symlink %s ---> %s", path, target); if (symlink(target, path) != 0) { int ret = 0; if (errno == ENOSPC) full = 1; else if (errno != ENAMETOOLONG) { pcv("cannot create symlink %s in directory %s to file %s", path, dir->entry->name, target); ret = -1; } free(target); free(name); free(path); return ret; } if (args.verify_ops) verify_symlink(target, path); s = add_dir_entry(dir, 's', name, NULL); s->target_pathname = target; free(path); free(name); return 0; } static int symlink_remove(struct symlink_info *symlink) { char *path; path = dir_path(symlink->entry->parent, symlink->entry->name); if (unlink(path) != 0) { pcv("cannot unlink symlink %s", path); free(path); return -1; } if (args.verify_ops) { struct stat st; CHECK(lstat(path, &st) == -1); CHECK(errno == ENOENT); } remove_dir_entry(symlink->entry, 1); free(path); return 0; } static int operate_on_dir(struct dir_info *dir); /* Randomly select something to do with a file */ static int operate_on_file(struct file_info *file) { /* Try to keep at least 10 files open */ if (open_files_count < 10) return file_open(file); /* Try to keep about 20 files open */ if (open_files_count < 20 && random_no(2) == 0) return file_open(file); /* Try to keep up to 40 files open */ if (open_files_count < 40 && random_no(20) == 0) return file_open(file); /* Occasionly truncate */ if (shrink && random_no(100) == 0) return file_truncate_file(file); /* Mostly just write */ if (file_write_file(file) != 0) return -1; /* Once in a while check it too */ if (random_no(100) == 1) { int fd = -2; if (file->links) fd = -1; else if (file->fds) fd = file->fds->fd; if (fd != -2) { check_run_no += 1; file_check(file, fd); } } return 0; } /* * The operate on entry function is recursive because it calls * 'operate_on_dir()' which calls 'operate_on_entry()' again. This variable is * used to limit the recursion depth. */ static int recursion_depth; /* Randomly select something to do with a directory entry */ static int operate_on_entry(struct dir_entry_info *entry) { int ret = 0; recursion_depth += 1; /* 1 time in 1000 rename */ if (random_no(1000) == 0) ret = rename_entry(entry); else if (entry->type == 's') { symlink_check(entry->symlink); /* If shrinking, 1 time in 50, remove a symlink */ if (shrink && random_no(50) == 0) ret = symlink_remove(entry->symlink); } else if (entry->type == 'd') { /* If shrinking, 1 time in 50, remove a directory */ if (shrink && random_no(50) == 0) ret = dir_remove(entry->dir); else if (recursion_depth < 20) ret = operate_on_dir(entry->dir); } else if (entry->type == 'f') { /* If shrinking, 1 time in 10, remove a file */ if (shrink && random_no(10) == 0) ret = file_delete(entry->file); /* If not growing, 1 time in 10, unlink a file with links > 1 */ else if (!grow && entry->file->link_count > 1 && random_no(10) == 0) ret = file_unlink_file(entry->file); else ret = operate_on_file(entry->file); } recursion_depth -= 1; return ret; } /* Synchronize a directory */ static int sync_directory(const char *path) { int fd, ret; fd = open(path, O_RDONLY); if (fd == -1) { pcv("cannot open directory %s", path); return -1; } if (random_no(100) >= 50) { v("fsyncing dir %s", path); ret = fsync(fd); if (ret) pcv("directory fsync failed for %s", path); } else { v("fdatasyncing dir %s", path); ret = fdatasync(fd); if (ret) pcv("directory fdatasync failed for %s", path); } close(fd); return ret; } /* * Randomly select something to do with a directory. */ static int operate_on_dir(struct dir_info *dir) { struct dir_entry_info *entry; struct file_info *file; unsigned int r; int ret = 0; r = random_no(14); if (r == 0 && grow) /* When growing, 1 time in 14 create a file */ ret = file_new(dir, make_name(dir)); else if (r == 1 && grow) /* When growing, 1 time in 14 create a directory */ ret = dir_new(dir, make_name(dir)); else if (r == 2 && grow && (file = pick_file()) != NULL) /* When growing, 1 time in 14 create a hard link */ ret = link_new(dir, make_name(dir), file); else if (r == 3 && grow && random_no(5) == 0) /* When growing, 1 time in 70 create a symbolic link */ ret = symlink_new(dir, make_name(dir)); else { /* Otherwise randomly select an entry to operate on */ r = random_no(dir->number_of_entries); entry = dir->first; while (entry && r) { entry = entry->next; --r; } if (entry) ret = operate_on_entry(entry); } if (ret) return ret; /* Synchronize the directory sometimes */ if (random_no(100) >= 99) { char *path; path = dir_path(dir->parent, dir->entry->name); ret = sync_directory(path); free(path); if (!ret) dir->clean = 1; } return ret; } /* * Randomly select something to do with an open file. */ static int operate_on_open_file(struct fd_info *fdi) { int ret = 0; unsigned int r = random_no(1000); if (shrink && r < 5) ret = file_truncate(fdi->file, fdi->fd); else if (r < 21) file_close(fdi); else if (shrink && r < 121 && fdi->file->link_count) ret = file_delete(fdi->file); else ret = file_write(fdi->file, fdi->fd); return ret; } /* * Randomly select an open file and do a random operation on it. */ static int operate_on_an_open_file(void) { unsigned int r; struct open_file_info *ofi; /* When shrinking, close all open files 1 time in 128 */ if (shrink) { static int x = 0; x += 1; x &= 127; if (x == 0) { close_open_files(); return 0; } } /* Close any open files that have errored */ if (!fsinfo.nospc_size_ok) { ofi = open_files; while (ofi) { if (ofi->fdi->file->no_space_error) { struct fd_info *fdi; fdi = ofi->fdi; ofi = ofi->next; file_close(fdi); } else ofi = ofi->next; } } r = random_no(open_files_count); for (ofi = open_files; ofi; ofi = ofi->next, r--) if (!r) { return operate_on_open_file(ofi->fdi); } return 0; } /* * Do a random file-system operation. */ static int do_an_operation(void) { /* Half the time operate on already open files */ if (random_no(100) < 50) return operate_on_dir(top_dir); else return operate_on_an_open_file(); } /* * Fill the tested file-system with random stuff. */ static int create_test_data(void) { uint64_t i, n; grow = 1; shrink = 0; full = 0; operation_count = 0; while (!full) { if (do_an_operation()) return -1; operation_count += 1; } /* Drop to less than 90% full */ grow = 0; shrink = 1; n = operation_count / 40; while (n--) { uint64_t free, total; for (i = 0; i < 10; i++) if (do_an_operation()) return -1; get_fs_space(&total, &free); if ((free * 100) / total >= 10) break; } grow = 0; shrink = 0; full = 0; n = operation_count * 2; for (i = 0; i < n; i++) if (do_an_operation()) return -1; return 0; } /* * Do more random operation on the tested file-system. */ static int update_test_data(void) { uint64_t i, n; grow = 1; shrink = 0; full = 0; while (!full) if (do_an_operation()) return -1; /* Drop to less than 50% full */ grow = 0; shrink = 1; n = operation_count / 10; while (n--) { uint64_t free, total; for (i = 0; i < 10; i++) if (do_an_operation()) return -1; get_fs_space(&total, &free); if ((free * 100) / total >= 50) break; } grow = 0; shrink = 0; full = 0; n = operation_count * 2; for (i = 0; i < n; i++) if (do_an_operation()) return -1; return 0; } /* * Recursively remove a directory, just like "rm -rf" shell command. */ static int rm_minus_rf_dir(const char *dir_name) { int ret; DIR *dir; struct dirent *dent; char buf[PATH_MAX]; v("removing all files"); dir = opendir(dir_name); CHECK(dir != NULL); CHECK(getcwd(buf, PATH_MAX) != NULL); CHECK(chdir(dir_name) == 0); for (;;) { errno = 0; dent = readdir(dir); if (!dent) { CHECK(errno == 0); break; } if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { if (dent->d_type == DT_DIR) { ret = rm_minus_rf_dir(dent->d_name); if (ret) { CHECK(closedir(dir) == 0); return -1; } } else { ret = unlink(dent->d_name); if (ret) { pcv("cannot unlink %s", dent->d_name); CHECK(closedir(dir) == 0); return -1; } } } } CHECK(chdir(buf) == 0); CHECK(closedir(dir) == 0); if (args.verify_ops) { dir = opendir(dir_name); CHECK(dir != NULL); do { errno = 0; dent = readdir(dir); if (dent) CHECK(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")); } while (dent); CHECK(errno == 0); CHECK(closedir(dir) == 0); } ret = rmdir(dir_name); if (ret) { pcv("cannot remove directory %s", dir_name); return -1; } if (args.verify_ops) { struct stat st; CHECK(lstat(dir_name, &st) == -1); CHECK(errno == ENOENT); } return 0; } /** * Re-mount the test file-system. This function randomly select how to * re-mount. */ static int remount_tested_fs(void) { int ret; unsigned long flags; unsigned int rorw1, um, um_ro, um_rorw, rorw2; CHECK(chdir("/") == 0); v("remounting file-system"); /* Choose what to do */ rorw1 = random_no(2); um = random_no(2); um_ro = random_no(2); um_rorw = random_no(2); rorw2 = random_no(2); if (rorw1 + um + rorw2 == 0) um = 1; if (rorw1) { flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT; ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("cannot remount %s R/O (1)", fsinfo.mount_point); return -1; } flags = fsinfo.mount_flags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("remounted %s R/O (1), but cannot re-mount it R/W", fsinfo.mount_point); return -1; } } if (um) { if (um_ro) { flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT; ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("cannot remount %s R/O (2)", fsinfo.mount_point); return -1; } } ret = umount(fsinfo.mount_point); if (ret) { pcv("cannot unmount %s", fsinfo.mount_point); return -1; } if (!um_rorw) { ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, fsinfo.mount_flags, fsinfo.mount_opts); if (ret) { pcv("unmounted %s, but cannot mount it back R/W", fsinfo.mount_point); return -1; } } else { ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, fsinfo.mount_flags | MS_RDONLY, fsinfo.mount_opts); if (ret) { pcv("unmounted %s, but cannot mount it back R/O", fsinfo.mount_point); return -1; } flags = fsinfo.mount_flags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("unmounted %s, mounted R/O, but cannot re-mount it R/W", fsinfo.mount_point); return -1; } } } if (rorw2) { flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT; ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("cannot re-mount %s R/O (3)", fsinfo.mount_point); return -1; } flags = fsinfo.mount_flags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("remounted %s R/O (3), but cannot re-mount it back R/W", fsinfo.mount_point); return -1; } } CHECK(chdir(fsinfo.mount_point) == 0); return 0; } static void check_tested_fs(void) { v("checking the file-sytem"); check_run_no += 1; dir_check(top_dir); check_deleted_files(); } /* * This is a helper function which just reads whole file. We do this in case of * emulated power cuts testing to make sure that unclean files can be at least * read. */ static void read_whole_file(const char *name) { size_t rd; char buf[IO_BUFFER_SIZE]; int fd; fd = open(name, O_RDONLY); CHECK(fd != -1); do { rd = read(fd, buf, IO_BUFFER_SIZE); CHECK(rd != -1); } while (rd); close(fd); } /* * Recursively walk whole tested file-system and make sure we can read * everything. This is done in case of power cuts emulation testing to ensure * that everything in the file-system is readable. */ static void read_all(const char *dir_name) { DIR *dir; struct dirent *dent; char buf[PATH_MAX]; assert(args.power_cut_mode); v("reading all files"); dir = opendir(dir_name); if (!dir) { errmsg("cannot open %s", dir_name); CHECK(0); } CHECK(getcwd(buf, PATH_MAX) != NULL); CHECK(chdir(dir_name) == 0); for (;;) { errno = 0; dent = readdir(dir); if (!dent) { CHECK(errno == 0); break; } if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; if (dent->d_type == DT_DIR) read_all(dent->d_name); else if (dent->d_type == DT_REG) read_whole_file(dent->d_name); else if (dent->d_type == DT_LNK) { char b[IO_BUFFER_SIZE]; CHECK(readlink(dent->d_name, b, IO_BUFFER_SIZE) != -1); } } CHECK(chdir(buf) == 0); CHECK(closedir(dir) == 0); } /* * Perform the test. Returns zero on success and -1 on failure. */ static int integck(void) { int ret; long rpt; CHECK(chdir(fsinfo.mount_point) == 0); assert(!top_dir); /* Create our top directory */ if (chdir(fsinfo.test_dir) == 0) { CHECK(chdir("..") == 0); ret = rm_minus_rf_dir(fsinfo.test_dir); if (ret) return -1; } v("creating top dir %s", fsinfo.test_dir); ret = mkdir(fsinfo.test_dir, 0777); if (ret) { pcv("cannot create top test directory %s", fsinfo.test_dir); return -1; } ret = sync_directory(fsinfo.test_dir); if (ret) return -1; top_dir = zalloc(sizeof(struct dir_info)); top_dir->entry = zalloc(sizeof(struct dir_entry_info)); top_dir->entry->name = dup_string(fsinfo.test_dir); ret = create_test_data(); if (ret) return -1; if (fsinfo.can_remount) { close_open_files(); ret = remount_tested_fs(); if (ret) return -1; } else assert(!args.power_cut_mode); /* Check everything */ check_tested_fs(); for (rpt = 0; args.repeat_cnt == 0 || rpt < args.repeat_cnt; ++rpt) { ret = update_test_data(); if (ret) return -1; if (fsinfo.can_remount) { close_open_files(); ret = remount_tested_fs(); if (ret) return -1; } /* Check everything */ check_tested_fs(); } /* Tidy up by removing everything */ close_open_files(); ret = rm_minus_rf_dir(fsinfo.test_dir); if (ret) return -1; return 0; } /* * This is a helper function for 'get_tested_fs_info()'. It parses file-system * mount options string, extracts standard mount options from there, and saves * them in the 'fsinfo.mount_flags' variable, and non-standard mount options * are saved in the 'fsinfo.mount_opts' variable. The reason for this is that * we want to preserve mount options when unmounting the file-system and * mounting it again. This is because we cannot pass standard mount optins * (like sync, ro, etc) as a string to the 'mount()' function, because it * fails. It accepts standard mount options only as flags. And only the * FS-specific mount options are accepted in form of a string. */ static void parse_mount_options(const char *mount_opts) { char *tmp, *opts, *p; const char *opt; /* * We are going to use 'strtok()' which modifies the original string, * so duplicate it. */ tmp = dup_string(mount_opts); p = opts = zalloc(strlen(mount_opts) + 1); opt = strtok(tmp, ","); while (opt) { if (!strcmp(opt, "rw")) ; else if (!strcmp(opt, "ro")) fsinfo.mount_flags |= MS_RDONLY; else if (!strcmp(opt, "dirsync")) fsinfo.mount_flags |= MS_DIRSYNC; else if (!strcmp(opt, "noatime")) fsinfo.mount_flags |= MS_NOATIME; else if (!strcmp(opt, "nodiratime")) fsinfo.mount_flags |= MS_NODIRATIME; else if (!strcmp(opt, "noexec")) fsinfo.mount_flags |= MS_NOEXEC; else if (!strcmp(opt, "nosuid")) fsinfo.mount_flags |= MS_NOSUID; else if (!strcmp(opt, "relatime")) fsinfo.mount_flags |= MS_RELATIME; else if (!strcmp(opt, "sync")) fsinfo.mount_flags |= MS_SYNCHRONOUS; else { int len = strlen(opt); if (p != opts) *p++ = ','; memcpy(p, opt, len); p += len; *p = '\0'; } opt = strtok(NULL, ","); } free(tmp); fsinfo.mount_opts = opts; } /* * This is a helper function which searches for the tested file-system mount * description. */ static struct mntent *get_tested_fs_mntent(void) { const char *mp; struct mntent *mntent; FILE *f; mp = "/proc/mounts"; f = fopen(mp, "rb"); if (!f) { mp = "/etc/mtab"; f = fopen(mp, "rb"); } CHECK(f != NULL); while ((mntent = getmntent(f)) != NULL) if (!strcmp(mntent->mnt_dir, fsinfo.mount_point)) break; CHECK(fclose(f) == 0); return mntent; } /* * Fill 'fsinfo' with information about the tested file-system. */ static void get_tested_fs_info(void) { struct statfs fs_info; struct mntent *mntent; uint64_t z; char *p; unsigned int pid; /* Remove trailing '/' symbols from the mount point */ p = dup_string(args.mount_point); fsinfo.mount_point = p; p += strlen(p); while (*--p == '/'); *(p + 1) = '\0'; CHECK(statfs(fsinfo.mount_point, &fs_info) == 0); fsinfo.max_name_len = fs_info.f_namelen; mntent = get_tested_fs_mntent(); if (!mntent) { errmsg("cannot find file-system info"); CHECK(0); } fsinfo.fstype = dup_string(mntent->mnt_type); fsinfo.fsdev = dup_string(mntent->mnt_fsname); parse_mount_options(mntent->mnt_opts); /* Get memory page size for 'mmap()' */ fsinfo.page_size = sysconf(_SC_PAGE_SIZE); CHECK(fsinfo.page_size > 0); /* * JFFS2 does not support shared writable mmap and it may report * incorrect file size after "no space" errors. */ if (strcmp(fsinfo.fstype, "jffs2") == 0) { fsinfo.nospc_size_ok = 0; fsinfo.can_mmap = 0; } get_fs_space(NULL, &z); for (; z >= 10; z /= 10) fsinfo.log10_initial_free += 1; /* Pick the test directory name */ p = malloc(sizeof(TEST_DIR_PATTERN) + 20); CHECK(p != NULL); pid = getpid(); CHECK(sprintf(p, "integck_test_dir_%u", pid) > 0); fsinfo.test_dir = p; normsg("pid %u, testing \"%s\" at \"%s\"", pid, fsinfo.fstype, fsinfo.mount_point); } static const char doc[] = PROGRAM_NAME " version " PROGRAM_VERSION " - a stress test which checks the file-system integrity.\n" "\n" "The test creates a directory named \"integck_test_dir_\", where where\n" " is the process id. Then it randomly creates and deletes files,\n" "directories, symlinks, and hardlinks, randomly writes and truncate files,\n" "sometimes makes holes in files, sometimes fsync()'s them. Then it un-mounts and\n" "re-mounts the tested file-system and checks the contents - everything (files,\n" "directories, etc) should be there and the contents of the files should be\n" "correct. This is repeated a number of times (set with -n, default 1).\n" "\n" "By default the test does not verify file-system modifications and assumes they\n" "are done correctly if the file-system returns success. However, the -e flag\n" "enables additional verifications and the test verifies all the file-system\n" "operations it performs.\n" "\n" "This test is also able to perform power cut testing. The underlying file-system\n" "or the device driver should be able to emulate power-cuts, by switching to R/O\n" "mode at random moments. And the file-system should return EROFS (read-only\n" "file-system error) for all operations which modify it. In this case this test\n" "program re-mounts the file-system and checks that all files and directories\n" "which have been successfully synchronized before the power cut. And the test\n" "continues forever.\n"; static const char optionsstr[] = "-n, --repeat= repeat count, default is 1; zero value - repeat forever\n" "-p, --power-cut power cut testing mode (-n parameter is ignored and the\n" " test continues forever)\n" "-e, --verify-ops verify all operations, e.g., every time a file is written\n" " to, read the data back and verify it, every time a\n" " directory is created, check that it exists, etc\n" "-v, --verbose be verbose\n" "-m, --reattach= re-attach mtd device number (only if doing UBIFS power\n" " cut emulation testing)\n" "-h, -?, --help print help message\n" "-V, --version print program version\n"; static const struct option long_options[] = { { .name = "repeat", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "power-cut", .has_arg = 0, .flag = NULL, .val = 'p' }, { .name = "verify-ops", .has_arg = 0, .flag = NULL, .val = 'e' }, { .name = "reattach", .has_arg = 1, .flag = NULL, .val = 'm' }, { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; /* * Parse and validate input command-line options. Returns zero on success and * -1 on error. */ static int parse_opts(int argc, char * const argv[]) { struct stat st; while (1) { int key, error = 0; key = getopt_long(argc, argv, "n:pm:evVh?", long_options, NULL); if (key == -1) break; switch (key) { case 'n': args.repeat_cnt = simple_strtoul(optarg, &error); if (error || args.repeat_cnt < 0) return errmsg("bad repeat count: \"%s\"", optarg); break; case 'p': args.power_cut_mode = 1; break; case 'm': args.mtdn = simple_strtoul(optarg, &error); if (error || args.mtdn < 0) return errmsg("bad mtd device number: \"%s\"", optarg); args.reattach = 1; break; case 'e': args.verify_ops = 1; break; case 'v': args.verbose = 1; break; case 'V': fprintf(stderr, "%s\n", PROGRAM_VERSION); exit(EXIT_SUCCESS); case 'h': case '?': fprintf(stderr, "%s\n\n", doc); fprintf(stderr, "%s\n", optionsstr); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) return errmsg("test file-system was not specified (use -h for help)"); else if (optind != argc - 1) return errmsg("more then one test file-system specified (use -h for help)"); if (args.reattach && !args.power_cut_mode) return errmsg("-m makes sense only together with -e"); if (args.power_cut_mode) /* Repeat forever if we are in power cut testing mode */ args.repeat_cnt = 0; args.mount_point = argv[optind]; if (chdir(args.mount_point) != 0 || lstat(args.mount_point, &st) != 0) return errmsg("invalid test file system mount directory: %s", args.mount_point); return 0; } /* * Free all the in-memory information about the tested file-system contents * starting from sub-directory 'dir'. */ static void free_fs_info(struct dir_info *dir) { struct dir_entry_info *entry; /* Now check each entry */ while (dir->first) { entry = dir->first; if (entry->type == 'd') { struct dir_info *d = entry->dir; remove_dir_entry(entry, 0); free_fs_info(d); free(d); } else if (entry->type == 'f') { remove_dir_entry(entry, 1); } else if (entry->type == 's') { remove_dir_entry(entry, 1); } else assert(0); } } /* * Detach the MTD device from UBI and attach it back. This function is used * whed performing emulated power cut testing andthe power cuts are amulated by * UBI, not by UBIFS. In this case, to recover from the emulated power cut we * have to unmount UBIFS and re-attach the MTD device. */ static int reattach(void) { int err = 0; libubi_t libubi; struct ubi_attach_request req; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } err = ubi_detach_mtd(libubi, "/dev/ubi_ctrl", args.mtdn); if (err) { sys_errmsg("cannot detach mtd%d", args.mtdn); goto out; } req.dev_num = UBI_DEV_NUM_AUTO; req.mtd_num = args.mtdn; req.vid_hdr_offset = 0; req.mtd_dev_node = NULL; err = ubi_attach(libubi, "/dev/ubi_ctrl", &req); if (err) sys_errmsg("cannot attach mtd%d", args.mtdn); out: libubi_close(libubi); return err; } /* * Recover the tested file-system from an emulated power cut failure by * unmounting it and mounting it again. */ static int recover_tested_fs(void) { int ret; unsigned long flags; unsigned int um_rorw, rorw2; struct mntent *mntent; CHECK(chdir("/") == 0); /* Choose what to do */ um_rorw = random_no(2); rorw2 = random_no(2); /* * At this point we do not know for sure whether the tested FS is * mounted, because the emulated power cut error could have happened * while mounting in 'remount_tested_fs()'. */ mntent = get_tested_fs_mntent(); if (mntent) CHECK(umount(fsinfo.mount_point) != -1); if (args.reattach) CHECK(reattach() == 0); if (!um_rorw) { ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, fsinfo.mount_flags, fsinfo.mount_opts); if (ret) { pcv("unmounted %s, but cannot mount it back R/W", fsinfo.mount_point); return -1; } } else { ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, fsinfo.mount_flags | MS_RDONLY, fsinfo.mount_opts); if (ret) { pcv("unmounted %s, but cannot mount it back R/O", fsinfo.mount_point); return -1; } flags = fsinfo.mount_flags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("unmounted %s, mounted R/O, but cannot re-mount it R/W", fsinfo.mount_point); return -1; } } if (rorw2) { flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT; ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("cannot re-mount %s R/O", fsinfo.mount_point); return -1; } flags = fsinfo.mount_flags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype, flags, fsinfo.mount_opts); if (ret) { pcv("remounted %s R/O, but cannot re-mount it back R/W", fsinfo.mount_point); return -1; } } return 0; } static void free_test_data(void) { if (top_dir) { free_fs_info(top_dir); free(top_dir->entry->name); free(top_dir->entry); free(top_dir); top_dir = NULL; } } int main(int argc, char *argv[]) { int ret; long rpt; ret = parse_opts(argc, argv); if (ret) return EXIT_FAILURE; get_tested_fs_info(); /* Seed the random generator with out PID */ random_seed = getpid(); random_name_buf = malloc(fsinfo.max_name_len + 1); CHECK(random_name_buf != NULL); /* Refuse the file-system if it is mounted R/O */ if (fsinfo.mount_flags & MS_RDONLY) { ret = -1; errmsg("the file-system is mounted read-only"); goto out_free; } /* Check whether we can re-mount the tested FS */ do { ret = recover_tested_fs(); } while (ret && args.power_cut_mode && errno == EROFS); if (!ret) { fsinfo.can_remount = 1; } else { warnmsg("file-system %s cannot be unmounted (%s)", fsinfo.mount_point, strerror(errno)); if (args.power_cut_mode) { /* * When testing emulated power cuts we have to be able * to re-mount the file-system to clean the EROFS * state. */ errmsg("power cut mode requers unmountable FS"); goto out_free; } } /* Do the actual test */ for (rpt = 0; ; rpt++) { ret = integck(); /* * Iterate forever only in case of power-cut emulation testing. */ if (!args.power_cut_mode) { CHECK(!ret); break; } CHECK(ret); CHECK(errno == EROFS || errno == EIO); close_open_files(); do { ret = recover_tested_fs(); if (ret) { CHECK(errno == EROFS); rpt += 1; } /* * Mount may also fail due to an emulated power cut * while mounting - keep re-starting. */ } while (ret); CHECK(chdir(fsinfo.mount_point) == 0); /* Make sure everything is readable after an emulated power cut */ if (top_dir) { /* Check the clean data */ check_tested_fs(); read_all(fsinfo.test_dir); } free_test_data(); /* * The file-system became read-only and we are in power cut * testing mode. Re-mount the file-system and re-start the * test. */ if (args.verbose) normsg("re-mount the FS and re-start - count %ld", rpt); } close_open_files(); free_test_data(); out_free: free(random_name_buf); free(fsinfo.mount_opts); free(fsinfo.mount_point); free(fsinfo.fstype); free(fsinfo.fsdev); free(fsinfo.test_dir); return ret ? EXIT_FAILURE : EXIT_SUCCESS; } mtd-utils-1.5.0/tests/fs-tests/lib/000077500000000000000000000000001175167361300171315ustar00rootroot00000000000000mtd-utils-1.5.0/tests/fs-tests/lib/Makefile000066400000000000000000000002461175167361300205730ustar00rootroot00000000000000 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 mtd-utils-1.5.0/tests/fs-tests/lib/tests.c000066400000000000000000000732471175167361300204540ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tests.h" char *tests_file_system_mount_dir = TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR; char *tests_file_system_type = TESTS_DEFAULT_FILE_SYSTEM_TYPE; int tests_ok_to_sync = 0; /* Whether to use fsync */ /* General purpose test parameter to specify some aspect of test size. May be used by different tests in different ways or not at all. Set by the -z or --size option. */ int64_t tests_size_parameter = 0; /* General purpose test parameter to specify some aspect of test repetition. May be used by different tests in different ways or not at all. Set by the -n, --repeat options. */ int64_t tests_repeat_parameter = 0; /* General purpose test parameter to specify some aspect of test sleeping. May be used by different tests in different ways or not at all. Set by the -p, --sleep options. */ int64_t tests_sleep_parameter = 0; /* Program name from argv[0] */ char *program_name = "unknown"; /* General purpose test parameter to specify a file should be unlinked. May be used by different tests in different ways or not at all. */ int tests_unlink_flag = 0; /* General purpose test parameter to specify a file should be closed. May be used by different tests in different ways or not at all. */ int tests_close_flag = 0; /* General purpose test parameter to specify a file should be deleted. May be used by different tests in different ways or not at all. */ int tests_delete_flag = 0; /* General purpose test parameter to specify a file have a hole. May be used by different tests in different ways or not at all. */ int tests_hole_flag = 0; /* Whether it is ok to test on the root file system */ static int rootok = 0; /* Maximum file name length of test file system (from statfs) */ long tests_max_fname_len = 255; /* Function invoked by the CHECK macro */ void tests_test(int test,const char *msg,const char *file,unsigned line) { int eno; time_t t; if (test) return; eno = errno; time(&t); fprintf(stderr, "Test failed: %s on %s" "Test failed: %s in %s at line %u\n", program_name, ctime(&t), msg, file, line); if (eno) { fprintf(stderr,"errno = %d\n",eno); fprintf(stderr,"strerror = %s\n",strerror(eno)); } exit(1); } static int is_zero(const char *p) { for (;*p;++p) if (*p != '0') return 0; return 1; } static void fold(const char *text, int width) { int pos, bpos = 0; const char *p; char line[1024]; if (width > 1023) { printf("%s\n", text); return; } p = text; pos = 0; while (p[pos]) { while (!isspace(p[pos])) { line[pos] = p[pos]; if (!p[pos]) break; ++pos; if (pos == width) { line[pos] = '\0'; printf("%s\n", line); p += pos; pos = 0; } } while (pos < width) { line[pos] = p[pos]; if (!p[pos]) { bpos = pos; break; } if (isspace(p[pos])) bpos = pos; ++pos; } line[bpos] = '\0'; printf("%s\n", line); p += bpos; pos = 0; while (p[pos] && isspace(p[pos])) ++p; } } /* Handle common program options */ int tests_get_args(int argc, char *argv[], const char *title, const char *desc, const char *opts) { int run_test = 0; int display_help = 0; int display_title = 0; int display_description = 0; int i; char *s; program_name = argv[0]; s = getenv("TEST_FILE_SYSTEM_MOUNT_DIR"); if (s) tests_file_system_mount_dir = strdup(s); s = getenv("TEST_FILE_SYSTEM_TYPE"); if (s) tests_file_system_type = strdup(s); run_test = 1; rootok = 1; for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) display_help = 1; else if (strcmp(argv[i], "--title") == 0 || strcmp(argv[i], "-t") == 0) display_title = 1; else if (strcmp(argv[i], "--description") == 0 || strcmp(argv[i], "-d") == 0) display_description = 1; else if (strcmp(argv[i], "--sync") == 0 || strcmp(argv[i], "-s") == 0) tests_ok_to_sync = 1; else if (strncmp(argv[i], "--size", 6) == 0 || strncmp(argv[i], "-z", 2) == 0) { int64_t n; char *p; if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++i; p = argv[i]; while (*p && !isdigit(*p)) ++p; n = atoll(p); if (n) tests_size_parameter = n; else { int all_zero = 1; for (; all_zero && *p; ++p) if (*p != '0') all_zero = 0; if (all_zero) tests_size_parameter = 0; else display_help = 1; } } else if (strncmp(argv[i], "--repeat", 8) == 0 || strncmp(argv[i], "-n", 2) == 0) { int64_t n; char *p; if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++i; p = argv[i]; while (*p && !isdigit(*p)) ++p; n = atoll(p); if (n || is_zero(p)) tests_repeat_parameter = n; else display_help = 1; } else if (strncmp(argv[i], "--sleep", 7) == 0 || strncmp(argv[i], "-p", 2) == 0) { int64_t n; char *p; if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++i; p = argv[i]; while (*p && !isdigit(*p)) ++p; n = atoll(p); if (n || is_zero(p)) tests_sleep_parameter = n; else display_help = 1; } else if (strcmp(argv[i], "--unlink") == 0 || strcmp(argv[i], "-u") == 0) tests_unlink_flag = 1; else if (strcmp(argv[i], "--hole") == 0 || strcmp(argv[i], "-o") == 0) tests_hole_flag = 1; else if (strcmp(argv[i], "--close") == 0 || strcmp(argv[i], "-c") == 0) tests_close_flag = 1; else if (strcmp(argv[i], "--delete") == 0 || strcmp(argv[i], "-e") == 0) tests_delete_flag = 1; else display_help = 1; } if (display_help) { run_test = 0; display_title = 0; display_description = 0; if (!opts) opts = ""; printf("File System Test Program\n\n"); printf("Test Title: %s\n\n", title); printf("Usage is: %s [ options ]\n",argv[0]); printf(" Options are:\n"); printf(" -h, --help "); printf("Display this help\n"); printf(" -t, --title "); printf("Display the test title\n"); printf(" -d, --description "); printf("Display the test description\n"); if (strchr(opts, 's')) { printf(" -s, --sync "); printf("Make use of fsync\n"); } if (strchr(opts, 'z')) { printf(" -z, --size "); printf("Set size parameter\n"); } if (strchr(opts, 'n')) { printf(" -n, --repeat "); printf("Set repeat parameter\n"); } if (strchr(opts, 'p')) { printf(" -p, --sleep "); printf("Set sleep parameter\n"); } if (strchr(opts, 'u')) { printf(" -u, --unlink "); printf("Unlink file\n"); } if (strchr(opts, 'o')) { printf(" -o, --hole "); printf("Create a hole in a file\n"); } if (strchr(opts, 'c')) { printf(" -c, --close "); printf("Close file\n"); } if (strchr(opts, 'e')) { printf(" -e, --delete "); printf("Delete file\n"); } printf("\nBy default, testing is done in directory "); printf("/mnt/test_file_system. To change this\nuse "); printf("environmental variable "); printf("TEST_FILE_SYSTEM_MOUNT_DIR. By default, "); printf("the file\nsystem tested is jffs2. To change this "); printf("set TEST_FILE_SYSTEM_TYPE.\n\n"); printf("Test Description:\n"); fold(desc, 80); } else { if (display_title) printf("%s\n", title); if (display_description) printf("%s\n", desc); if (display_title || display_description) if (argc == 2 || (argc == 3 && display_title && display_description)) run_test = 0; } return run_test; } /* Return the number of files (or directories) in the given directory */ unsigned tests_count_files_in_dir(const char *dir_name) { DIR *dir; struct dirent *entry; unsigned count = 0; dir = opendir(dir_name); CHECK(dir != NULL); for (;;) { errno = 0; entry = readdir(dir); if (entry) { if (strcmp(".",entry->d_name) != 0 && strcmp("..",entry->d_name) != 0) ++count; } else { CHECK(errno == 0); break; } } CHECK(closedir(dir) != -1); return count; } /* Change to the file system mount directory, check that it is empty, matches the file system type, and is not the root file system */ void tests_check_test_file_system(void) { struct statfs fs_info; struct stat f_info; struct stat root_f_info; if (chdir(tests_file_system_mount_dir) == -1 || statfs(tests_file_system_mount_dir, &fs_info) == -1) { fprintf(stderr, "Invalid test file system mount directory:" " %s\n", tests_file_system_mount_dir); fprintf(stderr, "Use environment variable " "TEST_FILE_SYSTEM_MOUNT_DIR\n"); CHECK(0); } tests_max_fname_len = fs_info.f_namelen; if (strcmp(tests_file_system_type, "jffs2") == 0 && fs_info.f_type != JFFS2_SUPER_MAGIC) { fprintf(stderr, "File system type is not jffs2\n"); CHECK(0); } /* Check that the test file system is not the root file system */ if (!rootok) { CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); CHECK(stat("/", &root_f_info) != -1); CHECK(f_info.st_dev != root_f_info.st_dev); } } /* Get the free space for the file system of the current directory */ uint64_t tests_get_free_space(void) { struct statvfs fs_info; CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); return (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize; } /* Get the total space for the file system of the current directory */ uint64_t tests_get_total_space(void) { struct statvfs fs_info; CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); return (uint64_t) fs_info.f_blocks * (uint64_t) fs_info.f_frsize; } #define WRITE_BUFFER_SIZE 32768 static char write_buffer[WRITE_BUFFER_SIZE]; static void init_write_buffer() { static int init = 0; if (!init) { int i, d; uint64_t u; u = RAND_MAX; u += 1; u /= 256; d = (int) u; srand(1); for (i = 0; i < WRITE_BUFFER_SIZE; ++i) write_buffer[i] = rand() / d; init = 1; } } /* Write size random bytes into file descriptor fd at the current position, returning the number of bytes actually written */ uint64_t tests_fill_file(int fd, uint64_t size) { ssize_t written; size_t sz; unsigned start = 0, length; uint64_t remains; uint64_t actual_size = 0; init_write_buffer(); remains = size; while (remains > 0) { length = WRITE_BUFFER_SIZE - start; if (remains > length) sz = length; else sz = (size_t) remains; written = write(fd, write_buffer + start, sz); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; break; } remains -= written; actual_size += written; if (written == sz) start = 0; else start += written; } tests_maybe_sync(fd); return actual_size; } /* Write size random bytes into file descriptor fd at offset, returning the number of bytes actually written */ uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size) { ssize_t written; size_t sz; unsigned start = 0, length; uint64_t remains; uint64_t actual_size = 0; CHECK(lseek(fd, offset, SEEK_SET) == offset); init_write_buffer(); remains = size; start = offset % WRITE_BUFFER_SIZE; while (remains > 0) { length = WRITE_BUFFER_SIZE - start; if (remains > length) sz = length; else sz = (size_t) remains; written = write(fd, write_buffer + start, sz); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; break; } remains -= written; actual_size += written; if (written == sz) start = 0; else start += written; } tests_maybe_sync(fd); return actual_size; } /* Check that a file written using tests_fill_file() and/or tests_write_filled_file() and/or tests_create_file() contains the expected random data */ void tests_check_filled_file_fd(int fd) { ssize_t sz; char buf[WRITE_BUFFER_SIZE]; CHECK(lseek(fd, 0, SEEK_SET) == 0); do { sz = read(fd, buf, WRITE_BUFFER_SIZE); CHECK(sz >= 0); CHECK(memcmp(buf, write_buffer, sz) == 0); } while (sz); } /* Check that a file written using tests_fill_file() and/or tests_write_filled_file() and/or tests_create_file() contains the expected random data */ void tests_check_filled_file(const char *file_name) { int fd; fd = open(file_name, O_RDONLY); CHECK(fd != -1); tests_check_filled_file_fd(fd); CHECK(close(fd) != -1); } void tests_sync_directory(const char *file_name) { char *path; char *dir; int fd; if (!tests_ok_to_sync) return; path = strdup(file_name); dir = dirname(path); fd = open(dir,O_RDONLY | tests_maybe_sync_flag()); CHECK(fd != -1); CHECK(fsync(fd) != -1); CHECK(close(fd) != -1); free(path); } /* Delete a file */ void tests_delete_file(const char *file_name) { CHECK(unlink(file_name) != -1); tests_sync_directory(file_name); } /* Create a file of size file_size */ uint64_t tests_create_file(const char *file_name, uint64_t file_size) { int fd; int flags; mode_t mode; uint64_t actual_size; /* Less than size if the file system is full */ flags = O_CREAT | O_TRUNC | O_WRONLY | tests_maybe_sync_flag(); mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; fd = open(file_name, flags, mode); if (fd == -1 && errno == ENOSPC) { errno = 0; return 0; /* File system full */ } CHECK(fd != -1); actual_size = tests_fill_file(fd, file_size); CHECK(close(fd) != -1); if (file_size != 0 && actual_size == 0) tests_delete_file(file_name); else tests_sync_directory(file_name); return actual_size; } /* Calculate: free_space * numerator / denominator */ uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator) { if (denominator == 0) denominator = 1; if (numerator > denominator) numerator = denominator; return numerator * (tests_get_free_space() / denominator); } /* Create file "fragment_n" where n is the file_number, and unlink it */ int tests_create_orphan(unsigned file_number) { int fd; int flags; mode_t mode; char file_name[256]; sprintf(file_name, "fragment_%u", file_number); flags = O_CREAT | O_TRUNC | O_RDWR | tests_maybe_sync_flag(); mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; fd = open(file_name, flags, mode); if (fd == -1 && (errno == ENOSPC || errno == EMFILE)) return fd; /* File system full or too many open files */ CHECK(fd != -1); tests_sync_directory(file_name); CHECK(unlink(file_name) != -1); return fd; } /* Write size bytes at offset to the file "fragment_n" where n is the file_number and file_number also determines the random data written i.e. seed for random numbers */ unsigned tests_write_fragment_file(unsigned file_number, int fd, off_t offset, unsigned size) { int i, d; uint64_t u; ssize_t written; off_t pos; char buf[WRITE_BUFFER_SIZE]; if (size > WRITE_BUFFER_SIZE) size = WRITE_BUFFER_SIZE; pos = lseek(fd, 0, SEEK_END); CHECK(pos != (off_t) -1); if (offset > pos) offset = pos; pos = lseek(fd, offset, SEEK_SET); CHECK(pos != (off_t) -1); CHECK(pos == offset); srand(file_number); while (offset--) rand(); u = RAND_MAX; u += 1; u /= 256; d = (int) u; for (i = 0; i < size; ++i) buf[i] = rand() / d; written = write(fd, buf, size); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; written = 0; } tests_maybe_sync(fd); return (unsigned) written; } /* Write size bytes to the end of file descriptor fd using file_number to determine the random data written i.e. seed for random numbers */ unsigned tests_fill_fragment_file(unsigned file_number, int fd, unsigned size) { off_t offset; offset = lseek(fd, 0, SEEK_END); CHECK(offset != (off_t) -1); return tests_write_fragment_file(file_number, fd, offset, size); } /* Write size bytes to the end of file "fragment_n" where n is the file_number and file_number also determines the random data written i.e. seed for random numbers */ unsigned tests_append_to_fragment_file(unsigned file_number, unsigned size, int create) { int fd; int flags; mode_t mode; unsigned actual_growth; char file_name[256]; sprintf(file_name, "fragment_%u", file_number); if (create) flags = O_CREAT | O_EXCL | O_WRONLY | tests_maybe_sync_flag(); else flags = O_WRONLY | tests_maybe_sync_flag(); mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; fd = open(file_name, flags, mode); if (fd == -1 && errno == ENOSPC) { errno = 0; return 0; /* File system full */ } CHECK(fd != -1); actual_growth = tests_fill_fragment_file(file_number, fd, size); CHECK(close(fd) != -1); if (create && !actual_growth) tests_delete_fragment_file(file_number); return actual_growth; } /* Write size bytes at offset to the file "fragment_n" where n is the file_number and file_number also determines the random data written i.e. seed for random numbers */ unsigned tests_overwite_fragment_file( unsigned file_number, off_t offset, unsigned size) { int fd; unsigned actual_size; char file_name[256]; sprintf(file_name, "fragment_%u", file_number); fd = open(file_name, O_RDWR | tests_maybe_sync_flag()); if (fd == -1 && errno == ENOSPC) { errno = 0; return 0; /* File system full */ } CHECK(fd != -1); actual_size = tests_write_fragment_file(file_number, fd, offset, size); CHECK(close(fd) != -1); return actual_size; } /* Delete file "fragment_n" where n is the file_number */ void tests_delete_fragment_file(unsigned file_number) { char file_name[256]; sprintf(file_name, "fragment_%u", file_number); tests_delete_file(file_name); } /* Check the random data in file "fragment_n" is what is expected */ void tests_check_fragment_file_fd(unsigned file_number, int fd) { ssize_t sz, i; int d; uint64_t u; char buf[8192]; CHECK(lseek(fd, 0, SEEK_SET) == 0); srand(file_number); u = RAND_MAX; u += 1; u /= 256; d = (int) u; for (;;) { sz = read(fd, buf, 8192); if (sz == 0) break; CHECK(sz >= 0); for (i = 0; i < sz; ++i) CHECK(buf[i] == (char) (rand() / d)); } } /* Check the random data in file "fragment_n" is what is expected */ void tests_check_fragment_file(unsigned file_number) { int fd; ssize_t sz, i; int d; uint64_t u; char file_name[256]; char buf[8192]; sprintf(file_name, "fragment_%u", file_number); fd = open(file_name, O_RDONLY); CHECK(fd != -1); srand(file_number); u = RAND_MAX; u += 1; u /= 256; d = (int) u; for (;;) { sz = read(fd, buf, 8192); if (sz == 0) break; CHECK(sz >= 0); for (i = 0; i < sz; ++i) CHECK(buf[i] == (char) (rand() / d)); } CHECK(close(fd) != -1); } /* Central point to decide whether to use fsync */ void tests_maybe_sync(int fd) { if (tests_ok_to_sync) CHECK(fsync(fd) != -1); } /* Return O_SYNC if ok to sync otherwise return 0 */ int tests_maybe_sync_flag(void) { if (tests_ok_to_sync) return O_SYNC; return 0; } /* Return random number from 0 to n - 1 */ size_t tests_random_no(size_t n) { uint64_t a, b; if (!n) return 0; if (n - 1 <= RAND_MAX) { a = rand(); b = RAND_MAX; b += 1; } else { const uint64_t u = 1 + (uint64_t) RAND_MAX; a = rand(); a *= u; a += rand(); b = u * u; CHECK(n <= b); } if (RAND_MAX <= UINT32_MAX && n <= UINT32_MAX) return a * n / b; else /*if (RAND_MAX <= UINT64_MAX && n <= UINT64_MAX)*/ { uint64_t x, y; if (a < n) { x = a; y = n; } else { x = n; y = a; } return (x * (y / b)) + ((x * (y % b)) / b); } } /* Make a directory empty */ void tests_clear_dir(const char *dir_name) { DIR *dir; struct dirent *entry; char buf[4096]; dir = opendir(dir_name); CHECK(dir != NULL); CHECK(getcwd(buf, 4096) != NULL); CHECK(chdir(dir_name) != -1); for (;;) { errno = 0; entry = readdir(dir); if (entry) { if (strcmp(".",entry->d_name) != 0 && strcmp("..",entry->d_name) != 0) { if (entry->d_type == DT_DIR) { tests_clear_dir(entry->d_name); CHECK(rmdir(entry->d_name) != -1); } else CHECK(unlink(entry->d_name) != -1); } } else { CHECK(errno == 0); break; } } CHECK(chdir(buf) != -1); CHECK(closedir(dir) != -1); } /* Create an empty sub-directory or small file in the current directory */ int64_t tests_create_entry(char *return_name) { int fd; char name[256]; for (;;) { sprintf(name, "%u", (unsigned) tests_random_no(10000000)); fd = open(name, O_RDONLY); if (fd == -1) break; close(fd); } if (return_name) strcpy(return_name, name); if (tests_random_no(2)) { return tests_create_file(name, tests_random_no(4096)); } else { if (mkdir(name, 0777) == -1) { CHECK(errno == ENOSPC); errno = 0; return 0; } return TESTS_EMPTY_DIR_SIZE; } } /* Remove a random file of empty sub-directory from the current directory */ int64_t tests_remove_entry(void) { DIR *dir; struct dirent *entry; unsigned count = 0, pos; int64_t result = 0; dir = opendir("."); CHECK(dir != NULL); for (;;) { errno = 0; entry = readdir(dir); if (entry) { if (strcmp(".",entry->d_name) != 0 && strcmp("..",entry->d_name) != 0) ++count; } else { CHECK(errno == 0); break; } } pos = tests_random_no(count); count = 0; rewinddir(dir); for (;;) { errno = 0; entry = readdir(dir); if (!entry) { CHECK(errno == 0); break; } if (strcmp(".",entry->d_name) != 0 && strcmp("..",entry->d_name) != 0) { if (count == pos) { if (entry->d_type == DT_DIR) { tests_clear_dir(entry->d_name); CHECK(rmdir(entry->d_name) != -1); result = TESTS_EMPTY_DIR_SIZE; } else { struct stat st; CHECK(stat(entry->d_name, &st) != -1); result = st.st_size; CHECK(unlink(entry->d_name) != -1); } } ++count; } } CHECK(closedir(dir) != -1); return result; } /* Read mount information from /proc/mounts or /etc/mtab */ int tests_get_mount_info(struct mntent *info) { FILE *f; struct mntent *entry; int found = 0; f = fopen("/proc/mounts", "rb"); if (!f) f = fopen("/etc/mtab", "rb"); CHECK(f != NULL); while (!found) { entry = getmntent(f); if (entry) { if (strcmp(entry->mnt_dir, tests_file_system_mount_dir) == 0) { found = 1; *info = *entry; } } else break; } CHECK(fclose(f) == 0); return found; } /* * This funcion parses file-system options string, extracts standard mount * options from there, and saves them in the @flags variable. The non-standard * (fs-specific) mount options are left in @mnt_opts string, while the standard * ones will be removed from it. * * The reason for this perverted function is that we want to preserve mount * options when unmounting the file-system and mounting it again. But we cannot * pass standard* mount optins (like sync, ro, etc) as a string to the * 'mount()' function, because it fails. It accepts standard mount options only * as flags. And only the FS-specific mount options are accepted in form of a * string. */ static int process_mount_options(char **mnt_opts, unsigned long *flags) { char *tmp, *opts, *p; const char *opt; /* * We are going to use 'strtok()' which modifies the original string, * so duplicate it. */ tmp = strdup(*mnt_opts); if (!tmp) goto out_mem; p = opts = calloc(1, strlen(*mnt_opts) + 1); if (!opts) { free(tmp); goto out_mem; } *flags = 0; opt = strtok(tmp, ","); while (opt) { if (!strcmp(opt, "rw")) ; else if (!strcmp(opt, "ro")) *flags |= MS_RDONLY; else if (!strcmp(opt, "dirsync")) *flags |= MS_DIRSYNC; else if (!strcmp(opt, "noatime")) *flags |= MS_NOATIME; else if (!strcmp(opt, "nodiratime")) *flags |= MS_NODIRATIME; else if (!strcmp(opt, "noexec")) *flags |= MS_NOEXEC; else if (!strcmp(opt, "nosuid")) *flags |= MS_NOSUID; else if (!strcmp(opt, "relatime")) *flags |= MS_RELATIME; else if (!strcmp(opt, "sync")) *flags |= MS_SYNCHRONOUS; else { int len = strlen(opt); if (p != opts) *p++ = ','; memcpy(p, opt, len); p += len; *p = '\0'; } opt = strtok(NULL, ","); } free(tmp); *mnt_opts = opts; return 0; out_mem: fprintf(stderr, "cannot allocate memory\n"); return 1; } /* * Re-mount test file system. Randomly choose how to do this: re-mount R/O then * re-mount R/W, or unmount, then mount R/W, or unmount then mount R/O then * re-mount R/W, etc. This should improve test coverage. */ void tests_remount(void) { int err; struct mntent mount_info; char *source, *target, *filesystemtype, *data; char cwd[4096]; unsigned long mountflags, flags; unsigned int rorw1, um, um_ro, um_rorw, rorw2; CHECK(tests_get_mount_info(&mount_info)); if (strcmp(mount_info.mnt_dir,"/") == 0) return; /* Save current working directory */ CHECK(getcwd(cwd, 4096) != NULL); /* Temporarily change working directory to '/' */ CHECK(chdir("/") != -1); /* Choose what to do */ rorw1 = tests_random_no(2); um = tests_random_no(2); um_ro = tests_random_no(2); um_rorw = tests_random_no(2); rorw2 = tests_random_no(2); if (rorw1 + um + rorw2 == 0) um = 1; source = mount_info.mnt_fsname; target = tests_file_system_mount_dir; filesystemtype = tests_file_system_type; data = mount_info.mnt_opts; process_mount_options(&data, &mountflags); if (rorw1) { /* Re-mount R/O and then re-mount R/W */ flags = mountflags | MS_RDONLY | MS_REMOUNT; err = mount(source, target, filesystemtype, flags, data); CHECK(err != -1); flags = mountflags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); err = mount(source, target, filesystemtype, flags, data); CHECK(err != -1); } if (um) { /* Unmount and mount */ if (um_ro) { /* But re-mount R/O before unmounting */ flags = mountflags | MS_RDONLY | MS_REMOUNT; err = mount(source, target, filesystemtype, flags, data); CHECK(err != -1); } CHECK(umount(target) != -1); if (!um_rorw) { /* Mount R/W straight away */ err = mount(source, target, filesystemtype, mountflags, data); CHECK(err != -1); } else { /* Mount R/O and then re-mount R/W */ err = mount(source, target, filesystemtype, mountflags | MS_RDONLY, data); CHECK(err != -1); flags = mountflags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); err = mount(source, target, filesystemtype, flags, data); CHECK(err != -1); } } if (rorw2) { /* Re-mount R/O and then re-mount R/W */ flags = mountflags | MS_RDONLY | MS_REMOUNT; err = mount(source, target, filesystemtype, flags, data); CHECK(err != -1); flags = mountflags | MS_REMOUNT; flags &= ~((unsigned long)MS_RDONLY); err = mount(source, target, filesystemtype, flags, data); CHECK(err != -1); } /* Restore the previous working directory */ CHECK(chdir(cwd) != -1); } /* Un-mount or re-mount test file system */ static void tests_mnt(int mnt) { static struct mntent mount_info; char *source; char *target; char *filesystemtype; unsigned long mountflags; char *data; static char cwd[4096]; if (mnt == 0) { CHECK(tests_get_mount_info(&mount_info)); if (strcmp(mount_info.mnt_dir,"/") == 0) return; CHECK(getcwd(cwd, 4096) != NULL); CHECK(chdir("/") != -1); CHECK(umount(tests_file_system_mount_dir) != -1); } else { source = mount_info.mnt_fsname; target = tests_file_system_mount_dir; filesystemtype = tests_file_system_type; data = mount_info.mnt_opts; process_mount_options(&data, &mountflags); CHECK(mount(source, target, filesystemtype, mountflags, data) != -1); CHECK(chdir(cwd) != -1); } } /* Unmount test file system */ void tests_unmount(void) { tests_mnt(0); } /* Mount test file system */ void tests_mount(void) { tests_mnt(1); } /* Check whether the test file system is also the root file system */ int tests_fs_is_rootfs(void) { struct stat f_info; struct stat root_f_info; CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); CHECK(stat("/", &root_f_info) != -1); if (f_info.st_dev == root_f_info.st_dev) return 1; else return 0; } /* Try to make a directory empty */ void tests_try_to_clear_dir(const char *dir_name) { DIR *dir; struct dirent *entry; char buf[4096]; dir = opendir(dir_name); if (dir == NULL) return; if (getcwd(buf, 4096) == NULL || chdir(dir_name) == -1) { closedir(dir); return; } for (;;) { errno = 0; entry = readdir(dir); if (entry) { if (strcmp(".",entry->d_name) != 0 && strcmp("..",entry->d_name) != 0) { if (entry->d_type == DT_DIR) { tests_try_to_clear_dir(entry->d_name); rmdir(entry->d_name); } else unlink(entry->d_name); } } else { CHECK(errno == 0); break; } } if (chdir(buf) < 0) perror("chdir"); closedir(dir); } /* Check whether the test file system is also the current file system */ int tests_fs_is_currfs(void) { struct stat f_info; struct stat curr_f_info; CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); CHECK(stat(".", &curr_f_info) != -1); if (f_info.st_dev == curr_f_info.st_dev) return 1; else return 0; } #define PID_BUF_SIZE 64 /* Concatenate a pid to a string in a signal safe way */ void tests_cat_pid(char *buf, const char *name, pid_t pid) { char *p; unsigned x; const char digits[] = "0123456789"; char pid_buf[PID_BUF_SIZE]; x = (unsigned) pid; p = pid_buf + PID_BUF_SIZE; *--p = '\0'; if (x) while (x) { *--p = digits[x % 10]; x /= 10; } else *--p = '0'; buf[0] = '\0'; strcat(buf, name); strcat(buf, p); } mtd-utils-1.5.0/tests/fs-tests/lib/tests.h000066400000000000000000000162401175167361300204470ustar00rootroot00000000000000/* * 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 /* Main macro for testing */ #define CHECK(x) tests_test((x),__func__,__FILE__,__LINE__) /* The default directory in which tests are conducted */ #define TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR "/mnt/test_file_system" /* The default file system type to test */ #define TESTS_DEFAULT_FILE_SYSTEM_TYPE "jffs2" /* Estimated size of an empty directory */ #define TESTS_EMPTY_DIR_SIZE 128 /* Function invoked by the CHECK macro */ void tests_test(int test,const char *msg,const char *file,unsigned line); /* Handle common program options */ int tests_get_args(int argc, char *argv[], const char *title, const char *desc, const char *opts); /* Return the number of files (or directories) in the given directory */ unsigned tests_count_files_in_dir(const char *dir_name); /* Change to the file system mount directory, check that it is empty, matches the file system type, and is not the root file system */ void tests_check_test_file_system(void); /* Get the free space for the file system of the current directory */ uint64_t tests_get_free_space(void); /* Get the total space for the file system of the current directory */ uint64_t tests_get_total_space(void); /* Write size random bytes into file descriptor fd at the current position, returning the number of bytes actually written */ uint64_t tests_fill_file(int fd, uint64_t size); /* Write size random bytes into file descriptor fd at offset, returning the number of bytes actually written */ uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size); /* Check that a file written using tests_fill_file() and/or tests_write_filled_file() and/or tests_create_file() contains the expected random data */ void tests_check_filled_file_fd(int fd); /* Check that a file written using tests_fill_file() and/or tests_write_filled_file() and/or tests_create_file() contains the expected random data */ void tests_check_filled_file(const char *file_name); /* Delete a file */ void tests_delete_file(const char *file_name); /* Create a file of size file_size */ uint64_t tests_create_file(const char *file_name, uint64_t file_size); /* Calculate: free_space * numerator / denominator */ uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator); /* Create file "fragment_n" where n is the file_number, and unlink it */ int tests_create_orphan(unsigned file_number); /* Write size bytes at offset to the file "fragment_n" where n is the file_number and file_number also determines the random data written i.e. seed for random numbers */ unsigned tests_write_fragment_file(unsigned file_number, int fd, off_t offset, unsigned size); /* Write size bytes to the end of file descriptor fd using file_number to determine the random data written i.e. seed for random numbers */ unsigned tests_fill_fragment_file(unsigned file_number, int fd, unsigned size); /* Write size bytes to the end of file "fragment_n" where n is the file_number and file_number also determines the random data written i.e. seed for random numbers */ unsigned tests_append_to_fragment_file(unsigned file_number, unsigned size, int create); /* Write size bytes at offset to the file "fragment_n" where n is the file_number and file_number also determines the random data written i.e. seed for random numbers */ unsigned tests_overwite_fragment_file( unsigned file_number, off_t offset, unsigned size); /* Delete file "fragment_n" where n is the file_number */ void tests_delete_fragment_file(unsigned file_number); /* Check the random data in file "fragment_n" is what is expected */ void tests_check_fragment_file_fd(unsigned file_number, int fd); /* Check the random data in file "fragment_n" is what is expected */ void tests_check_fragment_file(unsigned file_number); /* Central point to decide whether to use fsync */ void tests_maybe_sync(int fd); /* Return O_SYNC if ok to sync otherwise return 0 */ int tests_maybe_sync_flag(void); /* Return random number from 0 to n - 1 */ size_t tests_random_no(size_t n); /* Make a directory empty */ void tests_clear_dir(const char *dir_name); /* Create an empty sub-directory or small file in the current directory */ int64_t tests_create_entry(char *return_name); /* Remove a random file of empty sub-directory from the current directory */ int64_t tests_remove_entry(void); /* Un-mount and re-mount test file system */ void tests_remount(void); /* Un-mount test file system */ void tests_unmount(void); /* Mount test file system */ void tests_mount(void); /* Check whether the test file system is also the root file system */ int tests_fs_is_rootfs(void); /* Try to make a directory empty */ void tests_try_to_clear_dir(const char *dir_name); /* Check whether the test file system is also the current file system */ int tests_fs_is_currfs(void); /* Concatenate a pid to a string in a signal safe way */ void tests_cat_pid(char *buf, const char *name, pid_t pid); extern char *tests_file_system_mount_dir; extern char *tests_file_system_type; /* General purpose test parameter to specify some aspect of test size. May be used by different tests in different ways. Set by the -z, --size options. */ extern int64_t tests_size_parameter; /* General purpose test parameter to specify some aspect of test repetition. May be used by different tests in different ways. Set by the -n, --repeat options. */ extern int64_t tests_repeat_parameter; /* General purpose test parameter to specify some aspect of test sleeping. May be used by different tests in different ways. Set by the -p, --sleep options. */ extern int64_t tests_sleep_parameter; /* General purpose test parameter to specify a file should be unlinked. May be used by different tests in different ways or not at all. */ extern int tests_unlink_flag; /* General purpose test parameter to specify a file should be closed. May be used by different tests in different ways or not at all. */ extern int tests_close_flag; /* General purpose test parameter to specify a file should be deleted. May be used by different tests in different ways or not at all. */ extern int tests_delete_flag; /* General purpose test parameter to specify a file have a hole. May be used by different tests in different ways or not at all. */ extern int tests_hole_flag; /* Program name from argv[0] */ extern char *program_name; /* Maximum file name length of test file system (from statfs) */ extern long tests_max_fname_len; #endif mtd-utils-1.5.0/tests/fs-tests/run_all.sh000077500000000000000000000013131175167361300203540ustar00rootroot00000000000000#!/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 360 || exit 1 ./stress01.sh 360 || exit 1 cd .. || exit 1 mtd-utils-1.5.0/tests/fs-tests/simple/000077500000000000000000000000001175167361300176545ustar00rootroot00000000000000mtd-utils-1.5.0/tests/fs-tests/simple/.gitignore000066400000000000000000000000441175167361300216420ustar00rootroot00000000000000/ftrunc /orph /perf /test_1 /test_2 mtd-utils-1.5.0/tests/fs-tests/simple/Makefile000066400000000000000000000005361175167361300213200ustar00rootroot00000000000000 ifeq ($(origin CC),default) CC = gcc endif CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib LDFLAGS := $(LDFLAGS) TARGETS = test_1 \ test_2 \ ftrunc \ orph \ perf all: $(TARGETS) $(TARGETS): ../lib/tests.o ../lib/tests.o: ../lib/tests.h clean: rm -f *.o $(TARGETS) tests: all ./test_1 --sync ./test_2 --sync ./ftrunc ./orph --sync ./perf mtd-utils-1.5.0/tests/fs-tests/simple/ftrunc.c000066400000000000000000000051541175167361300213260ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/simple/orph.c000066400000000000000000000110311175167361300207640ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/simple/perf.c000066400000000000000000000117511175167361300207610ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include "tests.h" #define BLOCK_SIZE 32 * 1024 struct timeval tv_start; struct timeval tv_stop; static inline void start_timer(void) { CHECK(gettimeofday(&tv_start, NULL) != -1); } static inline long long stop_timer(void) { long long usecs; CHECK(gettimeofday(&tv_stop, NULL) != -1); usecs = (tv_stop.tv_sec - tv_start.tv_sec); usecs *= 1000000; usecs += tv_stop.tv_usec; usecs -= tv_start.tv_usec; return usecs; } static unsigned speed(size_t bytes, long long usecs) { unsigned long long k; k = bytes * 1000000ULL; k /= usecs; k /= 1024; CHECK(k <= UINT_MAX); return (unsigned) k; } void perf(void) { pid_t pid; int fd, i; ssize_t written, readsz; size_t remains, block, actual_size; char file_name[256]; unsigned char *buf; long long write_time, unmount_time, mount_time, read_time; /* Sync all file systems */ sync(); /* Make random data to write */ buf = malloc(BLOCK_SIZE); CHECK(buf != NULL); pid = getpid(); srand(pid); for (i = 0; i < BLOCK_SIZE; i++) buf[i] = rand(); /* Open file */ tests_cat_pid(file_name, "perf_test_file_", pid); start_timer(); fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); CHECK(fd != -1); CHECK(tests_size_parameter > 0); CHECK(tests_size_parameter <= SIZE_MAX); /* Write to file */ actual_size = 0; remains = tests_size_parameter; while (remains > 0) { if (remains > BLOCK_SIZE) block = BLOCK_SIZE; else block = remains; written = write(fd, buf, block); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; break; } remains -= written; actual_size += written; } CHECK(fsync(fd) != -1); CHECK(close(fd) != -1); write_time = stop_timer(); /* Unmount */ start_timer(); tests_unmount(); unmount_time = stop_timer(); /* Mount */ start_timer(); tests_mount(); mount_time = stop_timer(); /* Open file, read it, and close it */ start_timer(); fd = open(file_name, O_RDONLY); CHECK(fd != -1); remains = actual_size; while (remains > 0) { if (remains > BLOCK_SIZE) block = BLOCK_SIZE; else block = remains; readsz = read(fd, buf, block); CHECK(readsz == block); remains -= readsz; } CHECK(close(fd) != -1); read_time = stop_timer(); CHECK(unlink(file_name) != -1); /* Display timings */ printf("File system read and write speed\n"); printf("================================\n"); printf("Specfied file size: %lld\n", (unsigned long long)tests_size_parameter); printf("Actual file size: %zu\n", actual_size); printf("Write time (us): %lld\n", write_time); printf("Unmount time (us): %lld\n", unmount_time); printf("Mount time (us): %lld\n", mount_time); printf("Read time (us): %lld\n", read_time); printf("Write speed (KiB/s): %u\n", speed(actual_size, write_time)); printf("Read speed (KiB/s): %u\n", speed(actual_size, read_time)); printf("Test completed\n"); } /* Title of this test */ const char *perf_get_title(void) { return "Measure file system read and write speed"; } /* Description of this test */ const char *perf_get_description(void) { return "Syncs the file system (a newly created empty file system is " \ "preferable). Creates a file named perf_test_file_pid, where " \ "pid is the process id. The file is filled with random data. " \ "The size of the file is given by the -z or --size option, " \ "otherwise it defaults to 10MiB. Unmounts the file system. " \ "Mounts the file system. Reads the entire file in 32KiB size " \ "blocks. Displays the time taken for each activity. Deletes " \ "the file. Note that the file is synced after writing and " \ "that time is included in the write time and speed."; } int main(int argc, char *argv[]) { int run_test; /* Set default test file size */ tests_size_parameter = 10 * 1024 * 1024; /* Handle common arguments */ run_test = tests_get_args(argc, argv, perf_get_title(), perf_get_description(), "z"); if (!run_test) return 1; /* Change directory to the file system and check it is ok for testing */ tests_check_test_file_system(); /* Do the actual test */ perf(); return 0; } mtd-utils-1.5.0/tests/fs-tests/simple/test_1.c000066400000000000000000000104221175167361300212160ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #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", (unsigned long long)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", (unsigned long long)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; } mtd-utils-1.5.0/tests/fs-tests/simple/test_2.c000066400000000000000000000143241175167361300212240ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/000077500000000000000000000000001175167361300177065ustar00rootroot00000000000000mtd-utils-1.5.0/tests/fs-tests/stress/Makefile000066400000000000000000000002261175167361300213460ustar00rootroot00000000000000 SUBDIRS = atoms all tests: $(SUBDIRS) clean: $(SUBDIRS) rm -rf run_pdf_test_file_* .PHONY: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ $(MAKECMDGOALS) mtd-utils-1.5.0/tests/fs-tests/stress/atoms/000077500000000000000000000000001175167361300210315ustar00rootroot00000000000000mtd-utils-1.5.0/tests/fs-tests/stress/atoms/.gitignore000066400000000000000000000001431175167361300230170ustar00rootroot00000000000000/fwrite00 /gcd_hupper /pdfrun /rmdir00 /rndrm00 /rndrm99 /rndwrite00 /stress_1 /stress_2 /stress_3 mtd-utils-1.5.0/tests/fs-tests/stress/atoms/Makefile000066400000000000000000000007771175167361300225040ustar00rootroot00000000000000 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 mtd-utils-1.5.0/tests/fs-tests/stress/atoms/fwrite00.c000066400000000000000000000132741175167361300226440ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "tests.h" #define WRITE_BUFFER_SIZE 32768 #define HOLE_BLOCK_SIZE 10000000 void filestress00(void) { int fd, i, deleted; pid_t pid; ssize_t written; int64_t remains; int64_t repeat; size_t block; char file_name[256]; char buf[WRITE_BUFFER_SIZE]; fd = -1; deleted = 1; pid = getpid(); tests_cat_pid(file_name, "filestress00_test_file_", pid); srand(pid); repeat = tests_repeat_parameter; for (;;) { /* Open the file */ if (fd == -1) { fd = open(file_name, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if (fd == -1 && errno == ENOSPC) { /* Break if repeat count exceeded */ if (tests_repeat_parameter > 0 && --repeat <= 0) break; /* Sleep 2 secs and try again */ sleep(2); continue; } CHECK(fd != -1); deleted = 0; if (tests_unlink_flag) { CHECK(unlink(file_name) != -1); deleted = 1; } } /* Get a different set of random data */ for (i = 0; i < WRITE_BUFFER_SIZE;++i) buf[i] = rand(); if (tests_hole_flag) { /* Make a hole */ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1); written = write(fd, "!", 1); if (written <= 0) { /* File system full */ CHECK(errno == ENOSPC); errno = 0; } CHECK(lseek(fd, 0, SEEK_SET) != -1); /* Write at set points into the hole */ remains = tests_size_parameter; while (remains > HOLE_BLOCK_SIZE) { CHECK(lseek(fd, HOLE_BLOCK_SIZE, SEEK_CUR) != -1); written = write(fd, "!", 1); remains -= HOLE_BLOCK_SIZE; if (written <= 0) { /* File system full */ CHECK(errno == ENOSPC); errno = 0; break; } } } else { /* Write data into the file */ CHECK(lseek(fd, 0, SEEK_SET) != -1); remains = tests_size_parameter; while (remains > 0) { if (remains > WRITE_BUFFER_SIZE) block = WRITE_BUFFER_SIZE; else block = remains; written = write(fd, buf, block); if (written <= 0) { /* File system full */ CHECK(errno == ENOSPC); errno = 0; break; } remains -= written; } } /* Break if repeat count exceeded */ if (tests_repeat_parameter > 0 && --repeat <= 0) break; /* Close if tests_close_flag */ if (tests_close_flag) { CHECK(close(fd) != -1); fd = -1; } /* Sleep */ if (tests_sleep_parameter > 0) { unsigned us = tests_sleep_parameter * 1000; unsigned rand_divisor = RAND_MAX / us; unsigned s = (us / 2) + (rand() / rand_divisor); usleep(s); } /* Delete if tests_delete flag */ if (!deleted && tests_delete_flag) { CHECK(unlink(file_name) != -1); deleted = 1; } } CHECK(close(fd) != -1); /* Sleep */ if (tests_sleep_parameter > 0) { unsigned us = tests_sleep_parameter * 1000; unsigned rand_divisor = RAND_MAX / us; unsigned s = (us / 2) + (rand() / rand_divisor); usleep(s); } /* Tidy up */ if (!deleted) CHECK(unlink(file_name) != -1); } /* Title of this test */ const char *filestress00_get_title(void) { return "File stress test 00"; } /* Description of this test */ const char *filestress00_get_description(void) { return "Create a file named filestress00_test_file_pid, where " \ "pid is the process id. If the unlink option " \ "(-u or --unlink) is specified, " \ "unlink the file while holding the open file descriptor. " \ "If the hole option (-o or --hole) is specified, " \ "write a single character at the end of the file, creating a " \ "hole. " \ "Write a single character in the hole every 10 million " \ "bytes. " \ "If the hole option is not specified, then the file is " \ "filled with random data. " \ "If the close option (-c or --close) is specified the file " \ "is closed. " \ "If a sleep value is specified, the process sleeps. " \ "If the delete option (-e or --delete) is specified, then " \ "the file is deleted. " \ "If a repeat count is specified, then the task repeats " \ "that number of times. " \ "The repeat count is given by the -n or --repeat option, " \ "otherwise it defaults to 1. " \ "A repeat count of zero repeats forever. " \ "The file size is given by the -z or --size option, " \ "otherwise it defaults to 1000000. " \ "The sleep value is given by the -p or --sleep option, " \ "otherwise it defaults to 0. " \ "Sleep is specified in milliseconds."; } int main(int argc, char *argv[]) { int run_test; /* Set default test file size */ tests_size_parameter = 1000000; /* Set default test repetition */ tests_repeat_parameter = 1; /* Set default test sleep */ tests_sleep_parameter = 0; /* Handle common arguments */ run_test = tests_get_args(argc, argv, filestress00_get_title(), filestress00_get_description(), "znpuoce"); if (!run_test) return 1; /* Change directory to the file system and check it is ok for testing */ tests_check_test_file_system(); /* Do the actual test */ filestress00(); return 0; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/gcd_hupper.c000066400000000000000000000123121175167361300233140ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/pdfrun.c000066400000000000000000000070761175167361300225050ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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"); if (fscanf(f, "%s %lu", dummy, &total_memory) != 2) perror("fscanf error"); 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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/rmdir00.c000066400000000000000000000071541175167361300224610ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/rndrm00.c000066400000000000000000000103511175167361300224570ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/rndrm99.c000066400000000000000000000241201175167361300225000ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/rndwrite00.c000066400000000000000000000122421175167361300231740ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include #include "tests.h" #define BLOCK_SIZE 32768 #define BUFFER_SIZE 32768 static void check_file(int fd, char *data, size_t length) { ssize_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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/stress_1.c000066400000000000000000000051371175167361300227460ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/stress_2.c000066400000000000000000000057031175167361300227460ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/atoms/stress_3.c000066400000000000000000000060711175167361300227460ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #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; } mtd-utils-1.5.0/tests/fs-tests/stress/stress00.sh000077500000000000000000000021441175167361300217310ustar00rootroot00000000000000#!/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 mtd-utils-1.5.0/tests/fs-tests/stress/stress01.sh000077500000000000000000000012041175167361300217260ustar00rootroot00000000000000#!/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 mtd-utils-1.5.0/tests/fs-tests/utils/000077500000000000000000000000001175167361300175235ustar00rootroot00000000000000mtd-utils-1.5.0/tests/fs-tests/utils/.gitignore000066400000000000000000000000341175167361300215100ustar00rootroot00000000000000/free_space /fstest_monitor mtd-utils-1.5.0/tests/fs-tests/utils/Makefile000066400000000000000000000003721175167361300211650ustar00rootroot00000000000000 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 mtd-utils-1.5.0/tests/fs-tests/utils/free_space.c000066400000000000000000000027461175167361300217740ustar00rootroot00000000000000/* * 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 #include #include #include 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; } mtd-utils-1.5.0/tests/fs-tests/utils/fstest_monitor.c000066400000000000000000000122301175167361300227440ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include 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; } mtd-utils-1.5.0/tests/jittertest/000077500000000000000000000000001175167361300170145ustar00rootroot00000000000000mtd-utils-1.5.0/tests/jittertest/.gitignore000066400000000000000000000000361175167361300210030ustar00rootroot00000000000000/JitterTest /plotJittervsFill mtd-utils-1.5.0/tests/jittertest/COPYING000066400000000000000000000431271175167361300200560ustar00rootroot00000000000000 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. Copyright (C) 19yy 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. , 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. mtd-utils-1.5.0/tests/jittertest/JitterTest.c000066400000000000000000001037071175167361300212710ustar00rootroot00000000000000/*********************************************************************** * * Copyright: Daniel Measurement and Control, Inc. * 9753 Pine Lake Drive * Houston, TX 77055 * * Created by: Vipin Malik and Gail Murray * Released under GPL by permission of Daniel Industries. * * This software is licensed under the GPL version 2. Plese see the file * COPYING for details on the license. * * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose * are made in this software. Please use at your own risk. * * Filename: JitterTest.c * * Description: Program to be used to measure wake jitter. * See README file for more info. * * * Revision History: * $Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $ * $Log: JitterTest.c,v $ * Revision 1.13 2005/11/07 11:15:20 gleixner * [MTD / JFFS2] Clean up trailing white spaces * * Revision 1.12 2001/08/10 19:23:11 vipin * Ready to be released under GPL! Added proper headers etc. * * Revision 1.11 2001/07/09 15:35:50 vipin * Couple of new features:1. The program runs by default as a "regular" * (SCHED_OTHER) task by default, unless the -p n cmd line parameter is * specified. It then runs as SCHED_RR at that priority. * 2. Added ability to send SIGSTOP/SIGCONT to a specified PID. This * would presumably be the PID of the JFFS2 GC task. SIGSTOP is sent * before writing to the fs, and a SIGCONT after the write is done. * 3. The "pad" data now written to the file on the "fs under test" is * random, not sequential as previously. * * Revision 1.10 2001/06/27 19:14:24 vipin * Now console file to log at can be specified from cmd line. This can enable * you to run two instances of the program- one logging to the /dev/console * and another to a regular file (if you want the data) or /dev/null if you don't. * * Revision 1.9 2001/06/25 20:21:31 vipin * This is the latest version, NOT the one last checked in! * * Revision 1.7 2001/06/18 22:36:19 vipin * Fix minor typo that excluded '\n' from msg on console. * * Revision 1.6 2001/06/18 21:17:50 vipin * Added option to specify the amount of data written to outfile each period. * The regular info is written, but is then padded to the requested size. * This will enable testing of different sized writes to JFFS fs. * * Revision 1.5 2001/06/08 19:36:23 vipin * All write() are now checked for return err code. * * Revision 1.4 2001/06/06 23:10:31 vipin * Added README file. * In JitterTest.c: Changed operation of periodic timer to one shot. The timer is now * reset when the task wakes. This way every "jitter" is from the last time and * jitters from previous times are not accumulated and screw aroud with our display. * * All jitter is now +ve. (as it should be). Also added a "read file" functionality * to test for jitter in task if we have to read from JFFS fs. * The program now also prints data to console- where it can be logged, interspersed with * other "interesting" printk's from the kernel and drivers (flash sector erases etc.) * * Revision 1.3 2001/03/01 19:20:39 gmurray * Add priority scheduling. Shortened name of default output file. * Changed default interrupt period. Output delta time and jitter * instead of time of day. * * Revision 1.2 2001/02/28 16:20:19 vipin * Added version control Id and log fields. * ***********************************************************************/ /*************************** Included Files ***************************/ #include /* fopen, printf, fprintf, fclose */ #include /* strcpy, strcmp */ #include /* exit, atol, atoi */ #include /* setitimer, settimeofday, gettimeofday */ #include /* time */ #include /* signal */ #include /* sched_setscheduler, sched_get_priority_min,*/ /* sched_get_priority_max */ #include /* gettimeofday, sleep */ #include #include #include #include /**************************** Enumerations ****************************/ enum timerActions { ONESHOT, AUTORESETTING }; /****************************** Constants *****************************/ /* Exit error codes */ #define EXIT_FILE_OPEN_ERR (1) /* error opening output file*/ #define EXIT_REG_SIGALRM_ERR (2) /* error registering SIGALRM*/ #define EXIT_REG_SIGINT_ERR (3) /* error registering SIGINT */ #define EXIT_INV_INT_PERIOD (4) /* error invalid int. period*/ #define EXIT_MIN_PRIORITY_ERR (5) /* error, minimum priority */ #define EXIT_MAX_PRIORITY_ERR (6) /* error, maximum priority */ #define EXIT_INV_SCHED_PRIORITY (7) /* error, invalid priority */ #define EXIT_SET_SCHEDULER_ERR (8) /* error, set scheduler */ #define EXIT_PREV_TIME_OF_DAY_ERR (9) /* error, init. prev. */ /* time of day */ #define MAX_FILE_NAME_LEN (32) /* maximum file name length */ #define STRINGS_EQUAL ((int) 0) /* strcmp value if equal */ #define MIN_INT_PERIOD_MILLISEC ( 5L) /* minimum interrupt period */ #define MAX_INT_PERIOD_MILLISEC (5000L) /* maximum interrupt period */ #define DEF_INT_PERIOD_MILLISEC ( 10L) /* default interrupt period */ #define READ_FILE_MESSAGE "This is a junk file. Must contain at least 1 byte though!\n" /* The user can specify that the program pad out the write file to a given number of bytes. But a minimum number needs to be written. This will contain the jitter info. */ #define MIN_WRITE_BYTES 30 #define DEFAULT_WRITE_BYTES 30 #define MAX_WRITE_BYTES 4096 /* used for gen a printable ascii random # between spc and '~' */ #define MIN_ASCII 32 /* can be char*/ #define MAX_ASCII 126.0 /* needs to be float. See man rand() */ /*---------------------------------------------------------------------- * It appears that the preprocessor can't handle multi-line #if * statements. Thus, the check on the default is divided into two * #if statements. *---------------------------------------------------------------------*/ #if (DEF_INT_PERIOD_MILLISEC < MIN_INT_PERIOD_MILLISEC) #error *** Invalid default interrupt period. *** #endif #if (DEF_INT_PERIOD_MILLISEC > MAX_INT_PERIOD_MILLISEC) #error *** Invalid default interrupt period. *** #endif #define TRUE 1 /* Boolean true value */ #define FALSE 0 /* Time conversion constants. */ #define MILLISEC_PER_SEC (1000L) /* milliseconds per second */ #define MICROSEC_PER_MILLISEC (1000L) /* microsecs per millisec */ #define MICROSEC_PER_SEC (1000000L) /* microsecs per second */ #define PRIORITY_POLICY ((int) SCHED_RR) /* If specified iwth "-p" */ /************************** Module Variables **************************/ /* version identifier (value supplied by CVS)*/ static const char Version[] = "$Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $"; static char OutFileName[MAX_FILE_NAME_LEN+1]; /* output file name */ static char LogFile[MAX_FILE_NAME_LEN+1] = "/dev/console"; /* default */ static char ReadFile[MAX_FILE_NAME_LEN+1]; /* This file is read. Should contain at least 1 byte */ static int WriteBytes = DEFAULT_WRITE_BYTES; /* pad out file to these many bytes. */ static int Fd1; /* fd where the above buffer if o/p */ static int Cfd; /* fd to console (or file specified) */ static int Fd2; /* fd for the ReadFile */ static int DoRead = FALSE; /* should we attempt to ReadFile?*/ static long InterruptPeriodMilliSec; /* interrupt period, millisec */ static int MinPriority; /* minimum scheduler priority */ static int MaxPriority; /* maximum scheduler priority */ static int RequestedPriority; /* requested priority */ static struct itimerval ITimer; /* interrupt timer structure */ static struct timeval PrevTimeVal; /* previous time structure */ static struct timeval CurrTimeVal; /* current time structure */ static long LastMaxDiff = 0; /* keeps track of worst jitter encountered */ static int GrabKProfile = FALSE; /* To help determine system bottle necks this parameter can be set. This causes the /proc/profile file to be read and stored in unique filenames in current dir, and indication to be o/p on the /dev/console also. */ static long ProfileTriggerMSecs = 15000l; /* Jitter time in seconds that triggers a snapshot of the profile to be taken */ static int SignalGCTask = FALSE; /* should be signal SIGSTOP/SIGCONT to gc task?*/ static int GCTaskPID; static int RunAsRTTask = FALSE; /* default action unless priority is specified on cmd line */ /********************* Local Function Prototypes **********************/ void HandleCmdLineArgs(int argc, char *argv[]); void SetFileName(char * pFileName); void SetInterruptPeriod(char * pASCIIInterruptPeriodMilliSec); void SetSchedulerPriority(char * pASCIISchedulerPriority); void PrintVersionInfo(void); void PrintHelpInfo(void); int Write(int fd, void *buf, size_t bytes, int lineNo); void InitITimer(struct itimerval * pITimer, int action); /* For catching timer interrupts (SIGALRM). */ void AlarmHandler(int sigNum); /* For catching Ctrl+C SIGINT. */ void SignalHandler(int sigNum); /*********************************************************************** * main function * return: exit code ***********************************************************************/ int main( int argc, char *argv[]) { struct sched_param schedParam; int mypri; char tmpBuf[200]; strcpy(OutFileName,"jitter.dat"); InterruptPeriodMilliSec = MIN_INT_PERIOD_MILLISEC; /* Get the minimum and maximum priorities. */ MinPriority = sched_get_priority_min(PRIORITY_POLICY); MaxPriority = sched_get_priority_max(PRIORITY_POLICY); if (MinPriority == -1) { printf("\n*** Unable to get minimum priority. ***\n"); exit(EXIT_MIN_PRIORITY_ERR); } if (MaxPriority == -1) { printf("\n*** Unable to get maximum priority. ***\n"); exit(EXIT_MAX_PRIORITY_ERR); } /* Set requested priority to minimum value as default. */ RequestedPriority = MinPriority; HandleCmdLineArgs(argc, argv); if(mlockall(MCL_CURRENT|MCL_FUTURE) < 0){ printf("Could not lock task into memory. Bye\n"); perror("Error"); } if(RunAsRTTask){ /* Set the priority. */ schedParam.sched_priority = RequestedPriority; if (sched_setscheduler( 0, PRIORITY_POLICY, &schedParam) != (int) 0) { printf("Exiting: Unsuccessful sched_setscheduler.\n"); close(Fd1); exit(EXIT_SET_SCHEDULER_ERR); } /* Double check as I have some doubts that it's really running at realtime priority! */ if((mypri = sched_getscheduler(0)) != RequestedPriority) { printf("Not running with request priority %i. running with priority %i instead!\n", RequestedPriority, mypri); }else { printf("Running with %i priority. Good!\n", mypri); } } /*------------------------- Initializations --------------------------*/ if((Fd1 = open(OutFileName, O_RDWR|O_CREAT|O_SYNC, S_IRWXU)) <= 0) { perror("Cannot open outfile for write:"); exit(1); } /* If a request is made to read from a specified file, then create that file and fill with junk data so that there is something there to read. */ if(DoRead) { if((Fd2 = open(ReadFile, O_RDWR|O_CREAT|O_SYNC|O_TRUNC, S_IRWXU)) <= 0) { perror("cannot open read file:"); exit(1); }else { /* Don't really care how much we write here */ if(write(Fd2, READ_FILE_MESSAGE, strlen(READ_FILE_MESSAGE)) < 0) { perror("Problems writing to readfile:"); exit(1); } lseek(Fd2, 0, SEEK_SET); /* position back to byte #0 */ } } /* Also log output to console. This way we can capture it on a serial console to a log file. */ if((Cfd = open(LogFile, O_WRONLY|O_SYNC)) <= 0) { perror("cannot open o/p logfile:"); exit(1); } /* Set-up handler for periodic interrupt. */ if (signal(SIGALRM, &AlarmHandler) == SIG_ERR) { printf("Couldn't register signal handler for SIGALRM.\n"); sprintf(tmpBuf, "Couldn't register signal handler for SIGALRM.\n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); close(Fd1); exit(EXIT_REG_SIGALRM_ERR); } /* Set-up handler for Ctrl+C to exit the program. */ if (signal(SIGINT, &SignalHandler) == SIG_ERR) { printf("Couldn't register signal handler for SIGINT.\n"); sprintf(tmpBuf, "Couldn't register signal handler for SIGINT.\n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); close(Fd1); exit(EXIT_REG_SIGINT_ERR); } printf("Press Ctrl+C to exit the program.\n"); printf("Output File: %s\n", OutFileName); printf("Scheduler priority: %d\n", RequestedPriority); sprintf(tmpBuf, "\nScheduler priority: %d\n", RequestedPriority); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); printf("Interrupt period: %ld milliseconds\n", InterruptPeriodMilliSec); sprintf(tmpBuf, "Interrupt period: %ld milliseconds\n", InterruptPeriodMilliSec); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); fflush(0); /* Initialize the periodic timer. */ InitITimer(&ITimer, ONESHOT); /* Initialize "previous" time. */ if (gettimeofday(&PrevTimeVal, NULL) != (int) 0) { printf("Exiting - "); printf("Unable to initialize previous time of day.\n"); sprintf(tmpBuf, "Exiting - "); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); sprintf(tmpBuf, "Unable to initialize previous time of day.\n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); } /* Start the periodic timer. */ setitimer(ITIMER_REAL, &ITimer, NULL); while(TRUE) { /* Intentional infinite loop. */ /* Sleep for one second. */ sleep((unsigned int) 1); } /* Just in case. File should be closed in SignalHandler. */ close(Fd1); close(Cfd); return 0; } /*********************************************************************** * SignalHandler * This is a handler for the SIGINT signal. It is assumed that the * SIGINT is due to the user pressing Ctrl+C to halt the program. * output: N/A ***********************************************************************/ void SignalHandler( int sigNum) { char tmpBuf[200]; /* Note sigNum not used. */ printf("Ctrl+C detected. Worst Jitter time was:%fms.\nJitterTest exiting.\n", (float)LastMaxDiff/1000.0); sprintf(tmpBuf, "\nCtrl+C detected. Worst Jitter time was:%fms\nJitterTest exiting.\n", (float)LastMaxDiff/1000.0); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); close(Fd1); close(Cfd); exit(0); } /* A snapshot of the /proc/profile needs to be taken. This is stored as a new file every time, and the stats reset by doing a (any) write to the /proc/profile file. */ void doGrabKProfile(int jitterusec, char *fileName) { int fdSnapshot; int fdProfile; int readBytes; char readBuf[4096]; if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT, S_IRWXU)) <= 0) { fprintf(stderr, "Could not open file %s.\n", fileName); perror("Error:"); return; } if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) { fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); close(fdSnapshot); return; } while((readBytes = read(fdProfile, readBuf, sizeof(readBuf))) > 0) { int writeBytes = write(fdSnapshot, readBuf, readBytes); if (writeBytes != readBytes) { perror("write error"); break; } } close(fdSnapshot); if(write(fdProfile, readBuf, 1) != 1) { perror("Could Not clear profile by writing to /proc/profile:"); } close(fdProfile); }/* end doGrabKProfile()*/ /* Call this routine to clear the kernel profiling buffer /proc/profile */ void clearProfileBuf(void){ int fdProfile; char readBuf[10]; if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) { fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); return; } if(write(fdProfile, readBuf, 1) != 1) { perror("Could Not clear profile by writing to /proc/profile:"); } close(fdProfile); }/* end clearProfileBuf() */ /*********************************************************************** * AlarmHandler * This is a handler for the SIGALRM signal (due to the periodic * timer interrupt). It prints the time (seconds) to * the output file. * output: N/A ***********************************************************************/ void AlarmHandler( int sigNum) /* signal number (not used) */ { long timeDiffusec; /* diff time in micro seconds */ long intervalusec; char tmpBuf[MAX_WRITE_BYTES]; int cntr; char padChar; static int profileFileNo = 0; char profileFileName[150]; static int seedStarter = 0; /* carries over rand info to next time where time() will be the same as this time if invoked < 1sec apart. */ if (gettimeofday(&CurrTimeVal, NULL) == (int) 0) { /*---------------------------------------------------------------- * Compute the elapsed time between the current and previous * time of day values and store the result. * * Print the elapsed time to the output file. *---------------------------------------------------------------*/ timeDiffusec = (long)(((((long long)CurrTimeVal.tv_sec) * 1000000L) + CurrTimeVal.tv_usec) - (((long long)PrevTimeVal.tv_sec * 1000000L) + PrevTimeVal.tv_usec)); sprintf(tmpBuf," %f ms ", (float)timeDiffusec/1000.0); intervalusec = InterruptPeriodMilliSec * 1000L; timeDiffusec = timeDiffusec - intervalusec; sprintf(&tmpBuf[strlen(tmpBuf)]," %f ms", (float)timeDiffusec/1000.0); /* should we send a SIGSTOP/SIGCONT to the specified PID? */ if(SignalGCTask){ if(kill(GCTaskPID, SIGSTOP) < 0){ perror("error:"); } } /* Store some historical #'s */ if(abs(timeDiffusec) > LastMaxDiff) { LastMaxDiff = abs(timeDiffusec); sprintf(&tmpBuf[strlen(tmpBuf)],"!"); if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (abs(timeDiffusec)/1000))) { sprintf(profileFileName, "JitterTest.profilesnap-%i", profileFileNo); /* go and grab the kernel performance profile. */ doGrabKProfile(timeDiffusec, profileFileName); profileFileNo++; /* unique name next time */ /* Say so on the console so that a marker gets put in the console log */ sprintf(&tmpBuf[strlen(tmpBuf)],"", profileFileName); } } sprintf(&tmpBuf[strlen(tmpBuf)],"\n"); /* CR for the data going out of the console */ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); /* The "-1" below takes out the '\n' at the end that we appended for the msg printed on the console.*/ sprintf(&tmpBuf[strlen(tmpBuf)-1]," PadBytes:"); /* Now pad the output file if requested by user. */ if(WriteBytes > MIN_WRITE_BYTES) { /* start from a new place every time */ srand(time(NULL) + seedStarter); /* already written MIN_WRITE_BYTES by now */ for(cntr = strlen(tmpBuf); cntr < WriteBytes - 1 ; cntr++) /* "-1" adj for '\n' at end */ { /* don't accept any # < 'SPACE' */ padChar = (char)(MIN_ASCII+(int)((MAX_ASCII-(float)MIN_ASCII)*rand()/(RAND_MAX+1.0))); /* padChar = (cntr % (126-33)) + 33; */ tmpBuf[cntr] = padChar; } seedStarter = tmpBuf[cntr-1]; /* save for next time */ tmpBuf[cntr] = '\n'; /* CR for the data going into the outfile. */ tmpBuf[cntr+1] = '\0'; /* NULL terminate the string */ } /* write out the entire line to the output file. */ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); /* Read a byte from the specified file */ if(DoRead) { cntr = read(Fd2, tmpBuf, 1); if (cntr < 0) perror("read error"); lseek(Fd2, 0, SEEK_SET); /* back to start */ } /* Start the periodic timer again. */ setitimer(ITIMER_REAL, &ITimer, NULL); /* Update previous time with current time. */ PrevTimeVal.tv_sec = CurrTimeVal.tv_sec; PrevTimeVal.tv_usec = CurrTimeVal.tv_usec; } else { sprintf(tmpBuf, "gettimeofday error \n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); printf("gettimeofday error \n"); } /* now clear the profiling buffer */ if(GrabKProfile == TRUE){ clearProfileBuf(); } /* should we send a SIGSTOP/SIGCONT to the specified PID? */ if(SignalGCTask){ if(kill(GCTaskPID, SIGCONT) < 0){ perror("error:"); } } return; } /*********************************************************************** * InitITimer * This function initializes the 'struct itimerval' structure whose * address is passed to interrupt every InterruptPeriodMilliSec. * output: N/A ***********************************************************************/ void InitITimer( struct itimerval * pITimer, /* pointer to interrupt timer struct*/ int action) /* ONESHOT or autosetting? */ { long seconds; /* seconds portion of int. period */ long microSeconds; /* microsec. portion of int. period */ /*-------------------------------------------------------------------- * Divide the desired interrupt period into its seconds and * microseconds components. *-------------------------------------------------------------------*/ if (InterruptPeriodMilliSec < MILLISEC_PER_SEC) { seconds = 0L; microSeconds = InterruptPeriodMilliSec * MICROSEC_PER_MILLISEC; } else { seconds = InterruptPeriodMilliSec / MILLISEC_PER_SEC; microSeconds = (InterruptPeriodMilliSec - (seconds * MILLISEC_PER_SEC)) * MICROSEC_PER_MILLISEC; } /* Initialize the interrupt period structure. */ pITimer->it_value.tv_sec = seconds; pITimer->it_value.tv_usec = microSeconds; if(action == ONESHOT) { /* This will (should) prevent the timer from restarting itself */ pITimer->it_interval.tv_sec = 0; pITimer->it_interval.tv_usec = 0; }else { pITimer->it_interval.tv_sec = seconds; pITimer->it_interval.tv_usec = microSeconds; } return; } /*********************************************************************** * HandleCmdLineArgs * This function handles the command line arguments. * output: stack size ***********************************************************************/ void HandleCmdLineArgs( int argc, /* number of command-line arguments */ char *argv[]) /* ptrs to command-line arguments */ { int argNum; /* argument number */ if (argc > (int) 1) { for (argNum = (int) 1; argNum < argc; argNum++) { /* The command line contains an argument. */ if ((strcmp(argv[argNum],"--version") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-v") == STRINGS_EQUAL)) { /* Print version information and exit. */ PrintVersionInfo(); exit(0); } else if ((strcmp(argv[argNum],"--help") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-h") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-?") == STRINGS_EQUAL)) { /* Print help information and exit. */ PrintHelpInfo(); exit(0); } else if ((strcmp(argv[argNum],"--file") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-f") == STRINGS_EQUAL)) { /* Set the name of the output file. */ ++argNum; if (argNum < argc) { SetFileName(argv[argNum]); } else { printf("*** Output file name not specified. ***\n"); printf("Default output file name will be used.\n"); } } else if ((strcmp(argv[argNum],"--time") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-t") == STRINGS_EQUAL)) { /* Set the interrupt period. */ ++argNum; if (argNum < argc) { SetInterruptPeriod(argv[argNum]); } else { printf("*** Interrupt period not specified. ***\n"); printf("Default interrupt period will be used.\n"); } } else if ((strcmp(argv[argNum],"--priority") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-p") == STRINGS_EQUAL)) { /* Set the scheduler priority. */ ++argNum; if (argNum < argc) { SetSchedulerPriority(argv[argNum]); } else { printf("*** Scheduler priority not specified. ***\n"); printf("Default scheduler priority will be used.\n"); } } else if ((strcmp(argv[argNum],"--readfile") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-r") == STRINGS_EQUAL)) { /* Set the file to read*/ ++argNum; strncpy(ReadFile, argv[argNum], sizeof(ReadFile)); DoRead = TRUE; } else if ((strcmp(argv[argNum],"--write_bytes") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-w") == STRINGS_EQUAL)) { /* Set the file to read*/ ++argNum; WriteBytes = atoi(argv[argNum]); if(WriteBytes < MIN_WRITE_BYTES) { printf("Writing less than %i bytes is not allowed. Bye.\n", MIN_WRITE_BYTES); exit(0); } } else if ((strcmp(argv[argNum],"--consolefile") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-c") == STRINGS_EQUAL)) { /* Set the file to log console log on. */ ++argNum; strncpy(LogFile, argv[argNum], sizeof(LogFile)); } else if ((strcmp(argv[argNum],"--grab_kprofile") == STRINGS_EQUAL)) { /* We will read the /proc/profile file on configurable timeout */ GrabKProfile = TRUE; ++argNum; /* If the jittter is > this #, then the profile is grabbed. */ ProfileTriggerMSecs = (long) atoi(argv[argNum]); if(ProfileTriggerMSecs <= 0){ printf("Illegal value for profile trigger threshold.\n"); exit(0); } } else if ((strcmp(argv[argNum],"--siggc") == STRINGS_EQUAL)) { /* We will SIGSTOP/SIGCONT the specified pid */ SignalGCTask = TRUE; ++argNum; GCTaskPID = atoi(argv[argNum]); if(ProfileTriggerMSecs <= 0){ printf("Illegal value for JFFS(2) GC task pid.\n"); exit(0); } } else { /* Unknown argument. Print help information and exit. */ printf("Invalid option %s\n", argv[argNum]); printf("Try 'JitterTest --help' for more information.\n"); exit(0); } } } return; } /*********************************************************************** * SetFileName * This function sets the output file name. * output: N/A ***********************************************************************/ void SetFileName( char * pFileName) /* ptr to desired output file name */ { size_t fileNameLen; /* file name length (bytes) */ /* Check file name length. */ fileNameLen = strlen(pFileName); if (fileNameLen > (size_t) MAX_FILE_NAME_LEN) { printf("File name %s exceeds maximum length %d.\n", pFileName, MAX_FILE_NAME_LEN); exit(0); } /* File name length is OK so save the file name. */ strcpy(OutFileName, pFileName); return; } /*********************************************************************** * SetInterruptPeriod * This function sets the interrupt period. * output: N/A ***********************************************************************/ void SetInterruptPeriod( char * /* ptr to desired interrupt period */ pASCIIInterruptPeriodMilliSec) { long period; /* interrupt period */ period = atol(pASCIIInterruptPeriodMilliSec); if ((period < MIN_INT_PERIOD_MILLISEC) || (period > MAX_INT_PERIOD_MILLISEC)) { printf("Invalid interrupt period: %ld ms.\n", period); exit(EXIT_INV_INT_PERIOD); } else { InterruptPeriodMilliSec = period; } return; } /*********************************************************************** * SetSchedulerPriority * This function sets the desired scheduler priority. * output: N/A ***********************************************************************/ void SetSchedulerPriority( char * pASCIISchedulerPriority) /* ptr to desired scheduler priority*/ { int priority; /* desired scheduler priority value */ priority = atoi(pASCIISchedulerPriority); if ((priority < MinPriority) || (priority > MaxPriority)) { printf("Scheduler priority %d outside of range [%d, %d]\n", priority, MinPriority, MaxPriority); exit(EXIT_INV_SCHED_PRIORITY); } else { RequestedPriority = priority; RunAsRTTask = TRUE; /* We shall run as a POSIX real time task */ } return; } /*********************************************************************** * PrintVersionInfo * This function prints version information. * output: N/A ***********************************************************************/ void PrintVersionInfo(void) { printf("JitterTest version %s\n", Version); printf("Copyright (c) 2001, Daniel Industries, Inc.\n"); return; } /*********************************************************************** * PrintHelpInfo * This function prints help information. * output: N/A ***********************************************************************/ void PrintHelpInfo(void) { printf("Usage: JitterTest [options]\n"); printf(" *** Requires root privileges. ***\n"); printf("Option:\n"); printf(" [-h, --help, -?] Print this message and exit.\n"); printf(" [-v, --version] "); printf( "Print the version number of JitterTest and exit.\n"); printf(" [-f FILE, --file FILE] Set output file name to FILE. Typically you would put this on the fs under test\n"); printf(" [-c FILE, --consolefile] Set device or file to write the console log to.\n\tTypically you would set this to /dev/console and save it on another computer.\n"); printf(" [-w BYTES, --write_bytes BYTES Write BYTES to FILE each period.\n"); printf(" [-r FILE, --readfile FILE] Also read 1 byte every cycle from FILE. FILE will be created and filled with data.\n"); printf(" [-t , --time ] "); printf( "Set interrupt period to milliseconds.\n"); printf(" "); printf( "Range: [%ld, %ld] milliseconds.\n", MIN_INT_PERIOD_MILLISEC, MAX_INT_PERIOD_MILLISEC); printf(" [-p , --priority ] "); printf( "Set scheduler priority to .\n"); printf(" "); printf( "Range: [%d, %d] (higher number = higher priority)\n", MinPriority, MaxPriority); printf(" [--grab_kprofile ] Read the /proc/profile if jitter is > THRESHOLD and store in file.\n"); printf(" [--siggc ] Before writing to fs send SIGSTOP to PID. After write send SIGCONT\n"); return; } /* A common write that checks for write errors and exits. Pass it __LINE__ for lineNo */ int Write(int fd, void *buf, size_t bytes, int lineNo) { int err; err = write(fd, buf, bytes); if(err < bytes) { printf("Write Error at line %i! Wanted to write %zu bytes, but wrote only %i bytes.\n", lineNo, bytes, err); perror("Write did not complete. Error. Bye:"); /* show error from errno. */ exit(1); } return err; }/* end Write*/ mtd-utils-1.5.0/tests/jittertest/Makefile000066400000000000000000000033661175167361300204640ustar00rootroot00000000000000CC=gcc # uncomment following for performance CCFLAGS=-O3 -Wall -fomit-frame-pointer # uncomment following for debugging. Uncomment either this or the one above. Not both. # CCFLAGS=-Wall -g all: JitterTest plotJittervsFill JitterTest: JitterTest.c Makefile gcc $(CCFLAGS) -lm JitterTest.c -o JitterTest plotJittervsFill: plotJittervsFill.c Makefile gcc $(CCFLAGS) plotJittervsFill.c -o plotJittervsFill clean: rm -rf *~ rm -rf core rm -rf *.o rm -rf JitterTest dep: makedepend -I./ *.c # DO NOT DELETE JitterTest.o: /usr/include/stdio.h /usr/include/features.h JitterTest.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h JitterTest.o: /usr/include/bits/types.h /usr/include/libio.h JitterTest.o: /usr/include/_G_config.h /usr/include/bits/stdio_lim.h JitterTest.o: /usr/include/string.h /usr/include/stdlib.h JitterTest.o: /usr/include/sys/types.h /usr/include/time.h JitterTest.o: /usr/include/endian.h /usr/include/bits/endian.h JitterTest.o: /usr/include/sys/select.h /usr/include/bits/select.h JitterTest.o: /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h JitterTest.o: /usr/include/alloca.h /usr/include/sys/time.h JitterTest.o: /usr/include/bits/time.h /usr/include/signal.h JitterTest.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h JitterTest.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h JitterTest.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h JitterTest.o: /usr/include/sched.h /usr/include/bits/sched.h JitterTest.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h JitterTest.o: /usr/include/bits/confname.h /usr/include/getopt.h mtd-utils-1.5.0/tests/jittertest/README000066400000000000000000000151071175167361300177000ustar00rootroot00000000000000$Id: README,v 1.2 2001/08/10 19:23:11 vipin Exp $ This is the README file for the JitterTest (and friends) program. This program is used to measure what the jitter of a real time task would be under "standard" Linux. More particularly, what is the effect of running a real time task under Linux with background JFFS file system activity. The jitter is measured in milli seconds (ms) from the expected time of arrival of a signal from a periodic timer (set by the task) to when the task actually gets the signal. This jitter is then stored in a file specified (or the default output file "jitter.dat"). The data may also be sent out to the console by writing to /dev/console (See help options. This is highly desirable specially if you have redirected your console to the serial port and are storing it as a minicom log on another computer for later analysis using some tools provided here). This is particularly useful if you have a serial console and are outputting "interesting" info from inside some kernel task or driver. (or something as simple as a "df" program running periodically and redirecting o/p to the console). One "interesting" thing that I have measured is the effect of FLASH chip erases on the jitter of a real time task. One can do that by putting a printk at the beginning of the flash erase routine in the MTD flash chip driver. Now you will get jitter data interspersed with flash sector erase events. Other printk's can also be placed at suspected jitter causing locations in the system. EXECUTING THE PROGRAM "JitterTest" You may specify a file to be read by the program every time it wakes up (every cycle). This file is created and filled with some junk data. The purpose of this is to test the jitter of the program if it were reading from- say a JFFS (Journaling Flash File System) file system. By specifying the complete paths of the read and write (o/p) files you can test the jitter a POSIX type real time task will experience under Linux, under various conditions. These can be as follows: 1. O/P file on ram file system, no i/p file. In this case you would presumably generate other "typical" background activity for your system and examine the worst case jitter experienced by a task that is neither reading nor writing to a file system. Other cases could be: 2. O/P to ram fs, I/P from JFFS (type) fs: This is specially useful to test the proper operation of erase suspend type of operation in JFFS file systems (with an MTD layer that supports it). In this test you would generate some background write/erase type activity that would generate chip erases. Since this program is reading from the same file system, you contrast the latencies with those obtained with writes going to the same fs. 3. Both read and writes to (or just write to) JFFS file system: Here you would test for latencies experienced by a program if it were writing (and optionally also reading) from a JFFS fs. Grabing a kernel profile: This program can also conditionally grab a kernel profile. Specify --grab_kprofile on the cmd line as well as a "threshold" parameter (see help options by -?). Any jitter greater than this "threshold" will cause the program to read the /proc/profile file and dump it in a local file with increasing file numbers. It will also output the filename at that time to the console file specified. This will allow you to corelate later in time the various profiles with data in your console file and what was going on at that time. These profile files may then be later examined by running them through ksymoops. Make sure you specify "profile=2" on the kernel command line when you boot the kernel if you want to use this functionality. Signalling the JFFS[2] GC task: You can also force this program to send a SIGSTOP/SIGCONT to the JFFS (or JFFS2) gc task by specifing --siggc on the cmd line. This will let you investigate the effect of forcing the gc task to wake up and do its thing when you are not writing to the fs and to force it to sleep when you want to write to the fs. These are just various tools to investigate the possibility of achieving minimal read/write latency when using JFFS[2]. You need to manually do a "ps aux" and look up the PID of the gc thread and provide it to the program. EXECUTING THE PROGRAM "plotJittervsFill" This program is a post processing tool that will extract the jitter times as printed by the JitterTest program in its console log file as well as the data printed by the "df" command. This "df" data happens to be in the console log because you will run the shell file fillJffs2.sh on a console when you are doing your jitter test. This shell script copies a specified file to another specified file every programmable seconds. It also does a "df" and redirects output to /dev/console where it is mixed with the output from JitterTest. All this console data is stored on another computer, as all this data is being outputted to the serial port as you have redirected the console to the serial port (that is the only guaranteed way to not loose any console log or printk data). You can then run this saved console log through this program and it will output a very nice text file with the %fill in one col and corrosponding jitter values in the second. gnuplot then does a beautifull plot of this resulting file showing you graphically the jitters encountered at different fill % of your JFFS[2] fs. OTHER COMMENTS: Use the "-w BYTES" cmd line parameter to simulate your test case. Not everyone has the same requirements. Someone may want to measure the jitter of JFFS2 with 500 bytes being written every 500ms. Others may want to measure the system performance writing 2048 bytes every 5 seconds. RUNNING MULTIPLE INSTANCES: Things get real interesting when you run multiple instances of this program *at the same time*. You could have one version running as a real time task (by specifing the priority with the -p cmd line parameter), not interacting with any fs or at the very least not reading and writing to JFFS[2]. At the same time you could have another version running as a regular task (by not specifing any priority) but reading and writing to JFFS[2]. This way you can easily measure the blocking performance of the real time task while another non-real time task interacts with JFFS[2] in the back ground. You get the idea. WATCH OUT! Be particularly careful of running this program as a real time task AND writing to JFFS[2]. Any blocks will cause your whole system to block till any garbage collect initiated by writes by this task complete. I have measured these blocks to be of the order of 40-50 seconds on a reasonably powerful 32 bit embedded system. mtd-utils-1.5.0/tests/jittertest/filljffs2.sh000066400000000000000000000003721175167361300212330ustar00rootroot00000000000000#!/bin/bash # Pass following cmd line: # 1st - file to copy # 2nd - file to copy to # 3rd - time to sleep between copies while [ $(( 1 )) -gt $(( 0 )) ] do cp $1 $2 rm $2 df |grep mtd > /dev/console echo "sleeping $3" sleep $3 done mtd-utils-1.5.0/tests/jittertest/plotJittervsFill.c000066400000000000000000000240331175167361300225020ustar00rootroot00000000000000/* *********************************************************************** * * Copyright: Daniel Measurement and Control, Inc. * 9753 Pine Lake Drive * Houston, TX 77055 * * Created by: Vipin Malik * Released under GPL by permission of Daniel Industries. * * This software is licensed under the GPL version 2. Plese see the file * COPYING for details on the license. * * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose * are made in this software. Please use at your own risk. * File: plotJittervsFill.c By: Vipin Malik About: This program reads in a jitter log file as created by the JitterTest.c program and extracts all the jitters in the file that are greater than a threshold specified as a parameter on the cmd line. It also extracts the amount of disk space at (form the "df" out that should also be present in the log file) after the jitter extracted. It writes the data to the stderr (where you may redirect it). It is suitable for plotting, as the data is written as COL1=UsedSpace COL2=Jitter $Id: plotJittervsFill.c,v 1.6 2005/11/07 11:15:21 gleixner Exp $ $Log: plotJittervsFill.c,v $ Revision 1.6 2005/11/07 11:15:21 gleixner [MTD / JFFS2] Clean up trailing white spaces Revision 1.5 2001/08/10 19:23:11 vipin Ready to be released under GPL! Added proper headers etc. Revision 1.4 2001/07/02 22:25:40 vipin Fixed couple of minor cosmetic typos. Revision 1.3 2001/07/02 14:46:46 vipin Added a debug option where it o/p's line numbers to debug funky values. Revision 1.2 2001/06/26 19:48:57 vipin Now prints out jitter values found at end of log file, after which no new "df" disk usage values were encountered. The last "df" disk usage encountered is printed for these orphaned values. Revision 1.1 2001/06/25 19:13:55 vipin Added new file- plotJittervsFill.c- that mines the data log file outputed from the fillFlash.sh script file and JitterTest.c file and produces output suitable to be plotted. This output plot may be examined to see visually the relationship of the Jitter vs disk usage of the fs under test. */ #include #include #include #include #include static char Version_string[] = "$Id: plotJittervsFill.c,v 1.6 2005/11/07 11:15:21 gleixner Exp $"; static char LogFile[250] = "InputLogFile.log"; static int JitterThreshold_ms = 1000; static int Debug = 0; /* Debug level. Each "-d" on the cmd line increases the level */ #define TRUE 1 #define FALSE 0 #define MIN_JITTER_THRESHOLD 1 /* ms minimum jitter threshold */ void PrintHelpInfo(void) { printf("Usage: plotJittervsFill [options] -f [--file] -t [--jitter_threshold] \n"); printf("[options]:\n-v [--version] Print version and exit\n"); printf("-d Debug. Prints input file line number for each data point picked up.\n"); printf("-h [--help] [-?] Print this help screen and exit.\n"); } /*********************************************************************** * HandleCmdLineArgs * This function handles the command line arguments. * output: stack size ***********************************************************************/ void HandleCmdLineArgs( int argc, /* number of command-line arguments */ char *argv[]) /* ptrs to command-line arguments */ { int argNum; /* argument number */ if (argc > (int) 1) { for (argNum = (int) 1; argNum < argc; argNum++) { /* The command line contains an argument. */ if ((strcmp(argv[argNum],"--version") == 0) || (strcmp(argv[argNum],"-v") == 0)) { /* Print version information and exit. */ printf("%s\n", Version_string); exit(0); } else if ((strcmp(argv[argNum],"--help") == 0) || (strcmp(argv[argNum],"-h") == 0) || (strcmp(argv[argNum],"-?") == 0)) { /* Print help information and exit. */ PrintHelpInfo(); exit(0); } else if ((strcmp(argv[argNum],"--file") == 0) || (strcmp(argv[argNum],"-f") == 0)) { /* Set the name of the output file. */ ++argNum; if (argNum < argc) { strncpy(LogFile, argv[argNum], sizeof(LogFile)); } else { printf("*** Input file name not specified. ***\n"); exit(0); } } else if ((strcmp(argv[argNum],"--jitter_threshold") == 0) || (strcmp(argv[argNum],"-t") == 0)) { /* Set the file to read*/ ++argNum; JitterThreshold_ms = atoi(argv[argNum]); if(JitterThreshold_ms < MIN_JITTER_THRESHOLD) { printf("A jitter threshold less than %i ms is not allowed. Bye.\n", MIN_JITTER_THRESHOLD); exit(0); } } else if ((strcmp(argv[argNum],"-d") == 0)) { /* Increment debug level */ Debug++; } else { /* Unknown argument. Print help information and exit. */ printf("Invalid option %s\n", argv[argNum]); printf("Try 'plotJittervsFill --help' for more information.\n"); exit(0); } } } return; } int main( int argc, char *argv[]) { char lineBuf[1024]; /* how long a single line be? */ int converted; int lineNo = 0; int cnt; FILE *fp; int junkInt1, junkInt2, junkInt3; float junkFloat1; float jitter_ms; #define MAX_SAVE_BUFFER 1000 /* How many values will be picked up while searching for a % disk full line (i.e. before they can be printed out) */ int saveJitter[MAX_SAVE_BUFFER]; /* lets us record multiple jitter values that exceed our threshold till we find a "df" field- which is when we can o/p all these values. */ int dataLineNo[MAX_SAVE_BUFFER]; /* The saved line #'s for the above. Printed if debug specified. */ int saveJitterCnt = 0; int lookFor_df = FALSE; int dfPercent = -1; /* will be >= 0 if at least one found. The init value is a flag. */ char junkStr1[500], junkStr2[500]; HandleCmdLineArgs(argc, argv); if((fp = fopen(LogFile, "r")) == NULL) { printf("Unable to open input log file %s for read.\b", LogFile); perror("Error:"); exit(1); } while(fgets(lineBuf, sizeof(lineBuf), fp) != NULL) { lineNo++; /* Are we looking for a "df" o/p line? (to see how full the flash is?)*/ /* is there a "%" in this line? */ if((strstr(lineBuf, "%") != NULL) && (lookFor_df)) { converted = sscanf(lineBuf, "%s %i %i %i %i\n", junkStr1, &junkInt1, &junkInt2, &junkInt3, &dfPercent); if(converted < 5) { printf("Line %i contains \"%%\", but expected fields not found. Skipping.\n", lineNo); }else { /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */ for(cnt = 0; cnt < saveJitterCnt; cnt++) { if(Debug) { fprintf(stderr, "%i\t%i\t%i\n", (int)dataLineNo[cnt], dfPercent, (int)saveJitter[cnt]); }else { fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]); } } saveJitterCnt = 0; /* all flushed. Reset for next saves. */ lookFor_df = FALSE; } } /* is there a "ms" in this line?*/ if(strstr(lineBuf, "ms") == NULL) { continue; } /* grab the ms jitter value */ converted = sscanf(lineBuf, "%f %s %f %s\n", &junkFloat1, junkStr1, &jitter_ms, junkStr2); if(converted < 4) { printf("Line %i contains \"ms\", but expected fields not found. Converted %i, Skipping.", lineNo, converted); printf("1=%i, 2=%s.\n", junkInt1, junkStr1); continue; /* not our jitter line*/ } /* Is the jitter value > threshold value? */ if(abs(jitter_ms) > JitterThreshold_ms) { /* Found a jitter line that matches our crietrion. Now set flag to be on the look out for the next "df" output so that we can see how full the flash is. */ if(saveJitterCnt < MAX_SAVE_BUFFER) { saveJitter[saveJitterCnt] = (int)abs(jitter_ms); /* why keep the (ms) jitter in float */ dataLineNo[saveJitterCnt] = lineNo; saveJitterCnt++; lookFor_df = TRUE; } else { printf("Oops! I've run out of buffer space before I found a %% use line. Dropping itter value. Increase MAX_SAVE_BUFFER and recompile.\n"); } } } /* Now print out any saved jitter values that were not printed out because we did not find and "df" after these were picked up. Only print if a "df" disk usage was ever found. */ if(lookFor_df && (dfPercent >= 0)) { /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */ for(cnt = 0; cnt < saveJitterCnt; cnt++) { fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]); } } return 0; }/* end main() */ mtd-utils-1.5.0/tests/ubi-tests/000077500000000000000000000000001175167361300165325ustar00rootroot00000000000000mtd-utils-1.5.0/tests/ubi-tests/.gitignore000066400000000000000000000001461175167361300205230ustar00rootroot00000000000000/integ /io_basic /io_paral /io_read /io_update /mkvol_bad /mkvol_basic /mkvol_paral /rsvol /volrefcnt mtd-utils-1.5.0/tests/ubi-tests/Makefile000066400000000000000000000012761175167361300202000ustar00rootroot00000000000000LIBUBI_PATH = ../../ubi-utils/ LIBUBI_HEADER_PATH = $(LIBUBI_PATH)/include UBIUTILS_PATH=../../ubi-utils/ KERNELHDR := ../../include LIBS = libubi TARGETS=io_update volrefcnt integ io_paral io_read io_basic \ mkvol_basic mkvol_bad mkvol_paral rsvol CFLAGS += -I$(LIBUBI_HEADER_PATH) -I $(KERNELHDR) -lpthread include ../../common.mk # Compile ubilib with the udevsettle hack libubi.a: $(LIBUBI_PATH)/libubi.c $(LIBUBI_HEADER_PATH)/libubi.h $(LIBUBI_PATH)/libubi_int.h $(CC) $(CFLAGS) -I $(LIBUBI_PATH) -I../../include -DUDEV_SETTLE_HACK -c $(LIBUBI_PATH)/libubi.c -o libubi.o ar cr libubi.a libubi.o $(TARGETS): $(addprefix $(BUILDDIR)/, common.o) libubi.a clean:: rm -f libubi.a mtd-utils-1.5.0/tests/ubi-tests/README.udev000066400000000000000000000023451175167361300203600ustar00rootroot00000000000000There is a problem with udev: when a volume is created, there is a delay before corresponding /dev/ubiX_Y device node is created by udev, so some tests fail because of this. The symptom is error messages like "cannot open /dev/ubi0_0". One possible solution of this problem is to pre-create UBI device and volume nodes. There is even a script which may be used for this in ubi-utils/scripts/. But this is not enough because udev will still remove and re-create the nodes and tests will still fail. So you need to stop removing device nodes using the following udev rule: KERNEL=="ubi*_*", ACTION=="remove", OPTIONS+="ignore_device" In our Ubuntu distribution we put that to new file: /etc/udev/rules.d/50-local.rules Another possibility is to call udevsettle utility in libubi after the volume has been created See src/libubi.c - the call is compiled in only if UDEV_SETTLE_HACK is defined. This is anyway an ugly hack, but works, although makes the tests slower. Suggestions are welcome. So, if you have udevsettel unility in your system, you do not have to do anyting, and the tests should work, because we compile libubi with UDEV_SETTLE_HACK. Otherwise, you should remove -D UDEV_SETTLE_HACK from the makefile and pre-create UBI device nodes. mtd-utils-1.5.0/tests/ubi-tests/common.c000066400000000000000000000213561175167361300201750ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * The stuff which is common for many tests. */ #include #include #include #include #include #include #include #include #include #include #include #include "libubi.h" #include "common.h" /** * __initial_check - check that common prerequisites which are required to run * tests. * * @test test name * @argc count of command-line arguments * @argv command-line arguments * * This function returns %0 if all is fine and test may be run and %-1 if not. */ int __initial_check(const char *test, int argc, char * const argv[]) { libubi_t libubi; struct ubi_dev_info dev_info; /* * All tests require UBI character device name as the first parameter, * check this. */ if (argc < 2) { __errmsg(test, __func__, __LINE__, "UBI character device node is not specified"); return -1; } libubi = libubi_open(); if (libubi == NULL) { __failed(test, __func__, __LINE__, "libubi_open"); return -1; } if (ubi_get_dev_info(libubi, argv[1], &dev_info)) { __failed(test, __func__, __LINE__, "ubi_get_dev_info"); goto close; } if (dev_info.avail_lebs < MIN_AVAIL_EBS) { __errmsg(test, __func__, __LINE__, "insufficient available eraseblocks %d on UBI " "device, required %d", dev_info.avail_lebs, MIN_AVAIL_EBS); goto close; } if (dev_info.vol_count != 0) { __errmsg(test, __func__, __LINE__, "device %s is not empty", argv[1]); goto close; } libubi_close(libubi); return 0; close: libubi_close(libubi); return -1; } /** * __errmsg - print a message to stderr. * * @test test name * @func function name * @line line number * @fmt format string */ void __errmsg(const char *test, const char *func, int line, const char *fmt, ...) { va_list args; fprintf(stderr, "[%s] %s():%d: ", test, func, line); va_start(args, fmt); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); } /** * __failed - print function fail message. * * @test test name * @func calling function name * @line line number * @failed failed function name */ void __failed(const char *test, const char *func, int line, const char *failed) { fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n", test, func, line, failed, errno, strerror(errno)); } /** * __check_volume - check volume information. * * @libubi libubi descriptor * @dev_info UBI device description * @test test name * @func function name * @line line number * @vol_id ID of existing volume to check * @req volume creation request to compare with * * This function checks if a volume created using @req request has exactly the * requested characteristics. Returns 0 in case of success and %-1 in case of * error. */ int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, const char *test, const char *func, int line, int vol_id, const struct ubi_mkvol_request *req) { int ret; struct ubi_vol_info vol_info; int leb_size; long long rsvd_bytes; ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info); if (ret) { __failed(test, func, line, "ubi_get_vol_info"); return -1; } if (req->alignment != vol_info.alignment) { __errmsg(test, func, line, "bad alignment: requested %d, got %d", req->alignment, vol_info.alignment); return -1; } if (req->vol_type != vol_info.type) { __errmsg(test, func, line, "bad type: requested %d, got %d", req->vol_type, vol_info.type); return -1; } if (strlen(req->name) != strlen(vol_info.name) || strcmp(req->name, vol_info.name) != 0) { __errmsg(test, func, line, "bad name: requested \"%s\", got \"%s\"", req->name, vol_info.name); return -1; } if (vol_info.corrupted) { __errmsg(test, func, line, "corrupted new volume"); return -1; } leb_size = dev_info->leb_size - (dev_info->leb_size % req->alignment); if (leb_size != vol_info.leb_size) { __errmsg(test, func, line, "bad usable LEB size %d, should be %d", vol_info.leb_size, leb_size); return -1; } rsvd_bytes = req->bytes; if (rsvd_bytes % leb_size) rsvd_bytes += leb_size - (rsvd_bytes % leb_size); if (rsvd_bytes != vol_info.rsvd_bytes) { __errmsg(test, func, line, "bad reserved bytes %lld, should be %lld", vol_info.rsvd_bytes, rsvd_bytes); return -1; } return 0; } /** * __check_vol_patt - check that volume contains certain data * * @libubi libubi descriptor * @test test name * @func function name * @line line number * @node volume character device node * @byte data pattern to check * * This function returns %0 if the volume contains only @byte bytes, and %-1 if * not. */ int __check_vol_patt(libubi_t libubi, const char *test, const char *func, int line, const char *node, uint8_t byte) { int ret, fd; long long bytes = 0; struct ubi_vol_info vol_info; unsigned char buf[512]; fd = open(node, O_RDONLY); if (fd == -1) { __failed(test, func, line, "open"); __errmsg(test, func, line, "cannot open \"%s\"\n", node); return -1; } ret = ubi_get_vol_info(libubi, node, &vol_info); if (ret) { __failed(test, func, line, "ubi_get_vol_info"); goto close; } while (bytes < vol_info.data_bytes) { int i; memset(buf, ~byte, 512); ret = read(fd, buf, 512); if (ret == -1) { __failed(test, func, line, "read"); __errmsg(test, func, line, "bytes = %lld, ret = %d", bytes, ret); goto close; } if (ret == 0 && bytes + ret < vol_info.data_bytes) { __errmsg(test, func, line, "EOF, but read only %lld bytes of %lld", bytes + ret, vol_info.data_bytes); goto close; } for (i = 0; i < ret; i++) if (buf[i] != byte) { __errmsg(test, func, line, "byte at %lld is not %#x but %#x", bytes + i, byte, (int)buf[i]); goto close; } bytes += ret; } close(fd); return 0; close: close(fd); return -1; } /** * __update_vol_patt - update volume using a certain byte pattern * * @libubi libubi descriptor * @dev_info UBI device description * @test test name * @func function name * @line line number * @node volume character device node * @byte data pattern to check * * This function returns %0 in case of success, and %-1 if in case of failure. */ int __update_vol_patt(libubi_t libubi, const char *test, const char *func, int line, const char *node, long long bytes, uint8_t byte) { int ret, fd; long long written = 0; unsigned char buf[512]; fd = open(node, O_RDWR); if (fd == -1) { __failed(test, func, line, "open"); __errmsg(test, func, line, "cannot open \"%s\"\n", node); return -1; } if (ubi_update_start(libubi, fd, bytes)) { __failed(test, func, line, "ubi_update_start"); __errmsg(test, func, line, "bytes = %lld", bytes); goto close; } memset(buf, byte, 512); while (written != bytes) { ret = write(fd, buf, 512); if (ret == -1) { __failed(test, func, line, "write"); __errmsg(test, func, line, "written = %lld, ret = %d", written, ret); goto close; } written += ret; if (written > bytes) { __errmsg(test, func, line, "update length %lld bytes, " "but %lld bytes are already written", bytes, written); goto close; } } close(fd); return 0; close: close(fd); return -1; } /** * seed_random_generator - randomly seed the standard pseudo-random generator. * * This helper function seeds the standard libc pseudo-random generator with a * more or less random value to make sure the 'rand()' call does not return the * same sequence every time UBI utilities run. Returns the random seed in case * of success and a %-1 in case of error. */ int seed_random_generator(void) { struct timeval tv; struct timezone tz; int seed; /* * Just assume that a combination of the PID + current time is a * reasonably random number. */ if (gettimeofday(&tv, &tz)) return -1; seed = (unsigned int)tv.tv_sec; seed += (unsigned int)tv.tv_usec; seed *= getpid(); seed %= INT_MAX; srand(seed); return seed; } mtd-utils-1.5.0/tests/ubi-tests/common.h000066400000000000000000000112471175167361300202000ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * The stuff which is common for many tests. */ #ifndef __COMMON_H__ #define __COMMON_H__ #include #include #ifdef __cplusplus extern "C" { #endif #define UBI_VOLUME_PATTERN "/dev/ubi%d_%d" #define MIN_AVAIL_EBS 5 #define PAGE_SIZE 4096 #define min(a, b) ((a) < (b) ? (a) : (b)) /* Normal messages */ #define normsg(fmt, ...) do { \ printf(TESTNAME ": " fmt "\n", ##__VA_ARGS__); \ } while(0) #define errmsg(fmt, ...) ({ \ __errmsg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); \ -1; \ }) #define failed(name) ({ \ __failed(TESTNAME, __FUNCTION__, __LINE__, name); \ -1; \ }) #define initial_check(argc, argv) \ __initial_check(TESTNAME, argc, argv) #define check_volume(vol_id, req) \ __check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__, \ __LINE__, vol_id, req) #define check_vol_patt(node, byte) \ __check_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, node, byte) #define update_vol_patt(node, bytes, byte) \ __update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, \ node, bytes, byte) #define check_failed(ret, error, func, fmt, ...) ({ \ int __ret; \ \ if (!ret) { \ errmsg("%s() returned success but should have failed", func); \ errmsg(fmt, ##__VA_ARGS__); \ __ret = -1; \ } \ if (errno != (error)) { \ errmsg("%s failed with error %d (%s), expected %d (%s)", \ func, errno, strerror(errno), error, strerror(error)); \ errmsg(fmt, ##__VA_ARGS__); \ __ret = -1; \ } \ __ret = 0; \ }) /* Alignments to test, @s is eraseblock size */ #define ALIGNMENTS(s) \ {3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1, \ (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2, \ (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3, \ (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9, \ (s)-8, (s)-6, (s)-4, (s)-1, (s)}; extern int seed_random_generator(void); extern void __errmsg(const char *test, const char *func, int line, const char *fmt, ...); extern void __failed(const char *test, const char *func, int line, const char *failed); extern int __initial_check(const char *test, int argc, char * const argv[]); extern int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, const char *test, const char *func, int line, int vol_id, const struct ubi_mkvol_request *req); extern int __check_vol_patt(libubi_t libubi, const char *test, const char *func, int line, const char *node, uint8_t byte); extern int __update_vol_patt(libubi_t libubi, const char *test, const char *func, int line, const char *node, long long bytes, uint8_t byte); #ifdef __cplusplus } #endif #endif /* !__COMMON_H__ */ mtd-utils-1.5.0/tests/ubi-tests/integ.c000066400000000000000000000452371175167361300200170ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "libubi.h" #include "common.h" struct erase_block_info; struct volume_info; struct ubi_device_info; struct write_info { struct write_info *next; struct erase_block_info *erase_block; int offset_within_block; /* Offset within erase block */ off64_t offset; /* Offset within volume */ int size; int random_seed; }; struct erase_block_info { struct volume_info *volume; int block_number; off64_t offset; /* Offset within volume */ off64_t top_of_data; int touched; /* Have we done anything at all with this erase block */ int erased; /* This erased block is currently erased */ struct write_info *writes; }; struct volume_fd { struct volume_fd *next; struct volume_info *volume; int fd; }; struct volume_info { struct volume_info *next; struct ubi_device_info *ubi_device; struct volume_fd *fds; struct erase_block_info *erase_blocks; const char *device_file_name; struct ubi_vol_info info; }; struct ubi_device_info { struct volume_info *volumes; const char *device_file_name; struct ubi_dev_info info; }; struct open_volume_fd { struct open_volume_fd *next; struct volume_fd *vol_fd; }; #define MAX_UBI_DEVICES 64 static libubi_t libubi; static struct ubi_info info; static struct ubi_device_info ubi_array[MAX_UBI_DEVICES]; static uint64_t total_written = 0; static uint64_t total_space = 0; static struct open_volume_fd *open_volumes; static int open_volume_count = 0; static const char *ubi_module_load_string; static unsigned char *write_buffer = NULL; static unsigned char *read_buffer = NULL; static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */ static unsigned long next_seed = 1; static unsigned get_next_seed() { next_seed = next_seed * 1103515245 + 12345; return ((unsigned) (next_seed / 65536) % 32768); } static void error_exit(const char *msg) { int eno = errno; fprintf(stderr,"UBI Integrity Test Error: %s\n",msg); if (eno) { fprintf(stderr, "errno = %d\n", eno); fprintf(stderr, "strerror = %s\n", strerror(eno)); } exit(1); } static void *allocate(size_t n) { void *p = malloc(n); if (!p) error_exit("Memory allocation failure"); memset(p, 0, n); return p; } static unsigned get_random_number(unsigned n) { uint64_t r, b; if (n < 1) return 0; r = rand(); r *= n; b = RAND_MAX; b += 1; r /= b; return r; } static struct volume_fd *open_volume(struct volume_info *vol) { struct volume_fd *s; struct open_volume_fd *ofd; int fd; if (vol->fds) { /* If already open dup it */ fd = dup(vol->fds->fd); if (fd == -1) error_exit("Failed to dup volume device file des"); } else { fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE); if (fd == -1) error_exit("Failed to open volume device file"); } s = allocate(sizeof(*s)); s->fd = fd; s->volume = vol; s->next = vol->fds; vol->fds = s; /* Add to open volumes list */ ofd = allocate(sizeof(*ofd)); ofd->vol_fd = s; ofd->next = open_volumes; open_volumes = ofd; open_volume_count += 1; return 0; } static void close_volume(struct volume_fd *vol_fd) { struct volume_fd *vfd, *vfd_last; struct open_volume_fd *ofd, *ofd_last; int fd = vol_fd->fd; /* Remove from open volumes list */ ofd_last = NULL; ofd = open_volumes; while (ofd) { if (ofd->vol_fd == vol_fd) { if (ofd_last) ofd_last->next = ofd->next; else open_volumes = ofd->next; free(ofd); open_volume_count -= 1; break; } ofd_last = ofd; ofd = ofd->next; } /* Remove from volume fd list */ vfd_last = NULL; vfd = vol_fd->volume->fds; while (vfd) { if (vfd == vol_fd) { if (vfd_last) vfd_last->next = vfd->next; else vol_fd->volume->fds = vfd->next; free(vfd); break; } vfd_last = vfd; vfd = vfd->next; } /* Close volume device file */ if (close(fd) == -1) error_exit("Failed to close volume file descriptor"); } static void set_random_data(unsigned seed, unsigned char *buf, int size) { int i; unsigned r; r = rand(); srand(seed); for (i = 0; i < size; ++i) buf[i] = rand(); srand(r); } static void check_erase_block(struct erase_block_info *erase_block, int fd) { struct write_info *w; off64_t gap_end; int leb_size = erase_block->volume->info.leb_size; ssize_t bytes_read; w = erase_block->writes; gap_end = erase_block->offset + leb_size; while (w) { if (w->offset + w->size < gap_end) { /* There is a gap. Check all 0xff */ off64_t gap_start = w->offset + w->size; ssize_t size = gap_end - gap_start; if (lseek64(fd, gap_start, SEEK_SET) != gap_start) error_exit("lseek64 failed"); memset(read_buffer, 0 , size); errno = 0; bytes_read = read(fd, read_buffer, size); if (bytes_read != size) error_exit("read failed in gap"); while (size) if (read_buffer[--size] != 0xff) { fprintf(stderr, "block no. = %d\n" , erase_block->block_number); fprintf(stderr, "offset = %lld\n" , (long long) gap_start); fprintf(stderr, "size = %ld\n" , (long) bytes_read); error_exit("verify 0xff failed"); } } if (lseek64(fd, w->offset, SEEK_SET) != w->offset) error_exit("lseek64 failed"); memset(read_buffer, 0 , w->size); errno = 0; bytes_read = read(fd, read_buffer, w->size); if (bytes_read != w->size) { fprintf(stderr, "offset = %lld\n" , (long long) w->offset); fprintf(stderr, "size = %ld\n" , (long) w->size); fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read); error_exit("read failed"); } set_random_data(w->random_seed, write_buffer, w->size); if (memcmp(read_buffer, write_buffer, w->size)) error_exit("verify failed"); gap_end = w->offset; w = w->next; } if (gap_end > erase_block->offset) { /* Check all 0xff */ off64_t gap_start = erase_block->offset; ssize_t size = gap_end - gap_start; if (lseek64(fd, gap_start, SEEK_SET) != gap_start) error_exit("lseek64 failed"); memset(read_buffer, 0 , size); errno = 0; bytes_read = read(fd, read_buffer, size); if (bytes_read != size) error_exit("read failed in gap"); while (size) if (read_buffer[--size] != 0xff) { fprintf(stderr, "block no. = %d\n" , erase_block->block_number); fprintf(stderr, "offset = %lld\n" , (long long) gap_start); fprintf(stderr, "size = %ld\n" , (long) bytes_read); error_exit("verify 0xff failed!"); } } } static int write_to_erase_block(struct erase_block_info *erase_block, int fd) { int page_size = erase_block->volume->ubi_device->info.min_io_size; int leb_size = erase_block->volume->info.leb_size; int next_offset = 0; int space, size; off64_t offset; unsigned seed; struct write_info *w; if (erase_block->writes) next_offset = erase_block->writes->offset_within_block + erase_block->writes->size; space = leb_size - next_offset; if (space <= 0) return 0; /* No space */ if (!get_random_number(10)) { /* 1 time in 10 leave a gap */ next_offset += get_random_number(space); next_offset = (next_offset / page_size) * page_size; space = leb_size - next_offset; } if (get_random_number(2)) size = 1 * page_size; else if (get_random_number(2)) size = 2 * page_size; else if (get_random_number(2)) size = 3 * page_size; else if (get_random_number(2)) size = 4 * page_size; else { if (get_random_number(4)) size = get_random_number(space); else size = space; size = (size / page_size) * page_size; } if (size == 0 || size > space) size = page_size; if (next_offset + size > leb_size) error_exit("internal error"); offset = erase_block->offset + next_offset; if (offset < erase_block->top_of_data) error_exit("internal error!"); if (lseek64(fd, offset, SEEK_SET) != offset) error_exit("lseek64 failed"); /* Do write */ seed = get_next_seed(); if (!seed) seed = 1; set_random_data(seed, write_buffer, size); if (write(fd, write_buffer, size) != size) error_exit("write failed"); erase_block->top_of_data = offset + size; /* Make write info and add to eb */ w = allocate(sizeof(*w)); w->offset_within_block = next_offset; w->offset = offset; w->size = size; w->random_seed = seed; w->next = erase_block->writes; erase_block->writes = w; erase_block->touched = 1; erase_block->erased = 0; total_written += size; return 1; } static void erase_erase_block(struct erase_block_info *erase_block, int fd) { struct write_info *w; uint32_t eb_no; int res; eb_no = erase_block->block_number; res = ioctl(fd, UBI_IOCEBER, &eb_no); if (res) error_exit("Failed to erase an erase block"); /* Remove writes from this eb */ while (erase_block->writes) { w = erase_block->writes; erase_block->writes = erase_block->writes->next; free(w); } erase_block->erased = 1; erase_block->touched = 1; erase_block->top_of_data = erase_block->offset; } static void operate_on_erase_block(struct erase_block_info *erase_block, int fd) { /* Possible operations: read from it and verify write to it erase it */ int work_done = 1; static int no_work_done_count = 0; if (!get_random_number(10) && no_work_done_count <= 5) { check_erase_block(erase_block, fd); work_done = 0; } else if (get_random_number(100)) { if (!write_to_erase_block(erase_block, fd)) { /* The erase block was full */ if (get_random_number(2) || no_work_done_count > 5) erase_erase_block(erase_block, fd); else work_done = 0; } } else erase_erase_block(erase_block, fd); if (work_done) no_work_done_count = 0; else no_work_done_count += 1; } static void operate_on_open_volume(struct volume_fd *vol_fd) { /* Possible operations: operate on an erase block close volume */ if (get_random_number(100) == 0) close_volume(vol_fd); else { /* Pick an erase block at random */ int eb_no = get_random_number(vol_fd->volume->info.rsvd_lebs); operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd); } } static void operate_on_volume(struct volume_info *vol) { /* Possible operations: open it resize it (must close fd's first) <- TODO delete it (must close fd's first) <- TODO */ open_volume(vol); } static int ubi_major(const char *device_file_name) { struct stat buf; static int maj = 0; if (maj) return maj; if (stat(device_file_name, &buf) == -1) error_exit("Failed to stat ubi device file"); maj = major(buf.st_rdev); return maj; } static void operate_on_ubi_device(struct ubi_device_info *ubi_device) { /* TODO: Possible operations: create a new volume operate on existing volume */ /* Simplified operation (i.e. only have 1 volume): If there are no volumes create 1 volumne Then operate on the volume */ if (ubi_device->info.vol_count == 0) { /* Create the one-and-only volume we will use */ char dev_name[1024]; int i, n, maj, fd; struct volume_info *s; struct ubi_mkvol_request req; req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; /* TODO: What is this? */ req.bytes = ubi_device->info.leb_size * max_ebs_per_vol; if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes) req.bytes = ubi_device->info.avail_bytes; req.vol_type = UBI_DYNAMIC_VOLUME; req.name = "integ-test-vol"; if (ubi_mkvol(libubi, ubi_device->device_file_name, &req)) error_exit("ubi_mkvol failed"); s = allocate(sizeof(*s)); s->ubi_device = ubi_device; if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info)) error_exit("ubi_get_vol_info failed"); n = s->info.rsvd_lebs; s->erase_blocks = allocate(sizeof(struct erase_block_info) * n); for (i = 0; i < n; ++i) { s->erase_blocks[i].volume = s; s->erase_blocks[i].block_number = i; s->erase_blocks[i].offset = i * (off64_t) s->info.leb_size; s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset; } /* FIXME: Correctly get device file name */ sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id); s->device_file_name = strdup(dev_name); ubi_device->volumes = s; ubi_device->info.vol_count += 1; sleep(1); fd = open(s->device_file_name, O_RDONLY); if (fd == -1) { /* FIXME: Correctly make node */ maj = ubi_major(ubi_device->device_file_name); sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1); system(dev_name); } else if (close(fd) == -1) error_exit("Failed to close volume device file"); } operate_on_volume(ubi_device->volumes); } static void do_an_operation(void) { int too_few = (open_volume_count < info.dev_count * 3); int too_many = (open_volume_count > info.dev_count * 5); if (too_many || (!too_few && get_random_number(1000) > 0)) { /* Operate on an open volume */ size_t pos; struct open_volume_fd *ofd; pos = get_random_number(open_volume_count); for (ofd = open_volumes; pos && ofd && ofd->next; --pos) ofd = ofd->next; operate_on_open_volume(ofd->vol_fd); } else if (info.dev_count > 0) { /* Operate on a ubi device */ size_t ubi_pos = 0; if (info.dev_count > 1) ubi_pos = get_random_number(info.dev_count - 1); operate_on_ubi_device(&ubi_array[ubi_pos]); } else error_exit("Internal error"); } static void get_ubi_devices_info(void) { int i, ubi_pos = 0; char dev_name[1024]; ssize_t buf_size = 1024 * 128; if (ubi_get_info(libubi, &info)) error_exit("ubi_get_info failed"); if (info.dev_count > MAX_UBI_DEVICES) error_exit("Too many ubi devices"); for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) { struct ubi_device_info *s; s = &ubi_array[ubi_pos++]; if (ubi_get_dev_info1(libubi, i, &s->info)) error_exit("ubi_get_dev_info1 failed"); if (s->info.vol_count) error_exit("There are existing volumes"); /* FIXME: Correctly get device file name */ sprintf(dev_name, "/dev/ubi%d", i); s->device_file_name = strdup(dev_name); if (buf_size < s->info.leb_size) buf_size = s->info.leb_size; if (max_ebs_per_vol && s->info.leb_size * max_ebs_per_vol < s->info.avail_bytes) total_space += s->info.leb_size * max_ebs_per_vol; else total_space += s->info.avail_bytes; } write_buffer = allocate(buf_size); read_buffer = allocate(buf_size); } static void load_ubi(void) { system("rmmod ubi"); if (system(ubi_module_load_string) != 0) error_exit("Failed to load UBI module"); sleep(1); } static void do_some_operations(void) { unsigned i = 0; total_written = 0; printf("Total space: %llu\n", (unsigned long long) total_space); while (total_written < total_space * 3) { do_an_operation(); if (i++ % 10000 == 0) printf("Total written: %llu\n", (unsigned long long) total_written); } printf("Total written: %llu\n", (unsigned long long) total_written); } static void reload_ubi(void) { /* Remove module */ if (system("rmmod ubi") != 0) error_exit("Failed to remove UBI module"); /* Install module */ if (system(ubi_module_load_string) != 0) error_exit("Failed to load UBI module"); sleep(1); } static void integ_check_volume(struct volume_info *vol) { struct erase_block_info *eb = vol->erase_blocks; int pos; int fd; fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE); if (fd == -1) error_exit("Failed to open volume device file"); for (pos = 0; pos < vol->info.rsvd_lebs; ++pos) check_erase_block(eb++, fd); if (close(fd) == -1) error_exit("Failed to close volume device file"); } static void check_ubi_device(struct ubi_device_info *ubi_device) { struct volume_info *vol; vol = ubi_device->volumes; while (vol) { integ_check_volume(vol); vol = vol->next; } } static void check_ubi(void) { int i; for (i = 0; i < info.dev_count; ++i) check_ubi_device(&ubi_array[i]); } static int is_all_digits(const char *s) { const char *digits = "0123456789"; if (!s || !*s) return 0; for (;*s;++s) if (!strchr(digits,*s)) return 0; return 1; } static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[]) { const char *p = NULL; int i = *pos; size_t n = strlen(name); if (strlen(argv[i]) > n) p = argv[i] + n; else if (++i < argc) p = argv[i]; if (!is_all_digits(p)) return 1; *result = atoll(p); *pos = i; return 0; } static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[]) { const char *p = NULL; int i = *pos; size_t n = strlen(name); if (strlen(argv[i]) > n) p = argv[i] + n; else if (++i < argc) p = argv[i]; if (p && *p == '=') { p += 1; if (!*p && ++i < argc) p = argv[i]; } if (!is_all_digits(p)) return 1; *result = atoll(p); *pos = i; return 0; } static int remove_all_volumes(void) { int i; for (i = 0; i < info.dev_count; ++i) { struct ubi_device_info *ubi_device = &ubi_array[i]; struct volume_info *vol; vol = ubi_device->volumes; while (vol) { int res = ubi_rmvol(libubi, ubi_device->device_file_name, vol->info.vol_id); if (res) return res; vol = vol->next; } } return 0; } int main(int argc,char *argv[]) { int i; long long r, repeat = 1; int initial_seed = 1, args_ok = 1; printf("UBI Integrity Test\n"); /* Get arguments */ ubi_module_load_string = 0; for (i = 1; i < argc; ++i) { if (strncmp(argv[i], "-h", 2) == 0) args_ok = 0; else if (strncmp(argv[i], "--help", 6) == 0) args_ok = 0; else if (strncmp(argv[i], "-n", 2) == 0) { if (get_short_arg(&i, "-n", &repeat, argc, argv)) args_ok = 0; } else if (strncmp(argv[i], "--repeat", 8) == 0) { if (get_long_arg(&i, "--repeat", &repeat, argc, argv)) args_ok = 0; } else if (strncmp(argv[i], "-m", 2) == 0) { if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv)) args_ok = 0; } else if (strncmp(argv[i], "--maxebs", 8) == 0) { if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv)) args_ok = 0; } else if (!ubi_module_load_string) ubi_module_load_string = argv[i]; else args_ok = 0; } if (!args_ok || !ubi_module_load_string) { fprintf(stderr, "Usage is: ubi_integ [] \n"); fprintf(stderr, " Options: \n"); fprintf(stderr, " -h, --help Help\n"); fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n"); fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n"); return 1; } next_seed = initial_seed = seed_random_generator(); printf("Initial seed = %u\n", (unsigned) initial_seed); load_ubi(); libubi = libubi_open(); if (!libubi) error_exit("Failed to open libubi"); get_ubi_devices_info(); r = 0; while (repeat == 0 || r++ < repeat) { printf("Cycle %lld\n", r); do_some_operations(); /* Close all volumes */ while (open_volumes) close_volume(open_volumes->vol_fd); check_ubi(); libubi_close(libubi); reload_ubi(); libubi = libubi_open(); if (!libubi) error_exit("Failed to open libubi"); check_ubi(); } if (remove_all_volumes()) error_exit("Failed to remove all volumes"); libubi_close(libubi); printf("UBI Integrity Test completed ok\n"); return 0; } mtd-utils-1.5.0/tests/ubi-tests/io_basic.c000066400000000000000000000102511175167361300204450ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Test basic UBI volume I/O capabilities. */ #include #include #include #include #include #include #include #include #include "libubi.h" #define TESTNAME "io_basic" #include "common.h" static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; /** * test_basic - check basic volume read and update capabilities. * * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int test_basic(int type) { struct ubi_mkvol_request req; const char *name = TESTNAME ":test_basic()"; char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = dev_info.avail_bytes; req.vol_type = type; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); /* Make sure newly created volume contains only 0xFF bytes */ if (check_vol_patt(vol_node, 0xFF)) goto remove; /* Write 0xA5 bytes to the volume */ if (update_vol_patt(vol_node, dev_info.avail_bytes, 0xA5)) goto remove; if (check_vol_patt(vol_node, 0xA5)) goto remove; if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } return 0; remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } /** * test_aligned - test volume alignment feature. * * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int test_aligned(int type) { unsigned int i, ebsz; struct ubi_mkvol_request req; const char *name = TESTNAME ":test_aligned()"; char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; int alignments[] = ALIGNMENTS(dev_info.leb_size); req.vol_type = type; req.name = name; for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = alignments[i]; req.alignment -= req.alignment % dev_info.min_io_size; if (req.alignment == 0) req.alignment = dev_info.min_io_size; ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment; req.bytes = MIN_AVAIL_EBS * ebsz; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); /* Make sure newly created volume contains only 0xFF bytes */ if (check_vol_patt(vol_node, 0xFF)) goto remove; /* Write 0xA5 bytes to the volume */ if (update_vol_patt(vol_node, req.bytes, 0xA5)) goto remove; if (check_vol_patt(vol_node, 0xA5)) goto remove; if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } } return 0; remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } int main(int argc, char * const argv[]) { if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } if (test_basic(UBI_DYNAMIC_VOLUME)) goto close; if (test_basic(UBI_STATIC_VOLUME)) goto close; if (test_aligned(UBI_DYNAMIC_VOLUME)) goto close; if (test_aligned(UBI_STATIC_VOLUME)) goto close; libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/io_paral.c000066400000000000000000000164441175167361300204750ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * This test does a lot of I/O to volumes in parallel. */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include "libubi.h" #define TESTNAME "io_paral" #include "common.h" #define THREADS_NUM 4 #define ITERATIONS (1024 * 1) #define VOL_LEBS 10 static libubi_t libubi; static struct ubi_dev_info dev_info; static const char *node; static int vol_size; static struct ubi_mkvol_request reqests[THREADS_NUM + 1]; static char vol_name[THREADS_NUM + 1][100]; static char vol_nodes[THREADS_NUM + 1][sizeof(UBI_VOLUME_PATTERN) + 99]; static unsigned char *wbufs[THREADS_NUM + 1]; static unsigned char *rbufs[THREADS_NUM + 1]; static int update_volume(int vol_id, int bytes) { int i, fd, ret, written = 0, rd = 0; char *vol_node = vol_nodes[vol_id]; unsigned char *wbuf = wbufs[vol_id]; unsigned char *rbuf = rbufs[vol_id]; fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", vol_node); return -1; } for (i = 0; i < bytes; i++) wbuf[i] = rand() % 255; memset(rbuf, '\0', bytes); ret = ubi_update_start(libubi, fd, bytes); if (ret) { failed("ubi_update_start"); errmsg("volume id is %d", vol_id); goto err_close; } while (written < bytes) { int to_write = rand() % (bytes - written); if (to_write == 0) to_write = 1; ret = write(fd, wbuf + written, to_write); if (ret != to_write) { failed("write"); errmsg("failed to write %d bytes at offset %d " "of volume %d", to_write, written, vol_id); errmsg("update: %d bytes", bytes); goto err_close; } written += to_write; } close(fd); fd = open(vol_node, O_RDONLY); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", node); return -1; } /* read data back and check */ while (rd < bytes) { int to_read = rand() % (bytes - rd); if (to_read == 0) to_read = 1; ret = read(fd, rbuf + rd, to_read); if (ret != to_read) { failed("read"); errmsg("failed to read %d bytes at offset %d " "of volume %d", to_read, rd, vol_id); goto err_close; } rd += to_read; } if (memcmp(wbuf, rbuf, bytes)) { errmsg("written and read data are different"); goto err_close; } close(fd); return 0; err_close: close(fd); return -1; } static void *update_thread(void *ptr) { int vol_id = (long)ptr, i; for (i = 0; i < ITERATIONS; i++) { int ret, bytes = (rand() % (vol_size - 1)) + 1; int remove = !(rand() % 16); /* From time to time remove the volume */ if (remove) { ret = ubi_rmvol(libubi, node, vol_id); if (ret) { failed("ubi_rmvol"); errmsg("cannot remove volume %d", vol_id); return NULL; } ret = ubi_mkvol(libubi, node, &reqests[vol_id]); if (ret) { failed("ubi_mkvol"); errmsg("cannot create volume %d", vol_id); return NULL; } } ret = update_volume(vol_id, bytes); if (ret) return NULL; } return NULL; } static void *write_thread(void *ptr) { int ret, fd, vol_id = (long)ptr, i; char *vol_node = vol_nodes[vol_id]; unsigned char *wbuf = wbufs[vol_id]; unsigned char *rbuf = rbufs[vol_id]; fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", vol_node); return NULL; } ret = ubi_set_property(fd, UBI_PROP_DIRECT_WRITE, 1); if (ret) { failed("ubi_set_property"); errmsg("cannot set property for \"%s\"\n", vol_node); } for (i = 0; i < ITERATIONS * VOL_LEBS; i++) { int j, leb = rand() % VOL_LEBS; off_t offs = dev_info.leb_size * leb; ret = ubi_leb_unmap(fd, leb); if (ret) { failed("ubi_leb_unmap"); errmsg("cannot unmap LEB %d", leb); break; } for (j = 0; j < dev_info.leb_size; j++) wbuf[j] = rand() % 255; memset(rbuf, '\0', dev_info.leb_size); ret = pwrite(fd, wbuf, dev_info.leb_size, offs); if (ret != dev_info.leb_size) { failed("pwrite"); errmsg("cannot write %d bytes to offs %lld, wrote %d", dev_info.leb_size, offs, ret); break; } /* read data back and check */ ret = pread(fd, rbuf, dev_info.leb_size, offs); if (ret != dev_info.leb_size) { failed("read"); errmsg("failed to read %d bytes at offset %d " "of volume %d", dev_info.leb_size, offs, vol_id); break; } if (memcmp(wbuf, rbuf, dev_info.leb_size)) { errmsg("written and read data are different"); break; } } close(fd); return NULL; } int main(int argc, char * const argv[]) { int i, ret; pthread_t threads[THREADS_NUM]; seed_random_generator(); if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } /* * Create 1 volume more than threads count. The last volume * will not change to let WL move more stuff. */ vol_size = dev_info.leb_size * VOL_LEBS; for (i = 0; i <= THREADS_NUM; i++) { reqests[i].alignment = 1; reqests[i].bytes = vol_size; reqests[i].vol_id = i; sprintf(vol_name[i], TESTNAME":%d", i); reqests[i].name = vol_name[i]; reqests[i].vol_type = UBI_DYNAMIC_VOLUME; if (i == THREADS_NUM) reqests[i].vol_type = UBI_STATIC_VOLUME; sprintf(vol_nodes[i], UBI_VOLUME_PATTERN, dev_info.dev_num, i); if (ubi_mkvol(libubi, node, &reqests[i])) { failed("ubi_mkvol"); goto remove; } wbufs[i] = malloc(vol_size); rbufs[i] = malloc(vol_size); if (!wbufs[i] || !rbufs[i]) { failed("malloc"); goto remove; } ret = update_volume(i, vol_size); if (ret) goto remove; } for (i = 0; i < THREADS_NUM / 2; i++) { ret = pthread_create(&threads[i], NULL, &write_thread, (void *)(long)i); if (ret) { failed("pthread_create"); goto remove; } } for (i = THREADS_NUM / 2; i < THREADS_NUM; i++) { ret = pthread_create(&threads[i], NULL, &update_thread, (void *)(long)i); if (ret) { failed("pthread_create"); goto remove; } } for (i = 0; i < THREADS_NUM; i++) pthread_join(threads[i], NULL); for (i = 0; i <= THREADS_NUM; i++) { if (ubi_rmvol(libubi, node, i)) { failed("ubi_rmvol"); goto remove; } if (wbufs[i]) free(wbufs[i]); if (rbufs[i]) free(rbufs[i]); wbufs[i] = NULL; rbufs[i] = NULL; } libubi_close(libubi); return 0; remove: for (i = 0; i <= THREADS_NUM; i++) { ubi_rmvol(libubi, node, i); if (wbufs[i]) free(wbufs[i]); if (rbufs[i]) free(rbufs[i]); wbufs[i] = NULL; rbufs[i] = NULL; } close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/io_read.c000066400000000000000000000206461175167361300203100ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Test UBI volume read. */ #include #include #include #include #include #include #include #include #include "libubi.h" #define TESTNAME "io_basic" #include "common.h" static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; static int fd; /* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */ #define LENGTHES(io, s) \ {1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io), \ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \ 2*(s)+(io), 3*(s), 3*(s)+(io)}; /* * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz - * volume size. */ #define OFFSETS(io, s, sz) \ {0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io), \ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \ 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1}; /** * test_static - test static volume-specific features. * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int test_static(void) { struct ubi_mkvol_request req; const char *name = TESTNAME ":io_basic()"; char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; struct ubi_vol_info vol_info; int fd, ret; char buf[20]; req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = dev_info.avail_bytes; req.vol_type = UBI_STATIC_VOLUME; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", node); goto remove; } if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { failed("ubi_get_vol_info"); goto close; } /* Make sure new static volume contains no data */ if (vol_info.data_bytes != 0) { errmsg("data_bytes = %lld, not zero", vol_info.data_bytes); goto close; } /* Ensure read returns EOF */ ret = read(fd, buf, 1); if (ret < 0) { failed("read"); goto close; } if (ret != 0) { errmsg("read data from free static volume"); goto close; } if (ubi_update_start(libubi, fd, 10)) { failed("ubi_update_start"); goto close; } ret = write(fd, buf, 10); if (ret < 0) { failed("write"); goto close; } if (ret != 10) { errmsg("written %d bytes", ret); goto close; } if (lseek(fd, 0, SEEK_SET) != 0) { failed("seek"); goto close; } ret = read(fd, buf, 20); if (ret < 0) { failed("read"); goto close; } if (ret != 10) { errmsg("read %d bytes", ret); goto close; } close(fd); if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } return 0; close: close(fd); remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } /* * A helper function for test_read2(). */ static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off) { int i, len1; unsigned char ck_buf[len], buf[len]; off_t new_off; if (off + len > vol_info->data_bytes) len1 = vol_info->data_bytes - off; else len1 = len; if (lseek(fd, off, SEEK_SET) != off) { failed("seek"); errmsg("len = %d", len); return -1; } if (read(fd, buf, len) != len1) { failed("read"); errmsg("len = %d", len); return -1; } new_off = lseek(fd, 0, SEEK_CUR); if (new_off != off + len1) { if (new_off == -1) failed("lseek"); else errmsg("read %d bytes from %lld, but resulting " "offset is %lld", len1, (long long) off, (long long) new_off); return -1; } for (i = 0; i < len1; i++) ck_buf[i] = (unsigned char)(off + i); if (memcmp(buf, ck_buf, len1)) { errmsg("incorrect data read from offset %lld", (long long)off); errmsg("len = %d", len); return -1; } return 0; } /* * A helper function for test_read1(). */ static int test_read2(const struct ubi_vol_info *vol_info, int len) { int i; off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->leb_size, vol_info->data_bytes); for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) { if (test_read3(vol_info, len, offsets[i])) { errmsg("offset = %d", offsets[i]); return -1; } } return 0; } /* * A helper function for test_read(). */ static int test_read1(struct ubi_vol_info *vol_info) { int i, written = 0; char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->leb_size); sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, vol_info->vol_id); fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", node); return -1; } /* Write some pattern to the volume */ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) { failed("ubi_update_start"); errmsg("bytes = %lld", vol_info->rsvd_bytes); goto close; } while (written < vol_info->rsvd_bytes) { int i, ret; unsigned char buf[512]; for (i = 0; i < 512; i++) buf[i] = (unsigned char)(written + i); ret = write(fd, buf, 512); if (ret == -1) { failed("write"); errmsg("written = %d, ret = %d", written, ret); goto close; } written += ret; } close(fd); if (ubi_get_vol_info(libubi, vol_node, vol_info)) { failed("ubi_get_vol_info"); return -1; } fd = open(vol_node, O_RDONLY); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", node); return -1; } for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) { if (test_read2(vol_info, lengthes[i])) { errmsg("length = %d", lengthes[i]); goto close; } } close(fd); return 0; close: close(fd); return -1; } /** * test_read - test UBI volume reading from different offsets. * * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int test_read(int type) { const char *name = TESTNAME ":test_read()"; int alignments[] = ALIGNMENTS(dev_info.leb_size); char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; struct ubi_mkvol_request req; int i; for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { int leb_size; struct ubi_vol_info vol_info; req.vol_id = UBI_VOL_NUM_AUTO; req.vol_type = type; req.name = name; req.alignment = alignments[i]; req.alignment -= req.alignment % dev_info.min_io_size; if (req.alignment == 0) req.alignment = dev_info.min_io_size; leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; req.bytes = MIN_AVAIL_EBS * leb_size; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { failed("ubi_get_vol_info"); goto remove; } if (test_read1(&vol_info)) { errmsg("alignment = %d", req.alignment); goto remove; } if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } } return 0; remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } int main(int argc, char * const argv[]) { if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } if (test_static()) goto close; if (test_read(UBI_DYNAMIC_VOLUME)) goto close; if (test_read(UBI_STATIC_VOLUME)) goto close; libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/io_update.c000066400000000000000000000157351175167361300206620ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Test UBI volume update and atomic LEB change */ #include #include #include #include #include #include #include #include #include #include #define TESTNAME "io_update" #include "common.h" static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; #define SEQUENCES(io, s) { \ {3*(s)-(io)-1, 1}, \ {512}, \ {666}, \ {2048}, \ {(io), (io), PAGE_SIZE}, \ {(io)+1, (io)+1, PAGE_SIZE}, \ {PAGE_SIZE}, \ {PAGE_SIZE-1}, \ {PAGE_SIZE+(io)}, \ {(s)}, \ {(s)-1}, \ {(s)+1}, \ {(io), (s)+1}, \ {(s)+(io), PAGE_SIZE}, \ {2*(s), PAGE_SIZE}, \ {PAGE_SIZE, 2*(s), 1}, \ {PAGE_SIZE, 2*(s)}, \ {2*(s)-1, 2*(s)-1}, \ {3*(s), PAGE_SIZE + 1}, \ {1, PAGE_SIZE}, \ {(io), (s)} \ } #define SEQ_SZ 21 /* * test_update1 - helper function for test_update(). */ static int test_update1(struct ubi_vol_info *vol_info, int leb_change) { long long total_len = leb_change ? vol_info->leb_size : vol_info->rsvd_bytes; int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size, leb_change ? dev_info.min_io_size * 2 : vol_info->leb_size); char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; unsigned char buf[total_len]; int fd, i, j; sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, vol_info->vol_id); fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", node); return -1; } for (i = 0; i < SEQ_SZ; i++) { int ret, stop = 0, len = 0; off_t off = 0; long long test_len; unsigned char buf1[total_len]; /* * test_len is LEB size (if we test atomic LEB change) or * volume size (if we test update). For better test coverage, * use a little smaller LEB change/update length. */ test_len = total_len - (rand() % (total_len / 10)); if (leb_change) { if (ubi_leb_change_start(libubi, fd, 0, test_len, UBI_SHORTTERM)) { failed("ubi_update_start"); goto close; } } else { if (ubi_update_start(libubi, fd, test_len)) { failed("ubi_update_start"); goto close; } } for (j = 0; off < test_len; j++) { int n, rnd_len, l; if (!stop) { if (sequences[i][j] != 0) l = len = sequences[i][j]; else stop = 1; } /* * Fill some part of the write buffer with random data, * and the other part with 0xFFs to test how UBI * stripes 0xFFs multiple of I/O unit size. */ if (off + l > test_len) l = test_len - off; rnd_len = rand() % (l + 1); for (n = 0; n < rnd_len; n++) buf[off + n] = (unsigned char)rand(); memset(buf + off + rnd_len, 0xFF, l - rnd_len); /* * Deliberately pass len instead of l (len may be * greater then l if this is the last chunk) because * UBI have to read only l bytes anyway. */ ret = write(fd, buf + off, len); if (ret < 0) { failed("write"); errmsg("failed to write %d bytes at offset " "%lld", len, (long long)off); goto close; } len = l; if (ret != len) { errmsg("failed to write %d bytes at offset " "%lld, wrote %d", len, (long long)off, ret); goto close; } off += len; } /* Check data */ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) { failed("lseek"); errmsg("cannot seek to 0"); goto close; } memset(buf1, 0x01, test_len); if (vol_info->type == UBI_STATIC_VOLUME) /* * Static volume must not let use read more then it * contains. */ ret = read(fd, buf1, test_len + 100); else ret = read(fd, buf1, test_len); if (ret < 0) { failed("read"); errmsg("failed to read %d bytes", test_len); goto close; } if (ret != test_len) { errmsg("failed to read %d bytes, read %d", test_len, ret); goto close; } if (memcmp(buf, buf1, test_len)) { errmsg("data corruption"); goto close; } } close(fd); return 0; close: close(fd); return -1; } /** * test_update - check volume update and atomic LEB change capabilities. * * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * * This function returns %0 in case of success and %-1 in case of failure. */ static int test_update(int type) { struct ubi_mkvol_request req; const char *name = TESTNAME ":io_update()"; int alignments[] = ALIGNMENTS(dev_info.leb_size); struct ubi_vol_info vol_info; char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; unsigned int i; for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { int leb_size; req.vol_id = UBI_VOL_NUM_AUTO; req.vol_type = type; req.name = name; req.alignment = alignments[i]; req.alignment -= req.alignment % dev_info.min_io_size; if (req.alignment == 0) req.alignment = dev_info.min_io_size; leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; req.bytes = MIN_AVAIL_EBS * leb_size; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { failed("ubi_get_vol_info"); goto remove; } if (test_update1(&vol_info, 0)) { errmsg("alignment = %d", req.alignment); goto remove; } if (vol_info.type != UBI_STATIC_VOLUME) { if (test_update1(&vol_info, 1)) { errmsg("alignment = %d", req.alignment); goto remove; } } if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } } return 0; remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } int main(int argc, char * const argv[]) { seed_random_generator(); if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } if (test_update(UBI_DYNAMIC_VOLUME)) goto close; if (test_update(UBI_STATIC_VOLUME)) goto close; libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/mkvol_bad.c000066400000000000000000000162641175167361300206450ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Test UBI volume creation and deletion ioctl()s with bad input and in case of * incorrect usage. */ #include #include #include #include "libubi.h" #define TESTNAME "mkvol_bad" #include "common.h" static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; /** * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters. * * This function returns %0 if the test passed and %-1 if not. */ static int test_mkvol(void) { int ret, i; struct ubi_mkvol_request req; const char *name = TESTNAME ":test_mkvol()"; req.alignment = 1; req.bytes = dev_info.avail_bytes; req.vol_type = UBI_DYNAMIC_VOLUME; req.name = name; /* Bad volume ID */ req.vol_id = -2; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) return -1; req.vol_id = dev_info.max_vol_count; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) return -1; /* Bad alignment */ req.vol_id = 0; req.alignment = 0; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", req.alignment)) return -1; req.alignment = -1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", req.alignment)) return -1; req.alignment = dev_info.leb_size + 1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", req.alignment)) return -1; if (dev_info.min_io_size > 1) { req.alignment = dev_info.min_io_size + 1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", req.alignment)) return -1; } /* Bad bytes */ req.alignment = 1; req.bytes = -1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) return -1; req.bytes = 0; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) return -1; req.bytes = dev_info.avail_bytes + 1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) return -1; req.alignment = dev_info.leb_size - dev_info.min_io_size; req.bytes = (dev_info.leb_size - dev_info.leb_size % req.alignment) * dev_info.avail_lebs + 1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) return -1; /* Bad vol_type */ req.alignment = 1; req.bytes = dev_info.leb_size; req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d", req.vol_type)) return -1; req.vol_type = UBI_DYNAMIC_VOLUME; /* Too long name */ { char name[UBI_VOL_NAME_MAX + 5]; memset(name, 'x', UBI_VOL_NAME_MAX + 1); name[UBI_VOL_NAME_MAX + 1] = '\0'; req.name = name; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d", UBI_VOL_NAME_MAX + 1)) return -1; } /* Try to create 2 volumes with the same ID and name */ req.name = name; req.vol_id = 0; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EEXIST, "ubi_mkvol", "volume with ID 0 created twice")) return -1; req.vol_id = 1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EEXIST, "ubi_mkvol", "volume with name \"%s\" created twice", name)) return -1; if (ubi_rmvol(libubi, node, 0)) { failed("ubi_rmvol"); return -1; } /* Try to use too much space */ req.vol_id = 0; req.bytes = dev_info.avail_bytes; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } req.bytes = 1; req.vol_id = 1; ret = ubi_mkvol(libubi, node, &req); if (check_failed(ret, EEXIST, "ubi_mkvol", "created volume of maximum size %lld, but still " "can create more volumes", dev_info.avail_bytes)) return -1; if (ubi_rmvol(libubi, node, 0)) { failed("ubi_rmvol"); return -1; } /* Try to create too many volumes */ for (i = 0; i < dev_info.max_vol_count; i++) { char nm[strlen(name) + 50]; req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = 1; req.vol_type = UBI_STATIC_VOLUME; sprintf(nm, "%s:%d", name, i); req.name = nm; if (ubi_mkvol(libubi, node, &req)) { /* * Note, because of gluebi we may be unable to create * dev_info.max_vol_count devices (MTD restrictions). */ if (errno == ENFILE) break; failed("ubi_mkvol"); errmsg("vol_id %d", i); goto remove; } } for (i = 0; i < dev_info.max_vol_count + 1; i++) ubi_rmvol(libubi, node, i); return 0; remove: for (i = 0; i < dev_info.max_vol_count + 1; i++) ubi_rmvol(libubi, node, i); return -1; } /** * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters. * * This function returns %0 if the test passed and %-1 if not. */ static int test_rmvol(void) { int ret; struct ubi_mkvol_request req; const char *name = TESTNAME ":test_rmvol()"; /* Bad vol_id */ ret = ubi_rmvol(libubi, node, -1); if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1")) return -1; ret = ubi_rmvol(libubi, node, dev_info.max_vol_count); if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d", dev_info.max_vol_count)) return -1; /* Try to remove non-existing volume */ ret = ubi_rmvol(libubi, node, 0); if (check_failed(ret, ENODEV, "ubi_rmvol", "removed non-existing volume 0")) return -1; /* Try to remove volume twice */ req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = dev_info.avail_bytes; req.vol_type = UBI_DYNAMIC_VOLUME; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } ret = ubi_rmvol(libubi, node, req.vol_id); if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice", req.vol_id)) return -1; return 0; } int main(int argc, char * const argv[]) { if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } if (test_mkvol()) goto close; if (test_rmvol()) goto close; libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/mkvol_basic.c000066400000000000000000000125011175167361300211660ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Test test checks basic volume creation and deletion capabilities. */ #include #include #include #include "libubi.h" #define TESTNAME "mkvol_basic" #include "common.h" static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; /** * mkvol_alignment - create volumes with different alignments. * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int mkvol_alignment(void) { struct ubi_mkvol_request req; int i, vol_id, ebsz; const char *name = TESTNAME ":mkvol_alignment()"; int alignments[] = ALIGNMENTS(dev_info.leb_size); for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { req.vol_id = UBI_VOL_NUM_AUTO; /* Alignment should actually be multiple of min. I/O size */ req.alignment = alignments[i]; req.alignment -= req.alignment % dev_info.min_io_size; if (req.alignment == 0) req.alignment = dev_info.min_io_size; /* Bear in mind alignment reduces EB size */ ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment; req.bytes = dev_info.avail_lebs * ebsz; req.vol_type = UBI_DYNAMIC_VOLUME; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); errmsg("alignment %d", req.alignment); return -1; } vol_id = req.vol_id; if (check_volume(vol_id, &req)) goto remove; if (ubi_rmvol(libubi, node, vol_id)) { failed("ubi_rmvol"); return -1; } } return 0; remove: ubi_rmvol(libubi, node, vol_id); return -1; } /** * mkvol_basic - simple test that checks basic volume creation capability. * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int mkvol_basic(void) { struct ubi_mkvol_request req; struct ubi_vol_info vol_info; int vol_id, ret; const char *name = TESTNAME ":mkvol_basic()"; /* Create dynamic volume of maximum size */ req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = dev_info.avail_bytes; req.vol_type = UBI_DYNAMIC_VOLUME; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } vol_id = req.vol_id; if (check_volume(vol_id, &req)) goto remove; if (ubi_rmvol(libubi, node, vol_id)) { failed("ubi_rmvol"); return -1; } /* Create static volume of maximum size */ req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = dev_info.avail_bytes; req.vol_type = UBI_STATIC_VOLUME; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } vol_id = req.vol_id; if (check_volume(vol_id, &req)) goto remove; if (ubi_rmvol(libubi, node, vol_id)) { failed("ubi_rmvol"); return -1; } /* Make sure volume does not exist */ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info); if (ret == 0) { errmsg("removed volume %d exists", vol_id); goto remove; } return 0; remove: ubi_rmvol(libubi, node, vol_id); return -1; } /** * mkvol_multiple - test multiple volumes creation * * Thus function returns %0 if the test passed and %-1 if not. */ static int mkvol_multiple(void) { struct ubi_mkvol_request req; int i, ret, max = dev_info.max_vol_count; const char *name = TESTNAME ":mkvol_multiple()"; /* Create maximum number of volumes */ for (i = 0; i < max; i++) { char nm[strlen(name) + 50]; req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = 1; req.vol_type = UBI_STATIC_VOLUME; sprintf(nm, "%s:%d", name, i); req.name = nm; if (ubi_mkvol(libubi, node, &req)) { if (errno == ENFILE) { max = i; break; } failed("ubi_mkvol"); errmsg("vol_id %d", i); goto remove; } if (check_volume(req.vol_id, &req)) { errmsg("vol_id %d", i); goto remove; } } for (i = 0; i < max; i++) { struct ubi_vol_info vol_info; if (ubi_rmvol(libubi, node, i)) { failed("ubi_rmvol"); return -1; } /* Make sure volume does not exist */ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); if (ret == 0) { errmsg("removed volume %d exists", i); goto remove; } } return 0; remove: for (i = 0; i < dev_info.max_vol_count + 1; i++) ubi_rmvol(libubi, node, i); return -1; } int main(int argc, char * const argv[]) { if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } if (mkvol_basic()) goto close; if (mkvol_alignment()) goto close; if (mkvol_multiple()) goto close; libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/mkvol_paral.c000066400000000000000000000046641175167361300212170ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * This test creates and deletes volumes in parallel. */ #include #include #include #include #include "libubi.h" #define TESTNAME "mkvol_paral" #include "common.h" #define THREADS_NUM 4 #define ITERATIONS 500 static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; static int iterations = ITERATIONS; /** * the_thread - the testing thread. * * @ptr thread number */ static void * the_thread(void *ptr) { int n = (long)ptr, iter = iterations; struct ubi_mkvol_request req; const char *name = TESTNAME ":the_thread()"; char nm[strlen(name) + 50]; req.alignment = 1; req.bytes = dev_info.avail_bytes/ITERATIONS; req.vol_type = UBI_DYNAMIC_VOLUME; sprintf(nm, "%s:%d", name, n); req.name = nm; while (iter--) { req.vol_id = UBI_VOL_NUM_AUTO; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return NULL; } if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return NULL; } } return NULL; } int main(int argc, char * const argv[]) { int i, ret; pthread_t threads[THREADS_NUM]; if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } for (i = 0; i < THREADS_NUM; i++) { ret = pthread_create(&threads[i], NULL, &the_thread, (void*)(long)i); if (ret) { failed("pthread_create"); goto close; } } for (i = 0; i < THREADS_NUM; i++) pthread_join(threads[i], NULL); libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/rsvol.c000066400000000000000000000146161175167361300200530ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Tes UBI volume re-size. */ #include #include #include #include #include #include #include #include #include "libubi.h" #define TESTNAME "rsvol" #include "common.h" static libubi_t libubi; static struct ubi_dev_info dev_info; const char *node; /** * test_basic - check volume re-size capability. * * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int test_basic(int type) { struct ubi_mkvol_request req; const char *name = TESTNAME ":test_basic()"; req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = 1; req.bytes = MIN_AVAIL_EBS * dev_info.leb_size; req.vol_type = type; req.name = name; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } req.bytes = dev_info.leb_size; if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { failed("ubi_rsvol"); goto remove; } if (check_volume(req.vol_id, &req)) goto remove; req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.leb_size; if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { failed("ubi_rsvol"); goto remove; } if (check_volume(req.vol_id, &req)) goto remove; req.bytes -= 1; if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { failed("ubi_rsvol"); goto remove; } if (check_volume(req.vol_id, &req)) goto remove; if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } return 0; remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } /* * Helper function for test_rsvol(). */ static int test_rsvol1(struct ubi_vol_info *vol_info) { long long bytes; struct ubi_vol_info vol_info1; char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; unsigned char buf[vol_info->rsvd_bytes]; int fd, i, ret; /* Make the volume smaller and check basic volume I/O */ bytes = vol_info->rsvd_bytes - vol_info->leb_size; if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) { failed("ubi_rsvol"); return -1; } if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id, &vol_info1)) { failed("ubi_get_vol_info"); return -1; } if (vol_info1.rsvd_bytes != bytes) { errmsg("rsvd_bytes %lld, must be %lld", vol_info1.rsvd_bytes, bytes); return -1; } if (vol_info1.rsvd_lebs != vol_info->rsvd_lebs - 1) { errmsg("rsvd_lebs %d, must be %d", vol_info1.rsvd_lebs, vol_info->rsvd_lebs - 1); return -1; } /* Write data to the volume */ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, vol_info->vol_id); fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", vol_node); return -1; } bytes = vol_info->rsvd_bytes - vol_info->leb_size - 1; if (ubi_update_start(libubi, fd, bytes)) { failed("ubi_update_start"); goto close; } for (i = 0; i < bytes; i++) buf[i] = (unsigned char)i; ret = write(fd, buf, bytes); if (ret != bytes) { failed("write"); goto close; } close(fd); if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) { failed("ubi_rsvol"); return -1; } if (ubi_rsvol(libubi, node, vol_info->vol_id, vol_info->leb_size * dev_info.avail_lebs)) { failed("ubi_rsvol"); return -1; } fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errmsg("cannot open \"%s\"\n", vol_node); return -1; } /* Read data back */ if (lseek(fd, 0, SEEK_SET) != 0) { failed("seek"); goto close; } memset(buf, 0, bytes); ret = read(fd, buf, bytes); if (ret != bytes) { failed("read"); goto close; } for (i = 0; i < bytes; i++) { if (buf[i] != (unsigned char)i) { errmsg("bad data"); goto close; } } close(fd); return 0; close: close(fd); return -1; } /** * test_rsvol - test UBI volume re-size. * * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * * Thus function returns %0 in case of success and %-1 in case of failure. */ static int test_rsvol(int type) { const char *name = TESTNAME "test_rsvol:()"; int alignments[] = ALIGNMENTS(dev_info.leb_size); char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; struct ubi_mkvol_request req; int i; for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { int leb_size; struct ubi_vol_info vol_info; req.vol_id = UBI_VOL_NUM_AUTO; req.vol_type = type; req.name = name; req.alignment = alignments[i]; req.alignment -= req.alignment % dev_info.min_io_size; if (req.alignment == 0) req.alignment = dev_info.min_io_size; leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; req.bytes = MIN_AVAIL_EBS * leb_size; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); return -1; } sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { failed("ubi_get_vol_info"); goto remove; } if (test_rsvol1(&vol_info)) { errmsg("alignment = %d", req.alignment); goto remove; } if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); return -1; } } return 0; remove: ubi_rmvol(libubi, node, req.vol_id); return -1; } int main(int argc, char * const argv[]) { if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } if (test_basic(UBI_DYNAMIC_VOLUME)) goto close; if (test_basic(UBI_STATIC_VOLUME)) goto close; if (test_rsvol(UBI_DYNAMIC_VOLUME)) goto close; if (test_rsvol(UBI_STATIC_VOLUME)) goto close; libubi_close(libubi); return 0; close: libubi_close(libubi); return 1; } mtd-utils-1.5.0/tests/ubi-tests/runtests.sh000077500000000000000000000011501175167361300207550ustar00rootroot00000000000000#!/bin/sh ubidev="$1" tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update io_paral volrefcnt" if test -z "$ubidev"; then echo "Usage:" echo "$0 " exit 1 fi ubiname=`echo $ubidev | cut -d/ -f3` major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1` for minor in `seq 0 4`; do if test ! -e ${ubidev}_${minor} ; then mknod ${ubidev}_${minor} c $major $(($minor + 1)) fi done if ! test -c "$ubidev"; then echo "Error: $ubidev is not character device" exit 1 fi for t in `echo $tests`; do echo "Running $t $ubidev" "./$t" "$ubidev" || exit 1 done echo SUCCESS exit 0 mtd-utils-1.5.0/tests/ubi-tests/volrefcnt.c000066400000000000000000000057221175167361300207060ustar00rootroot00000000000000/* * Copyright (c) Nokia Corporation, 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * Test volume reference counting - create a volume, open a sysfs file * belonging to the volume, delete the volume but do not close the file, make * sure the file cannot be read, close the file, make sure the volume * disappeard, make sure its sysfs subtree disappeared. */ #include #include #include #include #include #include "libubi.h" #define TESTNAME "rmvol" #include "common.h" #define SYSFS_FILE "/sys/class/ubi/ubi%d_%d/usable_eb_size" int main(int argc, char * const argv[]) { int ret, fd; char fname[sizeof(SYSFS_FILE) + 20]; const char *node; libubi_t libubi; struct ubi_dev_info dev_info; struct ubi_mkvol_request req; char tmp[100]; if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto out_libubi; } /* Create a small dynamic volume */ req.vol_id = UBI_VOL_NUM_AUTO; req.alignment = dev_info.min_io_size; req.bytes = dev_info.leb_size; req.vol_type = UBI_DYNAMIC_VOLUME; req.name = "rmvol"; if (ubi_mkvol(libubi, node, &req)) { failed("ubi_mkvol"); goto out_libubi; } /* Open volume-related sysfs file */ sprintf(fname, SYSFS_FILE, dev_info.dev_num, req.vol_id); fd = open(fname, O_RDONLY); if (fd == -1) { errmsg("cannot open %s", fname); failed("open"); goto out_rmvol; } /* Remove the volume, but do not close the file */ if (ubi_rmvol(libubi, node, req.vol_id)) { failed("ubi_rmvol"); perror("ubi_rmvol"); goto out_close; } /* Try to read from the file, this should fail */ ret = read(fd, tmp, 100); if (ret != -1) { errmsg("read returned %d, expected -1", ret); failed("read"); goto out_close; } /* Close the file and try to open it again, should fail */ close(fd); fd = open(fname, O_RDONLY); if (fd != -1) { errmsg("opened %s again, open returned %d, expected -1", fname, fd); failed("open"); goto out_libubi; } libubi_close(libubi); return 0; out_rmvol: ubi_rmvol(libubi, node, req.vol_id); out_libubi: libubi_close(libubi); return 1; out_close: close(fd); libubi_close(libubi); return 1; } mtd-utils-1.5.0/ubi-utils/000077500000000000000000000000001175167361300153665ustar00rootroot00000000000000mtd-utils-1.5.0/ubi-utils/.gitignore000066400000000000000000000001741175167361300173600ustar00rootroot00000000000000/ubiattach /ubicrc32 /ubidetach /ubiformat /ubimkvol /ubinfo /ubinize /ubirename /ubirmvol /ubiupdatevol /ubirsvol /mtdinfo mtd-utils-1.5.0/ubi-utils/LICENSE.libiniparser000066400000000000000000000020731175167361300210570ustar00rootroot00000000000000Copyright (c) 2000-2007 by Nicolas Devillard. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mtd-utils-1.5.0/ubi-utils/dictionary.c000066400000000000000000000262621175167361300177070ustar00rootroot00000000000000/*-------------------------------------------------------------------------*/ /** @file dictionary.c @author N. Devillard @date Sep 2007 @version $Revision: 1.27 $ @brief Implements a dictionary for string variables. This module implements a simple dictionary object, i.e. a list of string/string associations. This object is useful to store e.g. informations retrieved from a configuration file (ini files). */ /*--------------------------------------------------------------------------*/ /* $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ $Revision: 1.27 $ */ /*--------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include "dictionary.h" #include #include #include #include /** Maximum value size for integers and doubles. */ #define MAXVALSZ 1024 /** Minimal allocated number of entries in a dictionary */ #define DICTMINSZ 128 /** Invalid key token */ #define DICT_INVALID_KEY ((char*)-1) /*--------------------------------------------------------------------------- Private functions ---------------------------------------------------------------------------*/ /* Doubles the allocated size associated to a pointer */ /* 'size' is the current allocated size. */ static void * mem_double(void * ptr, int size) { void * newptr ; newptr = calloc(2*size, 1); if (newptr==NULL) { return NULL ; } memcpy(newptr, ptr, size); free(ptr); return newptr ; } /*-------------------------------------------------------------------------*/ /** @brief Duplicate a string @param s String to duplicate @return Pointer to a newly allocated string, to be freed with free() This is a replacement for strdup(). This implementation is provided for systems that do not have it. */ /*--------------------------------------------------------------------------*/ static char * xstrdup(char * s) { char * t ; if (!s) return NULL ; t = malloc(strlen(s)+1) ; if (t) { strcpy(t,s); } return t ; } /*--------------------------------------------------------------------------- Function codes ---------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /** @brief Compute the hash key for a string. @param key Character string to use for key. @return 1 unsigned int on at least 32 bits. This hash function has been taken from an Article in Dr Dobbs Journal. This is normally a collision-free function, distributing keys evenly. The key is stored anyway in the struct so that collision can be avoided by comparing the key itself in last resort. */ /*--------------------------------------------------------------------------*/ unsigned dictionary_hash(char * key) { int len ; unsigned hash ; int i ; len = strlen(key); for (hash=0, i=0 ; i>6) ; } hash += (hash <<3); hash ^= (hash >>11); hash += (hash <<15); return hash ; } /*-------------------------------------------------------------------------*/ /** @brief Create a new dictionary object. @param size Optional initial size of the dictionary. @return 1 newly allocated dictionary objet. This function allocates a new dictionary object of given size and returns it. If you do not know in advance (roughly) the number of entries in the dictionary, give size=0. */ /*--------------------------------------------------------------------------*/ dictionary * dictionary_new(int size) { dictionary * d ; /* If no size was specified, allocate space for DICTMINSZ */ if (sizesize = size ; d->val = (char **)calloc(size, sizeof(char*)); d->key = (char **)calloc(size, sizeof(char*)); d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); return d ; } /*-------------------------------------------------------------------------*/ /** @brief Delete a dictionary object @param d dictionary object to deallocate. @return void Deallocate a dictionary object and all memory associated to it. */ /*--------------------------------------------------------------------------*/ void dictionary_del(dictionary * d) { int i ; if (d==NULL) return ; for (i=0 ; isize ; i++) { if (d->key[i]!=NULL) free(d->key[i]); if (d->val[i]!=NULL) free(d->val[i]); } free(d->val); free(d->key); free(d->hash); free(d); return ; } /*-------------------------------------------------------------------------*/ /** @brief Get a value from a dictionary. @param d dictionary object to search. @param key Key to look for in the dictionary. @param def Default value to return if key not found. @return 1 pointer to internally allocated character string. This function locates a key in a dictionary and returns a pointer to its value, or the passed 'def' pointer if no such key can be found in dictionary. The returned character pointer points to data internal to the dictionary object, you should not try to free it or modify it. */ /*--------------------------------------------------------------------------*/ char * dictionary_get(dictionary * d, char * key, char * def) { unsigned hash ; int i ; hash = dictionary_hash(key); for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; /* Compare hash */ if (hash==d->hash[i]) { /* Compare string, to avoid hash collisions */ if (!strcmp(key, d->key[i])) { return d->val[i] ; } } } return def ; } /*-------------------------------------------------------------------------*/ /** @brief Set a value in a dictionary. @param d dictionary object to modify. @param key Key to modify or add. @param val Value to add. @return int 0 if Ok, anything else otherwise If the given key is found in the dictionary, the associated value is replaced by the provided one. If the key cannot be found in the dictionary, it is added to it. It is Ok to provide a NULL value for val, but NULL values for the dictionary or the key are considered as errors: the function will return immediately in such a case. Notice that if you dictionary_set a variable to NULL, a call to dictionary_get will return a NULL value: the variable will be found, and its value (NULL) is returned. In other words, setting the variable content to NULL is equivalent to deleting the variable from the dictionary. It is not possible (in this implementation) to have a key in the dictionary without value. This function returns non-zero in case of failure. */ /*--------------------------------------------------------------------------*/ int dictionary_set(dictionary * d, char * key, char * val) { int i ; unsigned hash ; if (d==NULL || key==NULL) return -1 ; /* Compute hash for this key */ hash = dictionary_hash(key) ; /* Find if value is already in dictionary */ if (d->n>0) { for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (hash==d->hash[i]) { /* Same hash value */ if (!strcmp(key, d->key[i])) { /* Same key */ /* Found a value: modify and return */ if (d->val[i]!=NULL) free(d->val[i]); d->val[i] = val ? xstrdup(val) : NULL ; /* Value has been modified: return */ return 0 ; } } } } /* Add a new value */ /* See if dictionary needs to grow */ if (d->n==d->size) { /* Reached maximum size: reallocate dictionary */ d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { /* Cannot grow dictionary */ return -1 ; } /* Double size */ d->size *= 2 ; } /* Insert key in the first empty slot */ for (i=0 ; isize ; i++) { if (d->key[i]==NULL) { /* Add key here */ break ; } } /* Copy key */ d->key[i] = xstrdup(key); d->val[i] = val ? xstrdup(val) : NULL ; d->hash[i] = hash; d->n ++ ; return 0 ; } /*-------------------------------------------------------------------------*/ /** @brief Delete a key in a dictionary @param d dictionary object to modify. @param key Key to remove. @return void This function deletes a key in a dictionary. Nothing is done if the key cannot be found. */ /*--------------------------------------------------------------------------*/ void dictionary_unset(dictionary * d, char * key) { unsigned hash ; int i ; if (key == NULL) { return; } hash = dictionary_hash(key); for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; /* Compare hash */ if (hash==d->hash[i]) { /* Compare string, to avoid hash collisions */ if (!strcmp(key, d->key[i])) { /* Found key */ break ; } } } if (i>=d->size) /* Key not found */ return ; free(d->key[i]); d->key[i] = NULL ; if (d->val[i]!=NULL) { free(d->val[i]); d->val[i] = NULL ; } d->hash[i] = 0 ; d->n -- ; return ; } /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @param d Dictionary to dump @param f Opened file pointer. @return void Dumps a dictionary onto an opened file pointer. Key pairs are printed out as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as output file pointers. */ /*--------------------------------------------------------------------------*/ void dictionary_dump(dictionary * d, FILE * out) { int i ; if (d==NULL || out==NULL) return ; if (d->n<1) { fprintf(out, "empty dictionary\n"); return ; } for (i=0 ; isize ; i++) { if (d->key[i]) { fprintf(out, "%20s\t[%s]\n", d->key[i], d->val[i] ? d->val[i] : "UNDEF"); } } return ; } /* Test code */ #ifdef TESTDIC #define NVALS 20000 int main(int argc, char *argv[]) { dictionary * d ; char * val ; int i ; char cval[90] ; /* Allocate dictionary */ printf("allocating...\n"); d = dictionary_new(0); /* Set values in dictionary */ printf("setting %d values...\n", NVALS); for (i=0 ; in != 0) { printf("error deleting values\n"); } printf("deallocating...\n"); dictionary_del(d); return 0 ; } #endif /* vim: set ts=4 et sw=4 tw=75 */ mtd-utils-1.5.0/ubi-utils/include/000077500000000000000000000000001175167361300170115ustar00rootroot00000000000000mtd-utils-1.5.0/ubi-utils/include/dictionary.h000066400000000000000000000146751175167361300213440ustar00rootroot00000000000000 /*-------------------------------------------------------------------------*/ /** @file dictionary.h @author N. Devillard @date Sep 2007 @version $Revision: 1.12 $ @brief Implements a dictionary for string variables. This module implements a simple dictionary object, i.e. a list of string/string associations. This object is useful to store e.g. informations retrieved from a configuration file (ini files). */ /*--------------------------------------------------------------------------*/ /* $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $ $Author: ndevilla $ $Date: 2007-11-23 21:37:00 $ $Revision: 1.12 $ */ #ifndef _DICTIONARY_H_ #define _DICTIONARY_H_ /*--------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include #include #include #include /*--------------------------------------------------------------------------- New types ---------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /** @brief Dictionary object This object contains a list of string/string associations. Each association is identified by a unique string key. Looking up values in the dictionary is speeded up by the use of a (hopefully collision-free) hash function. */ /*-------------------------------------------------------------------------*/ typedef struct _dictionary_ { int n ; /** Number of entries in dictionary */ int size ; /** Storage size */ char ** val ; /** List of string values */ char ** key ; /** List of string keys */ unsigned * hash ; /** List of hash values for keys */ } dictionary ; /*--------------------------------------------------------------------------- Function prototypes ---------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /** @brief Compute the hash key for a string. @param key Character string to use for key. @return 1 unsigned int on at least 32 bits. This hash function has been taken from an Article in Dr Dobbs Journal. This is normally a collision-free function, distributing keys evenly. The key is stored anyway in the struct so that collision can be avoided by comparing the key itself in last resort. */ /*--------------------------------------------------------------------------*/ unsigned dictionary_hash(char * key); /*-------------------------------------------------------------------------*/ /** @brief Create a new dictionary object. @param size Optional initial size of the dictionary. @return 1 newly allocated dictionary objet. This function allocates a new dictionary object of given size and returns it. If you do not know in advance (roughly) the number of entries in the dictionary, give size=0. */ /*--------------------------------------------------------------------------*/ dictionary * dictionary_new(int size); /*-------------------------------------------------------------------------*/ /** @brief Delete a dictionary object @param d dictionary object to deallocate. @return void Deallocate a dictionary object and all memory associated to it. */ /*--------------------------------------------------------------------------*/ void dictionary_del(dictionary * vd); /*-------------------------------------------------------------------------*/ /** @brief Get a value from a dictionary. @param d dictionary object to search. @param key Key to look for in the dictionary. @param def Default value to return if key not found. @return 1 pointer to internally allocated character string. This function locates a key in a dictionary and returns a pointer to its value, or the passed 'def' pointer if no such key can be found in dictionary. The returned character pointer points to data internal to the dictionary object, you should not try to free it or modify it. */ /*--------------------------------------------------------------------------*/ char * dictionary_get(dictionary * d, char * key, char * def); /*-------------------------------------------------------------------------*/ /** @brief Set a value in a dictionary. @param d dictionary object to modify. @param key Key to modify or add. @param val Value to add. @return int 0 if Ok, anything else otherwise If the given key is found in the dictionary, the associated value is replaced by the provided one. If the key cannot be found in the dictionary, it is added to it. It is Ok to provide a NULL value for val, but NULL values for the dictionary or the key are considered as errors: the function will return immediately in such a case. Notice that if you dictionary_set a variable to NULL, a call to dictionary_get will return a NULL value: the variable will be found, and its value (NULL) is returned. In other words, setting the variable content to NULL is equivalent to deleting the variable from the dictionary. It is not possible (in this implementation) to have a key in the dictionary without value. This function returns non-zero in case of failure. */ /*--------------------------------------------------------------------------*/ int dictionary_set(dictionary * vd, char * key, char * val); /*-------------------------------------------------------------------------*/ /** @brief Delete a key in a dictionary @param d dictionary object to modify. @param key Key to remove. @return void This function deletes a key in a dictionary. Nothing is done if the key cannot be found. */ /*--------------------------------------------------------------------------*/ void dictionary_unset(dictionary * d, char * key); /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @param d Dictionary to dump @param f Opened file pointer. @return void Dumps a dictionary onto an opened file pointer. Key pairs are printed out as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as output file pointers. */ /*--------------------------------------------------------------------------*/ void dictionary_dump(dictionary * d, FILE * out); #endif mtd-utils-1.5.0/ubi-utils/include/libiniparser.h000066400000000000000000000240361175167361300216520ustar00rootroot00000000000000 /*-------------------------------------------------------------------------*/ /** @file iniparser.h @author N. Devillard @date Sep 2007 @version 3.0 @brief Parser for ini files. */ /*--------------------------------------------------------------------------*/ /* $Id: iniparser.h,v 1.24 2007-11-23 21:38:19 ndevilla Exp $ $Revision: 1.24 $ */ #ifndef _INIPARSER_H_ #define _INIPARSER_H_ /*--------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include #include #include /* * The following #include is necessary on many Unixes but not Linux. * It is not needed for Windows platforms. * Uncomment it if needed. */ /* #include */ #include "dictionary.h" /*--------------------------------------------------------------------------- Macros ---------------------------------------------------------------------------*/ /** For backwards compatibility only */ #define iniparser_getstr(d, k) iniparser_getstring(d, k, NULL) #define iniparser_setstr iniparser_setstring /*-------------------------------------------------------------------------*/ /** @brief Get number of sections in a dictionary @param d Dictionary to examine @return int Number of sections found in dictionary This function returns the number of sections found in a dictionary. The test to recognize sections is done on the string stored in the dictionary: a section name is given as "section" whereas a key is stored as "section:key", thus the test looks for entries that do not contain a colon. This clearly fails in the case a section name contains a colon, but this should simply be avoided. This function returns -1 in case of error. */ /*--------------------------------------------------------------------------*/ int iniparser_getnsec(dictionary * d); /*-------------------------------------------------------------------------*/ /** @brief Get name for section n in a dictionary. @param d Dictionary to examine @param n Section number (from 0 to nsec-1). @return Pointer to char string This function locates the n-th section in a dictionary and returns its name as a pointer to a string statically allocated inside the dictionary. Do not free or modify the returned string! This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ char * iniparser_getsecname(dictionary * d, int n); /*-------------------------------------------------------------------------*/ /** @brief Save a dictionary to a loadable ini file @param d Dictionary to dump @param f Opened file pointer to dump to @return void This function dumps a given dictionary into a loadable ini file. It is Ok to specify @c stderr or @c stdout as output files. */ /*--------------------------------------------------------------------------*/ void iniparser_dump_ini(dictionary * d, FILE * f); /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @param d Dictionary to dump. @param f Opened file pointer to dump to. @return void This function prints out the contents of a dictionary, one element by line, onto the provided file pointer. It is OK to specify @c stderr or @c stdout as output files. This function is meant for debugging purposes mostly. */ /*--------------------------------------------------------------------------*/ void iniparser_dump(dictionary * d, FILE * f); /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key @param d Dictionary to search @param key Key string to look for @param def Default value to return if key not found. @return pointer to statically allocated character string This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the pointer passed as 'def' is returned. The returned char pointer is pointing to a string allocated in the dictionary, do not free or modify it. */ /*--------------------------------------------------------------------------*/ char * iniparser_getstring(dictionary * d, const char * key, char * def); /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to an int @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return integer This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. Supported values for integers include the usual C notation so decimal, octal (starting with 0) and hexadecimal (starting with 0x) are supported. Examples: - "42" -> 42 - "042" -> 34 (octal -> decimal) - "0x42" -> 66 (hexa -> decimal) Warning: the conversion may overflow in various ways. Conversion is totally outsourced to strtol(), see the associated man page for overflow handling. Credits: Thanks to A. Becker for suggesting strtol() */ /*--------------------------------------------------------------------------*/ int iniparser_getint(dictionary * d, const char * key, int notfound); /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a double @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return double This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. */ /*--------------------------------------------------------------------------*/ double iniparser_getdouble(dictionary * d, char * key, double notfound); /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a boolean @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return integer This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. A true boolean is found if one of the following is matched: - A string starting with 'y' - A string starting with 'Y' - A string starting with 't' - A string starting with 'T' - A string starting with '1' A false boolean is found if one of the following is matched: - A string starting with 'n' - A string starting with 'N' - A string starting with 'f' - A string starting with 'F' - A string starting with '0' The notfound value returned if no boolean is identified, does not necessarily have to be 0 or 1. */ /*--------------------------------------------------------------------------*/ int iniparser_getboolean(dictionary * d, const char * key, int notfound); /*-------------------------------------------------------------------------*/ /** @brief Set an entry in a dictionary. @param ini Dictionary to modify. @param entry Entry to modify (entry name) @param val New value to associate to the entry. @return int 0 if Ok, -1 otherwise. If the given entry can be found in the dictionary, it is modified to contain the provided value. If it cannot be found, -1 is returned. It is Ok to set val to NULL. */ /*--------------------------------------------------------------------------*/ int iniparser_setstring(dictionary * ini, char * entry, char * val); /*-------------------------------------------------------------------------*/ /** @brief Delete an entry in a dictionary @param ini Dictionary to modify @param entry Entry to delete (entry name) @return void If the given entry can be found, it is deleted from the dictionary. */ /*--------------------------------------------------------------------------*/ void iniparser_unset(dictionary * ini, char * entry); /*-------------------------------------------------------------------------*/ /** @brief Finds out if a given entry exists in a dictionary @param ini Dictionary to search @param entry Name of the entry to look for @return integer 1 if entry exists, 0 otherwise Finds out if a given entry exists in the dictionary. Since sections are stored as keys with NULL associated values, this is the only way of querying for the presence of sections in a dictionary. */ /*--------------------------------------------------------------------------*/ int iniparser_find_entry(dictionary * ini, char * entry) ; /*-------------------------------------------------------------------------*/ /** @brief Parse an ini file and return an allocated dictionary object @param ininame Name of the ini file to read. @return Pointer to newly allocated dictionary This is the parser for ini files. This function is called, providing the name of the file to be read. It returns a dictionary object that should not be accessed directly, but through accessor functions instead. The returned dictionary must be freed using iniparser_freedict(). */ /*--------------------------------------------------------------------------*/ dictionary * iniparser_load(const char * ininame); /*-------------------------------------------------------------------------*/ /** @brief Free all memory associated to an ini dictionary @param d Dictionary to free @return void Free all memory associated to an ini dictionary. It is mandatory to call this function before the dictionary object gets out of the current context. */ /*--------------------------------------------------------------------------*/ void iniparser_freedict(dictionary * d); #endif mtd-utils-1.5.0/ubi-utils/include/libscan.h000066400000000000000000000061241175167361300206000ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * UBI scanning library. */ #ifndef __LIBSCAN_H__ #define __LIBSCAN_H__ #include #ifdef __cplusplus extern "C" { #endif /* * If an eraseblock does not contain an erase counter, this value is used * instead of the erase counter. */ #define NO_EC 0xFFFFFFFF /* * If an eraseblock contains a corrupted erase counter, this value is used * instead of the erase counter. */ #define CORRUPT_EC 0xFFFFFFFE /* * If an eraseblock does not contain an erase counter, one of these values is * used. * * @EB_EMPTY: the eraseblock appeared to be empty * @EB_CORRUPTED: the eraseblock contains corrupted erase counter header * @EB_ALIEN: the eraseblock contains some non-UBI data * @EC_MAX: maximum allowed erase counter value */ enum { EB_EMPTY = 0xFFFFFFFF, EB_CORRUPTED = 0xFFFFFFFE, EB_ALIEN = 0xFFFFFFFD, EB_BAD = 0xFFFFFFFC, EC_MAX = UBI_MAX_ERASECOUNTER, }; /** * struct ubi_scan_info - UBI scanning information. * @ec: erase counters or eraseblock status for all eraseblocks * @mean_ec: mean erase counter * @ok_cnt: count of eraseblock with correct erase counter header * @empty_cnt: count of supposedly eraseblocks * @corrupted_cnt: count of eraseblocks with corrupted erase counter header * @alien_cnt: count of eraseblock containing non-ubi data * @bad_cnt: count of bad eraseblocks * @bad_cnt: count of non-bad eraseblocks * @vid_hdr_offs: volume ID header offset from the found EC headers (%-1 means * undefined) * @data_offs: data offset from the found EC headers (%-1 means undefined) */ struct ubi_scan_info { uint32_t *ec; long long mean_ec; int ok_cnt; int empty_cnt; int corrupted_cnt; int alien_cnt; int bad_cnt; int good_cnt; int vid_hdr_offs; int data_offs; }; struct mtd_dev_info; /** * ubi_scan - scan an MTD device. * @mtd: information about the MTD device to scan * @fd: MTD device node file descriptor * @info: the result of the scanning is returned here * @verbose: verbose mode: %0 - be silent, %1 - output progress information, * 2 - debugging output mode */ int ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info, int verbose); /** * ubi_scan_free - free scanning information. * @si: scanning information to free */ void ubi_scan_free(struct ubi_scan_info *si); #ifdef __cplusplus } #endif #endif /* __LIBSCAN_H__ */ mtd-utils-1.5.0/ubi-utils/include/libubi.h000066400000000000000000000376351175167361300204460ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * UBI (Unsorted Block Images) library. */ #ifndef __LIBUBI_H__ #define __LIBUBI_H__ #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* UBI version libubi is made for */ #define LIBUBI_UBI_VERSION 1 /* Maximum physical eraseblock size in bytes */ #define UBI_MAX_PEB_SZ (2*1024*1024) /* UBI library descriptor */ typedef void * libubi_t; /** * struct ubi_attach_request - MTD device attachment request. * @dev_num: number to assign to the newly created UBI device * (%UBI_DEV_NUM_AUTO should be used to automatically assign the * number) * @mtd_num: MTD device number to attach (used if @mtd_dev_node is %NULL) * @mtd_dev_node: path to MTD device node to attach * @vid_hdr_offset: VID header offset (%0 means default offset and this is what * most of the users want) */ struct ubi_attach_request { int dev_num; int mtd_num; const char *mtd_dev_node; int vid_hdr_offset; }; /** * struct ubi_mkvol_request - volume creation request. * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to * automatically assign ID) * @alignment: volume alignment * @bytes: volume size in bytes * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @name: volume name */ struct ubi_mkvol_request { int vol_id; int alignment; long long bytes; int vol_type; const char *name; }; /** * struct ubi_info - general UBI information. * @dev_count: count of UBI devices in system * @lowest_dev_num: lowest UBI device number * @highest_dev_num: highest UBI device number * @version: UBI version * @ctrl_major: major number of the UBI control device * @ctrl_minor: minor number of the UBI control device */ struct ubi_info { int dev_count; int lowest_dev_num; int highest_dev_num; int version; int ctrl_major; int ctrl_minor; }; /** * struct ubi_dev_info - UBI device information. * @dev_num: UBI device number * @mtd_num: MTD device number on top of which this UBI device is working * @vol_count: count of volumes on this UBI device * @lowest_vol_id: lowest volume ID * @highest_vol_id: highest volume ID * @major: major number of corresponding character device * @minor: minor number of corresponding character device * @total_lebs: total number of logical eraseblocks on this UBI device * @avail_lebs: how many logical eraseblocks are not used and available for new * volumes * @total_bytes: @total_lebs * @leb_size * @avail_bytes: @avail_lebs * @leb_size * @bad_count: count of bad physical eraseblocks * @leb_size: logical eraseblock size * @max_ec: current highest erase counter value * @bad_rsvd: how many physical eraseblocks of the underlying flash device are * reserved for bad eraseblocks handling * @max_vol_count: maximum possible number of volumes on this UBI device * @min_io_size: minimum input/output unit size of the UBI device */ struct ubi_dev_info { int dev_num; int mtd_num; int vol_count; int lowest_vol_id; int highest_vol_id; int major; int minor; int total_lebs; int avail_lebs; long long total_bytes; long long avail_bytes; int bad_count; int leb_size; long long max_ec; int bad_rsvd; int max_vol_count; int min_io_size; }; /** * struct ubi_vol_info - UBI volume information. * @dev_num: UBI device number the volume resides on * @vol_id: ID of this volume * @major: major number of corresponding volume character device * @minor: minor number of corresponding volume character device * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @alignment: alignment of this volume * @data_bytes: how many data bytes are stored on this volume (equivalent to * @rsvd_bytes for dynamic volumes) * @rsvd_bytes: how many bytes are reserved for this volume * @rsvd_lebs: how many logical eraseblocks are reserved for this volume * @leb_size: logical eraseblock size of this volume (may be less then * device's logical eraseblock size due to alignment) * @corrupted: non-zero if the volume is corrupted * @name: volume name (null-terminated) */ struct ubi_vol_info { int dev_num; int vol_id; int major; int minor; int type; int alignment; long long data_bytes; long long rsvd_bytes; int rsvd_lebs; int leb_size; int corrupted; char name[UBI_VOL_NAME_MAX + 1]; }; /** * libubi_open - open UBI library. * * This function initializes and opens the UBI library and returns UBI library * descriptor in case of success and %NULL in case of failure. In case of * failure, errno contains the error code or zero if UBI is not present in the * system. */ libubi_t libubi_open(void); /** * libubi_close - close UBI library. * @desc: UBI library descriptor */ void libubi_close(libubi_t desc); /** * ubi_get_info - get general UBI information. * @desc: UBI library descriptor * @info: pointer to the &struct ubi_info object to fill * * This function fills the passed @info object with general UBI information and * returns %0 in case of success and %-1 in case of failure. */ int ubi_get_info(libubi_t desc, struct ubi_info *info); /** * mtd_num2ubi_dev - find UBI device by attached MTD device. * @@desc: UBI library descriptor * @mtd_num: MTD device number * @dev_num: UBI device number is returned here * * This function finds UBI device to which MTD device @mtd_num is attached. * Returns %0 if the UBI device was found and %-1 if not. */ int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num); /** * ubi_attach_mtd - attach MTD device to UBI. * @desc: UBI library descriptor * @node: name of the UBI control character device node * @req: MTD attach request. * * This function creates a new UBI device by attaching an MTD device as * described by @req. Returns %0 in case of success and %-1 in case of failure. * The newly created UBI device number is returned in @req->dev_num. */ int ubi_attach_mtd(libubi_t desc, const char *node, struct ubi_attach_request *req); /** * ubi_attach - attach an MTD device by its node path. * @desc: UBI library descriptor * @node: name of the UBI control character device node * @req: MTD attach request * * This function creates new UBI device by attaching an MTD device described by * @req. If @req->mtd_dev_node is given it should contain path to the MTD * device node. Otherwise functionality is similar than in function * 'ubi_attach_mtd()' where @req->mtd_num is used. * * Returns %0 in case of success and %-1 in case of failure (errno is set). The * newly created UBI device number is returned in @req->dev_num. */ int ubi_attach(libubi_t desc, const char *node, struct ubi_attach_request *req); /** * ubi_detach_mtd - detach an MTD device. * @desc: UBI library descriptor * @node: name of the UBI control character device node * @mtd_num: MTD device number to detach * * This function detaches MTD device number @mtd_num from UBI, which means the * corresponding UBI device is removed. Returns zero in case of success and %-1 * in case of failure. */ int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num); /** * ubi_detach - detach an MTD device by its node path. * @desc: UBI library descriptor * @node: name of the UBI control character device node * @mtd_dev_node: path to an MTD device node * * This function detaches an MTD device @mtd_dev_node from UBI. Returns zero in * case of success and %-1 in case of failure. */ int ubi_detach(libubi_t desc, const char *node, const char *mtd_dev_node); /** * ubi_remove_dev - remove an UBI device. * @desc: UBI library descriptor * @node: name of the UBI control character device node * @ubi_dev: UBI device number to remove * * This function removes UBI device number @ubi_dev and returns zero in case of * success and %-1 in case of failure. */ int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev); /** * ubi_mkvol - create an UBI volume. * @desc: UBI library descriptor * @node: name of the UBI character device to create a volume at * @req: UBI volume creation request * * This function creates a UBI volume as described at @req and returns %0 in * case of success and %-1 in case of failure. The assigned volume ID is * returned in @req->vol_id. */ int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); /** * ubi_rmvol - remove a UBI volume. * @desc: UBI library descriptor * @node: name of the UBI character device to remove a volume from * @vol_id: ID of the volume to remove * * This function removes volume @vol_id from UBI device @node and returns %0 in * case of success and %-1 in case of failure. */ int ubi_rmvol(libubi_t desc, const char *node, int vol_id); /** * ubi_rnvols - rename UBI volumes. * @desc: UBI library descriptor * @node: name of the UBI character device to remove a volume from * @rnvol: description of volumes to rename * * This function removes volume @vol_id from UBI device @node and returns %0 in * case of success and %-1 in case of failure. */ int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol); /** * ubi_rsvol - re-size UBI volume. * @desc: UBI library descriptor * @node: name of the UBI character device owning the volume which should be * re-sized * @vol_id: volume ID to re-size * @bytes: new volume size in bytes * * This function returns %0 in case of success and %-1 in case of error. */ int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes); /** * ubi_probe_node - test UBI node. * @desc: UBI library descriptor * @node: the node to test * * This function tests whether @node is a UBI device or volume node and returns * %1 if this is an UBI device node, %2 if this is a volume node, and %-1 if * this is not an UBI device or volume node (errno is ENODEV in this case) or * if an error occurred. */ int ubi_probe_node(libubi_t desc, const char *node); /** * ubi_get_dev_info - get UBI device information. * @desc: UBI library descriptor * @node: name of the UBI character device to fetch information about * @info: pointer to the &struct ubi_dev_info object to fill * * This function fills the passed @info object with UBI device information and * returns %0 in case of success and %-1 in case of failure. If the UBI device * corresponding to @node does not exist, errno is set to @ENODEV. */ int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info); /** * ubi_dev_present - check whether an UBI device is present. * @desc: UBI library descriptor * @dev_num: UBI device number to check * * This function returns %1 if UBI device is present and %0 if not. */ int ubi_dev_present(libubi_t desc, int dev_num); /** * ubi_get_dev_info1 - get UBI device information. * @desc: UBI library descriptor * @dev_num: UBI device number to fetch information about * @info: pointer to the &struct ubi_dev_info object to fill * * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI * device number, not UBI character device. If the UBI device @dev_num does not * exist, errno is set to @ENODEV. */ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); /** * ubi_get_vol_info - get UBI volume information. * @desc: UBI library descriptor * @node: name of the UBI volume character device to fetch information about * @info: pointer to the &struct ubi_vol_info object to fill * * This function fills the passed @info object with UBI volume information and * returns %0 in case of success and %-1 in case of failure. If the UBI volume * corresponding to @node does not exist, errno is set to @ENODEV. */ int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info); /** * ubi_get_vol_info1 - get UBI volume information. * @desc: UBI library descriptor * @dev_num: UBI device number * @vol_id: ID of the UBI volume to fetch information about * @info: pointer to the &struct ubi_vol_info object to fill * * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI * volume ID, not UBI volume character device. If the UBI device @dev_num does * not exist, or if the UBI volume @vol_id does not exist, errno is set to * @ENODEV. */ int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, struct ubi_vol_info *info); /** * ubi_get_vol_info1_nm - get UBI volume information by volume name. * @desc: UBI library descriptor * @dev_num: UBI device number * @name: name of the UBI volume to fetch information about * @info: pointer to the &struct ubi_vol_info object to fill * * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI * volume name, not UBI volume ID. If the UBI device @dev_num does not exist, * or if the UBI volume @name does not exist, errno is set to @ENODEV. */ int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name, struct ubi_vol_info *info); /** * ubi_update_start - start UBI volume update. * @desc: UBI library descriptor * @fd: volume character device file descriptor * @bytes: how many bytes will be written to the volume * * This function initiates UBI volume update and returns %0 in case of success * and %-1 in case of error. The caller is assumed to write @bytes data to the * volume @fd afterward. */ int ubi_update_start(libubi_t desc, int fd, long long bytes); /** * ubi_leb_change_start - start atomic LEB change. * @desc: UBI library descriptor * @fd: volume character device file descriptor * @lnum: LEB number to change * @bytes: how many bytes of new data will be written to the LEB * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) * * This function initiates atomic LEB change operation and returns %0 in case * of success and %-1 in case of error. he caller is assumed to write @bytes * data to the volume @fd afterward. */ int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype); /** * ubi_set_property - set volume propety. * @fd: volume character device file descriptor * @property: the property to change (%UBI_PROP_DIRECT_WRITE, etc) * @value: new value of the changed property * * This function changes a property of a volume. Returns zero in case of * success and a negative error code in case of error. */ int ubi_set_property(int fd, uint8_t property, uint64_t value); /** * ubi_leb_unmap - unmap a logical eraseblock. * @fd: volume character device file descriptor * @lnum: logical eraseblock to unmap * * This function unmaps LEB @lnum and returns zero in case of success and a * negative error code in case of error. */ int ubi_leb_unmap(int fd, int lnum); /** * ubi_is_mapped - check if logical eraseblock is mapped. * @fd: volume character device file descriptor * @lnum: logical eraseblock number * * This function checks if logical eraseblock @lnum is mapped to a physical * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily * mean it will still be un-mapped after the UBI device is re-attached. The * logical eraseblock may become mapped to the physical eraseblock it was last * mapped to. * * This function returns %1 if the LEB is mapped, %0 if not, and %-1 in case of * failure. If the volume is damaged because of an interrupted update errno * set with %EBADF error code. */ int ubi_is_mapped(int fd, int lnum); #ifdef __cplusplus } #endif #endif /* !__LIBUBI_H__ */ mtd-utils-1.5.0/ubi-utils/include/libubigen.h000066400000000000000000000137631175167361300211340ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Authors: Frank Haverkamp * Artem Bityutskiy */ #ifndef __LIBUBIGEN_H__ #define __LIBUBIGEN_H__ #include #include #ifdef __cplusplus extern "C" { #endif /** * struct ubigen_info - libubigen information. * @leb_size: logical eraseblock size * @peb_size: size of the physical eraseblock * @min_io_size: minimum input/output unit size * @vid_hdr_offs: offset of the VID header * @data_offs: data offset * @ubi_ver: UBI version * @vtbl_size: volume table size * @max_volumes: maximum amount of volumes * @image_seq: UBI image sequence number */ struct ubigen_info { int leb_size; int peb_size; int min_io_size; int vid_hdr_offs; int data_offs; int ubi_ver; int vtbl_size; int max_volumes; uint32_t image_seq; }; /** * struct ubigen_vol_info - information about a volume. * @id: volume id * @type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) * @alignment: volume alignment * @data_pad: how many bytes are unused at the end of the each physical * eraseblock to satisfy the requested alignment * @usable_leb_size: LEB size accessible for volume users * @name: volume name * @name_len: volume name length * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) * @used_ebs: total number of used logical eraseblocks in this volume (relevant * for static volumes only) * @bytes: size of the volume contents in bytes (relevant for static volumes * only) * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) */ struct ubigen_vol_info { int id; int type; int alignment; int data_pad; int usable_leb_size; const char *name; int name_len; int compat; int used_ebs; long long bytes; uint8_t flags; }; /** * ubigen_info_init - initialize libubigen. * @ui: libubigen information * @peb_size: flash physical eraseblock size * @min_io_size: flash minimum input/output unit size * @subpage_size: flash sub-page, if present (has to be equivalent to * @min_io_size if does not exist) * @vid_hdr_offs: offset of the VID header * @ubi_ver: UBI version * @image_seq: UBI image sequence number */ void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, int subpage_size, int vid_hdr_offs, int ubi_ver, uint32_t image_seq); /** * ubigen_create_empty_vtbl - creates empty volume table. * @ui: libubigen information * * This function creates an empty volume table and returns a pointer to it in * case of success and %NULL in case of failure. The returned object has to be * freed with 'free()' call. */ struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui); /** * ubigen_init_ec_hdr - initialize EC header. * @ui: libubigen information * @hdr: the EC header to initialize * @ec: erase counter value */ void ubigen_init_ec_hdr(const struct ubigen_info *ui, struct ubi_ec_hdr *hdr, long long ec); /** * ubigen_init_vid_hdr - initialize VID header. * @ui: libubigen information * @vi: volume information * @hdr: the VID header to initialize * @lnum: logical eraseblock number * @data: the contents of the LEB (static volumes only) * @data_size: amount of data in this LEB (static volumes only) * * Note, @used_ebs, @data and @data_size are ignored in case of dynamic * volumes. */ void ubigen_init_vid_hdr(const struct ubigen_info *ui, const struct ubigen_vol_info *vi, struct ubi_vid_hdr *hdr, int lnum, const void *data, int data_size); /** * ubigen_add_volume - add a volume to the volume table. * @ui: libubigen information * @vi: volume information * @vtbl: volume table to add to * * This function adds volume described by input parameters to the volume table * @vtbl. */ int ubigen_add_volume(const struct ubigen_info *ui, const struct ubigen_vol_info *vi, struct ubi_vtbl_record *vtbl); /** * ubigen_write_volume - write UBI volume. * @ui: libubigen information * @vi: volume information * @ec: erase counter value to put to EC headers * @bytes: volume size in bytes * @in: input file descriptor (has to be properly seeked) * @out: output file descriptor * * This function reads the contents of the volume from the input file @in and * writes the UBI volume to the output file @out. Returns zero on success and * %-1 on failure. */ int ubigen_write_volume(const struct ubigen_info *ui, const struct ubigen_vol_info *vi, long long ec, long long bytes, int in, int out); /** * ubigen_write_layout_vol - write UBI layout volume * @ui: libubigen information * @peb1: physical eraseblock number to write the first volume table copy * @peb2: physical eraseblock number to write the second volume table copy * @ec1: erase counter value for @peb1 * @ec2: erase counter value for @peb1 * @vtbl: volume table * @fd: output file descriptor seeked to the proper position * * This function creates the UBI layout volume which contains 2 copies of the * volume table. Returns zero in case of success and %-1 in case of failure. */ int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, long long ec1, long long ec2, struct ubi_vtbl_record *vtbl, int fd); #ifdef __cplusplus } #endif #endif /* !__LIBUBIGEN_H__ */ mtd-utils-1.5.0/ubi-utils/include/ubiutils-common.h000066400000000000000000000021401175167361300223050ustar00rootroot00000000000000/* * Copyright (c) Artem Bityutskiy, 2007, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __UBI_UTILS_COMMON_H__ #define __UBI_UTILS_COMMON_H__ #ifdef __cplusplus extern "C" { #endif long long ubiutils_get_bytes(const char *str); void ubiutils_print_bytes(long long bytes, int bracket); void ubiutils_print_text(FILE *stream, const char *txt, int len); int ubiutils_srand(void); #ifdef __cplusplus } #endif #endif /* !__UBI_UTILS_COMMON_H__ */ mtd-utils-1.5.0/ubi-utils/libiniparser.c000066400000000000000000000470411175167361300202230ustar00rootroot00000000000000 /*-------------------------------------------------------------------------*/ /** @file iniparser.c @author N. Devillard @date Sep 2007 @version 3.0 @brief Parser for ini files. */ /*--------------------------------------------------------------------------*/ /* $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $ $Revision: 2.18 $ $Date: 2008-01-03 18:35:39 $ */ /*---------------------------- Includes ------------------------------------*/ #include #include /*---------------------------- Defines -------------------------------------*/ #define ASCIILINESZ (1024) #define INI_INVALID_KEY ((char*)-1) /*--------------------------------------------------------------------------- Private to this module ---------------------------------------------------------------------------*/ /** * This enum stores the status for each parsed line (internal use only). */ typedef enum _line_status_ { LINE_UNPROCESSED, LINE_ERROR, LINE_EMPTY, LINE_COMMENT, LINE_SECTION, LINE_VALUE } line_status ; /*-------------------------------------------------------------------------*/ /** @brief Convert a string to lowercase. @param s String to convert. @return ptr to statically allocated string. This function returns a pointer to a statically allocated string containing a lowercased version of the input string. Do not free or modify the returned string! Since the returned string is statically allocated, it will be modified at each function call (not re-entrant). */ /*--------------------------------------------------------------------------*/ static char * strlwc(const char * s) { static char l[ASCIILINESZ+1]; int i ; if (s==NULL) return NULL ; memset(l, 0, ASCIILINESZ+1); i=0 ; while (s[i] && i l) { if (!isspace((int)*(last-1))) break ; last -- ; } *last = (char)0; return (char*)l ; } /*-------------------------------------------------------------------------*/ /** @brief Get number of sections in a dictionary @param d Dictionary to examine @return int Number of sections found in dictionary This function returns the number of sections found in a dictionary. The test to recognize sections is done on the string stored in the dictionary: a section name is given as "section" whereas a key is stored as "section:key", thus the test looks for entries that do not contain a colon. This clearly fails in the case a section name contains a colon, but this should simply be avoided. This function returns -1 in case of error. */ /*--------------------------------------------------------------------------*/ int iniparser_getnsec(dictionary * d) { int i ; int nsec ; if (d==NULL) return -1 ; nsec=0 ; for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (strchr(d->key[i], ':')==NULL) { nsec ++ ; } } return nsec ; } /*-------------------------------------------------------------------------*/ /** @brief Get name for section n in a dictionary. @param d Dictionary to examine @param n Section number (from 0 to nsec-1). @return Pointer to char string This function locates the n-th section in a dictionary and returns its name as a pointer to a string statically allocated inside the dictionary. Do not free or modify the returned string! This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ char * iniparser_getsecname(dictionary * d, int n) { int i ; int foundsec ; if (d==NULL || n<0) return NULL ; foundsec=0 ; for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (strchr(d->key[i], ':')==NULL) { foundsec++ ; if (foundsec>n) break ; } } if (foundsec<=n) { return NULL ; } return d->key[i] ; } /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @param d Dictionary to dump. @param f Opened file pointer to dump to. @return void This function prints out the contents of a dictionary, one element by line, onto the provided file pointer. It is OK to specify @c stderr or @c stdout as output files. This function is meant for debugging purposes mostly. */ /*--------------------------------------------------------------------------*/ void iniparser_dump(dictionary * d, FILE * f) { int i ; if (d==NULL || f==NULL) return ; for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; if (d->val[i]!=NULL) { fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); } else { fprintf(f, "[%s]=UNDEF\n", d->key[i]); } } return ; } /*-------------------------------------------------------------------------*/ /** @brief Save a dictionary to a loadable ini file @param d Dictionary to dump @param f Opened file pointer to dump to @return void This function dumps a given dictionary into a loadable ini file. It is Ok to specify @c stderr or @c stdout as output files. */ /*--------------------------------------------------------------------------*/ void iniparser_dump_ini(dictionary * d, FILE * f) { int i, j ; char keym[ASCIILINESZ+1]; int nsec ; char * secname ; int seclen ; if (d==NULL || f==NULL) return ; nsec = iniparser_getnsec(d); if (nsec<1) { /* No section in file: dump all keys as they are */ for (i=0 ; isize ; i++) { if (d->key[i]==NULL) continue ; fprintf(f, "%s = %s\n", d->key[i], d->val[i]); } return ; } for (i=0 ; isize ; j++) { if (d->key[j]==NULL) continue ; if (!strncmp(d->key[j], keym, seclen+1)) { fprintf(f, "%-30s = %s\n", d->key[j]+seclen+1, d->val[j] ? d->val[j] : ""); } } } fprintf(f, "\n"); return ; } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key @param d Dictionary to search @param key Key string to look for @param def Default value to return if key not found. @return pointer to statically allocated character string This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the pointer passed as 'def' is returned. The returned char pointer is pointing to a string allocated in the dictionary, do not free or modify it. */ /*--------------------------------------------------------------------------*/ char * iniparser_getstring(dictionary * d, const char * key, char * def) { char * lc_key ; char * sval ; if (d==NULL || key==NULL) return def ; lc_key = strlwc(key); sval = dictionary_get(d, lc_key, def); return sval ; } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to an int @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return integer This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. Supported values for integers include the usual C notation so decimal, octal (starting with 0) and hexadecimal (starting with 0x) are supported. Examples: "42" -> 42 "042" -> 34 (octal -> decimal) "0x42" -> 66 (hexa -> decimal) Warning: the conversion may overflow in various ways. Conversion is totally outsourced to strtol(), see the associated man page for overflow handling. Credits: Thanks to A. Becker for suggesting strtol() */ /*--------------------------------------------------------------------------*/ int iniparser_getint(dictionary * d, const char * key, int notfound) { char * str ; str = iniparser_getstring(d, key, INI_INVALID_KEY); if (str==INI_INVALID_KEY) return notfound ; return (int)strtol(str, NULL, 0); } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a double @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return double This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. */ /*--------------------------------------------------------------------------*/ double iniparser_getdouble(dictionary * d, char * key, double notfound) { char * str ; str = iniparser_getstring(d, key, INI_INVALID_KEY); if (str==INI_INVALID_KEY) return notfound ; return atof(str); } /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a boolean @param d Dictionary to search @param key Key string to look for @param notfound Value to return in case of error @return integer This function queries a dictionary for a key. A key as read from an ini file is given as "section:key". If the key cannot be found, the notfound value is returned. A true boolean is found if one of the following is matched: - A string starting with 'y' - A string starting with 'Y' - A string starting with 't' - A string starting with 'T' - A string starting with '1' A false boolean is found if one of the following is matched: - A string starting with 'n' - A string starting with 'N' - A string starting with 'f' - A string starting with 'F' - A string starting with '0' The notfound value returned if no boolean is identified, does not necessarily have to be 0 or 1. */ /*--------------------------------------------------------------------------*/ int iniparser_getboolean(dictionary * d, const char * key, int notfound) { char * c ; int ret ; c = iniparser_getstring(d, key, INI_INVALID_KEY); if (c==INI_INVALID_KEY) return notfound ; if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { ret = 1 ; } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { ret = 0 ; } else { ret = notfound ; } return ret; } /*-------------------------------------------------------------------------*/ /** @brief Finds out if a given entry exists in a dictionary @param ini Dictionary to search @param entry Name of the entry to look for @return integer 1 if entry exists, 0 otherwise Finds out if a given entry exists in the dictionary. Since sections are stored as keys with NULL associated values, this is the only way of querying for the presence of sections in a dictionary. */ /*--------------------------------------------------------------------------*/ int iniparser_find_entry( dictionary * ini, char * entry ) { int found=0 ; if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { found = 1 ; } return found ; } /*-------------------------------------------------------------------------*/ /** @brief Set an entry in a dictionary. @param ini Dictionary to modify. @param entry Entry to modify (entry name) @param val New value to associate to the entry. @return int 0 if Ok, -1 otherwise. If the given entry can be found in the dictionary, it is modified to contain the provided value. If it cannot be found, -1 is returned. It is Ok to set val to NULL. */ /*--------------------------------------------------------------------------*/ int iniparser_set(dictionary * ini, char * entry, char * val) { return dictionary_set(ini, strlwc(entry), val) ; } /*-------------------------------------------------------------------------*/ /** @brief Delete an entry in a dictionary @param ini Dictionary to modify @param entry Entry to delete (entry name) @return void If the given entry can be found, it is deleted from the dictionary. */ /*--------------------------------------------------------------------------*/ void iniparser_unset(dictionary * ini, char * entry) { dictionary_unset(ini, strlwc(entry)); } /*-------------------------------------------------------------------------*/ /** @brief Load a single line from an INI file @param input_line Input line, may be concatenated multi-line input @param section Output space to store section @param key Output space to store key @param value Output space to store value @return line_status value */ /*--------------------------------------------------------------------------*/ static line_status iniparser_line( char * input_line, char * section, char * key, char * value) { line_status sta ; char line[ASCIILINESZ+1]; int len ; strcpy(line, strstrip(input_line)); len = (int)strlen(line); sta = LINE_UNPROCESSED ; if (len<1) { /* Empty line */ sta = LINE_EMPTY ; } else if (line[0]=='#') { /* Comment line */ sta = LINE_COMMENT ; } else if (line[0]=='[' && line[len-1]==']') { /* Section name */ sscanf(line, "[%[^]]", section); strcpy(section, strstrip(section)); strcpy(section, strlwc(section)); sta = LINE_SECTION ; } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { /* Usual key=value, with or without comments */ strcpy(key, strstrip(key)); strcpy(key, strlwc(key)); strcpy(value, strstrip(value)); /* * sscanf cannot handle '' or "" as empty values * this is done here */ if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { value[0]=0 ; } sta = LINE_VALUE ; } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 || sscanf(line, "%[^=] %[=]", key, value) == 2) { /* * Special cases: * key= * key=; * key=# */ strcpy(key, strstrip(key)); strcpy(key, strlwc(key)); value[0]=0 ; sta = LINE_VALUE ; } else { /* Generate syntax error */ sta = LINE_ERROR ; } return sta ; } /*-------------------------------------------------------------------------*/ /** @brief Parse an ini file and return an allocated dictionary object @param ininame Name of the ini file to read. @return Pointer to newly allocated dictionary This is the parser for ini files. This function is called, providing the name of the file to be read. It returns a dictionary object that should not be accessed directly, but through accessor functions instead. The returned dictionary must be freed using iniparser_freedict(). */ /*--------------------------------------------------------------------------*/ dictionary * iniparser_load(const char * ininame) { FILE * in ; char line [ASCIILINESZ+1] ; char section [ASCIILINESZ+1] ; char key [ASCIILINESZ+1] ; char tmp [ASCIILINESZ+1] ; char val [ASCIILINESZ+1] ; int last=0 ; int len ; int lineno=0 ; int errs=0; dictionary * dict ; if ((in=fopen(ininame, "r"))==NULL) { fprintf(stderr, "iniparser: cannot open %s\n", ininame); return NULL ; } dict = dictionary_new(0) ; if (!dict) { fclose(in); return NULL ; } memset(line, 0, ASCIILINESZ); memset(section, 0, ASCIILINESZ); memset(key, 0, ASCIILINESZ); memset(val, 0, ASCIILINESZ); last=0 ; while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { lineno++ ; len = (int)strlen(line)-1; /* Safety check against buffer overflows */ if (line[len]!='\n') { fprintf(stderr, "iniparser: input line too long in %s (%d)\n", ininame, lineno); dictionary_del(dict); fclose(in); return NULL ; } /* Get rid of \n and spaces at end of line */ while ((len>=0) && ((line[len]=='\n') || (isspace(line[len])))) { line[len]=0 ; len-- ; } /* Detect multi-line */ if (line[len]=='\\') { /* Multi-line value */ last=len ; continue ; } else { last=0 ; } switch (iniparser_line(line, section, key, val)) { case LINE_EMPTY: case LINE_COMMENT: break ; case LINE_SECTION: errs = dictionary_set(dict, section, NULL); break ; case LINE_VALUE: sprintf(tmp, "%s:%s", section, key); errs = dictionary_set(dict, tmp, val) ; break ; case LINE_ERROR: fprintf(stderr, "iniparser: syntax error in %s (%d):\n", ininame, lineno); fprintf(stderr, "-> %s\n", line); errs++ ; break; default: break ; } memset(line, 0, ASCIILINESZ); last=0; if (errs<0) { fprintf(stderr, "iniparser: memory allocation failure\n"); break ; } } if (errs) { dictionary_del(dict); dict = NULL ; } fclose(in); return dict ; } /*-------------------------------------------------------------------------*/ /** @brief Free all memory associated to an ini dictionary @param d Dictionary to free @return void Free all memory associated to an ini dictionary. It is mandatory to call this function before the dictionary object gets out of the current context. */ /*--------------------------------------------------------------------------*/ void iniparser_freedict(dictionary * d) { dictionary_del(d); } /* vim: set ts=4 et sw=4 tw=75 */ mtd-utils-1.5.0/ubi-utils/libscan.c000066400000000000000000000126541175167361300171550ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * UBI scanning library. */ #define PROGRAM_NAME "libscan" #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" static int all_ff(const void *buf, int len) { int i; const uint8_t *p = buf; for (i = 0; i < len; i++) if (p[i] != 0xFF) return 0; return 1; } int ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info, int verbose) { int eb, v = (verbose == 2), pr = (verbose == 1); struct ubi_scan_info *si; unsigned long long sum = 0; si = calloc(1, sizeof(struct ubi_scan_info)); if (!si) return sys_errmsg("cannot allocate %zd bytes of memory", sizeof(struct ubi_scan_info)); si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); if (!si->ec) { sys_errmsg("cannot allocate %zd bytes of memory", sizeof(struct ubi_scan_info)); goto out_si; } si->vid_hdr_offs = si->data_offs = -1; verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); for (eb = 0; eb < mtd->eb_cnt; eb++) { int ret; uint32_t crc; struct ubi_ec_hdr ech; unsigned long long ec; if (v) { normsg_cont("scanning eraseblock %d", eb); fflush(stdout); } if (pr) { printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete ", eb, (long long)(eb + 1) * 100 / mtd->eb_cnt); fflush(stdout); } ret = mtd_is_bad(mtd, fd, eb); if (ret == -1) goto out_ec; if (ret) { si->bad_cnt += 1; si->ec[eb] = EB_BAD; if (v) printf(": bad\n"); continue; } ret = mtd_read(mtd, fd, eb, 0, &ech, sizeof(struct ubi_ec_hdr)); if (ret < 0) goto out_ec; if (be32_to_cpu(ech.magic) != UBI_EC_HDR_MAGIC) { if (all_ff(&ech, sizeof(struct ubi_ec_hdr))) { si->empty_cnt += 1; si->ec[eb] = EB_EMPTY; if (v) printf(": empty\n"); } else { si->alien_cnt += 1; si->ec[eb] = EB_ALIEN; if (v) printf(": alien\n"); } continue; } crc = mtd_crc32(UBI_CRC32_INIT, &ech, UBI_EC_HDR_SIZE_CRC); if (be32_to_cpu(ech.hdr_crc) != crc) { si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; if (v) printf(": bad CRC %#08x, should be %#08x\n", crc, be32_to_cpu(ech.hdr_crc)); continue; } ec = be64_to_cpu(ech.ec); if (ec > EC_MAX) { if (pr) printf("\n"); errmsg("erase counter in EB %d is %llu, while this " "program expects them to be less than %u", eb, ec, EC_MAX); goto out_ec; } if (si->vid_hdr_offs == -1) { si->vid_hdr_offs = be32_to_cpu(ech.vid_hdr_offset); si->data_offs = be32_to_cpu(ech.data_offset); if (si->data_offs % mtd->min_io_size) { if (pr) printf("\n"); if (v) printf(": corrupted because of the below\n"); warnmsg("bad data offset %d at eraseblock %d (n" "of multiple of min. I/O unit size %d)", si->data_offs, eb, mtd->min_io_size); warnmsg("treat eraseblock %d as corrupted", eb); si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; continue; } } else { if ((int)be32_to_cpu(ech.vid_hdr_offset) != si->vid_hdr_offs) { if (pr) printf("\n"); if (v) printf(": corrupted because of the below\n"); warnmsg("inconsistent VID header offset: was " "%d, but is %d in eraseblock %d", si->vid_hdr_offs, be32_to_cpu(ech.vid_hdr_offset), eb); warnmsg("treat eraseblock %d as corrupted", eb); si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; continue; } if ((int)be32_to_cpu(ech.data_offset) != si->data_offs) { if (pr) printf("\n"); if (v) printf(": corrupted because of the below\n"); warnmsg("inconsistent data offset: was %d, but" " is %d in eraseblock %d", si->data_offs, be32_to_cpu(ech.data_offset), eb); warnmsg("treat eraseblock %d as corrupted", eb); si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; continue; } } si->ok_cnt += 1; si->ec[eb] = ec; if (v) printf(": OK, erase counter %u\n", si->ec[eb]); } if (si->ok_cnt != 0) { /* Calculate mean erase counter */ for (eb = 0; eb < mtd->eb_cnt; eb++) { if (si->ec[eb] > EC_MAX) continue; sum += si->ec[eb]; } si->mean_ec = sum / si->ok_cnt; } si->good_cnt = mtd->eb_cnt - si->bad_cnt; verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, si->empty_cnt, si->alien_cnt, si->bad_cnt); *info = si; if (pr) printf("\n"); return 0; out_ec: free(si->ec); out_si: free(si); *info = NULL; return -1; } void ubi_scan_free(struct ubi_scan_info *si) { free(si->ec); free(si); } mtd-utils-1.5.0/ubi-utils/libubi.c000066400000000000000000000732331175167361300170100ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * UBI (Unsorted Block Images) library. */ #define PROGRAM_NAME "libubi" #include #include #include #include #include #include #include #include #include #include #include #include "libubi_int.h" #include "common.h" /** * mkpath - compose full path from 2 given components. * @path: the first component * @name: the second component * * This function returns the resulting path in case of success and %NULL in * case of failure. */ static char *mkpath(const char *path, const char *name) { char *n; int len1 = strlen(path); int len2 = strlen(name); n = malloc(len1 + len2 + 2); if (!n) { sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); return NULL; } memcpy(n, path, len1); if (n[len1 - 1] != '/') n[len1++] = '/'; memcpy(n + len1, name, len2 + 1); return n; } /** * read_positive_ll - read a positive 'long long' value from a file. * @file: the file to read from * @value: the result is stored here * * This function reads file @file and interprets its contents as a positive * 'long long' integer. If this is not true, it fails with %EINVAL error code. * Returns %0 in case of success and %-1 in case of failure. */ static int read_positive_ll(const char *file, long long *value) { int fd, rd; char buf[50]; fd = open(file, O_RDONLY); if (fd == -1) return -1; rd = read(fd, buf, sizeof(buf)); if (rd == -1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (rd == sizeof(buf)) { errmsg("contents of \"%s\" is too long", file); errno = EINVAL; goto out_error; } buf[rd] = '\0'; if (sscanf(buf, "%lld\n", value) != 1) { errmsg("cannot read integer from \"%s\"\n", file); errno = EINVAL; goto out_error; } if (*value < 0) { errmsg("negative value %lld in \"%s\"", *value, file); errno = EINVAL; goto out_error; } if (close(fd)) return sys_errmsg("close failed on \"%s\"", file); return 0; out_error: close(fd); return -1; } /** * read_positive_int - read a positive 'int' value from a file. * @file: the file to read from * @value: the result is stored here * * This function is the same as 'read_positive_ll()', but it reads an 'int' * value, not 'long long'. */ static int read_positive_int(const char *file, int *value) { long long res; if (read_positive_ll(file, &res)) return -1; /* Make sure the value is not too big */ if (res > INT_MAX) { errmsg("value %lld read from file \"%s\" is out of range", res, file); errno = EINVAL; return -1; } *value = res; return 0; } /** * read_data - read data from a file. * @file: the file to read from * @buf: the buffer to read to * @buf_len: buffer length * * This function returns number of read bytes in case of success and %-1 in * case of failure. Note, if the file contains more then @buf_len bytes of * date, this function fails with %EINVAL error code. */ static int read_data(const char *file, void *buf, int buf_len) { int fd, rd, tmp, tmp1; fd = open(file, O_RDONLY); if (fd == -1) return -1; rd = read(fd, buf, buf_len); if (rd == -1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (rd == buf_len) { errmsg("contents of \"%s\" is too long", file); errno = EINVAL; goto out_error; } ((char *)buf)[rd] = '\0'; /* Make sure all data is read */ tmp1 = read(fd, &tmp, 1); if (tmp1 == 1) { sys_errmsg("cannot read \"%s\"", file); goto out_error; } if (tmp1) { errmsg("file \"%s\" contains too much data (> %d bytes)", file, buf_len); errno = EINVAL; goto out_error; } if (close(fd)) { sys_errmsg("close failed on \"%s\"", file); return -1; } return rd; out_error: close(fd); return -1; } /** * read_major - read major and minor numbers from a file. * @file: name of the file to read from * @major: major number is returned here * @minor: minor number is returned here * * This function returns % in case of succes, and %-1 in case of failure. */ static int read_major(const char *file, int *major, int *minor) { int ret; char buf[50]; ret = read_data(file, buf, 50); if (ret < 0) return ret; ret = sscanf(buf, "%d:%d\n", major, minor); if (ret != 2) { errno = EINVAL; return errmsg("\"%s\" does not have major:minor format", file); } if (*major < 0 || *minor < 0) { errno = EINVAL; return errmsg("bad major:minor %d:%d in \"%s\"", *major, *minor, file); } return 0; } /** * dev_read_int - read a positive 'int' value from an UBI device sysfs file. * @patt: file pattern to read from * @dev_num: UBI device number * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_read_int(const char *patt, int dev_num, int *value) { char file[strlen(patt) + 50]; sprintf(file, patt, dev_num); return read_positive_int(file, value); } /** * vol_read_int - read a positive 'int' value from an UBI volume sysfs file. * @patt: file pattern to read from * @dev_num: UBI device number * @vol_id: volume ID * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) { char file[strlen(patt) + 100]; sprintf(file, patt, dev_num, vol_id); return read_positive_int(file, value); } /** * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file. * @patt: file pattern to read from * @dev_num: UBI device number * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_read_ll(const char *patt, int dev_num, long long *value) { char file[strlen(patt) + 50]; sprintf(file, patt, dev_num); return read_positive_ll(file, value); } /** * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file. * @patt: file pattern to read from * @dev_num: UBI device number * @vol_id: volume ID * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. */ static int vol_read_ll(const char *patt, int dev_num, int vol_id, long long *value) { char file[strlen(patt) + 100]; sprintf(file, patt, dev_num, vol_id); return read_positive_ll(file, value); } /** * vol_read_data - read data from an UBI volume's sysfs file. * @patt: file pattern to read from * @dev_num: UBI device number * @vol_id: volume ID * @buf: buffer to read to * @buf_len: buffer length * * This function returns number of read bytes in case of success and %-1 in * case of failure. */ static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, int buf_len) { char file[strlen(patt) + 100]; sprintf(file, patt, dev_num, vol_id); return read_data(file, buf, buf_len); } /** * dev_get_major - get major and minor numbers of an UBI device. * @lib: libubi descriptor * @dev_num: UBI device number * @major: major number is returned here * @minor: minor number is returned here * * This function returns zero in case of succes and %-1 in case of failure. */ static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor) { char file[strlen(lib->dev_dev) + 50]; sprintf(file, lib->dev_dev, dev_num); return read_major(file, major, minor); } /** * vol_get_major - get major and minor numbers of an UBI volume. * @lib: libubi descriptor * @dev_num: UBI device number * @vol_id: volume ID * @major: major number is returned here * @minor: minor number is returned here * * This function returns zero in case of succes and %-1 in case of failure. */ static int vol_get_major(struct libubi *lib, int dev_num, int vol_id, int *major, int *minor) { char file[strlen(lib->vol_dev) + 100]; sprintf(file, lib->vol_dev, dev_num, vol_id); return read_major(file, major, minor); } /** * vol_node2nums - find UBI device number and volume ID by volume device node * file. * @lib: UBI library descriptor * @node: UBI character device node name * @dev_num: UBI device number is returned here * @vol_id: volume ID is returned hers * * This function returns zero in case of succes and %-1 in case of failure. */ static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, int *vol_id) { struct stat st; struct ubi_info info; int i, fd, major, minor; char file[strlen(lib->ubi_vol) + 100]; if (stat(node, &st)) return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errno = EINVAL; return errmsg("\"%s\" is not a character device", node); } major = major(st.st_rdev); minor = minor(st.st_rdev); if (minor == 0) { errno = EINVAL; return errmsg("\"%s\" is not a volume character device", node); } if (ubi_get_info((libubi_t *)lib, &info)) return -1; for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { int major1, minor1, ret; ret = dev_get_major(lib, i, &major1, &minor1); if (ret) { if (errno == ENOENT) continue; return -1; } if (major1 == major) break; } if (i > info.highest_dev_num) { errno = ENODEV; return -1; } /* Make sure this UBI volume exists */ sprintf(file, lib->ubi_vol, i, minor - 1); fd = open(file, O_RDONLY); if (fd == -1) { errno = ENODEV; return -1; } *dev_num = i; *vol_id = minor - 1; errno = 0; return 0; } /** * dev_node2num - find UBI device number by its character device node. * @lib: UBI library descriptor * @node: UBI character device node name * @dev_num: UBI device number is returned here * * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) { struct stat st; struct ubi_info info; int i, major, minor; if (stat(node, &st)) return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errno = EINVAL; return errmsg("\"%s\" is not a character device", node); } major = major(st.st_rdev); minor = minor(st.st_rdev); if (minor != 0) { errno = EINVAL; return errmsg("\"%s\" is not an UBI character device", node); } if (ubi_get_info((libubi_t *)lib, &info)) return -1; for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { int major1, minor1, ret; ret = dev_get_major(lib, i, &major1, &minor1); if (ret) { if (errno == ENOENT) continue; return -1; } if (major1 == major) { if (minor1 != 0) { errmsg("UBI character device minor number is " "%d, but must be 0", minor1); errno = EINVAL; return -1; } errno = 0; *dev_num = i; return 0; } } errno = ENODEV; return -1; } int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num) { struct ubi_info info; int i, ret, mtd_num1; struct libubi *lib = desc; if (ubi_get_info(desc, &info)) return -1; for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1); if (ret) { if (errno == ENOENT) continue; return -1; } if (mtd_num1 == mtd_num) { errno = 0; *dev_num = i; return 0; } } errno = 0; return -1; } libubi_t libubi_open(void) { int fd, version; struct libubi *lib; lib = calloc(1, sizeof(struct libubi)); if (!lib) return NULL; lib->sysfs_ctrl = mkpath("/sys", SYSFS_CTRL); if (!lib->sysfs_ctrl) goto out_error; lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); if (!lib->ctrl_dev) goto out_error; lib->sysfs_ubi = mkpath("/sys", SYSFS_UBI); if (!lib->sysfs_ubi) goto out_error; /* Make sure UBI is present */ fd = open(lib->sysfs_ubi, O_RDONLY); if (fd == -1) { errno = 0; goto out_error; } if (close(fd)) { sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi); goto out_error; } lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); if (!lib->ubi_dev) goto out_error; lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); if (!lib->ubi_version) goto out_error; lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); if (!lib->dev_dev) goto out_error; lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); if (!lib->dev_avail_ebs) goto out_error; lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); if (!lib->dev_total_ebs) goto out_error; lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); if (!lib->dev_bad_count) goto out_error; lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); if (!lib->dev_eb_size) goto out_error; lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); if (!lib->dev_max_ec) goto out_error; lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); if (!lib->dev_bad_rsvd) goto out_error; lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); if (!lib->dev_max_vols) goto out_error; lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); if (!lib->dev_min_io_size) goto out_error; lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM); if (!lib->dev_mtd_num) goto out_error; lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); if (!lib->ubi_vol) goto out_error; lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); if (!lib->vol_type) goto out_error; lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); if (!lib->vol_dev) goto out_error; lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); if (!lib->vol_alignment) goto out_error; lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); if (!lib->vol_data_bytes) goto out_error; lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); if (!lib->vol_rsvd_ebs) goto out_error; lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); if (!lib->vol_eb_size) goto out_error; lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); if (!lib->vol_corrupted) goto out_error; lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); if (!lib->vol_name) goto out_error; if (read_positive_int(lib->ubi_version, &version)) goto out_error; if (version != LIBUBI_UBI_VERSION) { errmsg("this library was made for UBI version %d, but UBI " "version %d is detected\n", LIBUBI_UBI_VERSION, version); goto out_error; } return lib; out_error: libubi_close((libubi_t)lib); return NULL; } void libubi_close(libubi_t desc) { struct libubi *lib = (struct libubi *)desc; free(lib->vol_name); free(lib->vol_corrupted); free(lib->vol_eb_size); free(lib->vol_rsvd_ebs); free(lib->vol_data_bytes); free(lib->vol_alignment); free(lib->vol_dev); free(lib->vol_type); free(lib->ubi_vol); free(lib->dev_mtd_num); free(lib->dev_min_io_size); free(lib->dev_max_vols); free(lib->dev_bad_rsvd); free(lib->dev_max_ec); free(lib->dev_eb_size); free(lib->dev_bad_count); free(lib->dev_total_ebs); free(lib->dev_avail_ebs); free(lib->dev_dev); free(lib->ubi_version); free(lib->ubi_dev); free(lib->sysfs_ubi); free(lib->ctrl_dev); free(lib->sysfs_ctrl); free(lib); } /** * do_attach - perform the actual attach operation. * @node: name of the UBI control character device node * @r: attach request * * This function performs the actual UBI attach operation. Returns %0 in case of * success and %-1 in case of failure. @r->ubi_num contains newly created UBI * device number. */ static int do_attach(const char *node, const struct ubi_attach_req *r) { int fd, ret; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); ret = ioctl(fd, UBI_IOCATT, r); close(fd); if (ret == -1) return -1; #ifdef UDEV_SETTLE_HACK // if (system("udevsettle") == -1) // return -1; usleep(100000); #endif return ret; } int ubi_attach_mtd(libubi_t desc, const char *node, struct ubi_attach_request *req) { struct ubi_attach_req r; int ret; (void)desc; memset(&r, 0, sizeof(struct ubi_attach_req)); r.ubi_num = req->dev_num; r.mtd_num = req->mtd_num; r.vid_hdr_offset = req->vid_hdr_offset; ret = do_attach(node, &r); if (ret == 0) req->dev_num = r.ubi_num; return ret; } #ifndef MTD_CHAR_MAJOR /* * This is taken from kernel and is unlikely to change anytime * soon. */ #define MTD_CHAR_MAJOR 90 #endif /** * mtd_node_to_num - converts device node to MTD number. * @mtd_dev_node: path to device node to convert * * This function converts given @mtd_dev_node to MTD device number. * @mtd_dev_node should contain path to the MTD device node. Returns MTD device * number in case of success and %-1 in case of failure (errno is set). */ static int mtd_node_to_num(const char *mtd_dev_node) { int major, minor; struct stat sb; if (stat(mtd_dev_node, &sb) < 0) return sys_errmsg("cannot stat \"%s\"", mtd_dev_node); if (!S_ISCHR(sb.st_mode)) { errno = EINVAL; return sys_errmsg("\"%s\" is not a character device", mtd_dev_node); } major = major(sb.st_rdev); minor = minor(sb.st_rdev); if (major != MTD_CHAR_MAJOR) { errno = EINVAL; return sys_errmsg("\"%s\" is not an MTD device", mtd_dev_node); } return minor / 2; } int ubi_attach(libubi_t desc, const char *node, struct ubi_attach_request *req) { struct ubi_attach_req r; int ret; if (!req->mtd_dev_node) /* Fallback to opening by mtd_num */ return ubi_attach_mtd(desc, node, req); memset(&r, 0, sizeof(struct ubi_attach_req)); r.ubi_num = req->dev_num; r.vid_hdr_offset = req->vid_hdr_offset; /* * User has passed path to device node. Lets find out MTD device number * of the device and pass it to the kernel. */ r.mtd_num = mtd_node_to_num(req->mtd_dev_node); if (r.mtd_num == -1) return -1; ret = do_attach(node, &r); if (ret == 0) req->dev_num = r.ubi_num; return ret; } int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) { int ret, ubi_dev; ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); if (ret == -1) { errno = ENODEV; return ret; } return ubi_remove_dev(desc, node, ubi_dev); } int ubi_detach(libubi_t desc, const char *node, const char *mtd_dev_node) { int mtd_num; if (!mtd_dev_node) { errno = EINVAL; return -1; } mtd_num = mtd_node_to_num(mtd_dev_node); if (mtd_num == -1) return -1; return ubi_detach_mtd(desc, node, mtd_num); } int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) { int fd, ret; desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); ret = ioctl(fd, UBI_IOCDET, &ubi_dev); if (ret == -1) goto out_close; #ifdef UDEV_SETTLE_HACK // if (system("udevsettle") == -1) // return -1; usleep(100000); #endif out_close: close(fd); return ret; } int ubi_probe_node(libubi_t desc, const char *node) { struct stat st; struct ubi_info info; int i, fd, major, minor; struct libubi *lib = (struct libubi *)desc; char file[strlen(lib->ubi_vol) + 100]; if (stat(node, &st)) return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errmsg("\"%s\" is not a character device", node); errno = EINVAL; return -1; } major = major(st.st_rdev); minor = minor(st.st_rdev); if (ubi_get_info((libubi_t *)lib, &info)) return -1; for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { int major1, minor1, ret; ret = dev_get_major(lib, i, &major1, &minor1); if (ret) { if (errno == ENOENT) continue; if (!errno) goto out_not_ubi; return -1; } if (major1 == major) break; } if (i > info.highest_dev_num) goto out_not_ubi; if (minor == 0) return 1; /* This is supposdely an UBI volume device node */ sprintf(file, lib->ubi_vol, i, minor - 1); fd = open(file, O_RDONLY); if (fd == -1) goto out_not_ubi; return 2; out_not_ubi: errmsg("\"%s\" has major:minor %d:%d, but this does not correspond to " "any existing UBI device or volume", node, major, minor); errno = ENODEV; return -1; } int ubi_get_info(libubi_t desc, struct ubi_info *info) { DIR *sysfs_ubi; struct dirent *dirent; struct libubi *lib = (struct libubi *)desc; memset(info, 0, sizeof(struct ubi_info)); if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) { /* * Older UBI versions did not have control device, so we do not * panic here for compatibility reasons. May be few years later * we could return -1 here, but for now just set major:minor to * -1. */ info->ctrl_major = info->ctrl_minor = -1; } /* * We have to scan the UBI sysfs directory to identify how many UBI * devices are present. */ sysfs_ubi = opendir(lib->sysfs_ubi); if (!sysfs_ubi) return -1; info->lowest_dev_num = INT_MAX; while (1) { int dev_num, ret; char tmp_buf[256]; errno = 0; dirent = readdir(sysfs_ubi); if (!dirent) break; if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_ubi, dirent->d_name); errno = EINVAL; goto out_close; } ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s", &dev_num, tmp_buf); if (ret == 1) { info->dev_count += 1; if (dev_num > info->highest_dev_num) info->highest_dev_num = dev_num; if (dev_num < info->lowest_dev_num) info->lowest_dev_num = dev_num; } } if (!dirent && errno) { sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); goto out_close; } if (closedir(sysfs_ubi)) return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); if (info->lowest_dev_num == INT_MAX) info->lowest_dev_num = 0; if (read_positive_int(lib->ubi_version, &info->version)) return -1; return 0; out_close: closedir(sysfs_ubi); return -1; } int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) { int fd, ret; struct ubi_mkvol_req r; size_t n; memset(&r, 0, sizeof(struct ubi_mkvol_req)); desc = desc; r.vol_id = req->vol_id; r.alignment = req->alignment; r.bytes = req->bytes; r.vol_type = req->vol_type; n = strlen(req->name); if (n > UBI_MAX_VOLUME_NAME) return -1; strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); r.name_len = n; desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); ret = ioctl(fd, UBI_IOCMKVOL, &r); if (ret == -1) { close(fd); return ret; } close(fd); req->vol_id = r.vol_id; #ifdef UDEV_SETTLE_HACK // if (system("udevsettle") == -1) // return -1; usleep(100000); #endif return 0; } int ubi_rmvol(libubi_t desc, const char *node, int vol_id) { int fd, ret; desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); if (ret == -1) { close(fd); return ret; } close(fd); #ifdef UDEV_SETTLE_HACK // if (system("udevsettle") == -1) // return -1; usleep(100000); #endif return 0; } int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol) { int fd, ret; desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return -1; ret = ioctl(fd, UBI_IOCRNVOL, rnvol); if (ret == -1) { close(fd); return ret; } close(fd); #ifdef UDEV_SETTLE_HACK // if (system("udevsettle") == -1) // return -1; usleep(100000); #endif return 0; } int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) { int fd, ret; struct ubi_rsvol_req req; desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); req.bytes = bytes; req.vol_id = vol_id; ret = ioctl(fd, UBI_IOCRSVOL, &req); close(fd); return ret; } int ubi_update_start(libubi_t desc, int fd, long long bytes) { desc = desc; if (ioctl(fd, UBI_IOCVOLUP, &bytes)) return -1; return 0; } int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype) { struct ubi_leb_change_req req; desc = desc; memset(&req, 0, sizeof(struct ubi_leb_change_req)); req.lnum = lnum; req.bytes = bytes; req.dtype = dtype; if (ioctl(fd, UBI_IOCEBCH, &req)) return -1; return 0; } int ubi_dev_present(libubi_t desc, int dev_num) { struct stat st; struct libubi *lib = (struct libubi *)desc; char file[strlen(lib->ubi_dev) + 50]; sprintf(file, lib->ubi_dev, dev_num); return !stat(file, &st); } int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) { DIR *sysfs_ubi; struct dirent *dirent; struct libubi *lib = (struct libubi *)desc; memset(info, 0, sizeof(struct ubi_dev_info)); info->dev_num = dev_num; if (!ubi_dev_present(desc, dev_num)) return -1; sysfs_ubi = opendir(lib->sysfs_ubi); if (!sysfs_ubi) return -1; info->lowest_vol_id = INT_MAX; while (1) { int vol_id, ret, devno; char tmp_buf[256]; errno = 0; dirent = readdir(sysfs_ubi); if (!dirent) break; if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_ubi, dirent->d_name); goto out_close; } ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); if (ret == 2 && devno == dev_num) { info->vol_count += 1; if (vol_id > info->highest_vol_id) info->highest_vol_id = vol_id; if (vol_id < info->lowest_vol_id) info->lowest_vol_id = vol_id; } } if (!dirent && errno) { sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); goto out_close; } if (closedir(sysfs_ubi)) return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); if (info->lowest_vol_id == INT_MAX) info->lowest_vol_id = 0; if (dev_get_major(lib, dev_num, &info->major, &info->minor)) return -1; if (dev_read_int(lib->dev_mtd_num, dev_num, &info->mtd_num)) return -1; if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) return -1; if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) return -1; if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) return -1; if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size)) return -1; if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) return -1; if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) return -1; if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) return -1; if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) return -1; info->avail_bytes = (long long)info->avail_lebs * info->leb_size; info->total_bytes = (long long)info->total_lebs * info->leb_size; return 0; out_close: closedir(sysfs_ubi); return -1; } int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) { int err, dev_num; struct libubi *lib = (struct libubi *)desc; err = ubi_probe_node(desc, node); if (err != 1) { if (err == 2) errno = ENODEV; return -1; } if (dev_node2num(lib, node, &dev_num)) return -1; return ubi_get_dev_info1(desc, dev_num, info); } int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, struct ubi_vol_info *info) { int ret; struct libubi *lib = (struct libubi *)desc; char buf[50]; memset(info, 0, sizeof(struct ubi_vol_info)); info->dev_num = dev_num; info->vol_id = vol_id; if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) return -1; ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); if (ret < 0) return -1; if (strncmp(buf, "static\n", ret) == 0) info->type = UBI_STATIC_VOLUME; else if (strncmp(buf, "dynamic\n", ret) == 0) info->type = UBI_DYNAMIC_VOLUME; else { errmsg("bad value at \"%s\"", buf); errno = EINVAL; return -1; } ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, &info->alignment); if (ret) return -1; ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, &info->data_bytes); if (ret) return -1; ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs); if (ret) return -1; ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size); if (ret) return -1; ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, &info->corrupted); if (ret) return -1; info->rsvd_bytes = (long long)info->leb_size * info->rsvd_lebs; ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, UBI_VOL_NAME_MAX + 2); if (ret < 0) return -1; info->name[ret - 1] = '\0'; return 0; } int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) { int err, vol_id, dev_num; struct libubi *lib = (struct libubi *)desc; err = ubi_probe_node(desc, node); if (err != 2) { if (err == 1) errno = ENODEV; return -1; } if (vol_node2nums(lib, node, &dev_num, &vol_id)) return -1; return ubi_get_vol_info1(desc, dev_num, vol_id, info); } int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name, struct ubi_vol_info *info) { int i, err; unsigned int nlen = strlen(name); struct ubi_dev_info dev_info; if (nlen == 0) { errmsg("bad \"name\" input parameter"); errno = EINVAL; return -1; } err = ubi_get_dev_info1(desc, dev_num, &dev_info); if (err) return err; for (i = dev_info.lowest_vol_id; i <= dev_info.highest_vol_id; i++) { err = ubi_get_vol_info1(desc, dev_num, i, info); if (err == -1) { if (errno == ENOENT) continue; return -1; } if (nlen == strlen(info->name) && !strcmp(name, info->name)) return 0; } errno = ENOENT; return -1; } int ubi_set_property(int fd, uint8_t property, uint64_t value) { struct ubi_set_prop_req r; memset(&r, 0, sizeof(struct ubi_set_prop_req)); r.property = property; r.value = value; return ioctl(fd, UBI_IOCSETPROP, &r); } int ubi_leb_unmap(int fd, int lnum) { return ioctl(fd, UBI_IOCEBUNMAP, &lnum); } int ubi_is_mapped(int fd, int lnum) { return ioctl(fd, UBI_IOCEBISMAP, &lnum); } mtd-utils-1.5.0/ubi-utils/libubi_int.h000066400000000000000000000103051175167361300176560ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem Bityutskiy * * UBI (Unsorted Block Images) library. */ #ifndef __LIBUBI_INT_H__ #define __LIBUBI_INT_H__ #ifdef __cplusplus extern "C" { #endif /* * The below are pre-define UBI file and directory names. * * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' * directories to '/sys/class/ubi/'. For now libubi assumes old layout. */ #define SYSFS_UBI "class/ubi" #define SYSFS_CTRL "class/misc/ubi_ctrl/" #define CTRL_DEV "dev" #define UBI_VER "version" #define UBI_DEV_NAME_PATT "ubi%d" #define DEV_DEV "dev" #define DEV_AVAIL_EBS "avail_eraseblocks" #define DEV_TOTAL_EBS "total_eraseblocks" #define DEV_BAD_COUNT "bad_peb_count" #define DEV_EB_SIZE "eraseblock_size" #define DEV_MAX_EC "max_ec" #define DEV_MAX_RSVD "reserved_for_bad" #define DEV_MAX_VOLS "max_vol_count" #define DEV_MIN_IO_SIZE "min_io_size" #define DEV_MTD_NUM "mtd_num" #define UBI_VOL_NAME_PATT "ubi%d_%d" #define VOL_TYPE "type" #define VOL_DEV "dev" #define VOL_ALIGNMENT "alignment" #define VOL_DATA_BYTES "data_bytes" #define VOL_RSVD_EBS "reserved_ebs" #define VOL_EB_SIZE "usable_eb_size" #define VOL_CORRUPTED "corrupted" #define VOL_NAME "name" /** * libubi - UBI library description data structure. * @sysfs: sysfs file system path * @sysfs_ctrl: UBI control device directory in sysfs * @ctrl_dev: UBI control device major/minor numbers sysfs file * @sysfs_ubi: UBI directory in sysfs * @ubi_dev: UBI device sysfs directory pattern * @ubi_version: UBI version file sysfs path * @dev_dev: UBI device major/minor numbers file pattern * @dev_avail_ebs: count of available eraseblocks sysfs path pattern * @dev_total_ebs: total eraseblocks count sysfs path pattern * @dev_bad_count: count of bad eraseblocks sysfs path pattern * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern * @dev_max_ec: maximum erase counter sysfs path pattern * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks * handling * @dev_max_vols: maximum volumes number count sysfs path pattern * @dev_min_io_size: minimum I/O unit size sysfs path pattern * @dev_mtd_num: MTD device number * @ubi_vol: UBI volume sysfs directory pattern * @vol_type: volume type sysfs path pattern * @vol_dev: volume major/minor numbers file pattern * @vol_alignment: volume alignment sysfs path pattern * @vol_data_bytes: volume data size sysfs path pattern * @vol_rsvd_ebs: volume reserved size sysfs path pattern * @vol_eb_size: volume eraseblock size sysfs path pattern * @vol_corrupted: volume corruption flag sysfs path pattern * @vol_name: volume name sysfs path pattern */ struct libubi { char *sysfs; char *sysfs_ctrl; char *ctrl_dev; char *sysfs_ubi; char *ubi_dev; char *ubi_version; char *dev_dev; char *dev_avail_ebs; char *dev_total_ebs; char *dev_bad_count; char *dev_eb_size; char *dev_max_ec; char *dev_bad_rsvd; char *dev_max_vols; char *dev_min_io_size; char *dev_mtd_num; char *ubi_vol; char *vol_type; char *vol_dev; char *vol_alignment; char *vol_data_bytes; char *vol_rsvd_ebs; char *vol_eb_size; char *vol_corrupted; char *vol_name; char *vol_max_count; }; #ifdef __cplusplus } #endif #endif /* !__LIBUBI_INT_H__ */ mtd-utils-1.5.0/ubi-utils/libubigen.c000066400000000000000000000204171175167361300174760ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Generating UBI images. * * Authors: Oliver Lohmann * Artem Bityutskiy */ #define PROGRAM_NAME "libubigen" #include #include #include #include #include #include #include #include #include "common.h" void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, int subpage_size, int vid_hdr_offs, int ubi_ver, uint32_t image_seq) { if (!vid_hdr_offs) { vid_hdr_offs = UBI_EC_HDR_SIZE + subpage_size - 1; vid_hdr_offs /= subpage_size; vid_hdr_offs *= subpage_size; } ui->peb_size = peb_size; ui->min_io_size = min_io_size; ui->vid_hdr_offs = vid_hdr_offs; ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1; ui->data_offs /= min_io_size; ui->data_offs *= min_io_size; ui->leb_size = peb_size - ui->data_offs; ui->ubi_ver = ubi_ver; ui->image_seq = image_seq; ui->max_volumes = ui->leb_size / UBI_VTBL_RECORD_SIZE; if (ui->max_volumes > UBI_MAX_VOLUMES) ui->max_volumes = UBI_MAX_VOLUMES; ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE; } struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui) { struct ubi_vtbl_record *vtbl; int i; vtbl = calloc(1, ui->vtbl_size); if (!vtbl) { sys_errmsg("cannot allocate %d bytes of memory", ui->vtbl_size); return NULL; } for (i = 0; i < ui->max_volumes; i++) { uint32_t crc = mtd_crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); vtbl[i].crc = cpu_to_be32(crc); } return vtbl; } int ubigen_add_volume(const struct ubigen_info *ui, const struct ubigen_vol_info *vi, struct ubi_vtbl_record *vtbl) { struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id]; uint32_t tmp; if (vi->id >= ui->max_volumes) { errmsg("too high volume id %d, max. volumes is %d", vi->id, ui->max_volumes); errno = EINVAL; return -1; } if (vi->alignment >= ui->leb_size) { errmsg("too large alignment %d, max is %d (LEB size)", vi->alignment, ui->leb_size); errno = EINVAL; return -1; } memset(vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size; vtbl_rec->reserved_pebs = cpu_to_be32(tmp); vtbl_rec->alignment = cpu_to_be32(vi->alignment); vtbl_rec->vol_type = vi->type; tmp = ui->leb_size % vi->alignment; vtbl_rec->data_pad = cpu_to_be32(tmp); vtbl_rec->flags = vi->flags; memcpy(vtbl_rec->name, vi->name, vi->name_len); vtbl_rec->name[vi->name_len] = '\0'; vtbl_rec->name_len = cpu_to_be16(vi->name_len); tmp = mtd_crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); vtbl_rec->crc = cpu_to_be32(tmp); return 0; } void ubigen_init_ec_hdr(const struct ubigen_info *ui, struct ubi_ec_hdr *hdr, long long ec) { uint32_t crc; memset(hdr, 0, sizeof(struct ubi_ec_hdr)); hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); hdr->version = ui->ubi_ver; hdr->ec = cpu_to_be64(ec); hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs); hdr->data_offset = cpu_to_be32(ui->data_offs); hdr->image_seq = cpu_to_be32(ui->image_seq); crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); hdr->hdr_crc = cpu_to_be32(crc); } void ubigen_init_vid_hdr(const struct ubigen_info *ui, const struct ubigen_vol_info *vi, struct ubi_vid_hdr *hdr, int lnum, const void *data, int data_size) { uint32_t crc; memset(hdr, 0, sizeof(struct ubi_vid_hdr)); hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); hdr->version = ui->ubi_ver; hdr->vol_type = vi->type; hdr->vol_id = cpu_to_be32(vi->id); hdr->lnum = cpu_to_be32(lnum); hdr->data_pad = cpu_to_be32(vi->data_pad); hdr->compat = vi->compat; if (vi->type == UBI_VID_STATIC) { hdr->data_size = cpu_to_be32(data_size); hdr->used_ebs = cpu_to_be32(vi->used_ebs); crc = mtd_crc32(UBI_CRC32_INIT, data, data_size); hdr->data_crc = cpu_to_be32(crc); } crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC); hdr->hdr_crc = cpu_to_be32(crc); } int ubigen_write_volume(const struct ubigen_info *ui, const struct ubigen_vol_info *vi, long long ec, long long bytes, int in, int out) { int len = vi->usable_leb_size, rd, lnum = 0; char *inbuf, *outbuf; if (vi->id >= ui->max_volumes) { errmsg("too high volume id %d, max. volumes is %d", vi->id, ui->max_volumes); errno = EINVAL; return -1; } if (vi->alignment >= ui->leb_size) { errmsg("too large alignment %d, max is %d (LEB size)", vi->alignment, ui->leb_size); errno = EINVAL; return -1; } inbuf = malloc(ui->leb_size); if (!inbuf) return sys_errmsg("cannot allocate %d bytes of memory", ui->leb_size); outbuf = malloc(ui->peb_size); if (!outbuf) { sys_errmsg("cannot allocate %d bytes of memory", ui->peb_size); goto out_free; } memset(outbuf, 0xFF, ui->data_offs); ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec); while (bytes) { int l; struct ubi_vid_hdr *vid_hdr; if (bytes < len) len = bytes; bytes -= len; l = len; do { rd = read(in, inbuf + len - l, l); if (rd != l) { sys_errmsg("cannot read %d bytes from the input file", l); goto out_free1; } l -= rd; } while (l); vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); ubigen_init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf, len); memcpy(outbuf + ui->data_offs, inbuf, len); memset(outbuf + ui->data_offs + len, 0xFF, ui->peb_size - ui->data_offs - len); if (write(out, outbuf, ui->peb_size) != ui->peb_size) { sys_errmsg("cannot write %d bytes to the output file", ui->peb_size); goto out_free1; } lnum += 1; } free(outbuf); free(inbuf); return 0; out_free1: free(outbuf); out_free: free(inbuf); return -1; } int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, long long ec1, long long ec2, struct ubi_vtbl_record *vtbl, int fd) { int ret; struct ubigen_vol_info vi; char *outbuf; struct ubi_vid_hdr *vid_hdr; off_t seek; vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS; vi.id = UBI_LAYOUT_VOLUME_ID; vi.alignment = UBI_LAYOUT_VOLUME_ALIGN; vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN; vi.usable_leb_size = ui->leb_size - vi.data_pad; vi.data_pad = ui->leb_size - vi.usable_leb_size; vi.type = UBI_LAYOUT_VOLUME_TYPE; vi.name = UBI_LAYOUT_VOLUME_NAME; vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME); vi.compat = UBI_LAYOUT_VOLUME_COMPAT; outbuf = malloc(ui->peb_size); if (!outbuf) return sys_errmsg("failed to allocate %d bytes", ui->peb_size); memset(outbuf, 0xFF, ui->data_offs); vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); memcpy(outbuf + ui->data_offs, vtbl, ui->vtbl_size); memset(outbuf + ui->data_offs + ui->vtbl_size, 0xFF, ui->peb_size - ui->data_offs - ui->vtbl_size); seek = peb1 * ui->peb_size; if (lseek(fd, seek, SEEK_SET) != seek) { sys_errmsg("cannot seek output file"); goto out_free; } ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1); ubigen_init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0); ret = write(fd, outbuf, ui->peb_size); if (ret != ui->peb_size) { sys_errmsg("cannot write %d bytes", ui->peb_size); goto out_free; } seek = peb2 * ui->peb_size; if (lseek(fd, seek, SEEK_SET) != seek) { sys_errmsg("cannot seek output file"); goto out_free; } ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2); ubigen_init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0); ret = write(fd, outbuf, ui->peb_size); if (ret != ui->peb_size) { sys_errmsg("cannot write %d bytes", ui->peb_size); goto out_free; } free(outbuf); return 0; out_free: free(outbuf); return -1; } mtd-utils-1.5.0/ubi-utils/mtdinfo.c000066400000000000000000000256531175167361300172050ustar00rootroot00000000000000/* * Copyright (C) 2009 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * An utility to get MTD information. * * Author: Artem Bityutskiy */ #define PROGRAM_NAME "mtdinfo" #include #include #include #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" /* The variables below are set by command line arguments */ struct args { unsigned int all:1; unsigned int ubinfo:1; unsigned int map:1; const char *node; }; static struct args args = { .ubinfo = 0, .all = 0, .node = NULL, }; static void display_help(void) { printf( "%1$s version %2$s - a tool to print MTD information.\n" "\n" "Usage: %1$s [--map | -M] [--ubi-info | -u]\n" " %1$s --all [--ubi-info | -u]\n" " %1$s [--help | --version]\n" "\n" "Options:\n" "-u, --ubi-info print what would UBI layout be if it was put\n" " on this MTD device\n" "-M, --map print eraseblock map\n" "-a, --all print information about all MTD devices\n" " Note: `--all' may give less info per device\n" " than, e.g., `mtdinfo /dev/mtdX'\n" "-h, --help print help message\n" "-V, --version print program version\n" "\n" "Examples:\n" " %1$s /dev/mtd0 print information MTD device /dev/mtd0\n" " %1$s /dev/mtd0 -u print information MTD device /dev/mtd0\n" " %4$*3$s and include UBI layout information\n" " %1$s -a print information about all MTD devices\n", PROGRAM_NAME, VERSION, (int)strlen(PROGRAM_NAME) + 3, ""); } static const struct option long_options[] = { { .name = "ubi-info", .has_arg = 0, .flag = NULL, .val = 'u' }, { .name = "map", .has_arg = 0, .flag = NULL, .val = 'M' }, { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int parse_opt(int argc, char * const argv[]) { while (1) { int key; key = getopt_long(argc, argv, "auMhV", long_options, NULL); if (key == -1) break; switch (key) { case 'a': args.all = 1; break; case 'u': args.ubinfo = 1; break; case 'M': args.map = 1; break; case 'h': display_help(); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc - 1) args.node = argv[optind]; else if (optind < argc) return errmsg("more then one MTD device specified (use -h for help)"); if (args.all && args.node) args.node = NULL; if (args.map && !args.node) return errmsg("-M requires MTD device node name"); return 0; } static int translate_dev(libmtd_t libmtd, const char *node) { int err; struct mtd_dev_info mtd; err = mtd_get_dev_info(libmtd, node, &mtd); if (err) { if (errno == ENODEV) return errmsg("\"%s\" does not correspond to any " "existing MTD device", node); return sys_errmsg("cannot get information about MTD " "device \"%s\"", node); } return mtd.mtd_num; } static void print_ubi_info(const struct mtd_info *mtd_info, const struct mtd_dev_info *mtd) { struct ubigen_info ui; if (!mtd_info->sysfs_supported) { errmsg("cannot provide UBI info, becasue sub-page size is " "not known"); return; } ubigen_info_init(&ui, mtd->eb_size, mtd->min_io_size, mtd->subpage_size, 0, 1, 0); printf("Default UBI VID header offset: %d\n", ui.vid_hdr_offs); printf("Default UBI data offset: %d\n", ui.data_offs); printf("Default UBI LEB size: "); ubiutils_print_bytes(ui.leb_size, 0); printf("\n"); printf("Maximum UBI volumes count: %d\n", ui.max_volumes); } static void print_region_map(const struct mtd_dev_info *mtd, int fd, const region_info_t *reginfo) { unsigned long start; int i, width; int ret_locked, errno_locked, ret_bad, errno_bad; printf("Eraseblock map:\n"); /* Figure out the number of spaces to pad w/out libm */ for (i = 1, width = 0; i < reginfo->numblocks; i *= 10, ++width) continue; /* If we don't have a fd to query, just show the bare map */ if (fd == -1) { ret_locked = ret_bad = -1; errno_locked = errno_bad = ENODEV; } else ret_locked = ret_bad = errno_locked = errno_bad = 0; for (i = 0; i < reginfo->numblocks; ++i) { start = reginfo->offset + i * reginfo->erasesize; printf(" %*i: %08lx ", width, i, start); if (ret_locked != -1) { ret_locked = mtd_is_locked(mtd, fd, i); if (ret_locked == 1) printf("RO "); else errno_locked = errno; } if (ret_locked != 1) printf(" "); if (ret_bad != -1) { ret_bad = mtd_is_bad(mtd, fd, i); if (ret_bad == 1) printf("BAD "); else errno_bad = errno; } if (ret_bad != 1) printf(" "); if (((i + 1) % 4) == 0) printf("\n"); } if (i % 4) printf("\n"); if (ret_locked == -1 && errno_locked != EOPNOTSUPP) { errno = errno_locked; sys_errmsg("could not read locked block info"); } if (mtd->bb_allowed && ret_bad == -1 && errno_bad != EOPNOTSUPP) { errno = errno_bad; sys_errmsg("could not read bad block info"); } } static void print_region_info(const struct mtd_dev_info *mtd) { region_info_t reginfo; int r, fd; /* * If we don't have any region info, just return * * FIXME: We can't get region_info (via ioctl) without having the MTD * node path. This is a problem for `mtdinfo -a', for example, * since it doesn't provide any filepath information. */ if (!args.node || (!args.map && mtd->region_cnt == 0)) return; /* First open the device so we can query it */ fd = open(args.node, O_RDONLY | O_CLOEXEC); if (fd == -1) { sys_errmsg("couldn't open MTD dev: %s", args.node); if (mtd->region_cnt) return; } /* Walk all the regions and show the map for them */ if (mtd->region_cnt) { for (r = 0; r < mtd->region_cnt; ++r) { printf("Eraseblock region %i: ", r); if (mtd_regioninfo(fd, r, ®info) == 0) { printf(" offset: %#x size: %#x numblocks: %#x\n", reginfo.offset, reginfo.erasesize, reginfo.numblocks); if (args.map) print_region_map(mtd, fd, ®info); } else printf(" info is unavailable\n"); } } else { reginfo.offset = 0; reginfo.erasesize = mtd->eb_size; reginfo.numblocks = mtd->eb_cnt; reginfo.regionindex = 0; print_region_map(mtd, fd, ®info); } if (fd != -1) close(fd); } static int print_dev_info(libmtd_t libmtd, const struct mtd_info *mtd_info, int mtdn) { int err; struct mtd_dev_info mtd; err = mtd_get_dev_info1(libmtd, mtdn, &mtd); if (err) { if (errno == ENODEV) return errmsg("mtd%d does not correspond to any " "existing MTD device", mtdn); return sys_errmsg("cannot get information about MTD device %d", mtdn); } printf("mtd%d\n", mtd.mtd_num); printf("Name: %s\n", mtd.name); printf("Type: %s\n", mtd.type_str); printf("Eraseblock size: "); ubiutils_print_bytes(mtd.eb_size, 0); printf("\n"); printf("Amount of eraseblocks: %d (", mtd.eb_cnt); ubiutils_print_bytes(mtd.size, 0); printf(")\n"); printf("Minimum input/output unit size: %d %s\n", mtd.min_io_size, mtd.min_io_size > 1 ? "bytes" : "byte"); if (mtd_info->sysfs_supported) printf("Sub-page size: %d %s\n", mtd.subpage_size, mtd.subpage_size > 1 ? "bytes" : "byte"); else if (mtd.type == MTD_NANDFLASH) printf("Sub-page size: unknown\n"); if (mtd.oob_size > 0) printf("OOB size: %d bytes\n", mtd.oob_size); if (mtd.region_cnt > 0) printf("Additional erase regions: %d\n", mtd.oob_size); if (mtd_info->sysfs_supported) printf("Character device major/minor: %d:%d\n", mtd.major, mtd.minor); printf("Bad blocks are allowed: %s\n", mtd.bb_allowed ? "true" : "false"); printf("Device is writable: %s\n", mtd.writable ? "true" : "false"); if (args.ubinfo) print_ubi_info(mtd_info, &mtd); print_region_info(&mtd); printf("\n"); return 0; } static int print_general_info(libmtd_t libmtd, const struct mtd_info *mtd_info, int all) { int i, err, first = 1; struct mtd_dev_info mtd; printf("Count of MTD devices: %d\n", mtd_info->mtd_dev_cnt); if (mtd_info->mtd_dev_cnt == 0) return 0; for (i = mtd_info->lowest_mtd_num; i <= mtd_info->highest_mtd_num; i++) { err = mtd_get_dev_info1(libmtd, i, &mtd); if (err == -1) { if (errno == ENODEV) continue; return sys_errmsg("libmtd failed to get MTD device %d " "information", i); } if (!first) printf(", mtd%d", i); else { printf("Present MTD devices: mtd%d", i); first = 0; } } printf("\n"); printf("Sysfs interface supported: %s\n", mtd_info->sysfs_supported ? "yes" : "no"); if (!all) return 0; printf("\n"); for (i = mtd_info->lowest_mtd_num; i <= mtd_info->highest_mtd_num; i++) { if (!mtd_dev_present(libmtd, i)) continue; err = print_dev_info(libmtd, mtd_info, i); if (err) return err; } return 0; } int main(int argc, char * const argv[]) { int err; libmtd_t libmtd; struct mtd_info mtd_info; err = parse_opt(argc, argv); if (err) return -1; libmtd = libmtd_open(); if (libmtd == NULL) { if (errno == 0) return errmsg("MTD is not present in the system"); return sys_errmsg("cannot open libmtd"); } err = mtd_get_info(libmtd, &mtd_info); if (err) { if (errno == ENODEV) return errmsg("MTD is not present"); return sys_errmsg("cannot get MTD information"); } if (!args.all && args.node) { int mtdn; /* * A character device was specified, translate this to MTD * device number. */ mtdn = translate_dev(libmtd, args.node); if (mtdn < 0) goto out_libmtd; err = print_dev_info(libmtd, &mtd_info, mtdn); } else err = print_general_info(libmtd, &mtd_info, args.all); if (err) goto out_libmtd; libmtd_close(libmtd); return 0; out_libmtd: libmtd_close(libmtd); return -1; } mtd-utils-1.5.0/ubi-utils/ubiattach.c000066400000000000000000000141071175167361300175010ustar00rootroot00000000000000/* * Copyright (C) 2007 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * An utility to attach MTD devices to UBI. * * Author: Artem Bityutskiy */ #define PROGRAM_NAME "ubiattach" #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" #define DEFAULT_CTRL_DEV "/dev/ubi_ctrl" /* The variables below are set by command line arguments */ struct args { int devn; int mtdn; int vidoffs; const char *node; const char *dev; }; static struct args args = { .devn = UBI_DEV_NUM_AUTO, .mtdn = -1, .vidoffs = 0, .node = NULL, .dev = NULL, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to attach MTD device to UBI."; static const char optionsstr[] = "-d, --devn= the number to assign to the newly created UBI device\n" " (assigned automatically if this is not specified)\n" "-p, --dev-path= path to MTD device node to attach\n" "-m, --mtdn= MTD device number to attach (alternative method, e.g\n" " if the character device node does not exist)\n" "-O, --vid-hdr-offset VID header offset (do not specify this unless you really\n" " know what you are doing, the default should be optimal)\n" "-h, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " []\n" "\t[-m ] [-d ] [-p ]\n" "\t[--mtdn=] [--devn=]\n" "\t[--dev-path=]\n" "UBI control device defaults to " DEFAULT_CTRL_DEV " if not supplied.\n" "Example 1: " PROGRAM_NAME " -p /dev/mtd0 - attach /dev/mtd0 to UBI\n" "Example 2: " PROGRAM_NAME " -m 0 - attach MTD device 0 (mtd0) to UBI\n" "Example 3: " PROGRAM_NAME " -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI\n" " and create UBI device number 3 (ubi3)"; static const struct option long_options[] = { { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, { .name = "dev-path", .has_arg = 1, .flag = NULL, .val = 'p' }, { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "p:m:d:O:hV", long_options, NULL); if (key == -1) break; switch (key) { case 'p': args.dev = optarg; break; case 'd': args.devn = simple_strtoul(optarg, &error); if (error || args.devn < 0) return errmsg("bad UBI device number: \"%s\"", optarg); break; case 'm': args.mtdn = simple_strtoul(optarg, &error); if (error || args.mtdn < 0) return errmsg("bad MTD device number: \"%s\"", optarg); break; case 'O': args.vidoffs = simple_strtoul(optarg, &error); if (error || args.vidoffs <= 0) return errmsg("bad VID header offset: \"%s\"", optarg); break; case 'h': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) args.node = DEFAULT_CTRL_DEV; else if (optind != argc - 1) return errmsg("more then one UBI control device specified (use -h for help)"); else args.node = argv[optind]; if (args.mtdn == -1 && args.dev == NULL) return errmsg("MTD device to attach was not specified (use -h for help)"); return 0; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; struct ubi_info ubi_info; struct ubi_dev_info dev_info; struct ubi_attach_request req; err = parse_opt(argc, argv); if (err) return -1; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } /* * Make sure the kernel is fresh enough and this feature is supported. */ err = ubi_get_info(libubi, &ubi_info); if (err) { sys_errmsg("cannot get UBI information"); goto out_libubi; } if (ubi_info.ctrl_major == -1) { errmsg("MTD attach/detach feature is not supported by your kernel"); goto out_libubi; } req.dev_num = args.devn; req.mtd_num = args.mtdn; req.vid_hdr_offset = args.vidoffs; req.mtd_dev_node = args.dev; err = ubi_attach(libubi, args.node, &req); if (err) { if (args.dev) sys_errmsg("cannot attach \"%s\"", args.dev); else sys_errmsg("cannot attach mtd%d", args.mtdn); goto out_libubi; } /* Print some information about the new UBI device */ err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info); if (err) { sys_errmsg("cannot get information about newly created UBI device"); goto out_libubi; } printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs); ubiutils_print_bytes(dev_info.total_bytes, 0); printf("), available %d LEBs (", dev_info.avail_lebs); ubiutils_print_bytes(dev_info.avail_bytes, 0); printf("), LEB size "); ubiutils_print_bytes(dev_info.leb_size, 1); printf("\n"); libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubicrc32.c000066400000000000000000000053551175167361300171560ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image. * * Author: Oliver Lohmann */ #define PROGRAM_NAME "ubicrc32" #include #include #include #include #include #include #include #include "common.h" #define BUFSIZE 4096 static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)"; static const char optionsstr[] = "-h, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " [-h] [--help]"; static const struct option long_options[] = { { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int parse_opt(int argc, char * const argv[]) { while (1) { int key; key = getopt_long(argc, argv, "hV", long_options, NULL); if (key == -1) break; switch (key) { case 'h': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } return 0; } int main(int argc, char * const argv[]) { int err = 0; uint32_t crc = UBI_CRC32_INIT; char buf[BUFSIZE]; FILE *fp; if (argc > 1) { fp = fopen(argv[1], "r"); if (!fp) return sys_errmsg("cannot open \"%s\"", argv[1]); } else fp = stdin; err = parse_opt(argc, argv); if (err) return err; while (!feof(fp)) { size_t read; read = fread(buf, 1, BUFSIZE, fp); if (ferror(fp)) { sys_errmsg("cannot read input file"); err = -1; goto out_close; } crc = mtd_crc32(crc, buf, read); } printf("0x%08x\n", crc); out_close: if (fp != stdin) fclose(fp); return err; } mtd-utils-1.5.0/ubi-utils/ubidetach.c000066400000000000000000000125071175167361300174670ustar00rootroot00000000000000/* * Copyright (C) 2007 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * An utility to delete UBI devices (detach MTD devices from UBI). * * Author: Artem Bityutskiy */ #define PROGRAM_NAME "ubidetach" #include #include #include #include #include #include #include "common.h" #define DEFAULT_CTRL_DEV "/dev/ubi_ctrl" /* The variables below are set by command line arguments */ struct args { int devn; int mtdn; const char *node; const char *dev; }; static struct args args = { .devn = UBI_DEV_NUM_AUTO, .mtdn = -1, .node = NULL, .dev = NULL, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - tool to remove UBI devices (detach MTD devices from UBI)"; static const char optionsstr[] = "-d, --devn= UBI device number to delete\n" "-p, --dev-path= or alternatively, MTD device node path to detach\n" "-m, --mtdn= or alternatively, MTD device number to detach\n" "-h, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " []\n" "\t[-d ] [-m ] [-p ]\n" "\t[--devn=] [--mtdn=]\n" "\t[--dev-path=]\n" "UBI control device defaults to " DEFAULT_CTRL_DEV " if not supplied.\n" "Example 1: " PROGRAM_NAME " -p /dev/mtd0 - detach MTD device /dev/mtd0\n" "Example 2: " PROGRAM_NAME " -d 2 - delete UBI device 2 (ubi2)\n" "Example 3: " PROGRAM_NAME " -m 0 - detach MTD device 0 (mtd0)"; static const struct option long_options[] = { { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, { .name = "dev-path", .has_arg = 1, .flag = NULL, .val = 'p' }, { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "p:m:d:hV", long_options, NULL); if (key == -1) break; switch (key) { case 'p': args.dev = optarg; break; case 'd': args.devn = simple_strtoul(optarg, &error); if (error || args.devn < 0) return errmsg("bad UBI device number: \"%s\"", optarg); break; case 'm': args.mtdn = simple_strtoul(optarg, &error); if (error || args.mtdn < 0) return errmsg("bad MTD device number: \"%s\"", optarg); break; case 'h': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) args.node = DEFAULT_CTRL_DEV; else if (optind != argc - 1) return errmsg("more then one UBI control device specified (use -h for help)"); else args.node = argv[optind]; if (args.mtdn == -1 && args.devn == -1 && args.dev == NULL) return errmsg("neither MTD nor UBI devices were specified (use -h for help)"); if (args.devn != -1) { if (args.mtdn != -1 || args.dev != NULL) return errmsg("specify either MTD or UBI device (use -h for help)"); } else if (args.mtdn != -1 && args.dev != NULL) return errmsg("specify either MTD number or device node (use -h for help)"); return 0; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; struct ubi_info ubi_info; err = parse_opt(argc, argv); if (err) return -1; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } /* * Make sure the kernel is fresh enough and this feature is supported. */ err = ubi_get_info(libubi, &ubi_info); if (err) { sys_errmsg("cannot get UBI information"); goto out_libubi; } if (ubi_info.ctrl_major == -1) { errmsg("MTD detach/detach feature is not supported by your kernel"); goto out_libubi; } if (args.devn != -1) { err = ubi_remove_dev(libubi, args.node, args.devn); if (err) { sys_errmsg("cannot remove ubi%d", args.devn); goto out_libubi; } } else { if (args.dev != NULL) { err = ubi_detach(libubi, args.node, args.dev); if (err) { sys_errmsg("cannot detach \"%s\"", args.dev); goto out_libubi; } } else { err = ubi_detach_mtd(libubi, args.node, args.mtdn); if (err) { sys_errmsg("cannot detach mtd%d", args.mtdn); goto out_libubi; } } } libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubiformat.c000066400000000000000000000601451175167361300175300ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * An utility to format MTD devices into UBI and flash UBI images. * * Author: Artem Bityutskiy */ /* * Maximum amount of consequtive eraseblocks which are considered as normal by * this utility. Otherwise it is assume that something is wrong with the flash * or the driver, and eraseblocks are stopped being marked as bad. */ #define MAX_CONSECUTIVE_BAD_BLOCKS 4 #define PROGRAM_NAME "ubiformat" #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" /* The variables below are set by command line arguments */ struct args { unsigned int yes:1; unsigned int quiet:1; unsigned int verbose:1; unsigned int override_ec:1; unsigned int novtbl:1; unsigned int manual_subpage; int subpage_size; int vid_hdr_offs; int ubi_ver; uint32_t image_seq; off_t image_sz; long long ec; const char *image; const char *node; int node_fd; }; static struct args args = { .ubi_ver = 1, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to format MTD devices and flash UBI images"; static const char optionsstr[] = "-s, --sub-page-size= minimum input/output unit used for UBI\n" " headers, e.g. sub-page size in case of NAND\n" " flash (equivalent to the minimum input/output\n" " unit size by default)\n" "-O, --vid-hdr-offset= offset if the VID header from start of the\n" " physical eraseblock (default is the next\n" " minimum I/O unit or sub-page after the EC\n" " header)\n" "-n, --no-volume-table only erase all eraseblock and preserve erase\n" " counters, do not write empty volume table\n" "-f, --flash-image= flash image file, or '-' for stdin\n" "-S, --image-size= bytes in input, if not reading from file\n" "-e, --erase-counter= use as the erase counter value for all\n" " eraseblocks\n" "-x, --ubi-ver= UBI version number to put to EC headers\n" " (default is 1)\n" "-Q, --image-seq= 32-bit UBI image sequence number to use\n" " (by default a random number is picked)\n" "-y, --yes assume the answer is \"yes\" for all question\n" " this program would otherwise ask\n" "-q, --quiet suppress progress percentage information\n" "-v, --verbose be verbose\n" "-h, -?, --help print help message\n" "-V, --version print program version\n"; static const char usage[] = "Usage: " PROGRAM_NAME " [-s ] [-O ] [-n]\n" "\t\t\t[-f ] [-S ] [-e ] [-x ] [-y] [-q] [-v] [-h] [-v]\n" "\t\t\t[--sub-page-size=] [--vid-hdr-offset=] [--no-volume-table]\n" "\t\t\t[--flash-image=] [--image-size=] [--erase-counter=]\n" "\t\t\t[--ubi-ver=] [--yes] [--quiet] [--verbose] [--help] [--version]\n\n" "Example 1: " PROGRAM_NAME " /dev/mtd0 -y - format MTD device number 0 and do\n" " not ask questions.\n" "Example 2: " PROGRAM_NAME " /dev/mtd0 -q -e 0 - format MTD device number 0,\n" " be quiet and force erase counter value 0."; static const struct option long_options[] = { { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, { .name = "no-volume-table", .has_arg = 0, .flag = NULL, .val = 'n' }, { .name = "flash-image", .has_arg = 1, .flag = NULL, .val = 'f' }, { .name = "image-size", .has_arg = 1, .flag = NULL, .val = 'S' }, { .name = "yes", .has_arg = 0, .flag = NULL, .val = 'y' }, { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, { .name = "quiet", .has_arg = 0, .flag = NULL, .val = 'q' }, { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int parse_opt(int argc, char * const argv[]) { ubiutils_srand(); args.image_seq = rand(); while (1) { int key, error = 0; unsigned long int image_seq; key = getopt_long(argc, argv, "nh?Vyqve:x:s:O:f:S:", long_options, NULL); if (key == -1) break; switch (key) { case 's': args.subpage_size = ubiutils_get_bytes(optarg); if (args.subpage_size <= 0) return errmsg("bad sub-page size: \"%s\"", optarg); if (!is_power_of_2(args.subpage_size)) return errmsg("sub-page size should be power of 2"); break; case 'O': args.vid_hdr_offs = simple_strtoul(optarg, &error); if (error || args.vid_hdr_offs <= 0) return errmsg("bad VID header offset: \"%s\"", optarg); break; case 'e': args.ec = simple_strtoull(optarg, &error); if (error || args.ec < 0) return errmsg("bad erase counter value: \"%s\"", optarg); if (args.ec >= EC_MAX) return errmsg("too high erase %llu, counter, max is %u", args.ec, EC_MAX); args.override_ec = 1; break; case 'f': args.image = optarg; break; case 'S': args.image_sz = ubiutils_get_bytes(optarg); if (args.image_sz <= 0) return errmsg("bad image-size: \"%s\"", optarg); break; case 'n': args.novtbl = 1; break; case 'y': args.yes = 1; break; case 'q': args.quiet = 1; break; case 'x': args.ubi_ver = simple_strtoul(optarg, &error); if (error || args.ubi_ver < 0) return errmsg("bad UBI version: \"%s\"", optarg); break; case 'Q': image_seq = simple_strtoul(optarg, &error); if (error || image_seq > 0xFFFFFFFF) return errmsg("bad UBI image sequence number: \"%s\"", optarg); args.image_seq = image_seq; break; case 'v': args.verbose = 1; break; case 'V': common_print_version(); exit(EXIT_SUCCESS); case 'h': case '?': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (args.quiet && args.verbose) return errmsg("using \"-q\" and \"-v\" at the same time does not make sense"); if (optind == argc) return errmsg("MTD device name was not specified (use -h for help)"); else if (optind != argc - 1) return errmsg("more then one MTD device specified (use -h for help)"); if (args.image && args.novtbl) return errmsg("-n cannot be used together with -f"); args.node = argv[optind]; return 0; } static int want_exit(void) { char buf[4]; while (1) { normsg_cont("continue? (yes/no) "); if (scanf("%3s", buf) == EOF) { sys_errmsg("scanf returned unexpected EOF, assume \"yes\""); return 1; } if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) return 0; if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) return 1; } } static int answer_is_yes(void) { char buf[4]; while (1) { if (scanf("%3s", buf) == EOF) { sys_errmsg("scanf returned unexpected EOF, assume \"no\""); return 0; } if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) return 1; if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) return 0; } } static void print_bad_eraseblocks(const struct mtd_dev_info *mtd, const struct ubi_scan_info *si) { int first = 1, eb; if (si->bad_cnt == 0) return; normsg_cont("%d bad eraseblocks found, numbers: ", si->bad_cnt); for (eb = 0; eb < mtd->eb_cnt; eb++) { if (si->ec[eb] != EB_BAD) continue; if (first) { printf("%d", eb); first = 0; } else printf(", %d", eb); } printf("\n"); } static int change_ech(struct ubi_ec_hdr *hdr, uint32_t image_seq, long long ec) { uint32_t crc; /* Check the EC header */ if (be32_to_cpu(hdr->magic) != UBI_EC_HDR_MAGIC) return errmsg("bad UBI magic %#08x, should be %#08x", be32_to_cpu(hdr->magic), UBI_EC_HDR_MAGIC); crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); if (be32_to_cpu(hdr->hdr_crc) != crc) return errmsg("bad CRC %#08x, should be %#08x\n", crc, be32_to_cpu(hdr->hdr_crc)); hdr->image_seq = cpu_to_be32(image_seq); hdr->ec = cpu_to_be64(ec); crc = mtd_crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); hdr->hdr_crc = cpu_to_be32(crc); return 0; } static int drop_ffs(const struct mtd_dev_info *mtd, const void *buf, int len) { int i; for (i = len - 1; i >= 0; i--) if (((const uint8_t *)buf)[i] != 0xFF) break; /* The resulting length must be aligned to the minimum flash I/O size */ len = i + 1; len = (len + mtd->min_io_size - 1) / mtd->min_io_size; len *= mtd->min_io_size; return len; } static int open_file(off_t *sz) { int fd; if (!strcmp(args.image, "-")) { if (args.image_sz == 0) return errmsg("must use '-S' with non-zero value when reading from stdin"); *sz = args.image_sz; fd = dup(STDIN_FILENO); if (fd < 0) return sys_errmsg("failed to dup stdin"); } else { struct stat st; if (stat(args.image, &st)) return sys_errmsg("cannot open \"%s\"", args.image); *sz = st.st_size; fd = open(args.image, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", args.image); } return fd; } static int read_all(int fd, void *buf, size_t len) { while (len > 0) { ssize_t l = read(fd, buf, len); if (l == 0) return errmsg("eof reached; %zu bytes remaining", len); else if (l > 0) { buf += l; len -= l; } else if (errno == EINTR || errno == EAGAIN) continue; else return sys_errmsg("reading failed; %zu bytes remaining", len); } return 0; } /* * Returns %-1 if consecutive bad blocks exceeds the * MAX_CONSECUTIVE_BAD_BLOCKS and returns %0 otherwise. */ static int consecutive_bad_check(int eb) { static int consecutive_bad_blocks = 1; static int prev_bb = -1; if (prev_bb == -1) prev_bb = eb; if (eb == prev_bb + 1) consecutive_bad_blocks += 1; else consecutive_bad_blocks = 1; prev_bb = eb; if (consecutive_bad_blocks >= MAX_CONSECUTIVE_BAD_BLOCKS) { if (!args.quiet) printf("\n"); return errmsg("consecutive bad blocks exceed limit: %d, bad flash?", MAX_CONSECUTIVE_BAD_BLOCKS); } return 0; } /* TODO: we should actually torture the PEB before marking it as bad */ static int mark_bad(const struct mtd_dev_info *mtd, struct ubi_scan_info *si, int eb) { int err; if (!args.yes) { normsg_cont("mark it as bad? Continue (yes/no) "); if (!answer_is_yes()) return -1; } if (!args.quiet) normsg_cont("marking block %d bad", eb); if (!args.quiet) printf("\n"); if (!mtd->bb_allowed) { if (!args.quiet) printf("\n"); return errmsg("bad blocks not supported by this flash"); } err = mtd_mark_bad(mtd, args.node_fd, eb); if (err) return err; si->bad_cnt += 1; si->ec[eb] = EB_BAD; return consecutive_bad_check(eb); } static int flash_image(libmtd_t libmtd, const struct mtd_dev_info *mtd, const struct ubigen_info *ui, struct ubi_scan_info *si) { int fd, img_ebs, eb, written_ebs = 0, divisor, skip_data_read = 0; off_t st_size; fd = open_file(&st_size); if (fd < 0) return fd; img_ebs = st_size / mtd->eb_size; if (img_ebs > si->good_cnt) { sys_errmsg("file \"%s\" is too large (%lld bytes)", args.image, (long long)st_size); goto out_close; } if (st_size % mtd->eb_size) { return sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of ""eraseblock size (%d bytes)", args.image, (long long)st_size, mtd->eb_size); goto out_close; } verbose(args.verbose, "will write %d eraseblocks", img_ebs); divisor = img_ebs; for (eb = 0; eb < mtd->eb_cnt; eb++) { int err, new_len; char buf[mtd->eb_size]; long long ec; if (!args.quiet && !args.verbose) { printf("\r" PROGRAM_NAME ": flashing eraseblock %d -- %2lld %% complete ", eb, (long long)(eb + 1) * 100 / divisor); fflush(stdout); } if (si->ec[eb] == EB_BAD) { divisor += 1; continue; } if (args.verbose) { normsg_cont("eraseblock %d: erase", eb); fflush(stdout); } err = mtd_erase(libmtd, mtd, args.node_fd, eb); if (err) { if (!args.quiet) printf("\n"); sys_errmsg("failed to erase eraseblock %d", eb); if (errno != EIO) goto out_close; if (mark_bad(mtd, si, eb)) goto out_close; continue; } if (!skip_data_read) { err = read_all(fd, buf, mtd->eb_size); if (err) { sys_errmsg("failed to read eraseblock %d from \"%s\"", written_ebs, args.image); goto out_close; } } skip_data_read = 0; if (args.override_ec) ec = args.ec; else if (si->ec[eb] <= EC_MAX) ec = si->ec[eb] + 1; else ec = si->mean_ec; if (args.verbose) { printf(", change EC to %lld", ec); fflush(stdout); } err = change_ech((struct ubi_ec_hdr *)buf, ui->image_seq, ec); if (err) { errmsg("bad EC header at eraseblock %d of \"%s\"", written_ebs, args.image); goto out_close; } if (args.verbose) { printf(", write data\n"); fflush(stdout); } new_len = drop_ffs(mtd, buf, mtd->eb_size); err = mtd_write(libmtd, mtd, args.node_fd, eb, 0, buf, new_len, NULL, 0, 0); if (err) { sys_errmsg("cannot write eraseblock %d", eb); if (errno != EIO) goto out_close; err = mtd_torture(libmtd, mtd, args.node_fd, eb); if (err) { if (mark_bad(mtd, si, eb)) goto out_close; } /* * We have to make sure that we do not read next block * of data from the input image or stdin - we have to * write buf first instead. */ skip_data_read = 1; continue; } if (++written_ebs >= img_ebs) break; } if (!args.quiet && !args.verbose) printf("\n"); close(fd); return eb + 1; out_close: close(fd); return -1; } static int format(libmtd_t libmtd, const struct mtd_dev_info *mtd, const struct ubigen_info *ui, struct ubi_scan_info *si, int start_eb, int novtbl) { int eb, err, write_size; struct ubi_ec_hdr *hdr; struct ubi_vtbl_record *vtbl; int eb1 = -1, eb2 = -1; long long ec1 = -1, ec2 = -1; write_size = UBI_EC_HDR_SIZE + mtd->subpage_size - 1; write_size /= mtd->subpage_size; write_size *= mtd->subpage_size; hdr = malloc(write_size); if (!hdr) return sys_errmsg("cannot allocate %d bytes of memory", write_size); memset(hdr, 0xFF, write_size); for (eb = start_eb; eb < mtd->eb_cnt; eb++) { long long ec; if (!args.quiet && !args.verbose) { printf("\r" PROGRAM_NAME ": formatting eraseblock %d -- %2lld %% complete ", eb, (long long)(eb + 1 - start_eb) * 100 / (mtd->eb_cnt - start_eb)); fflush(stdout); } if (si->ec[eb] == EB_BAD) continue; if (args.override_ec) ec = args.ec; else if (si->ec[eb] <= EC_MAX) ec = si->ec[eb] + 1; else ec = si->mean_ec; ubigen_init_ec_hdr(ui, hdr, ec); if (args.verbose) { normsg_cont("eraseblock %d: erase", eb); fflush(stdout); } err = mtd_erase(libmtd, mtd, args.node_fd, eb); if (err) { if (!args.quiet) printf("\n"); sys_errmsg("failed to erase eraseblock %d", eb); if (errno != EIO) goto out_free; if (mark_bad(mtd, si, eb)) goto out_free; continue; } if ((eb1 == -1 || eb2 == -1) && !novtbl) { if (eb1 == -1) { eb1 = eb; ec1 = ec; } else if (eb2 == -1) { eb2 = eb; ec2 = ec; } if (args.verbose) printf(", do not write EC, leave for vtbl\n"); continue; } if (args.verbose) { printf(", write EC %lld\n", ec); fflush(stdout); } err = mtd_write(libmtd, mtd, args.node_fd, eb, 0, hdr, write_size, NULL, 0, 0); if (err) { if (!args.quiet && !args.verbose) printf("\n"); sys_errmsg("cannot write EC header (%d bytes buffer) to eraseblock %d", write_size, eb); if (errno != EIO) { if (!args.subpage_size != mtd->min_io_size) normsg("may be sub-page size is " "incorrect?"); goto out_free; } err = mtd_torture(libmtd, mtd, args.node_fd, eb); if (err) { if (mark_bad(mtd, si, eb)) goto out_free; } continue; } } if (!args.quiet && !args.verbose) printf("\n"); if (!novtbl) { if (eb1 == -1 || eb2 == -1) { errmsg("no eraseblocks for volume table"); goto out_free; } verbose(args.verbose, "write volume table to eraseblocks %d and %d", eb1, eb2); vtbl = ubigen_create_empty_vtbl(ui); if (!vtbl) goto out_free; err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl, args.node_fd); free(vtbl); if (err) { errmsg("cannot write layout volume"); goto out_free; } } free(hdr); return 0; out_free: free(hdr); return -1; } int main(int argc, char * const argv[]) { int err, verbose; libmtd_t libmtd; struct mtd_info mtd_info; struct mtd_dev_info mtd; libubi_t libubi; struct ubigen_info ui; struct ubi_scan_info *si; libmtd = libmtd_open(); if (!libmtd) return errmsg("MTD subsystem is not present"); err = parse_opt(argc, argv); if (err) goto out_close_mtd; err = mtd_get_info(libmtd, &mtd_info); if (err) { if (errno == ENODEV) errmsg("MTD is not present"); sys_errmsg("cannot get MTD information"); goto out_close_mtd; } err = mtd_get_dev_info(libmtd, args.node, &mtd); if (err) { sys_errmsg("cannot get information about \"%s\"", args.node); goto out_close_mtd; } if (!is_power_of_2(mtd.min_io_size)) { errmsg("min. I/O size is %d, but should be power of 2", mtd.min_io_size); goto out_close; } if (!mtd_info.sysfs_supported) { /* * Linux kernels older than 2.6.30 did not support sysfs * interface, and it is impossible to find out sub-page * size in these kernels. This is why users should * provide -s option. */ if (args.subpage_size == 0) { warnmsg("your MTD system is old and it is impossible " "to detect sub-page size. Use -s to get rid " "of this warning"); normsg("assume sub-page to be %d", mtd.subpage_size); } else { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } } else if (args.subpage_size && args.subpage_size != mtd.subpage_size) { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } if (args.manual_subpage) { /* Do some sanity check */ if (args.subpage_size > mtd.min_io_size) { errmsg("sub-page cannot be larger than min. I/O unit"); goto out_close; } if (mtd.min_io_size % args.subpage_size) { errmsg("min. I/O unit size should be multiple of " "sub-page size"); goto out_close; } } args.node_fd = open(args.node, O_RDWR); if (args.node_fd == -1) { sys_errmsg("cannot open \"%s\"", args.node); goto out_close_mtd; } /* Validate VID header offset if it was specified */ if (args.vid_hdr_offs != 0) { if (args.vid_hdr_offs % 8) { errmsg("VID header offset has to be multiple of min. I/O unit size"); goto out_close; } if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) { errmsg("bad VID header offset"); goto out_close; } } if (!mtd.writable) { errmsg("mtd%d (%s) is a read-only device", mtd.mtd_num, args.node); goto out_close; } /* Make sure this MTD device is not attached to UBI */ libubi = libubi_open(); if (libubi) { int ubi_dev_num; err = mtd_num2ubi_dev(libubi, mtd.mtd_num, &ubi_dev_num); libubi_close(libubi); if (!err) { errmsg("please, first detach mtd%d (%s) from ubi%d", mtd.mtd_num, args.node, ubi_dev_num); goto out_close; } } if (!args.quiet) { normsg_cont("mtd%d (%s), size ", mtd.mtd_num, mtd.type_str); ubiutils_print_bytes(mtd.size, 1); printf(", %d eraseblocks of ", mtd.eb_cnt); ubiutils_print_bytes(mtd.eb_size, 1); printf(", min. I/O size %d bytes\n", mtd.min_io_size); } if (args.quiet) verbose = 0; else if (args.verbose) verbose = 2; else verbose = 1; err = ubi_scan(&mtd, args.node_fd, &si, verbose); if (err) { errmsg("failed to scan mtd%d (%s)", mtd.mtd_num, args.node); goto out_close; } if (si->good_cnt == 0) { errmsg("all %d eraseblocks are bad", si->bad_cnt); goto out_free; } if (si->good_cnt < 2 && (!args.novtbl || args.image)) { errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.mtd_num); goto out_free; } if (!args.quiet) { if (si->ok_cnt) normsg("%d eraseblocks have valid erase counter, mean value is %lld", si->ok_cnt, si->mean_ec); if (si->empty_cnt) normsg("%d eraseblocks are supposedly empty", si->empty_cnt); if (si->corrupted_cnt) normsg("%d corrupted erase counters", si->corrupted_cnt); print_bad_eraseblocks(&mtd, si); } if (si->alien_cnt) { if (!args.yes || !args.quiet) warnmsg("%d of %d eraseblocks contain non-ubifs data", si->alien_cnt, si->good_cnt); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } } if (!args.override_ec && si->empty_cnt < si->good_cnt) { int percent = ((double)si->ok_cnt)/si->good_cnt * 100; /* * Make sure the majority of eraseblocks have valid * erase counters. */ if (percent < 50) { if (!args.yes || !args.quiet) warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); normsg("erase counter 0 will be used for all eraseblocks"); normsg("note, arbitrary erase counter value may be specified using -e option"); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } args.ec = 0; args.override_ec = 1; } else if (percent < 95) { if (!args.yes || !args.quiet) warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); normsg("mean erase counter %lld will be used for the rest of eraseblock", si->mean_ec); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } args.ec = si->mean_ec; args.override_ec = 1; } } if (!args.quiet && args.override_ec) normsg("use erase counter %lld for all eraseblocks", args.ec); ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, args.vid_hdr_offs, args.ubi_ver, args.image_seq); if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { /* * Hmm, what we read from flash and what we calculated using * min. I/O unit size and sub-page size differs. */ if (!args.yes || !args.quiet) { warnmsg("VID header and data offsets on flash are %d and %d, " "which is different to requested offsets %d and %d", si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs, ui.data_offs); normsg_cont("use new offsets %d and %d? (yes/no) ", ui.vid_hdr_offs, ui.data_offs); } if (args.yes || answer_is_yes()) { if (args.yes && !args.quiet) printf("yes\n"); } else ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, 0, si->vid_hdr_offs, args.ubi_ver, args.image_seq); normsg("use offsets %d and %d", ui.vid_hdr_offs, ui.data_offs); } if (args.image) { err = flash_image(libmtd, &mtd, &ui, si); if (err < 0) goto out_free; err = format(libmtd, &mtd, &ui, si, err, 1); if (err) goto out_free; } else { err = format(libmtd, &mtd, &ui, si, 0, args.novtbl); if (err) goto out_free; } ubi_scan_free(si); close(args.node_fd); libmtd_close(libmtd); return 0; out_free: ubi_scan_free(si); out_close: close(args.node_fd); out_close_mtd: libmtd_close(libmtd); return -1; } mtd-utils-1.5.0/ubi-utils/ubimkvol.c000066400000000000000000000201401175167361300173570ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * An utility to create UBI volumes. * * Authors: Artem Bityutskiy * Frank Haverkamp */ #define PROGRAM_NAME "ubimkvol" #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" /* The variables below are set by command line arguments */ struct args { int vol_id; int vol_type; long long bytes; int lebs; int alignment; const char *name; const char *node; int maxavs; }; static struct args args = { .vol_type = UBI_DYNAMIC_VOLUME, .bytes = -1, .lebs = -1, .alignment = 1, .vol_id = UBI_VOL_NUM_AUTO, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to create UBI volumes."; static const char optionsstr[] = "-a, --alignment= volume alignment (default is 1)\n" "-n, --vol_id= UBI volume ID, if not specified, the volume ID\n" " will be assigned automatically\n" "-N, --name= volume name\n" "-s, --size= volume size volume size in bytes, kilobytes (KiB)\n" " or megabytes (MiB)\n" "-S, --lebs= alternative way to give volume size in logical\n" " eraseblocks\n" "-m, --maxavsize set volume size to maximum available size\n" "-t, --type= volume type (dynamic, static), default is dynamic\n" "-h, -?, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " [-h] [-a ] [-n ] [-N ]\n" "\t\t\t[-s ] [-S ] [-t ] [-V] [-m]\n" "\t\t\t[--alignment=][--vol_id=] [--name=]\n" "\t\t\t[--size=] [--lebs=] [--type=] [--help]\n" "\t\t\t[--version] [--maxavsize]\n\n" "Example: " PROGRAM_NAME " /dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n" " named \"config_data\" on UBI device /dev/ubi0."; static const struct option long_options[] = { { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' }, { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' }, { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { .name = "maxavsize", .has_arg = 0, .flag = NULL, .val = 'm' }, { NULL, 0, NULL, 0}, }; static int param_sanity_check(void) { int len; if (args.bytes == -1 && !args.maxavs && args.lebs == -1) return errmsg("volume size was not specified (use -h for help)"); if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) || (args.lebs != -1 && (args.maxavs || args.bytes != -1)) || (args.maxavs && (args.bytes != -1 || args.lebs != -1))) return errmsg("size specified with more then one option"); if (args.name == NULL) return errmsg("volume name was not specified (use -h for help)"); len = strlen(args.name); if (len > UBI_MAX_VOLUME_NAME) return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME); return 0; } static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "a:n:N:s:S:t:h?Vm", long_options, NULL); if (key == -1) break; switch (key) { case 't': if (!strcmp(optarg, "dynamic")) args.vol_type = UBI_DYNAMIC_VOLUME; else if (!strcmp(optarg, "static")) args.vol_type = UBI_STATIC_VOLUME; else return errmsg("bad volume type: \"%s\"", optarg); break; case 's': args.bytes = ubiutils_get_bytes(optarg); if (args.bytes <= 0) return errmsg("bad volume size: \"%s\"", optarg); break; case 'S': args.lebs = simple_strtoull(optarg, &error); if (error || args.lebs <= 0) return errmsg("bad LEB count: \"%s\"", optarg); break; case 'a': args.alignment = simple_strtoul(optarg, &error); if (error || args.alignment <= 0) return errmsg("bad volume alignment: \"%s\"", optarg); break; case 'n': args.vol_id = simple_strtoul(optarg, &error); if (error || args.vol_id < 0) return errmsg("bad volume ID: " "\"%s\"", optarg); break; case 'N': args.name = optarg; break; case 'h': case '?': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case 'm': args.maxavs = 1; break; case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) return errmsg("UBI device name was not specified (use -h for help)"); else if (optind != argc - 1) return errmsg("more then one UBI device specified (use -h for help)"); args.node = argv[optind]; if (param_sanity_check()) return -1; return 0; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; struct ubi_dev_info dev_info; struct ubi_vol_info vol_info; struct ubi_mkvol_request req; err = parse_opt(argc, argv); if (err) return err; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } err = ubi_probe_node(libubi, args.node); if (err == 2) { errmsg("\"%s\" is an UBI volume node, not an UBI device node", args.node); goto out_libubi; } else if (err < 0) { if (errno == ENODEV) errmsg("\"%s\" is not an UBI device node", args.node); else sys_errmsg("error while probing \"%s\"", args.node); goto out_libubi; } err = ubi_get_dev_info(libubi, args.node, &dev_info); if (err) { sys_errmsg("cannot get information about UBI device \"%s\"", args.node); goto out_libubi; } if (dev_info.avail_bytes == 0) { errmsg("UBI device does not have free logical eraseblocks"); goto out_libubi; } if (args.maxavs) { args.bytes = dev_info.avail_bytes; printf("Set volume size to %lld\n", args.bytes); } if (args.lebs != -1) { args.bytes = dev_info.leb_size; args.bytes -= dev_info.leb_size % args.alignment; args.bytes *= args.lebs; } req.vol_id = args.vol_id; req.alignment = args.alignment; req.bytes = args.bytes; req.vol_type = args.vol_type; req.name = args.name; err = ubi_mkvol(libubi, args.node, &req); if (err < 0) { sys_errmsg("cannot UBI create volume"); goto out_libubi; } args.vol_id = req.vol_id; /* Print information about the created device */ err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info); if (err) { sys_errmsg("cannot get information about newly created UBI volume"); goto out_libubi; } printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs); ubiutils_print_bytes(vol_info.rsvd_bytes, 0); printf("), LEB size "); ubiutils_print_bytes(vol_info.leb_size, 1); printf(", %s, name \"%s\", alignment %d\n", req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", vol_info.name, vol_info.alignment); libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubinfo.c000066400000000000000000000266451175167361300170310ustar00rootroot00000000000000/* * Copyright (C) 2007, 2008 Nokia Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * An utility to get UBI information. * * Author: Artem Bityutskiy */ #define PROGRAM_NAME "ubinfo" #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" /* The variables below are set by command line arguments */ struct args { int devn; int vol_id; int all; const char *node; const char *vol_name; }; static struct args args = { .vol_id = -1, .devn = -1, .all = 0, .node = NULL, .vol_name = NULL, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to print UBI information."; static const char optionsstr[] = "-d, --devn= UBI device number to get information about\n" "-n, --vol_id= ID of UBI volume to print information about\n" "-N, --name= name of UBI volume to print information about\n" "-a, --all print information about all devices and volumes,\n" " or about all volumes if the UBI device was\n" " specified\n" "-h, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage 1: " PROGRAM_NAME " [-d ] [-n | -N ] [-a] [-h] [-V]\n" "\t\t[--vol_id= | --name ] [--devn ] [--all] [--help] [--version]\n" "Usage 2: " PROGRAM_NAME " [-a] [-h] [-V] [--all] [--help] [--version]\n" "Usage 3: " PROGRAM_NAME " [-h] [-V] [--help] [--version]\n\n" "Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n" "Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n" "Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n" " device /dev/ubi0\n" "Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n" "Example 5: " PROGRAM_NAME " -a - print all information\n"; static const struct option long_options[] = { { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "an:N:d:hV", long_options, NULL); if (key == -1) break; switch (key) { case 'a': args.all = 1; break; case 'n': args.vol_id = simple_strtoul(optarg, &error); if (error || args.vol_id < 0) return errmsg("bad volume ID: " "\"%s\"", optarg); break; case 'N': args.vol_name = optarg; break; case 'd': args.devn = simple_strtoul(optarg, &error); if (error || args.devn < 0) return errmsg("bad UBI device number: \"%s\"", optarg); break; case 'h': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc - 1) args.node = argv[optind]; else if (optind < argc) return errmsg("more then one UBI device specified (use -h for help)"); return 0; } static int translate_dev(libubi_t libubi, const char *node) { int err; err = ubi_probe_node(libubi, node); if (err == -1) { if (errno != ENODEV) return sys_errmsg("error while probing \"%s\"", node); return errmsg("\"%s\" does not correspond to any UBI device or volume", node); } if (err == 1) { struct ubi_dev_info dev_info; err = ubi_get_dev_info(libubi, node, &dev_info); if (err) return sys_errmsg("cannot get information about UBI device \"%s\"", node); args.devn = dev_info.dev_num; } else { struct ubi_vol_info vol_info; err = ubi_get_vol_info(libubi, node, &vol_info); if (err) return sys_errmsg("cannot get information about UBI volume \"%s\"", node); if (args.vol_id != -1) return errmsg("both volume character device node (\"%s\") and " "volume ID (%d) are specify, use only one of them" "(use -h for help)", node, args.vol_id); args.devn = vol_info.dev_num; args.vol_id = vol_info.vol_id; } return 0; } static int get_vol_id_by_name(libubi_t libubi, int dev_num, const char *name) { int err; struct ubi_vol_info vol_info; err = ubi_get_vol_info1_nm(libubi, dev_num, name, &vol_info); if (err) return sys_errmsg("cannot get information about volume \"%s\" on ubi%d\n", name, dev_num); args.vol_id = vol_info.vol_id; return 0; } static int print_vol_info(libubi_t libubi, int dev_num, int vol_id) { int err; struct ubi_vol_info vol_info; err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info); if (err) return sys_errmsg("cannot get information about UBI volume %d on ubi%d", vol_id, dev_num); printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num); printf("Type: %s\n", vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"); printf("Alignment: %d\n", vol_info.alignment); printf("Size: %d LEBs (", vol_info.rsvd_lebs); ubiutils_print_bytes(vol_info.rsvd_bytes, 0); printf(")\n"); if (vol_info.type == UBI_STATIC_VOLUME) { printf("Data bytes: "); ubiutils_print_bytes(vol_info.data_bytes, 1); printf("\n"); } printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK"); printf("Name: %s\n", vol_info.name); printf("Character device major/minor: %d:%d\n", vol_info.major, vol_info.minor); return 0; } static int print_dev_info(libubi_t libubi, int dev_num, int all) { int i, err, first = 1; struct ubi_dev_info dev_info; struct ubi_vol_info vol_info; err = ubi_get_dev_info1(libubi, dev_num, &dev_info); if (err) return sys_errmsg("cannot get information about UBI device %d", dev_num); printf("ubi%d\n", dev_info.dev_num); printf("Volumes count: %d\n", dev_info.vol_count); printf("Logical eraseblock size: "); ubiutils_print_bytes(dev_info.leb_size, 0); printf("\n"); printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs); ubiutils_print_bytes(dev_info.total_bytes, 0); printf(")\n"); printf("Amount of available logical eraseblocks: %d (", dev_info.avail_lebs); ubiutils_print_bytes(dev_info.avail_bytes, 0); printf(")\n"); printf("Maximum count of volumes %d\n", dev_info.max_vol_count); printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count); printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd); printf("Current maximum erase counter value: %lld\n", dev_info.max_ec); printf("Minimum input/output unit size: %d %s\n", dev_info.min_io_size, dev_info.min_io_size > 1 ? "bytes" : "byte"); printf("Character device major/minor: %d:%d\n", dev_info.major, dev_info.minor); if (dev_info.vol_count == 0) return 0; printf("Present volumes: "); for (i = dev_info.lowest_vol_id; i <= dev_info.highest_vol_id; i++) { err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); if (err == -1) { if (errno == ENOENT) continue; return sys_errmsg("libubi failed to probe volume %d on ubi%d", i, dev_info.dev_num); } if (!first) printf(", %d", i); else { printf("%d", i); first = 0; } } printf("\n"); if (!all) return 0; first = 1; printf("\n"); for (i = dev_info.lowest_vol_id; i <= dev_info.highest_vol_id; i++) { if(!first) printf("-----------------------------------\n"); err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); if (err == -1) { if (errno == ENOENT) continue; return sys_errmsg("libubi failed to probe volume %d on ubi%d", i, dev_info.dev_num); } first = 0; err = print_vol_info(libubi, dev_info.dev_num, i); if (err) return err; } return 0; } static int print_general_info(libubi_t libubi, int all) { int i, err, first = 1; struct ubi_info ubi_info; struct ubi_dev_info dev_info; err = ubi_get_info(libubi, &ubi_info); if (err) return sys_errmsg("cannot get UBI information"); printf("UBI version: %d\n", ubi_info.version); printf("Count of UBI devices: %d\n", ubi_info.dev_count); if (ubi_info.ctrl_major != -1) printf("UBI control device major/minor: %d:%d\n", ubi_info.ctrl_major, ubi_info.ctrl_minor); else printf("UBI control device is not supported by this kernel\n"); if (ubi_info.dev_count == 0) return 0; printf("Present UBI devices: "); for (i = ubi_info.lowest_dev_num; i <= ubi_info.highest_dev_num; i++) { err = ubi_get_dev_info1(libubi, i, &dev_info); if (err == -1) { if (errno == ENOENT) continue; printf("\n"); return sys_errmsg("libubi failed to probe UBI device %d", i); } if (!first) printf(", ubi%d", i); else { printf("ubi%d", i); first = 0; } } printf("\n"); if (!all) return 0; first = 1; printf("\n"); for (i = ubi_info.lowest_dev_num; i <= ubi_info.highest_dev_num; i++) { if (!ubi_dev_present(libubi, i)) continue; if(!first) printf("\n===================================\n\n"); first = 0; err = print_dev_info(libubi, i, all); if (err) return err; } return 0; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; err = parse_opt(argc, argv); if (err) return -1; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } if (args.node) { /* * A character device was specified, translate this into UBI * device number and volume ID. */ err = translate_dev(libubi, args.node); if (err) goto out_libubi; } if (args.vol_name) { err = get_vol_id_by_name(libubi, args.devn, args.vol_name); if (err) goto out_libubi; } if (args.vol_id != -1 && args.devn == -1) { errmsg("volume ID is specified, but UBI device number is not " "(use -h for help)\n"); goto out_libubi; } if (args.devn != -1 && args.vol_id != -1) { print_vol_info(libubi, args.devn, args.vol_id); goto out; } if (args.devn == -1 && args.vol_id == -1) err = print_general_info(libubi, args.all); else if (args.devn != -1 && args.vol_id == -1) err = print_dev_info(libubi, args.devn, args.all); if (err) goto out_libubi; out: libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubinize.c000066400000000000000000000470351175167361300172100ustar00rootroot00000000000000/* * Copyright (C) 2008 Nokia Corporation * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Generate UBI images. * * Authors: Artem Bityutskiy * Oliver Lohmann */ #define PROGRAM_NAME "ubinize" #include #include #include #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to generate UBI images. An UBI image may contain one or more UBI " "volumes which have to be defined in the input configuration ini-file. The " "ini file defines all the UBI volumes - their characteristics and the and the " "contents, but it does not define the characteristics of the flash the UBI " "image is generated for. Instead, the flash characteristics are defined via " "the command-line options. Note, if not sure about some of the command-line " "parameters, do not specify them and let the utility to use default values."; static const char optionsstr[] = "-o, --output= output file name\n" "-p, --peb-size= size of the physical eraseblock of the flash\n" " this UBI image is created for in bytes,\n" " kilobytes (KiB), or megabytes (MiB)\n" " (mandatory parameter)\n" "-m, --min-io-size= minimum input/output unit size of the flash\n" " in bytes\n" "-s, --sub-page-size= minimum input/output unit used for UBI\n" " headers, e.g. sub-page size in case of NAND\n" " flash (equivalent to the minimum input/output\n" " unit size by default)\n" "-O, --vid-hdr-offset= offset if the VID header from start of the\n" " physical eraseblock (default is the next\n" " minimum I/O unit or sub-page after the EC\n" " header)\n" "-e, --erase-counter= the erase counter value to put to EC headers\n" " (default is 0)\n" "-x, --ubi-ver= UBI version number to put to EC headers\n" " (default is 1)\n" "-Q, --image-seq= 32-bit UBI image sequence number to use\n" " (by default a random number is picked)\n" "-v, --verbose be verbose\n" "-h, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " [-o filename] [-p ] [-m ] [-s ] [-O ] [-e ]\n" "\t\t[-x ] [-Q ] [-v] [-h] [-V] [--output=] [--peb-size=]\n" "\t\t[--min-io-size=] [--sub-page-size=] [--vid-hdr-offset=]\n" "\t\t[--erase-counter=] [--ubi-ver=] [--image-seq=] [--verbose] [--help]\n" "\t\t[--version] ini-file\n" "Example: " PROGRAM_NAME " -o ubi.img -p 16KiB -m 512 -s 256 cfg.ini - create UBI image\n" " 'ubi.img' as described by configuration file 'cfg.ini'"; static const char ini_doc[] = "INI-file format.\n" "The input configuration ini-file describes all the volumes which have to\n" "be included to the output UBI image. Each volume is described in its own\n" "section which may be named arbitrarily. The section consists on\n" "\"key=value\" pairs, for example:\n\n" "[jffs2-volume]\n" "mode=ubi\n" "image=../jffs2.img\n" "vol_id=1\n" "vol_size=30MiB\n" "vol_type=dynamic\n" "vol_name=jffs2_volume\n" "vol_flags=autoresize\n" "vol_alignment=1\n\n" "This example configuration file tells the utility to create an UBI image\n" "with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n" "name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n" "\"image=../jffs2.img\" line tells the utility to take the contents of the\n" "volume from the \"../jffs2.img\" file. The size of the image file has to be\n" "less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n" "mandatory and just tells that the section describes an UBI volume - other\n" "section modes may be added in the future.\n" "Notes:\n" " * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n" " gigabytes (GiB) or bytes (no modifier);\n" " * if \"vol_size\" key is absent, the volume size is assumed to be\n" " equivalent to the size of the image file (defined by \"image\" key);\n" " * if the \"image\" is absent, the volume is assumed to be empty;\n" " * volume alignment must not be greater than the logical eraseblock size;\n" " * one ini file may contain arbitrary number of sections, the utility will\n" " put all the volumes which are described by these section to the output\n" " UBI image file."; static const struct option long_options[] = { { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' }, { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' }, { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, { .name = "image-seq", .has_arg = 1, .flag = NULL, .val = 'Q' }, { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0} }; struct args { const char *f_in; const char *f_out; int out_fd; int peb_size; int min_io_size; int subpage_size; int vid_hdr_offs; int ec; int ubi_ver; uint32_t image_seq; int verbose; dictionary *dict; }; static struct args args = { .peb_size = -1, .min_io_size = -1, .subpage_size = -1, .ubi_ver = 1, }; static int parse_opt(int argc, char * const argv[]) { ubiutils_srand(); args.image_seq = rand(); while (1) { int key, error = 0; unsigned long int image_seq; key = getopt_long(argc, argv, "o:p:m:s:O:e:x:Q:vhV", long_options, NULL); if (key == -1) break; switch (key) { case 'o': args.out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH); if (args.out_fd == -1) return sys_errmsg("cannot open file \"%s\"", optarg); args.f_out = optarg; break; case 'p': args.peb_size = ubiutils_get_bytes(optarg); if (args.peb_size <= 0) return errmsg("bad physical eraseblock size: \"%s\"", optarg); break; case 'm': args.min_io_size = ubiutils_get_bytes(optarg); if (args.min_io_size <= 0) return errmsg("bad min. I/O unit size: \"%s\"", optarg); if (!is_power_of_2(args.min_io_size)) return errmsg("min. I/O unit size should be power of 2"); break; case 's': args.subpage_size = ubiutils_get_bytes(optarg); if (args.subpage_size <= 0) return errmsg("bad sub-page size: \"%s\"", optarg); if (!is_power_of_2(args.subpage_size)) return errmsg("sub-page size should be power of 2"); break; case 'O': args.vid_hdr_offs = simple_strtoul(optarg, &error); if (error || args.vid_hdr_offs < 0) return errmsg("bad VID header offset: \"%s\"", optarg); break; case 'e': args.ec = simple_strtoul(optarg, &error); if (error || args.ec < 0) return errmsg("bad erase counter value: \"%s\"", optarg); break; case 'x': args.ubi_ver = simple_strtoul(optarg, &error); if (error || args.ubi_ver < 0) return errmsg("bad UBI version: \"%s\"", optarg); break; case 'Q': image_seq = simple_strtoul(optarg, &error); if (error || image_seq > 0xFFFFFFFF) return errmsg("bad UBI image sequence number: \"%s\"", optarg); args.image_seq = image_seq; break; case 'v': args.verbose = 1; break; case 'h': ubiutils_print_text(stdout, doc, 80); printf("\n%s\n\n", ini_doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) return errmsg("input configuration file was not specified (use -h for help)"); if (optind != argc - 1) return errmsg("more then one configuration file was specified (use -h for help)"); args.f_in = argv[optind]; if (args.peb_size < 0) return errmsg("physical eraseblock size was not specified (use -h for help)"); if (args.peb_size > UBI_MAX_PEB_SZ) return errmsg("too high physical eraseblock size %d", args.peb_size); if (args.min_io_size < 0) return errmsg("min. I/O unit size was not specified (use -h for help)"); if (args.subpage_size < 0) args.subpage_size = args.min_io_size; if (args.subpage_size > args.min_io_size) return errmsg("sub-page cannot be larger then min. I/O unit"); if (args.peb_size % args.min_io_size) return errmsg("physical eraseblock should be multiple of min. I/O units"); if (args.min_io_size % args.subpage_size) return errmsg("min. I/O unit size should be multiple of sub-page size"); if (!args.f_out) return errmsg("output file was not specified (use -h for help)"); if (args.vid_hdr_offs) { if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE >= args.peb_size) return errmsg("bad VID header position"); if (args.vid_hdr_offs % 8) return errmsg("VID header offset has to be multiple of min. I/O unit size"); } return 0; } static int read_section(const struct ubigen_info *ui, const char *sname, struct ubigen_vol_info *vi, const char **img, struct stat *st) { char buf[256]; const char *p; *img = NULL; if (strlen(sname) > 128) return errmsg("too long section name \"%s\"", sname); /* Make sure mode is UBI, otherwise ignore this section */ sprintf(buf, "%s:mode", sname); p = iniparser_getstring(args.dict, buf, NULL); if (!p) { errmsg("\"mode\" key not found in section \"%s\"", sname); errmsg("the \"mode\" key is mandatory and has to be " "\"mode=ubi\" if the section describes an UBI volume"); return -1; } /* If mode is not UBI, skip this section */ if (strcmp(p, "ubi")) { verbose(args.verbose, "skip non-ubi section \"%s\"", sname); return 1; } verbose(args.verbose, "mode=ubi, keep parsing"); /* Fetch volume type */ sprintf(buf, "%s:vol_type", sname); p = iniparser_getstring(args.dict, buf, NULL); if (!p) { normsg("volume type was not specified in " "section \"%s\", assume \"dynamic\"\n", sname); vi->type = UBI_VID_DYNAMIC; } else { if (!strcmp(p, "static")) vi->type = UBI_VID_STATIC; else if (!strcmp(p, "dynamic")) vi->type = UBI_VID_DYNAMIC; else return errmsg("invalid volume type \"%s\" in section \"%s\"", p, sname); } verbose(args.verbose, "volume type: %s", vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static"); /* Fetch the name of the volume image file */ sprintf(buf, "%s:image", sname); p = iniparser_getstring(args.dict, buf, NULL); if (p) { *img = p; if (stat(p, st)) return sys_errmsg("cannot stat \"%s\" referred from section \"%s\"", p, sname); if (st->st_size == 0) return errmsg("empty file \"%s\" referred from section \"%s\"", p, sname); } else if (vi->type == UBI_VID_STATIC) return errmsg("image is not specified for static volume in section \"%s\"", sname); /* Fetch volume id */ sprintf(buf, "%s:vol_id", sname); vi->id = iniparser_getint(args.dict, buf, -1); if (vi->id == -1) return errmsg("\"vol_id\" key not found in section \"%s\"", sname); if (vi->id < 0) return errmsg("negative volume ID %d in section \"%s\"", vi->id, sname); if (vi->id >= ui->max_volumes) return errmsg("too high volume ID %d in section \"%s\", max. is %d", vi->id, sname, ui->max_volumes); verbose(args.verbose, "volume ID: %d", vi->id); /* Fetch volume size */ sprintf(buf, "%s:vol_size", sname); p = iniparser_getstring(args.dict, buf, NULL); if (p) { vi->bytes = ubiutils_get_bytes(p); if (vi->bytes <= 0) return errmsg("bad \"vol_size\" key value \"%s\" (section \"%s\")", p, sname); /* Make sure the image size is not larger than volume size */ if (*img && st->st_size > vi->bytes) return errmsg("error in section \"%s\": size of the image file " "\"%s\" is %lld, which is larger than volume size %lld", sname, *img, (long long)st->st_size, vi->bytes); verbose(args.verbose, "volume size: %lld bytes", vi->bytes); } else { struct stat st; if (!*img) return errmsg("neither image file (\"image=\") nor volume size " "(\"vol_size=\") specified in section \"%s\"", sname); if (stat(*img, &st)) return sys_errmsg("cannot stat \"%s\"", *img); vi->bytes = st.st_size; if (vi->bytes == 0) return errmsg("file \"%s\" referred from section \"%s\" is empty", *img, sname); normsg_cont("volume size was not specified in section \"%s\", assume" " minimum to fit image \"%s\"", sname, *img); ubiutils_print_bytes(vi->bytes, 1); printf("\n"); } /* Fetch volume name */ sprintf(buf, "%s:vol_name", sname); p = iniparser_getstring(args.dict, buf, NULL); if (!p) return errmsg("\"vol_name\" key not found in section \"%s\"", sname); vi->name = p; vi->name_len = strlen(p); if (vi->name_len > UBI_VOL_NAME_MAX) return errmsg("too long volume name in section \"%s\", max. is %d characters", vi->name, UBI_VOL_NAME_MAX); verbose(args.verbose, "volume name: %s", p); /* Fetch volume alignment */ sprintf(buf, "%s:vol_alignment", sname); vi->alignment = iniparser_getint(args.dict, buf, -1); if (vi->alignment == -1) vi->alignment = 1; else if (vi->id < 0) return errmsg("negative volume alignement %d in section \"%s\"", vi->alignment, sname); verbose(args.verbose, "volume alignment: %d", vi->alignment); /* Fetch volume flags */ sprintf(buf, "%s:vol_flags", sname); p = iniparser_getstring(args.dict, buf, NULL); if (p) { if (!strcmp(p, "autoresize")) { verbose(args.verbose, "autoresize flags found"); vi->flags |= UBI_VTBL_AUTORESIZE_FLG; } else { return errmsg("unknown flags \"%s\" in section \"%s\"", p, sname); } } /* Initialize the rest of the volume information */ vi->data_pad = ui->leb_size % vi->alignment; vi->usable_leb_size = ui->leb_size - vi->data_pad; if (vi->type == UBI_VID_DYNAMIC) vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size; else vi->used_ebs = (st->st_size + vi->usable_leb_size - 1) / vi->usable_leb_size; vi->compat = 0; return 0; } int main(int argc, char * const argv[]) { int err = -1, sects, i, autoresize_was_already = 0; struct ubigen_info ui; struct ubi_vtbl_record *vtbl; struct ubigen_vol_info *vi; off_t seek; err = parse_opt(argc, argv); if (err) return -1; ubigen_info_init(&ui, args.peb_size, args.min_io_size, args.subpage_size, args.vid_hdr_offs, args.ubi_ver, args.image_seq); verbose(args.verbose, "LEB size: %d", ui.leb_size); verbose(args.verbose, "PEB size: %d", ui.peb_size); verbose(args.verbose, "min. I/O size: %d", ui.min_io_size); verbose(args.verbose, "sub-page size: %d", args.subpage_size); verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs); verbose(args.verbose, "data offset: %d", ui.data_offs); verbose(args.verbose, "UBI image sequence number: %u", ui.image_seq); vtbl = ubigen_create_empty_vtbl(&ui); if (!vtbl) goto out; args.dict = iniparser_load(args.f_in); if (!args.dict) { errmsg("cannot load the input ini file \"%s\"", args.f_in); goto out_vtbl; } verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in); /* Each section describes one volume */ sects = iniparser_getnsec(args.dict); if (sects == -1) { errmsg("ini-file parsing error (iniparser_getnsec)"); goto out_dict; } verbose(args.verbose, "count of sections: %d", sects); if (sects == 0) { errmsg("no sections found the ini-file \"%s\"", args.f_in); goto out_dict; } if (sects > ui.max_volumes) { errmsg("too many sections (%d) in the ini-file \"%s\"", sects, args.f_in); normsg("each section corresponds to an UBI volume, maximum " "count of volumes is %d", ui.max_volumes); goto out_dict; } vi = calloc(sizeof(struct ubigen_vol_info), sects); if (!vi) { errmsg("cannot allocate memory"); goto out_dict; } /* * Skip 2 PEBs at the beginning of the file for the volume table which * will be written later. */ seek = ui.peb_size * 2; if (lseek(args.out_fd, seek, SEEK_SET) != seek) { sys_errmsg("cannot seek file \"%s\"", args.f_out); goto out_free; } for (i = 0; i < sects; i++) { const char *sname = iniparser_getsecname(args.dict, i); const char *img = NULL; struct stat st; int fd, j; if (!sname) { errmsg("ini-file parsing error (iniparser_getsecname)"); goto out_free; } if (args.verbose) printf("\n"); verbose(args.verbose, "parsing section \"%s\"", sname); err = read_section(&ui, sname, &vi[i], &img, &st); if (err == -1) goto out_free; verbose(args.verbose, "adding volume %d", vi[i].id); /* * Make sure that volume ID and name is unique and that only * one volume has auto-resize flag */ for (j = 0; j < i; j++) { if (vi[i].id == vi[j].id) { errmsg("volume IDs must be unique, but ID %d " "in section \"%s\" is not", vi[i].id, sname); goto out_free; } if (!strcmp(vi[i].name, vi[j].name)) { errmsg("volume name must be unique, but name " "\"%s\" in section \"%s\" is not", vi[i].name, sname); goto out_free; } } if (vi[i].flags & UBI_VTBL_AUTORESIZE_FLG) { if (autoresize_was_already) return errmsg("only one volume is allowed " "to have auto-resize flag"); autoresize_was_already = 1; } err = ubigen_add_volume(&ui, &vi[i], vtbl); if (err) { errmsg("cannot add volume for section \"%s\"", sname); goto out_free; } if (img) { fd = open(img, O_RDONLY); if (fd == -1) { sys_errmsg("cannot open \"%s\"", img); goto out_free; } verbose(args.verbose, "writing volume %d", vi[i].id); verbose(args.verbose, "image file: %s", img); err = ubigen_write_volume(&ui, &vi[i], args.ec, st.st_size, fd, args.out_fd); close(fd); if (err) { errmsg("cannot write volume for section \"%s\"", sname); goto out_free; } } if (args.verbose) printf("\n"); } verbose(args.verbose, "writing layout volume"); err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl, args.out_fd); if (err) { errmsg("cannot write layout volume"); goto out_free; } verbose(args.verbose, "done"); free(vi); iniparser_freedict(args.dict); free(vtbl); close(args.out_fd); return 0; out_free: free(vi); out_dict: iniparser_freedict(args.dict); out_vtbl: free(vtbl); out: close(args.out_fd); remove(args.f_out); return err; } mtd-utils-1.5.0/ubi-utils/ubirename.c000066400000000000000000000076421175167361300175120ustar00rootroot00000000000000/* * Copyright (C) 2008 Logitech. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * An utility to get rename UBI volumes. * * Author: Richard Titmuss */ #define PROGRAM_NAME "ubirename" #include #include #include #include #include #include #include "common.h" static const char usage[] = "Usage: " PROGRAM_NAME " [ |...]\n\n" "Example: " PROGRAM_NAME "/dev/ubi0 A B C D - rename volume A to B, and C to D\n\n" "This utility allows re-naming several volumes in one go atomically.\n" "For example, if you have volumes A and B, then you may rename A into B\n" "and B into A at one go, and the operation will be atomic. This allows\n" "implementing atomic UBI volumes upgrades. E.g., if you have volume A\n" "and want to upgrade it atomically, you create a temporary volume B,\n" "put your new data to B, then rename A to B and B to A, and then you\n" "may remove old volume B.\n" "It is also allowed to re-name multiple volumes at a time, but 16 max.\n" "renames at once, which means you may specify up to 32 volume names.\n" "If you have volumes A and B, and re-name A to B, bud do not re-name\n" "B to something else in the same request, old volume B will be removed\n" "and A will be renamed into B.\n"; static int get_vol_id(libubi_t libubi, struct ubi_dev_info *dev_info, char *name) { int err, i; struct ubi_vol_info vol_info; for (i=dev_info->lowest_vol_id; i<=dev_info->highest_vol_id; i++) { err = ubi_get_vol_info1(libubi, dev_info->dev_num, i, &vol_info); if (err == -1) { if (errno == ENOENT) continue; return -1; } if (strcmp(name, vol_info.name) == 0) return vol_info.vol_id; } return -1; } int main(int argc, char * const argv[]) { int i, err; int count = 0; libubi_t libubi; struct ubi_dev_info dev_info; struct ubi_rnvol_req rnvol; const char *node; if (argc < 3 || (argc & 1) == 1) { errmsg("too few arguments"); fprintf(stderr, "%s\n", usage); return -1; } if (argc > UBI_MAX_RNVOL + 2) { errmsg("too many volumes to re-name, max. is %d", UBI_MAX_RNVOL); return -1; } node = argv[1]; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } err = ubi_probe_node(libubi, node); if (err == 2) { errmsg("\"%s\" is an UBI volume node, not an UBI device node", node); goto out_libubi; } else if (err < 0) { if (errno == ENODEV) errmsg("\"%s\" is not an UBI device node", node); else sys_errmsg("error while probing \"%s\"", node); goto out_libubi; } err = ubi_get_dev_info(libubi, node, &dev_info); if (err == -1) { sys_errmsg("cannot get information about UBI device \"%s\"", node); goto out_libubi; } for (i = 2; i < argc; i += 2) { err = get_vol_id(libubi, &dev_info, argv[i]); if (err == -1) { errmsg("\"%s\" volume not found", argv[i]); goto out_libubi; } rnvol.ents[count].vol_id = err; rnvol.ents[count].name_len = strlen(argv[i + 1]); strcpy(rnvol.ents[count++].name, argv[i + 1]); } rnvol.count = count; err = ubi_rnvols(libubi, node, &rnvol); if (err == -1) { sys_errmsg("cannot rename volumes"); goto out_libubi; } libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubirmvol.c000066400000000000000000000117241175167361300173760ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * An utility to remove UBI volumes. * * Authors: Artem Bityutskiy * Frank Haverkamp */ #define PROGRAM_NAME "ubirmvol" #include #include #include #include #include #include #include "common.h" /* The variables below are set by command line arguments */ struct args { int vol_id; const char *node; const char *name; }; static struct args args = { .vol_id = -1, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to remove UBI volumes."; static const char optionsstr[] = "-n, --vol_id= volume ID to remove\n" "-N, --name= volume name to remove\n" "-h, -?, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " [-n ] [--vol_id=]\n\n" " [-N ] [--name=] [-h] [--help]\n\n" "Example: " PROGRAM_NAME "/dev/ubi0 -n 1 - remove UBI volume 1 from UBI device corresponding\n" " to /dev/ubi0\n" " " PROGRAM_NAME "/dev/ubi0 -N my_vol - remove UBI named \"my_vol\" from UBI device\n" " corresponding to /dev/ubi0"; static const struct option long_options[] = { { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0}, }; static int param_sanity_check(void) { if (args.vol_id == -1 && !args.name) { errmsg("please, specify either volume ID or volume name"); return -1; } if (args.vol_id != -1 && args.name) { errmsg("please, specify either volume ID or volume name, not both"); return -1; } return 0; } static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "n:N:h?V", long_options, NULL); if (key == -1) break; switch (key) { case 'n': args.vol_id = simple_strtoul(optarg, &error); if (error || args.vol_id < 0) { errmsg("bad volume ID: " "\"%s\"", optarg); return -1; } break; case 'N': args.name = optarg; break; case 'h': case '?': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': errmsg("parameter is missing"); return -1; default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) { errmsg("UBI device name was not specified (use -h for help)"); return -1; } else if (optind != argc - 1) { errmsg("more then one UBI device specified (use -h for help)"); return -1; } args.node = argv[optind]; if (param_sanity_check()) return -1; return 0; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; err = parse_opt(argc, argv); if (err) return -1; libubi = libubi_open(); if (!libubi) { if (errno == 0) return errmsg("UBI is not present in the system"); return sys_errmsg("cannot open libubi"); } err = ubi_probe_node(libubi, args.node); if (err == 2) { errmsg("\"%s\" is an UBI volume node, not an UBI device node", args.node); goto out_libubi; } else if (err < 0) { if (errno == ENODEV) errmsg("\"%s\" is not an UBI device node", args.node); else sys_errmsg("error while probing \"%s\"", args.node); goto out_libubi; } if (args.name) { struct ubi_dev_info dev_info; struct ubi_vol_info vol_info; err = ubi_get_dev_info(libubi, args.node, &dev_info); if (err) { sys_errmsg("cannot get information about UBI device \"%s\"", args.node); goto out_libubi; } err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num, args.name, &vol_info); if (err) { sys_errmsg("cannot find UBI volume \"%s\"", args.name); goto out_libubi; } args.vol_id = vol_info.vol_id; } err = ubi_rmvol(libubi, args.node, args.vol_id); if (err) { sys_errmsg("cannot UBI remove volume"); goto out_libubi; } libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubirsvol.c000066400000000000000000000142141175167361300174010ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * An utility to resize UBI volumes. * * Authors: Artem Bityutskiy * Frank Haverkamp */ #define PROGRAM_NAME "ubirsvol" #include #include #include #include #include #include #include "common.h" #include "ubiutils-common.h" /* The variables below are set by command line arguments */ struct args { int vol_id; const char *node; const char *name; long long bytes; int lebs; }; static struct args args = { .vol_id = -1, .bytes = -1, .lebs = -1, }; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to resize UBI volumes."; static const char optionsstr[] = "-n, --vol_id= volume ID to resize\n" "-N, --name= volume name to resize\n" "-s, --size= volume size volume size in bytes, kilobytes (KiB)\n" " or megabytes (MiB)\n" "-S, --lebs= alternative way to give volume size in logical\n" " eraseblocks\n" "-h, -?, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " [-n ] [--vol_id=]\n\n" " [-N ] [--name=] [-s ] [-S ] [-h] [--help]\n\n" "Example: " PROGRAM_NAME " /dev/ubi0 -n 1 -s 1MiB resize UBI volume 1 to 1 MiB on\n" " UBI device corresponding to /dev/ubi0\n" " " PROGRAM_NAME " /dev/ubi0 -N my_vol -s 1MiB - resize UBI volume named \"my_vol\" to 1 MiB\n" " on UBI device corresponding to /dev/ubi0"; static const struct option long_options[] = { { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' }, { NULL, 0, NULL, 0}, }; static int param_sanity_check(void) { if (args.vol_id == -1 && !args.name) { errmsg("please, specify either volume ID or volume name"); return -1; } if (args.vol_id != -1 && args.name) { errmsg("please, specify either volume ID or volume name, not both"); return -1; } if (args.bytes == -1 && args.lebs == -1) return errmsg("volume size was not specified (use -h for help)"); if (args.bytes != -1 && args.lebs != -1) return errmsg("size specified with more then one option"); return 0; } static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "s:S:n:N:h?V", long_options, NULL); if (key == -1) break; switch (key) { case 's': args.bytes = ubiutils_get_bytes(optarg); if (args.bytes <= 0) return errmsg("bad volume size: \"%s\"", optarg); break; case 'S': args.lebs = simple_strtoull(optarg, &error); if (error || args.lebs <= 0) return errmsg("bad LEB count: \"%s\"", optarg); break; case 'n': args.vol_id = simple_strtoul(optarg, &error); if (error || args.vol_id < 0) { errmsg("bad volume ID: " "\"%s\"", optarg); return -1; } break; case 'N': args.name = optarg; break; case 'h': case '?': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': errmsg("parameter is missing"); return -1; default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) { errmsg("UBI device name was not specified (use -h for help)"); return -1; } else if (optind != argc - 1) { errmsg("more then one UBI device specified (use -h for help)"); return -1; } args.node = argv[optind]; if (param_sanity_check()) return -1; return 0; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; struct ubi_dev_info dev_info; struct ubi_vol_info vol_info; err = parse_opt(argc, argv); if (err) return -1; libubi = libubi_open(); if (!libubi) return sys_errmsg("cannot open libubi"); err = ubi_probe_node(libubi, args.node); if (err == 2) { errmsg("\"%s\" is an UBI volume node, not an UBI device node", args.node); goto out_libubi; } else if (err < 0) { if (errno == ENODEV) errmsg("\"%s\" is not an UBI device node", args.node); else sys_errmsg("error while probing \"%s\"", args.node); } err = ubi_get_dev_info(libubi, args.node, &dev_info); if (err) { sys_errmsg("cannot get information about UBI device \"%s\"", args.node); goto out_libubi; } if (args.name) { err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num, args.name, &vol_info); if (err) { sys_errmsg("cannot find UBI volume \"%s\"", args.name); goto out_libubi; } args.vol_id = vol_info.vol_id; } else { err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info); if (err) { sys_errmsg("cannot find UBI volume ID %d", args.vol_id); goto out_libubi; } } if (args.lebs != -1) args.bytes = vol_info.leb_size * args.lebs; err = ubi_rsvol(libubi, args.node, args.vol_id, args.bytes); if (err) { sys_errmsg("cannot UBI resize volume"); goto out_libubi; } libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubiupdatevol.c000066400000000000000000000161601175167361300202410ustar00rootroot00000000000000/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * An utility to update UBI volumes. * * Authors: Frank Haverkamp * Joshua W. Boyer * Artem Bityutskiy */ #define PROGRAM_NAME "ubiupdatevol" #include #include #include #include #include #include #include #include #include #include #include "common.h" struct args { int truncate; const char *node; const char *img; /* For deprecated -d and -B options handling */ char dev_name[256]; int size; int use_stdin; }; static struct args args; static const char doc[] = PROGRAM_NAME " version " VERSION " - a tool to write data to UBI volumes."; static const char optionsstr[] = "-t, --truncate truncate volume (wipe it out)\n" "-s, --size= bytes in input, if not reading from file\n" "-h, --help print help message\n" "-V, --version print program version"; static const char usage[] = "Usage: " PROGRAM_NAME " [-t] [-s ] [-h] [-V] [--truncate]\n" "\t\t\t[--size=] [--help] [--version] \n\n" "Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n" "Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1"; static const struct option long_options[] = { { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, { NULL, 0, NULL, 0} }; static int parse_opt(int argc, char * const argv[]) { while (1) { int key, error = 0; key = getopt_long(argc, argv, "ts:h?V", long_options, NULL); if (key == -1) break; switch (key) { case 't': args.truncate = 1; break; case 's': args.size = simple_strtoul(optarg, &error); if (error || args.size < 0) return errmsg("bad size: " "\"%s\"", optarg); break; case 'h': case '?': printf("%s\n\n", doc); printf("%s\n\n", usage); printf("%s\n", optionsstr); exit(EXIT_SUCCESS); case 'V': common_print_version(); exit(EXIT_SUCCESS); case ':': return errmsg("parameter is missing"); default: fprintf(stderr, "Use -h for help\n"); return -1; } } if (optind == argc) return errmsg("UBI device name was not specified (use -h for help)"); else if (optind != argc - 2 && !args.truncate) return errmsg("specify UBI device name and image file name as first 2 " "parameters (use -h for help)"); args.node = argv[optind]; args.img = argv[optind + 1]; if (args.img && args.truncate) return errmsg("You can't truncate and specify an image (use -h for help)"); if (args.img && !args.truncate) { if (strcmp(args.img, "-") == 0) args.use_stdin = 1; if (args.use_stdin && !args.size) return errmsg("file size must be specified if input is stdin"); } return 0; } static int truncate_volume(libubi_t libubi) { int err, fd; fd = open(args.node, O_RDWR); if (fd == -1) return sys_errmsg("cannot open \"%s\"", args.node); err = ubi_update_start(libubi, fd, 0); if (err) { sys_errmsg("cannot truncate volume \"%s\"", args.node); close(fd); return -1; } close(fd); return 0; } static int ubi_write(int fd, const void *buf, int len) { int ret; while (len) { ret = write(fd, buf, len); if (ret < 0) { if (errno == EINTR) { warnmsg("do not interrupt me!"); continue; } return sys_errmsg("cannot write %d bytes to volume \"%s\"", len, args.node); } if (ret == 0) return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node); len -= ret; buf += ret; } return 0; } static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info) { int err, fd, ifd; long long bytes; char *buf; buf = malloc(vol_info->leb_size); if (!buf) return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); if (!args.size) { struct stat st; err = stat(args.img, &st); if (err < 0) { errmsg("stat failed on \"%s\"", args.img); goto out_free; } bytes = st.st_size; } else bytes = args.size; if (bytes > vol_info->rsvd_bytes) { errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)", args.img, bytes, args.node, vol_info->rsvd_bytes); goto out_free; } fd = open(args.node, O_RDWR); if (fd == -1) { sys_errmsg("cannot open UBI volume \"%s\"", args.node); goto out_free; } if (args.use_stdin) ifd = STDIN_FILENO; else { ifd = open(args.img, O_RDONLY); if (ifd == -1) { sys_errmsg("cannot open \"%s\"", args.img); goto out_close1; } } err = ubi_update_start(libubi, fd, bytes); if (err) { sys_errmsg("cannot start volume \"%s\" update", args.node); goto out_close; } while (bytes) { int ret, to_copy = vol_info->leb_size; if (to_copy > bytes) to_copy = bytes; ret = read(ifd, buf, to_copy); if (ret <= 0) { if (errno == EINTR) { warnmsg("do not interrupt me!"); continue; } else { sys_errmsg("cannot read %d bytes from \"%s\"", to_copy, args.img); goto out_close; } } err = ubi_write(fd, buf, ret); if (err) goto out_close; bytes -= ret; } close(ifd); close(fd); free(buf); return 0; out_close: close(ifd); out_close1: close(fd); out_free: free(buf); return -1; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; struct ubi_vol_info vol_info; err = parse_opt(argc, argv); if (err) return -1; libubi = libubi_open(); if (!libubi) { if (errno == 0) errmsg("UBI is not present in the system"); else sys_errmsg("cannot open libubi"); goto out_libubi; } err = ubi_probe_node(libubi, args.node); if (err == 1) { errmsg("\"%s\" is an UBI device node, not an UBI volume node", args.node); goto out_libubi; } else if (err < 0) { if (errno == ENODEV) errmsg("\"%s\" is not an UBI volume node", args.node); else sys_errmsg("error while probing \"%s\"", args.node); goto out_libubi; } err = ubi_get_vol_info(libubi, args.node, &vol_info); if (err) { sys_errmsg("cannot get information about UBI volume \"%s\"", args.node); goto out_libubi; } if (args.truncate) err = truncate_volume(libubi); else err = update_volume(libubi, &vol_info); if (err) goto out_libubi; libubi_close(libubi); return 0; out_libubi: libubi_close(libubi); return -1; } mtd-utils-1.5.0/ubi-utils/ubiutils-common.c000066400000000000000000000116331175167361300206640ustar00rootroot00000000000000/* * Copyright (C) 2007, 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This file contains various common stuff used by UBI utilities. * * Authors: Artem Bityutskiy * Adrian Hunter */ #define PROGRAM_NAME "ubiutils" #include #include #include #include #include #include #include #include "common.h" /** * get_multiplier - convert size specifier to an integer multiplier. * @str: the size specifier string * * This function parses the @str size specifier, which may be one of * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive * size multiplier in case of success and %-1 in case of failure. */ static int get_multiplier(const char *str) { if (!str) return 1; /* Remove spaces before the specifier */ while (*str == ' ' || *str == '\t') str += 1; if (!strcmp(str, "KiB")) return 1024; if (!strcmp(str, "MiB")) return 1024 * 1024; if (!strcmp(str, "GiB")) return 1024 * 1024 * 1024; return -1; } /** * ubiutils_get_bytes - convert a string containing amount of bytes into an * integer * @str: string to convert * * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' * size specifiers. Returns positive amount of bytes in case of success and %-1 * in case of failure. */ long long ubiutils_get_bytes(const char *str) { char *endp; long long bytes = strtoull(str, &endp, 0); if (endp == str || bytes < 0) { fprintf(stderr, "incorrect amount of bytes: \"%s\"\n", str); return -1; } if (*endp != '\0') { int mult = get_multiplier(endp); if (mult == -1) { fprintf(stderr, "bad size specifier: \"%s\" - " "should be 'KiB', 'MiB' or 'GiB'\n", endp); return -1; } bytes *= mult; } return bytes; } /** * ubiutils_print_bytes - print bytes. * @bytes: variable to print * @bracket: whether brackets have to be put or not * * This is a helper function which prints amount of bytes in a human-readable * form, i.e., it prints the exact amount of bytes following by the approximate * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes * is. */ void ubiutils_print_bytes(long long bytes, int bracket) { const char *p; if (bracket) p = " ("; else p = ", "; printf("%lld bytes", bytes); if (bytes > 1024 * 1024 * 1024) printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024)); else if (bytes > 1024 * 1024) printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024)); else if (bytes > 1024 && bytes != 0) printf("%s%.1f KiB", p, (double)bytes / 1024); else return; if (bracket) printf(")"); } /** * ubiutils_print_text - print text and fold it. * @stream: file stream to print to * @text: text to print * @width: maximum allowed text width * * Print text and fold it so that each line would not have more then @width * characters. */ void ubiutils_print_text(FILE *stream, const char *text, int width) { int pos, bpos = 0; const char *p; char line[1024]; if (width > 1023) { fprintf(stream, "%s\n", text); return; } p = text; pos = 0; while (p[pos]) { while (!isspace(p[pos])) { line[pos] = p[pos]; if (!p[pos]) break; ++pos; if (pos == width) { line[pos] = '\0'; fprintf(stream, "%s\n", line); p += pos; pos = 0; } } while (pos < width) { line[pos] = p[pos]; if (!p[pos]) { bpos = pos; break; } if (isspace(p[pos])) bpos = pos; ++pos; } line[bpos] = '\0'; fprintf(stream, "%s\n", line); p += bpos; pos = 0; while (p[pos] && isspace(p[pos])) ++p; } } /** * ubiutils_srand - randomly seed the standard pseudo-random generator. * * This helper function seeds the standard libc pseudo-random generator with a * more or less random value to make sure the 'rand()' call does not return the * same sequence every time UBI utilities run. Returns zero in case of success * and a %-1 in case of error. */ int ubiutils_srand(void) { struct timeval tv; struct timezone tz; unsigned int seed; /* * Just assume that a combination of the PID + current time is a * reasonably random number. */ if (gettimeofday(&tv, &tz)) return -1; seed = (unsigned int)tv.tv_sec; seed += (unsigned int)tv.tv_usec; seed *= getpid(); seed %= RAND_MAX; srand(seed); return 0; }