From a4177ccef5c675f2276fcd6dfe39a3829d49fff4 Mon Sep 17 00:00:00 2001 From: Xinyu Chen Date: Thu, 15 Oct 2009 23:23:02 +0800 Subject: Initial version of mtd utils Push the ubifs utils firstly Signed-off-by: Xinyu Chen --- Android.mk | 3 + mkfs.ubifs/Android.mk | 30 + mkfs.ubifs/COPYING | 340 +++++ mkfs.ubifs/README | 10 + mkfs.ubifs/compr.c | 217 +++ mkfs.ubifs/compr.h | 46 + mkfs.ubifs/crc16.c | 56 + mkfs.ubifs/crc16.h | 27 + mkfs.ubifs/crc32.c | 95 ++ mkfs.ubifs/crc32.h | 22 + mkfs.ubifs/defs.h | 107 ++ mkfs.ubifs/devtable.c | 524 +++++++ mkfs.ubifs/hashtable/hashtable.c | 274 ++++ mkfs.ubifs/hashtable/hashtable.h | 199 +++ mkfs.ubifs/hashtable/hashtable_itr.c | 188 +++ mkfs.ubifs/hashtable/hashtable_itr.h | 112 ++ mkfs.ubifs/hashtable/hashtable_private.h | 85 ++ mkfs.ubifs/key.h | 189 +++ mkfs.ubifs/lpt.c | 578 ++++++++ mkfs.ubifs/lpt.h | 28 + mkfs.ubifs/mkfs.ubifs.c | 2236 ++++++++++++++++++++++++++++++ mkfs.ubifs/mkfs.ubifs.h | 146 ++ mkfs.ubifs/ubifs-media.h | 745 ++++++++++ mkfs.ubifs/ubifs.h | 430 ++++++ new-utils/Android.mk | 153 ++ new-utils/Makefile | 57 + new-utils/include/libiniparser.h | 280 ++++ new-utils/include/libmtd.h | 73 + new-utils/include/libscan.h | 112 ++ new-utils/include/libubi.h | 382 +++++ new-utils/include/libubigen.h | 110 ++ new-utils/include/linux/jffs2.h | 218 +++ new-utils/include/mtd/ftl-user.h | 76 + new-utils/include/mtd/inftl-user.h | 91 ++ new-utils/include/mtd/jffs2-user.h | 82 ++ new-utils/include/mtd/mtd-abi.h | 152 ++ new-utils/include/mtd/mtd-user.h | 21 + new-utils/include/mtd/nftl-user.h | 76 + new-utils/include/mtd/ubi-header.h | 372 +++++ new-utils/include/mtd/ubi-user.h | 268 ++++ new-utils/include/mtd_swab.h | 51 + new-utils/src/common.c | 194 +++ new-utils/src/common.h | 85 ++ new-utils/src/crc32.c | 95 ++ new-utils/src/crc32.h | 19 + new-utils/src/dictionary.c | 405 ++++++ new-utils/src/dictionary.h | 174 +++ new-utils/src/libiniparser.c | 646 +++++++++ new-utils/src/libmtd.c | 316 +++++ new-utils/src/libscan.c | 225 +++ new-utils/src/libubi.c | 1187 ++++++++++++++++ new-utils/src/libubi_int.h | 133 ++ new-utils/src/libubigen.c | 330 +++++ new-utils/src/ubiattach.c | 205 +++ new-utils/src/ubicrc32.c | 124 ++ new-utils/src/ubidetach.c | 181 +++ new-utils/src/ubiformat.c | 780 +++++++++++ new-utils/src/ubimkvol.c | 313 +++++ new-utils/src/ubinfo.c | 410 ++++++ new-utils/src/ubinize.c | 612 ++++++++ new-utils/src/ubirmvol.c | 230 +++ new-utils/src/ubiupdatevol.c | 355 +++++ 62 files changed, 16280 insertions(+) create mode 100644 Android.mk create mode 100644 mkfs.ubifs/Android.mk create mode 100644 mkfs.ubifs/COPYING create mode 100644 mkfs.ubifs/README create mode 100644 mkfs.ubifs/compr.c create mode 100644 mkfs.ubifs/compr.h create mode 100644 mkfs.ubifs/crc16.c create mode 100644 mkfs.ubifs/crc16.h create mode 100644 mkfs.ubifs/crc32.c create mode 100644 mkfs.ubifs/crc32.h create mode 100644 mkfs.ubifs/defs.h create mode 100644 mkfs.ubifs/devtable.c create mode 100644 mkfs.ubifs/hashtable/hashtable.c create mode 100644 mkfs.ubifs/hashtable/hashtable.h create mode 100644 mkfs.ubifs/hashtable/hashtable_itr.c create mode 100644 mkfs.ubifs/hashtable/hashtable_itr.h create mode 100644 mkfs.ubifs/hashtable/hashtable_private.h create mode 100644 mkfs.ubifs/key.h create mode 100644 mkfs.ubifs/lpt.c create mode 100644 mkfs.ubifs/lpt.h create mode 100644 mkfs.ubifs/mkfs.ubifs.c create mode 100644 mkfs.ubifs/mkfs.ubifs.h create mode 100644 mkfs.ubifs/ubifs-media.h create mode 100644 mkfs.ubifs/ubifs.h create mode 100644 new-utils/Android.mk create mode 100644 new-utils/Makefile create mode 100644 new-utils/include/libiniparser.h create mode 100644 new-utils/include/libmtd.h create mode 100644 new-utils/include/libscan.h create mode 100644 new-utils/include/libubi.h create mode 100644 new-utils/include/libubigen.h create mode 100644 new-utils/include/linux/jffs2.h create mode 100644 new-utils/include/mtd/ftl-user.h create mode 100644 new-utils/include/mtd/inftl-user.h create mode 100644 new-utils/include/mtd/jffs2-user.h create mode 100644 new-utils/include/mtd/mtd-abi.h create mode 100644 new-utils/include/mtd/mtd-user.h create mode 100644 new-utils/include/mtd/nftl-user.h create mode 100644 new-utils/include/mtd/ubi-header.h create mode 100644 new-utils/include/mtd/ubi-user.h create mode 100644 new-utils/include/mtd_swab.h create mode 100644 new-utils/src/common.c create mode 100644 new-utils/src/common.h create mode 100644 new-utils/src/crc32.c create mode 100644 new-utils/src/crc32.h create mode 100644 new-utils/src/dictionary.c create mode 100644 new-utils/src/dictionary.h create mode 100644 new-utils/src/libiniparser.c create mode 100644 new-utils/src/libmtd.c create mode 100644 new-utils/src/libscan.c create mode 100644 new-utils/src/libubi.c create mode 100644 new-utils/src/libubi_int.h create mode 100644 new-utils/src/libubigen.c create mode 100644 new-utils/src/ubiattach.c create mode 100644 new-utils/src/ubicrc32.c create mode 100644 new-utils/src/ubidetach.c create mode 100644 new-utils/src/ubiformat.c create mode 100644 new-utils/src/ubimkvol.c create mode 100644 new-utils/src/ubinfo.c create mode 100644 new-utils/src/ubinize.c create mode 100644 new-utils/src/ubirmvol.c create mode 100644 new-utils/src/ubiupdatevol.c diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..407f033 --- /dev/null +++ b/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(strip $(TARGET_USERIMAGES_USE_UBIFS)),true) +include $(all-subdir-makefiles) +endif diff --git a/mkfs.ubifs/Android.mk b/mkfs.ubifs/Android.mk new file mode 100644 index 0000000..dea9ea6 --- /dev/null +++ b/mkfs.ubifs/Android.mk @@ -0,0 +1,30 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +ifeq ($(BUILD_MKFS_UBIFS),true) + include $(CLEAR_VARS) + + LOCAL_SRC_FILES := \ + mkfs.ubifs.c \ + crc16.c \ + crc32.c \ + devtable.c \ + lpt.c \ + compr.c \ + hashtable/hashtable.c \ + hashtable/hashtable_itr.c + + LOCAL_CFLAGS = -O2 -Wall + LOCAL_CFLAGS+= -Wpointer-arith -Wwrite-strings -Wstrict-prototypes + LOCAL_CFLAGS+= -Wmissing-prototypes -Winline + LOCAL_LDLIBS:= -lz -llzo2 -lm -luuid -static + + LOCAL_C_INCLUDES += $(LOCAL_PATH)/ + LOCAL_C_INCLUDES += $(LOCAL_PATH)/hashtable + + LOCAL_MODULE := mkfs.ubifs + + include $(BUILD_HOST_EXECUTABLE) + +endif diff --git a/mkfs.ubifs/COPYING b/mkfs.ubifs/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/mkfs.ubifs/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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. diff --git a/mkfs.ubifs/README b/mkfs.ubifs/README new file mode 100644 index 0000000..4fbaa7d --- /dev/null +++ b/mkfs.ubifs/README @@ -0,0 +1,10 @@ +UBIFS File System - Make File System program + +* crc16.h and crc16.c were copied from the linux kernel. +* crc32.h and crc32.c were copied from mtd-utils and amended. +* ubifs-media.h is fs/ubifs/ubifs-media.h from the linux kernel +* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. +* key.h is copied from fs/ubifs/key.h from the linux kernel. +* defs.h is a bunch of definitions to smooth things over. +* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. +* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ diff --git a/mkfs.ubifs/compr.c b/mkfs.ubifs/compr.c new file mode 100644 index 0000000..e378c5d --- /dev/null +++ b/mkfs.ubifs/compr.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 University of Szeged, Hungary + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "compr.h" +#include "ubifs-media.h" +#include "mkfs.ubifs.h" + +static void *lzo_mem; +static unsigned long long errcnt = 0; +static struct ubifs_info *c = &info_; + +#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION +#define DEFLATE_DEF_WINBITS 11 +#define DEFLATE_DEF_MEMLEVEL 8 + +static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len) +{ + z_stream strm; + + strm.zalloc = NULL; + strm.zfree = NULL; + + /* + * Match exactly the zlib parameters used by the Linux kernel crypto + * API. + */ + if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, + -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, + Z_DEFAULT_STRATEGY)) { + errcnt += 1; + return -1; + } + + strm.next_in = in_buf; + strm.avail_in = in_len; + strm.total_in = 0; + + strm.next_out = out_buf; + strm.avail_out = *out_len; + strm.total_out = 0; + + if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { + deflateEnd(&strm); + errcnt += 1; + return -1; + } + + if (deflateEnd(&strm) != Z_OK) { + errcnt += 1; + return -1; + } + + *out_len = strm.total_out; + + return 0; +} + +static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len) +{ + lzo_uint len; + int ret; + + len = *out_len; + ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); + *out_len = len; + + if (ret != LZO_E_OK) { + errcnt += 1; + return -1; + } + + return 0; +} + +static int no_compress(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len) +{ + memcpy(out_buf, in_buf, in_len); + *out_len = in_len; + return 0; +} + +static char *zlib_buf; + +static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, + size_t *out_len, int *type) +{ + int lzo_ret, zlib_ret; + size_t lzo_len, zlib_len; + + lzo_len = zlib_len = *out_len; + lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); + zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); + + if (lzo_ret && zlib_ret) + /* Both compressors failed */ + return -1; + + if (!lzo_ret && !zlib_ret) { + double percent; + + /* Both compressors succeeded */ + if (lzo_len <= zlib_len ) + goto select_lzo; + + percent = (double)zlib_len / (double)lzo_len; + percent *= 100; + if (percent > 100 - c->favor_percent) + goto select_lzo; + goto select_zlib; + } + + if (lzo_ret) + /* Only zlib compressor succeeded */ + goto select_zlib; + + /* Only LZO compressor succeeded */ + +select_lzo: + *out_len = lzo_len; + *type = MKFS_UBIFS_COMPR_LZO; + return 0; + +select_zlib: + *out_len = zlib_len; + *type = MKFS_UBIFS_COMPR_ZLIB; + memcpy(out_buf, zlib_buf, zlib_len); + return 0; +} + +int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, + int type) +{ + int ret; + + if (in_len < UBIFS_MIN_COMPR_LEN) { + no_compress(in_buf, in_len, out_buf, out_len); + return MKFS_UBIFS_COMPR_NONE; + } + + if (c->favor_lzo) + ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); + else { + switch (type) { + case MKFS_UBIFS_COMPR_LZO: + ret = lzo_compress(in_buf, in_len, out_buf, out_len); + break; + case MKFS_UBIFS_COMPR_ZLIB: + ret = zlib_deflate(in_buf, in_len, out_buf, out_len); + break; + case MKFS_UBIFS_COMPR_NONE: + ret = 1; + break; + default: + errcnt += 1; + ret = 1; + break; + } + } + if (ret || *out_len >= in_len) { + no_compress(in_buf, in_len, out_buf, out_len); + return MKFS_UBIFS_COMPR_NONE; + } + return type; +} + +int init_compression(void) +{ + lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); + if (!lzo_mem) + return -1; + + zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); + if (!zlib_buf) { + free(lzo_mem); + return -1; + } + + return 0; +} + +void destroy_compression(void) +{ + free(zlib_buf); + free(lzo_mem); + if (errcnt) + fprintf(stderr, "%llu compression errors occurred\n", errcnt); +} diff --git a/mkfs.ubifs/compr.h b/mkfs.ubifs/compr.h new file mode 100644 index 0000000..e3dd95c --- /dev/null +++ b/mkfs.ubifs/compr.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 University of Szeged, Hungary + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#ifndef __UBIFS_COMPRESS_H__ +#define __UBIFS_COMPRESS_H__ + +/* + * Compressors may end-up with more data in the output buffer than in the input + * buffer. This constant defined the worst case factor, i.e. we assume that the + * output buffer may be at max. WORST_COMPR_FACTOR times larger than input + * buffer. + */ +#define WORST_COMPR_FACTOR 4 + +enum compression_type +{ + MKFS_UBIFS_COMPR_NONE, + MKFS_UBIFS_COMPR_LZO, + MKFS_UBIFS_COMPR_ZLIB, +}; + +int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, + int type); +int init_compression(void); +void destroy_compression(void); + +#endif diff --git a/mkfs.ubifs/crc16.c b/mkfs.ubifs/crc16.c new file mode 100644 index 0000000..a19512e --- /dev/null +++ b/mkfs.ubifs/crc16.c @@ -0,0 +1,56 @@ +/* + * This code was taken from the linux kernel. The license is GPL Version 2. + */ + +#include "crc16.h" + +/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ +uint16_t const crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * crc16 - compute the CRC-16 for the data buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer + * + * Returns the updated CRC value. + */ +uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) +{ + while (len--) + crc = crc16_byte(crc, *buffer++); + return crc; +} diff --git a/mkfs.ubifs/crc16.h b/mkfs.ubifs/crc16.h new file mode 100644 index 0000000..539d21a --- /dev/null +++ b/mkfs.ubifs/crc16.h @@ -0,0 +1,27 @@ +/* + * Implements the standard CRC-16: + * Width 16 + * Poly 0x8005 (x^16 + x^15 + x^2 + 1) + * Init 0 + * + * Copyright (c) 2005 Ben Gardner + * + * 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__ */ diff --git a/mkfs.ubifs/crc32.c b/mkfs.ubifs/crc32.c new file mode 100644 index 0000000..6b1e50c --- /dev/null +++ b/mkfs.ubifs/crc32.c @@ -0,0 +1,95 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +#include + +const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; diff --git a/mkfs.ubifs/crc32.h b/mkfs.ubifs/crc32.h new file mode 100644 index 0000000..86fc841 --- /dev/null +++ b/mkfs.ubifs/crc32.h @@ -0,0 +1,22 @@ +/* + * This code was taken from the linux kernel. The license is GPL Version 2. + */ + +#ifndef __CRC32_H__ +#define __CRC32_H__ + +#include + +extern const uint32_t crc32_table[256]; + +/* Return a 32-bit CRC of the contents of the buffer. */ +static inline uint32_t ubifs_crc32(uint32_t val, const void *ss, int len) +{ + const unsigned char *s = ss; + + while (--len >= 0) + val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); + return val; +} + +#endif /* __CRC32_H__ */ diff --git a/mkfs.ubifs/defs.h b/mkfs.ubifs/defs.h new file mode 100644 index 0000000..06cf9e5 --- /dev/null +++ b/mkfs.ubifs/defs.h @@ -0,0 +1,107 @@ +/* + * Greate deal of the code was taken from the kernel UBIFS implementation, and + * this file contains some "glue" definitions. + */ + +#ifndef __UBIFS_DEFS_H__ +#define __UBIFS_DEFS_H__ + +#define t16(x) ({ \ + uint16_t __b = (x); \ + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ +}) + +#define t32(x) ({ \ + uint32_t __b = (x); \ + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ +}) + +#define t64(x) ({ \ + uint64_t __b = (x); \ + (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ +}) + +#define cpu_to_le16(x) ((__le16){t16(x)}) +#define cpu_to_le32(x) ((__le32){t32(x)}) +#define cpu_to_le64(x) ((__le64){t64(x)}) + +#define le16_to_cpu(x) (t16((x))) +#define le32_to_cpu(x) (t32((x))) +#define le64_to_cpu(x) (t64((x))) + +#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) +#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) + +#define min_t(t,x,y) ({ \ + typeof((x)) _x = (x); \ + typeof((y)) _y = (y); \ + (_x < _y) ? _x : _y; \ +}) + +#define max_t(t,x,y) ({ \ + typeof((x)) _x = (x); \ + typeof((y)) _y = (y); \ + (_x > _y) ? _x : _y; \ +}) + +#define unlikely(x) (x) + +#define ubifs_assert(x) ({}) + +struct qstr +{ + char *name; + size_t len; +}; + +/** + * fls - find last (most-significant) bit set + * @x: the word to search + * + * This is defined the same way as ffs. + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ +static inline int fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +#if INT_MAX != 0x7fffffff +#error : sizeof(int) must be 4 for this program +#endif + +#if (~0ULL) != 0xffffffffffffffffULL +#error : sizeof(long long) must be 8 for this program +#endif + +#endif diff --git a/mkfs.ubifs/devtable.c b/mkfs.ubifs/devtable.c new file mode 100644 index 0000000..236a6e7 --- /dev/null +++ b/mkfs.ubifs/devtable.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Artem Bityutskiy + * + * Part of the device table parsing code was taken from the mkfs.jffs2 utility. + * The original author of that code is Erik Andersen, hence: + * Copyright (C) 2001, 2002 Erik Andersen + */ + +/* + * 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 + increment * 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); + + nm = malloc(len); + if (!nm) { + err_msg("cannot allocate %d bytes of memory", len); + goto out_free; + } + + sprintf(nm, "%s%d", name, i); + nh_elt->name = nm; + + dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", + nm, major(nh_elt->dev), minor(nh_elt->dev)); + + if (hashtable_search(ph_elt->name_htbl, nm)) { + err_msg("'%s' is referred twice", buf); + free (nm); + goto out_free; + } + + if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { + err_msg("cannot insert into name hash table"); + free (nm); + goto out_free; + } + } + free(name); + name = NULL; + } + + return 0; + +out_free: + free(ph_elt); + free(nh_elt); + free(path); + free(name); + return -1; +} + +/** + * parse_devtable - parse the device table. + * @tbl_file: device table file name + * + * This function parses the device table and prepare the hash table which will + * later be used by mkfs.ubifs to create the specified files/device nodes. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int parse_devtable(const char *tbl_file) +{ + FILE *f; + char *line = NULL; + struct stat st; + size_t len; + + dbg_msg(1, "parsing device table file '%s'", tbl_file); + + path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); + if (!path_htbl) + return err_msg("cannot create path hash table"); + + f = fopen(tbl_file, "r"); + if (!f) + return sys_err_msg("cannot open '%s'", tbl_file); + + if (fstat(fileno(f), &st) < 0) { + sys_err_msg("cannot stat '%s'", tbl_file); + goto out_close; + } + + if (st.st_size < 10) { + sys_err_msg("'%s' is too short", tbl_file); + goto out_close; + } + + /* + * The general plan now is to read in one line at a time, check for + * leading comment delimiters ('#'), then try and parse the line as a + * device table + */ + while (getline(&line, &len, f) != -1) { + /* First trim off any white-space */ + len = strlen(line); + + /* Trim trailing white-space */ + while (len > 0 && isspace(line[len - 1])) + line[--len] = '\0'; + /* Trim leading white-space */ + memmove(line, &line[strspn(line, " \n\r\t\v")], len); + + /* How long are we after trimming? */ + len = strlen(line); + + /* If this is not a comment line, try to interpret it */ + if (len && *line != '#') { + if (interpret_table_entry(line)) { + err_msg("cannot parse '%s'", line); + goto out_close; + } + } + + free(line); + line = NULL; + } + + dbg_msg(1, "finished parsing"); + fclose(f); + return 0; + +out_close: + fclose(f); + free_devtable_info(); + return -1; +} + +/** + * devtbl_find_path - find a path in the path hash table. + * @path: UBIFS path to find. + * + * This looks up the path hash table. Returns the path hash table element + * reference if @path was found and %NULL if not. + */ +struct path_htbl_element *devtbl_find_path(const char *path) +{ + if (!path_htbl) + return NULL; + + return hashtable_search(path_htbl, (void *)path); +} + +/** + * devtbl_find_name - find a name in the name hash table. + * @ph_etl: path hash table element to find at + * @name: name to find + * + * This looks up the name hash table. Returns the name hash table element + * reference if @name found and %NULL if not. + */ +struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, + const char *name) +{ + if (!path_htbl) + return NULL; + + return hashtable_search(ph_elt->name_htbl, (void *)name); +} + +/** + * override_attributes - override inode attributes. + * @st: struct stat object to containing the attributes to override + * @ph_elt: path hash table element object + * @nh_elt: name hash table element object containing the new values + * + * The device table file may override attributes like UID of files. For + * example, the device table may contain a "/dev" entry, and the UBIFS FS on + * the host may contain "/dev" directory. In this case the attributes of the + * "/dev" directory inode has to be as the device table specifies. + * + * Note, the hash element is removed by this function as well. + */ +int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, + struct name_htbl_element *nh_elt) +{ + if (!path_htbl) + return 0; + + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || + S_ISFIFO(st->st_mode)) + return err_msg("%s/%s both exists at UBIFS root at host, " + "and is referred from the device table", + strcmp(ph_elt->path, "/") ? ph_elt->path : "", + nh_elt->name); + + if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) + return err_msg("%s/%s is referred from the device table also exists in " + "the UBIFS root directory at host, but the file type is " + "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", + nh_elt->name); + + dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", + nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); + + st->st_uid = nh_elt->uid; + st->st_gid = nh_elt->gid; + st->st_mode = nh_elt->mode; + + hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); + return 0; +} + +/** + * first_name_htbl_element - return first element of the name hash table. + * @ph_elt: the path hash table the name hash table belongs to + * @itr: double pointer to a 'struct hashtable_itr' object where the + * information about further iterations is stored + * + * This function implements name hash table iteration together with + * 'next_name_htbl_element()'. Returns the first name hash table element or + * %NULL if the hash table is empty. + */ +struct name_htbl_element * +first_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr) +{ + if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) + return NULL; + + *itr = hashtable_iterator(ph_elt->name_htbl); + return hashtable_iterator_value(*itr); +} + +/** + * first_name_htbl_element - return next element of the name hash table. + * @ph_elt: the path hash table the name hash table belongs to + * @itr: double pointer to a 'struct hashtable_itr' object where the + * information about further iterations is stored + * + * This function implements name hash table iteration together with + * 'first_name_htbl_element()'. Returns the next name hash table element or + * %NULL if there are no more elements. + */ +struct name_htbl_element * +next_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr) +{ + if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) + return NULL; + + return hashtable_iterator_value(*itr); +} + +/** + * free_devtable_info - free device table information. + * + * This function frees the path hash table and the name hash tables. + */ +void free_devtable_info(void) +{ + struct hashtable_itr *ph_itr; + struct path_htbl_element *ph_elt; + + if (!path_htbl) + return; + + if (hashtable_count(path_htbl) > 0) { + ph_itr = hashtable_iterator(path_htbl); + do { + ph_elt = hashtable_iterator_value(ph_itr); + /* + * Note, since we use the same string for the key and + * @name in the name hash table elements, we do not + * have to iterate name hash table because @name memory + * will be freed when freeing the key. + */ + hashtable_destroy(ph_elt->name_htbl, 1); + } while (hashtable_iterator_advance(ph_itr)); + } + hashtable_destroy(path_htbl, 1); +} diff --git a/mkfs.ubifs/hashtable/hashtable.c b/mkfs.ubifs/hashtable/hashtable.c new file mode 100644 index 0000000..763357e --- /dev/null +++ b/mkfs.ubifs/hashtable/hashtable.c @@ -0,0 +1,274 @@ +/* Copyright (C) 2004 Christopher Clark */ + +#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 = sizeof(primes)/sizeof(primes[0]); +const float max_load_factor = 0.65; + +/*****************************************************************************/ +struct hashtable * +create_hashtable(unsigned int minsize, + unsigned int (*hashf) (void*), + int (*eqf) (void*,void*)) +{ + struct hashtable *h; + unsigned int pindex, size = primes[0]; + /* Check requested hashtable isn't too large */ + if (minsize > (1u << 30)) return NULL; + /* Enforce size as prime */ + for (pindex=0; pindex < prime_table_length; pindex++) { + if (primes[pindex] > minsize) { size = primes[pindex]; break; } + } + h = (struct hashtable *)malloc(sizeof(struct hashtable)); + if (NULL == h) return NULL; /*oom*/ + h->table = (struct entry **)malloc(sizeof(struct entry*) * size); + if (NULL == h->table) { free(h); return NULL; } /*oom*/ + memset(h->table, 0, size * sizeof(struct entry *)); + h->tablelength = size; + h->primeindex = pindex; + h->entrycount = 0; + h->hashfn = hashf; + h->eqfn = eqf; + h->loadlimit = (unsigned int) ceil(size * max_load_factor); + return h; +} + +/*****************************************************************************/ +unsigned int +hash(struct hashtable *h, void *k) +{ + /* Aim to protect against poor hash functions by adding logic here + * - logic taken from java 1.4 hashtable source */ + unsigned int i = h->hashfn(k); + i += ~(i << 9); + i ^= ((i >> 14) | (i << 18)); /* >>> */ + i += (i << 4); + i ^= ((i >> 10) | (i << 22)); /* >>> */ + return i; +} + +/*****************************************************************************/ +static int +hashtable_expand(struct hashtable *h) +{ + /* Double the size of the table to accomodate more entries */ + struct entry **newtable; + struct entry *e; + struct entry **pE; + unsigned int newsize, i, index; + /* Check we're not hitting max capacity */ + if (h->primeindex == (prime_table_length - 1)) return 0; + newsize = primes[++(h->primeindex)]; + + newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); + if (NULL != newtable) + { + memset(newtable, 0, newsize * sizeof(struct entry *)); + /* This algorithm is not 'stable'. ie. it reverses the list + * when it transfers entries between the tables */ + for (i = 0; i < h->tablelength; i++) { + while (NULL != (e = h->table[i])) { + h->table[i] = e->next; + index = indexFor(newsize,e->h); + e->next = newtable[index]; + newtable[index] = e; + } + } + free(h->table); + h->table = newtable; + } + /* Plan B: realloc instead */ + else + { + newtable = (struct entry **) + realloc(h->table, newsize * sizeof(struct entry *)); + if (NULL == newtable) { (h->primeindex)--; return 0; } + h->table = newtable; + memset(newtable[h->tablelength], 0, newsize - h->tablelength); + for (i = 0; i < h->tablelength; i++) { + for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { + index = indexFor(newsize,e->h); + if (index == i) + { + pE = &(e->next); + } + else + { + *pE = e->next; + e->next = newtable[index]; + newtable[index] = e; + } + } + } + } + h->tablelength = newsize; + h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); + return -1; +} + +/*****************************************************************************/ +unsigned int +hashtable_count(struct hashtable *h) +{ + return h->entrycount; +} + +/*****************************************************************************/ +int +hashtable_insert(struct hashtable *h, void *k, void *v) +{ + /* This method allows duplicate keys - but they shouldn't be used */ + unsigned int index; + struct entry *e; + if (++(h->entrycount) > h->loadlimit) + { + /* Ignore the return value. If expand fails, we should + * still try cramming just this value into the existing table + * -- we may not have memory for a larger table, but one more + * element may be ok. Next time we insert, we'll try expanding again.*/ + hashtable_expand(h); + } + e = (struct entry *)malloc(sizeof(struct entry)); + if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ + e->h = hash(h,k); + index = indexFor(h->tablelength,e->h); + e->k = k; + e->v = v; + e->next = h->table[index]; + h->table[index] = e; + return -1; +} + +/*****************************************************************************/ +void * /* returns value associated with key */ +hashtable_search(struct hashtable *h, void *k) +{ + struct entry *e; + unsigned int hashvalue, index; + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + e = h->table[index]; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +void * /* returns value associated with key */ +hashtable_remove(struct hashtable *h, void *k) +{ + /* TODO: consider compacting the table when the load factor drops enough, + * or provide a 'compact' method. */ + + struct entry *e; + struct entry **pE; + void *v; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hash(h,k)); + pE = &(h->table[index]); + e = *pE; + while (NULL != e) + { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) + { + *pE = e->next; + h->entrycount--; + v = e->v; + freekey(e->k); + free(e); + return v; + } + pE = &(e->next); + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +/* destroy */ +void +hashtable_destroy(struct hashtable *h, int free_values) +{ + unsigned int i; + struct entry *e, *f; + struct entry **table = h->table; + if (free_values) + { + for (i = 0; i < h->tablelength; i++) + { + e = table[i]; + while (NULL != e) + { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } + } + } + else + { + for (i = 0; i < h->tablelength; i++) + { + e = table[i]; + while (NULL != e) + { f = e; e = e->next; freekey(f->k); free(f); } + } + } + free(h->table); + free(h); +} + +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ diff --git a/mkfs.ubifs/hashtable/hashtable.h b/mkfs.ubifs/hashtable/hashtable.h new file mode 100644 index 0000000..b90781a --- /dev/null +++ b/mkfs.ubifs/hashtable/hashtable.h @@ -0,0 +1,199 @@ +/* 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. +*/ diff --git a/mkfs.ubifs/hashtable/hashtable_itr.c b/mkfs.ubifs/hashtable/hashtable_itr.c new file mode 100644 index 0000000..5dced84 --- /dev/null +++ b/mkfs.ubifs/hashtable/hashtable_itr.c @@ -0,0 +1,188 @@ +/* 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. +*/ diff --git a/mkfs.ubifs/hashtable/hashtable_itr.h b/mkfs.ubifs/hashtable/hashtable_itr.h new file mode 100644 index 0000000..eea699a --- /dev/null +++ b/mkfs.ubifs/hashtable/hashtable_itr.h @@ -0,0 +1,112 @@ +/* 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. +*/ diff --git a/mkfs.ubifs/hashtable/hashtable_private.h b/mkfs.ubifs/hashtable/hashtable_private.h new file mode 100644 index 0000000..3e95f60 --- /dev/null +++ b/mkfs.ubifs/hashtable/hashtable_private.h @@ -0,0 +1,85 @@ +/* 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. +*/ diff --git a/mkfs.ubifs/key.h b/mkfs.ubifs/key.h new file mode 100644 index 0000000..d3a02d4 --- /dev/null +++ b/mkfs.ubifs/key.h @@ -0,0 +1,189 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2006-2008 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Adrian Hunter + */ + +/* + * This header contains various key-related definitions and helper function. + * UBIFS allows several key schemes, so we access key fields only via these + * helpers. At the moment only one key scheme is supported. + * + * Simple key scheme + * ~~~~~~~~~~~~~~~~~ + * + * Keys are 64-bits long. First 32-bits are inode number (parent inode number + * in case of direntry key). Next 3 bits are node type. The last 29 bits are + * 4KiB offset in case of inode node, and direntry hash in case of a direntry + * node. We use "r5" hash borrowed from reiserfs. + */ + +#ifndef __UBIFS_KEY_H__ +#define __UBIFS_KEY_H__ + +/** + * key_mask_hash - mask a valid hash value. + * @val: value to be masked + * + * We use hash values as offset in directories, so values %0 and %1 are + * reserved for "." and "..". %2 is reserved for "end of readdir" marker. This + * function makes sure the reserved values are not used. + */ +static inline uint32_t key_mask_hash(uint32_t hash) +{ + hash &= UBIFS_S_KEY_HASH_MASK; + if (unlikely(hash <= 2)) + hash += 3; + return hash; +} + +/** + * key_r5_hash - R5 hash function (borrowed from reiserfs). + * @s: direntry name + * @len: name length + */ +static inline uint32_t key_r5_hash(const char *s, int len) +{ + uint32_t a = 0; + const signed char *str = (const signed char *)s; + + len = len; + while (*str) { + a += *str << 4; + a += *str >> 4; + a *= 11; + str++; + } + + return key_mask_hash(a); +} + +/** + * key_test_hash - testing hash function. + * @str: direntry name + * @len: name length + */ +static inline uint32_t key_test_hash(const char *str, int len) +{ + uint32_t a = 0; + + len = min_t(uint32_t, len, 4); + memcpy(&a, str, len); + return key_mask_hash(a); +} + +/** + * ino_key_init - initialize inode key. + * @c: UBIFS file-system description object + * @key: key to initialize + * @inum: inode number + */ +static inline void ino_key_init(union ubifs_key *key, ino_t inum) +{ + key->u32[0] = inum; + key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; +} + +/** + * dent_key_init - initialize directory entry key. + * @c: UBIFS file-system description object + * @key: key to initialize + * @inum: parent inode number + * @nm: direntry name and length + */ +static inline void dent_key_init(const struct ubifs_info *c, + union ubifs_key *key, ino_t inum, + const struct qstr *nm) +{ + uint32_t hash = c->key_hash(nm->name, nm->len); + + ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); + key->u32[0] = inum; + key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); +} + +/** + * data_key_init - initialize data key. + * @c: UBIFS file-system description object + * @key: key to initialize + * @inum: inode number + * @block: block number + */ +static inline void data_key_init(union ubifs_key *key, ino_t inum, + unsigned int block) +{ + ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); + key->u32[0] = inum; + key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); +} + +/** + * key_write - transform a key from in-memory format. + * @c: UBIFS file-system description object + * @from: the key to transform + * @to: the key to store the result + */ +static inline void key_write(const union ubifs_key *from, void *to) +{ + union ubifs_key *t = to; + + t->j32[0] = cpu_to_le32(from->u32[0]); + t->j32[1] = cpu_to_le32(from->u32[1]); + memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); +} + +/** + * key_write_idx - transform a key from in-memory format for the index. + * @c: UBIFS file-system description object + * @from: the key to transform + * @to: the key to store the result + */ +static inline void key_write_idx(const union ubifs_key *from, void *to) +{ + union ubifs_key *t = to; + + t->j32[0] = cpu_to_le32(from->u32[0]); + t->j32[1] = cpu_to_le32(from->u32[1]); +} + +/** + * keys_cmp - compare keys. + * @c: UBIFS file-system description object + * @key1: the first key to compare + * @key2: the second key to compare + * + * This function compares 2 keys and returns %-1 if @key1 is less than + * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. + */ +static inline int keys_cmp(const union ubifs_key *key1, + const union ubifs_key *key2) +{ + if (key1->u32[0] < key2->u32[0]) + return -1; + if (key1->u32[0] > key2->u32[0]) + return 1; + if (key1->u32[1] < key2->u32[1]) + return -1; + if (key1->u32[1] > key2->u32[1]) + return 1; + + return 0; +} + +#endif /* !__UBIFS_KEY_H__ */ diff --git a/mkfs.ubifs/lpt.c b/mkfs.ubifs/lpt.c new file mode 100644 index 0000000..f6d4352 --- /dev/null +++ b/mkfs.ubifs/lpt.c @@ -0,0 +1,578 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2006, 2007 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Adrian Hunter + * Artem Bityutskiy + */ + +#include "mkfs.ubifs.h" + +/** + * do_calc_lpt_geom - calculate sizes for the LPT area. + * @c: the UBIFS file-system description object + * + * Calculate the sizes of LPT bit fields, nodes, and tree, based on the + * properties of the flash and whether LPT is "big" (c->big_lpt). + */ +static void do_calc_lpt_geom(struct ubifs_info *c) +{ + int n, bits, per_leb_wastage; + long long sz, tot_wastage; + + c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + + n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + c->nnode_cnt = n; + while (n > 1) { + n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + c->nnode_cnt += n; + } + + c->lpt_hght = 1; + n = UBIFS_LPT_FANOUT; + while (n < c->pnode_cnt) { + c->lpt_hght += 1; + n <<= UBIFS_LPT_FANOUT_SHIFT; + } + + c->space_bits = fls(c->leb_size) - 3; + c->lpt_lnum_bits = fls(c->lpt_lebs); + c->lpt_offs_bits = fls(c->leb_size - 1); + c->lpt_spc_bits = fls(c->leb_size); + + n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + c->pcnt_bits = fls(n - 1); + + c->lnum_bits = fls(c->max_leb_cnt - 1); + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + (c->big_lpt ? c->pcnt_bits : 0) + + (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; + c->pnode_sz = (bits + 7) / 8; + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + (c->big_lpt ? c->pcnt_bits : 0) + + (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; + c->nnode_sz = (bits + 7) / 8; + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + c->lpt_lebs * c->lpt_spc_bits * 2; + c->ltab_sz = (bits + 7) / 8; + + bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + + c->lnum_bits * c->lsave_cnt; + c->lsave_sz = (bits + 7) / 8; + + /* Calculate the minimum LPT size */ + c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; + c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; + c->lpt_sz += c->ltab_sz; + c->lpt_sz += c->lsave_sz; + + /* Add wastage */ + sz = c->lpt_sz; + per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); + sz += per_leb_wastage; + tot_wastage = per_leb_wastage; + while (sz > c->leb_size) { + sz += per_leb_wastage; + sz -= c->leb_size; + tot_wastage += per_leb_wastage; + } + tot_wastage += ALIGN(sz, c->min_io_size) - sz; + c->lpt_sz += tot_wastage; +} + +/** + * calc_dflt_lpt_geom - calculate default LPT geometry. + * @c: the UBIFS file-system description object + * @main_lebs: number of main area LEBs is passed and returned here + * @big_lpt: whether the LPT area is "big" is returned here + * + * The size of the LPT area depends on parameters that themselves are dependent + * on the size of the LPT area. This function, successively recalculates the LPT + * area geometry until the parameters and resultant geometry are consistent. + * + * This function returns %0 on success and a negative error code on failure. + */ +int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) +{ + int i, lebs_needed; + long long sz; + + /* Start by assuming the minimum number of LPT LEBs */ + c->lpt_lebs = UBIFS_MIN_LPT_LEBS; + c->main_lebs = *main_lebs - c->lpt_lebs; + if (c->main_lebs <= 0) + return -EINVAL; + + /* And assume we will use the small LPT model */ + c->big_lpt = 0; + + /* + * Calculate the geometry based on assumptions above and then see if it + * makes sense + */ + do_calc_lpt_geom(c); + + /* Small LPT model must have lpt_sz < leb_size */ + if (c->lpt_sz > c->leb_size) { + /* Nope, so try again using big LPT model */ + c->big_lpt = 1; + do_calc_lpt_geom(c); + } + + /* Now check there are enough LPT LEBs */ + for (i = 0; i < 64 ; i++) { + sz = c->lpt_sz * 4; /* Allow 4 times the size */ + sz += c->leb_size - 1; + do_div(sz, c->leb_size); + lebs_needed = sz; + if (lebs_needed > c->lpt_lebs) { + /* Not enough LPT LEBs so try again with more */ + c->lpt_lebs = lebs_needed; + c->main_lebs = *main_lebs - c->lpt_lebs; + if (c->main_lebs <= 0) + return -EINVAL; + do_calc_lpt_geom(c); + continue; + } + if (c->ltab_sz > c->leb_size) { + err_msg("LPT ltab too big"); + return -EINVAL; + } + *main_lebs = c->main_lebs; + *big_lpt = c->big_lpt; + return 0; + } + return -EINVAL; +} + +/** + * pack_bits - pack bit fields end-to-end. + * @addr: address at which to pack (passed and next address returned) + * @pos: bit position at which to pack (passed and next position returned) + * @val: value to pack + * @nrbits: number of bits of value to pack (1-32) + */ +static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) +{ + uint8_t *p = *addr; + int b = *pos; + + if (b) { + *p |= ((uint8_t)val) << b; + nrbits += b; + if (nrbits > 8) { + *++p = (uint8_t)(val >>= (8 - b)); + if (nrbits > 16) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 24) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 32) + *++p = (uint8_t)(val >>= 8); + } + } + } + } else { + *p = (uint8_t)val; + if (nrbits > 8) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 16) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 24) + *++p = (uint8_t)(val >>= 8); + } + } + } + b = nrbits & 7; + if (b == 0) + p++; + *addr = p; + *pos = b; +} + +/** + * pack_pnode - pack all the bit fields of a pnode. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @pnode: pnode to pack + */ +static void pack_pnode(struct ubifs_info *c, void *buf, + struct ubifs_pnode *pnode) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); + if (c->big_lpt) + pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, + c->space_bits); + pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, + c->space_bits); + if (pnode->lprops[i].flags & LPROPS_INDEX) + pack_bits(&addr, &pos, 1, 1); + else + pack_bits(&addr, &pos, 0, 1); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->pnode_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * pack_nnode - pack all the bit fields of a nnode. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @nnode: nnode to pack + */ +static void pack_nnode(struct ubifs_info *c, void *buf, + struct ubifs_nnode *nnode) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); + if (c->big_lpt) + pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + int lnum = nnode->nbranch[i].lnum; + + if (lnum == 0) + lnum = c->lpt_last + 1; + pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); + pack_bits(&addr, &pos, nnode->nbranch[i].offs, + c->lpt_offs_bits); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->nnode_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * pack_ltab - pack the LPT's own lprops table. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @ltab: LPT's own lprops table to pack + */ +static void pack_ltab(struct ubifs_info *c, void *buf, + struct ubifs_lpt_lprops *ltab) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); + for (i = 0; i < c->lpt_lebs; i++) { + pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); + pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->ltab_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * pack_lsave - pack the LPT's save table. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @lsave: LPT's save table to pack + */ +static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); + for (i = 0; i < c->lsave_cnt; i++) + pack_bits(&addr, &pos, lsave[i], c->lnum_bits); + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->lsave_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * set_ltab - set LPT LEB properties. + * @c: UBIFS file-system description object + * @lnum: LEB number + * @free: amount of free space + * @dirty: amount of dirty space + */ +static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) +{ + dbg_msg(3, "LEB %d free %d dirty %d to %d %d", + lnum, c->ltab[lnum - c->lpt_first].free, + c->ltab[lnum - c->lpt_first].dirty, free, dirty); + c->ltab[lnum - c->lpt_first].free = free; + c->ltab[lnum - c->lpt_first].dirty = dirty; +} + +/** + * calc_nnode_num - calculate nnode number. + * @row: the row in the tree (root is zero) + * @col: the column in the row (leftmost is zero) + * + * The nnode number is a number that uniquely identifies a nnode and can be used + * easily to traverse the tree from the root to that nnode. + * + * This function calculates and returns the nnode number for the nnode at @row + * and @col. + */ +static int calc_nnode_num(int row, int col) +{ + int num, bits; + + num = 1; + while (row--) { + bits = (col & (UBIFS_LPT_FANOUT - 1)); + col >>= UBIFS_LPT_FANOUT_SHIFT; + num <<= UBIFS_LPT_FANOUT_SHIFT; + num |= bits; + } + return num; +} + +/** + * create_lpt - create LPT. + * @c: UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +int create_lpt(struct ubifs_info *c) +{ + int lnum, err = 0, i, j, cnt, len, alen, row; + int blnum, boffs, bsz, bcnt; + struct ubifs_pnode *pnode = NULL; + struct ubifs_nnode *nnode = NULL; + void *buf = NULL, *p; + int *lsave = NULL; + + pnode = malloc(sizeof(struct ubifs_pnode)); + nnode = malloc(sizeof(struct ubifs_nnode)); + buf = malloc(c->leb_size); + lsave = malloc(sizeof(int) * c->lsave_cnt); + if (!pnode || !nnode || !buf || !lsave) { + err = -ENOMEM; + goto out; + } + memset(pnode, 0 , sizeof(struct ubifs_pnode)); + memset(nnode, 0 , sizeof(struct ubifs_pnode)); + + c->lscan_lnum = c->main_first; + + lnum = c->lpt_first; + p = buf; + len = 0; + /* Number of leaf nodes (pnodes) */ + cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; + //printf("pnode_cnt=%d\n",cnt); + + /* + * To calculate the internal node branches, we keep information about + * the level below. + */ + blnum = lnum; /* LEB number of level below */ + boffs = 0; /* Offset of level below */ + bcnt = cnt; /* Number of nodes in level below */ + bsz = c->pnode_sz; /* Size of nodes in level below */ + + /* Add pnodes */ + for (i = 0; i < cnt; i++) { + if (len + c->pnode_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + /* Fill in the pnode */ + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { + int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; + + if (k < c->main_lebs) + pnode->lprops[j] = c->lpt[k]; + else { + pnode->lprops[j].free = c->leb_size; + pnode->lprops[j].dirty = 0; + pnode->lprops[j].flags = 0; + } + } + pack_pnode(c, p, pnode); + p += c->pnode_sz; + len += c->pnode_sz; + /* + * pnodes are simply numbered left to right starting at zero, + * which means the pnode number can be used easily to traverse + * down the tree to the corresponding pnode. + */ + pnode->num += 1; + } + + row = c->lpt_hght - 1; + /* Add all nnodes, one level at a time */ + while (1) { + /* Number of internal nodes (nnodes) at next level */ + cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; + if (cnt == 0) + cnt = 1; + for (i = 0; i < cnt; i++) { + if (len + c->nnode_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, + alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + /* The root is on row zero */ + if (row == 0) { + c->lpt_lnum = lnum; + c->lpt_offs = len; + } + /* Set branches to the level below */ + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { + if (bcnt) { + if (boffs + bsz > c->leb_size) { + blnum += 1; + boffs = 0; + } + nnode->nbranch[j].lnum = blnum; + nnode->nbranch[j].offs = boffs; + boffs += bsz; + bcnt--; + } else { + nnode->nbranch[j].lnum = 0; + nnode->nbranch[j].offs = 0; + } + } + nnode->num = calc_nnode_num(row, i); + pack_nnode(c, p, nnode); + p += c->nnode_sz; + len += c->nnode_sz; + } + /* Row zero is the top row */ + if (row == 0) + break; + /* Update the information about the level below */ + bcnt = cnt; + bsz = c->nnode_sz; + row -= 1; + } + + if (c->big_lpt) { + /* Need to add LPT's save table */ + if (len + c->lsave_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + + c->lsave_lnum = lnum; + c->lsave_offs = len; + + for (i = 0; i < c->lsave_cnt; i++) + lsave[i] = c->main_first + i; + + pack_lsave(c, p, lsave); + p += c->lsave_sz; + len += c->lsave_sz; + } + + /* Need to add LPT's own LEB properties table */ + if (len + c->ltab_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = write_leb(lnum++, alen, buf); + if (err) + goto out; + p = buf; + len = 0; + } + + c->ltab_lnum = lnum; + c->ltab_offs = len; + + /* Update ltab before packing it */ + len += c->ltab_sz; + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + + pack_ltab(c, p, c->ltab); + p += c->ltab_sz; + + /* Write remaining buffer */ + memset(p, 0xff, alen - len); + err = write_leb(lnum, alen, buf); + if (err) + goto out; + + c->nhead_lnum = lnum; + c->nhead_offs = ALIGN(len, c->min_io_size); + + dbg_msg(1, "lpt_sz: %lld", c->lpt_sz); + dbg_msg(1, "space_bits: %d", c->space_bits); + dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); + dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); + dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); + dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); + dbg_msg(1, "lnum_bits: %d", c->lnum_bits); + dbg_msg(1, "pnode_sz: %d", c->pnode_sz); + dbg_msg(1, "nnode_sz: %d", c->nnode_sz); + dbg_msg(1, "ltab_sz: %d", c->ltab_sz); + dbg_msg(1, "lsave_sz: %d", c->lsave_sz); + dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); + dbg_msg(1, "lpt_hght: %d", c->lpt_hght); + dbg_msg(1, "big_lpt: %d", c->big_lpt); + dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); + dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); + dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); + if (c->big_lpt) + dbg_msg(1, "LPT lsave is at %d:%d", + c->lsave_lnum, c->lsave_offs); +out: + free(lsave); + free(buf); + free(nnode); + free(pnode); + return err; +} diff --git a/mkfs.ubifs/lpt.h b/mkfs.ubifs/lpt.h new file mode 100644 index 0000000..4cde59d --- /dev/null +++ b/mkfs.ubifs/lpt.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 University of Szeged, Hungary + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + */ + +#ifndef __UBIFS_LPT_H__ +#define __UBIFS_LPT_H__ + +int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); +int create_lpt(struct ubifs_info *c); + +#endif diff --git a/mkfs.ubifs/mkfs.ubifs.c b/mkfs.ubifs/mkfs.ubifs.c new file mode 100644 index 0000000..bedf8a7 --- /dev/null +++ b/mkfs.ubifs/mkfs.ubifs.c @@ -0,0 +1,2236 @@ +/* + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 University of Szeged, Hungary + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Adrian Hunter + * Artem Bityutskiy + * Zoltan Sogor + */ + +#include "mkfs.ubifs.h" + +#define PROGRAM_VERSION "1.2" + +/* 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_; + +/* 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 squash_owner; + +/* The 'head' (position) which nodes are written */ +static int head_lnum; +static int head_offs; +static int head_flags; + +/* The index list */ +static struct idx_entry *idx_list_first; +static struct idx_entry *idx_list_last; +static size_t idx_cnt; + +/* Global buffers */ +static void *leb_buf; +static void *node_buf; +static void *block_buf; + +/* Hash table for inode link counting */ +static struct inum_mapping **hash_table; + +/* Inode creation sequence number */ +static unsigned long long creat_sqnum; + +static const char *optstring = "d:r:m:o:D:h?vVe:c:g:f:P:k:x:X:j:R:l:j:U:"; + +static const struct option longopts[] = { + {"root", 1, NULL, 'r'}, + {"min-io-size", 1, NULL, 'm'}, + {"leb-size", 1, NULL, 'e'}, + {"max-leb-cnt", 1, NULL, 'c'}, + {"output", 1, NULL, 'o'}, + {"devtable", 1, NULL, 'D'}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"version", 0, NULL, 'V'}, + {"debug-level", 1, NULL, 'g'}, + {"jrn-size", 1, NULL, 'j'}, + {"reserved", 1, NULL, 'R'}, + {"compr", 1, NULL, 'x'}, + {"favor-percent", 1, NULL, 'X'}, + {"fanout", 1, NULL, 'f'}, + {"keyhash", 1, NULL, 'k'}, + {"log-lebs", 1, NULL, 'l'}, + {"orph-lebs", 1, NULL, 'p'}, + {"squash-uids" , 0, NULL, 'U'}, + {NULL, 0, NULL, 0} +}; + +static const char *helptext = +"Usage: mkfs.ubifs [OPTIONS]\n" +"Make a UBIFS file system image from an existing directory tree\n\n" +"Options:\n" +"-r, -d, --root=DIR build file system from directory DIR\n" +"-m, --min-io-size=SIZE minimum I/O unit size\n" +"-e, --leb-size=SIZE logical erase block size\n" +"-c, --max-leb-cnt=COUNT maximum logical erase block count\n" +"-o, --output=FILE output to FILE\n" +"-j, --jrn-size=SIZE journal size\n" +"-R, --reserved=SIZE how much space should be reserved for the super-user\n" +"-x, --compr=TYPE compression type - \"lzo\", \"favor_lzo\", \"zlib\" or\n" +" \"none\" (default: \"lzo\")\n" +"-X, --favor-percent may only be used with favor LZO compression and defines\n" +" how many percent better zlib should compress to make\n" +" mkfs.ubifs use zlib instead of LZO (default 20%)\n" +"-f, --fanout=NUM fanout NUM (default: 8)\n" +"-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" +"-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" +"-D, --devtable=FILE use device table FILE\n" +"-U, --squash-uids squash owners making all files owned by root\n" +"-l, --log-lebs=COUNT count of erase blocks for the log (used only for\n" +" debugging)\n" +"-v, --verbose verbose operation\n" +"-V, --version display version information\n" +"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" +" 2 - files, 3 - more details)\n" +"-h, --help display this help text\n\n" +"Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" +"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n" +"If you specify \"lzo\" or \"zlib\" compressors, mkfs.ubifs will use this compressor\n" +"for all data. The \"none\" disables any data compression. The \"favor_lzo\" is not\n" +"really a separate compressor. It is just a method of combining \"lzo\" and \"zlib\"\n" +"compressors. Namely, mkfs.ubifs tries to compress data with both \"lzo\" and \"zlib\"\n" +"compressors, then it compares which compressor is better. If \"zlib\" compresses 20\n" +"or more percent better than \"lzo\", mkfs.ubifs chooses \"lzo\", otherwise it chooses\n" +"\"zlib\". The \"--favor-percent\" may specify arbitrary threshold instead of the\n" +"default 20%.\n\n" +"The -R parameter specifies amount of bytes reserved for the super-user.\n"; + +/** + * make_path - make a path name from a directory and a name. + * @dir: directory path name + * @name: name + */ +static char *make_path(const char *dir, const char *name) +{ + char *s; + + s = malloc(strlen(dir) + strlen(name) + 2); + if (!s) + return NULL; + strcpy(s, dir); + if (dir[strlen(dir) - 1] != '/') + strcat(s, "/"); + strcat(s, name); + return s; +} + +/** + * same_dir - determine if two file descriptors refer to the same directory. + * @fd1: file descriptor 1 + * @fd2: file descriptor 2 + */ +static int same_dir(int fd1, int fd2) +{ + struct stat stat1, stat2; + + if (fstat(fd1, &stat1) == -1) + return -1; + if (fstat(fd2, &stat2) == -1) + return -1; + return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino; +} + +/** + * do_openat - open a file in a directory. + * @fd: file descriptor of open directory + * @path: path relative to directory + * @flags: open flags + * + * This function is provided because the library function openat is sometimes + * not available. + */ +static int do_openat(int fd, const char *path, int flags) +{ + int ret; + char *cwd; + + cwd = getcwd(NULL, 0); + if (!cwd) + return -1; + ret = fchdir(fd); + if (ret != -1) + ret = open(path, flags); + if (chdir(cwd) && !ret) + ret = -1; + free(cwd); + return ret; +} + +/** + * in_path - determine if a file is beneath a directory. + * @dir_name: directory path name + * @file_name: file path name + */ +static int in_path(const char *dir_name, const char *file_name) +{ + char *fn = strdup(file_name); + char *dn; + int fd1, fd2, fd3, ret = -1, top_fd; + + if (!fn) + return -1; + top_fd = open("/", O_RDONLY); + if (top_fd != -1) { + dn = dirname(fn); + fd1 = open(dir_name, O_RDONLY); + if (fd1 != -1) { + fd2 = open(dn, O_RDONLY); + if (fd2 != -1) { + while (1) { + int same; + + same = same_dir(fd1, fd2); + if (same) { + ret = same; + break; + } + if (same_dir(fd2, top_fd)) { + ret = 0; + break; + } + fd3 = do_openat(fd2, "..", O_RDONLY); + if (fd3 == -1) + break; + close(fd2); + fd2 = fd3; + } + close(fd2); + } + close(fd1); + } + close(top_fd); + } + free(fn); + return ret; +} + +/** + * calc_min_log_lebs - calculate the minimum number of log LEBs needed. + * @max_bud_bytes: journal size (buds only) + */ +static int calc_min_log_lebs(unsigned long long max_bud_bytes) +{ + int buds, log_lebs; + unsigned long long log_size; + + buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; + log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); + log_size *= buds; + log_size += ALIGN(UBIFS_CS_NODE_SZ + + UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), + c->min_io_size); + log_lebs = (log_size + c->leb_size - 1) / c->leb_size; + log_lebs += 1; + return log_lebs; +} + +/** + * add_space_overhead - add UBIFS overhead. + * @size: flash space which should be visible to the user + * + * UBIFS has overhead, and if we need to reserve @size bytes for the user data, + * we have to reserve more flash space, to compensate the overhead. This + * function calculates and returns the amount of physical flash space which + * should be reserved to provide @size bytes for the user. + */ +static long long add_space_overhead(long long size) +{ + int divisor, factor, f, max_idx_node_sz; + + /* + * Do the opposite to what the 'ubifs_reported_space()' kernel UBIFS + * function does. + */ + max_idx_node_sz = ubifs_idx_node_sz(c, c->fanout); + f = c->fanout > 3 ? c->fanout >> 1 : 2; + divisor = UBIFS_BLOCK_SIZE; + factor = UBIFS_MAX_DATA_NODE_SZ; + factor += (max_idx_node_sz * 3) / (f - 1); + size *= factor; + return size / divisor; +} + +static inline int is_power_of_2(unsigned long long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +static int validate_options(void) +{ + int tmp; + + if (!root) + return err_msg("root directory was not specified"); + if (!output) + return err_msg("no output file specified"); + if (in_path(root, output)) + return err_msg("output file cannot be in the UBIFS root " + "directory"); + if (!is_power_of_2(c->min_io_size)) + return err_msg("min. I/O unit size should be power of 2"); + if (c->leb_size < c->min_io_size) + return err_msg("min. I/O unit cannot be larger than LEB size"); + if (c->leb_size < UBIFS_MIN_LEB_SZ) + return err_msg("too small LEB size %d, minimum is %d", + c->min_io_size, UBIFS_MIN_LEB_SZ); + if (c->leb_size % c->min_io_size) + return err_msg("LEB should be multiple of min. I/O units"); + if (c->leb_size % 8) + return err_msg("LEB size has to be multiple of 8"); + if (c->leb_size > 1024*1024) + return err_msg("too large LEB size %d", c->leb_size); + if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) + return err_msg("too low max. count of LEBs, minimum is %d", + UBIFS_MIN_LEB_CNT); + if (c->fanout < UBIFS_MIN_FANOUT) + return err_msg("too low fanout, minimum is %d", + UBIFS_MIN_FANOUT); + tmp = c->leb_size - UBIFS_IDX_NODE_SZ; + tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; + if (c->fanout > tmp) + return err_msg("too high fanout, maximum is %d", tmp); + if (c->log_lebs < UBIFS_MIN_LOG_LEBS) + return err_msg("too few log LEBs, minimum is %d", + UBIFS_MIN_LOG_LEBS); + if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) + return err_msg("too many log LEBs, maximum is %d", + c->max_leb_cnt - UBIFS_MIN_LEB_CNT); + if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) + return err_msg("too few orphan LEBs, minimum is %d", + UBIFS_MIN_ORPH_LEBS); + if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) + return err_msg("too many orphan LEBs, maximum is %d", + c->max_leb_cnt - UBIFS_MIN_LEB_CNT); + tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; + tmp += c->orph_lebs + 4; + if (tmp > c->max_leb_cnt) + return err_msg("too low max. count of LEBs, expected at " + "least %d", tmp); + tmp = calc_min_log_lebs(c->max_bud_bytes); + if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) + return err_msg("too few log LEBs, expected at least %d", tmp); + if (c->rp_size >= ((long long)c->leb_size * c->max_leb_cnt) / 2) + return err_msg("too much reserved space %lld", c->rp_size); + return 0; +} + +/** + * get_multiplier - convert size specifier to an integer multiplier. + * @str: the size specifier string + * + * This function parses the @str size specifier, which may be one of + * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive + * size multiplier in case of success and %-1 in case of failure. + */ +static int get_multiplier(const char *str) +{ + if (!str) + return 1; + + /* Remove spaces before the specifier */ + while (*str == ' ' || *str == '\t') + str += 1; + + if (!strcmp(str, "KiB")) + return 1024; + if (!strcmp(str, "MiB")) + return 1024 * 1024; + if (!strcmp(str, "GiB")) + return 1024 * 1024 * 1024; + + return -1; +} + +/** + * get_bytes - convert a string containing amount of bytes into an + * integer. + * @str: string to convert + * + * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size + * specifiers. Returns positive amount of bytes in case of success and %-1 in + * case of failure. + */ +static long long get_bytes(const char *str) +{ + char *endp; + long long bytes = strtoull(str, &endp, 0); + + if (endp == str || bytes < 0) + return err_msg("incorrect amount of bytes: \"%s\"", str); + + if (*endp != '\0') { + int mult = get_multiplier(endp); + + if (mult == -1) + return err_msg("bad size specifier: \"%s\" - " + "should be 'KiB', 'MiB' or 'GiB'", endp); + bytes *= mult; + } + + return bytes; +} + +static int get_options(int argc, char**argv) +{ + int opt, i; + const char *tbl_file = NULL; + struct stat st; + char *endp; + + c->fanout = 8; + c->orph_lebs = 1; + c->key_hash = key_r5_hash; + c->key_len = UBIFS_SK_LEN; + c->default_compr = UBIFS_COMPR_LZO; + c->favor_percent = 20; + c->lsave_cnt = 256; + c->leb_size = -1; + c->min_io_size = -1; + c->max_leb_cnt = -1; + c->max_bud_bytes = -1; + c->log_lebs = -1; + + while (1) { + opt = getopt_long(argc, argv, optstring, longopts, &i); + if (opt == -1) + break; + switch (opt) { + case 'r': + case 'd': + root_len = strlen(optarg); + root = malloc(root_len + 2); + if (!root) + return err_msg("cannot allocate memory"); + + /* + * The further code expects '/' at the end of the root + * UBIFS directory on the host. + */ + memcpy(root, optarg, root_len); + if (root[root_len - 1] != '/') + root[root_len++] = '/'; + root[root_len] = 0; + + /* Make sure the root directory exists */ + if (stat(root, &st)) + return sys_err_msg("bad root directory '%s'", + root); + break; + case 'm': + c->min_io_size = get_bytes(optarg); + if (c->min_io_size <= 0) + return err_msg("bad min. I/O size"); + break; + case 'e': + c->leb_size = get_bytes(optarg); + if (c->leb_size <= 0) + return err_msg("bad LEB size"); + break; + case 'c': + c->max_leb_cnt = get_bytes(optarg); + if (c->max_leb_cnt <= 0) + return err_msg("bad maximum LEB count"); + break; + case 'o': + output = strdup(optarg); + break; + case 'D': + tbl_file = optarg; + if (stat(tbl_file, &st) < 0) + return sys_err_msg("bad device table file '%s'", + tbl_file); + break; + case 'h': + case '?': + printf("%s", helptext); + exit(0); + case 'v': + verbose = 1; + break; + case 'V': + printf("Version " PROGRAM_VERSION "\n"); + exit(0); + case 'g': + debug_level = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + debug_level < 0 || debug_level > 3) + return err_msg("bad debugging level '%s'", + optarg); + break; + case 'f': + c->fanout = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || c->fanout <= 0) + return err_msg("bad fanout %s", optarg); + break; + case 'l': + c->log_lebs = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) + return err_msg("bad count of log LEBs '%s'", + optarg); + break; + case 'p': + c->orph_lebs = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + c->orph_lebs <= 0) + return err_msg("bad orphan LEB count '%s'", + optarg); + break; + case 'k': + if (strcmp(optarg, "r5") == 0) { + c->key_hash = key_r5_hash; + c->key_hash_type = UBIFS_KEY_HASH_R5; + } else if (strcmp(optarg, "test") == 0) { + c->key_hash = key_test_hash; + c->key_hash_type = UBIFS_KEY_HASH_TEST; + } else + return err_msg("bad key hash"); + break; + case 'x': + if (strcmp(optarg, "favor_lzo") == 0) + c->favor_lzo = 1; + else if (strcmp(optarg, "zlib") == 0) + c->default_compr = UBIFS_COMPR_ZLIB; + else if (strcmp(optarg, "none") == 0) + c->default_compr = UBIFS_COMPR_NONE; + else if (strcmp(optarg, "lzo") != 0) + return err_msg("bad compressor name"); + break; + case 'X': + c->favor_percent = strtol(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + c->favor_percent <= 0 || c->favor_percent >= 100) + return err_msg("bad favor LZO percent '%s'", + optarg); + break; + case 'j': + c->max_bud_bytes = get_bytes(optarg); + if (c->max_bud_bytes <= 0) + return err_msg("bad maximum amount of buds"); + break; + case 'R': + c->rp_size = get_bytes(optarg); + if (c->rp_size < 0) + return err_msg("bad reserved bytes count"); + break; + case 'U': + squash_owner = 1; + break; + } + } + + if (c->min_io_size == -1) + return err_msg("min. I/O unit was not specified " + "(use -h for help)"); + + if (c->leb_size == -1) + return err_msg("LEB size was not specified (use -h for help)"); + + if (c->max_leb_cnt == -1) + return err_msg("Maximum count of LEBs was not specified " + "(use -h for help)"); + + if (c->max_bud_bytes == -1) { + int lebs; + + lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; + lebs -= c->orph_lebs; + if (c->log_lebs != -1) + lebs -= c->log_lebs; + else + lebs -= UBIFS_MIN_LOG_LEBS; + /* + * We do not know lprops geometry so far, so assume minimum + * count of lprops LEBs. + */ + lebs -= UBIFS_MIN_LPT_LEBS; + /* Make the journal about 12.5% of main area lebs */ + c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; + /* Make the max journal size 8MiB */ + if (c->max_bud_bytes > 8 * 1024 * 1024) + c->max_bud_bytes = 8 * 1024 * 1024; + if (c->max_bud_bytes < 4 * c->leb_size) + c->max_bud_bytes = 4 * c->leb_size; + } + + if (c->log_lebs == -1) { + c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); + c->log_lebs += 2; + } + + if (c->min_io_size < 8) + c->min_io_size = 8; + c->rp_size = add_space_overhead(c->rp_size); + + if (verbose) { + printf("mkfs.ubifs\n"); + printf("\troot: %s\n", root); + printf("\tmin_io_size: %d\n", c->min_io_size); + printf("\tleb_size: %d\n", c->leb_size); + printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); + printf("\toutput: %s\n", output); + printf("\tjrn_size: %llu\n", c->max_bud_bytes); + printf("\treserved: %llu\n", c->rp_size); + switch (c->default_compr) { + case UBIFS_COMPR_LZO: + printf("\tcompr: lzo\n"); + break; + case UBIFS_COMPR_ZLIB: + printf("\tcompr: zlib\n"); + break; + case UBIFS_COMPR_NONE: + printf("\tcompr: none\n"); + break; + } + printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? + "r5" : "test"); + printf("\tfanout: %d\n", c->fanout); + printf("\torph_lebs: %d\n", c->orph_lebs); + } + + if (validate_options()) + return -1; + + if (tbl_file && parse_devtable(tbl_file)) + return err_msg("cannot parse device table file '%s'", tbl_file); + + return 0; +} + +/** + * prepare_node - fill in the common header. + * @node: node + * @len: node length + */ +static void prepare_node(void *node, int len) +{ + uint32_t crc; + struct ubifs_ch *ch = node; + + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); + ch->len = cpu_to_le32(len); + ch->group_type = UBIFS_NO_NODE_GROUP; + ch->sqnum = cpu_to_le64(++c->max_sqnum); + ch->padding[0] = ch->padding[1] = 0; + crc = ubifs_crc32(UBIFS_CRC32_INIT, node + 8, len - 8); + ch->crc = cpu_to_le32(crc); +} + +/** + * write_leb - copy the image of a LEB to the output file. + * @lnum: LEB number + * @len: length of data in the buffer + * @buf: buffer (must be at least c->leb_size bytes) + */ +int write_leb(int lnum, int len, void *buf) +{ + off64_t pos = (off64_t)lnum * c->leb_size; + + dbg_msg(3, "LEB %d len %d", lnum, len); + if (lseek64(out_fd, pos, SEEK_SET) != pos) + return sys_err_msg("lseek64 failed seeking %lld", + (long long)pos); + + memset(buf + len, 0xff, c->leb_size - len); + + 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 file. + * @lnum: LEB number + */ +static int write_empty_leb(int lnum) +{ + return write_leb(lnum, 0, leb_buf); +} + +/** + * do_pad - pad a buffer to the minimum I/O size. + * @buf: buffer + * @len: buffer length + */ +static int do_pad(void *buf, int len) +{ + int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); + uint32_t crc; + + memset(buf + len, 0xff, alen - len); + pad_len = wlen - alen; + dbg_msg(3, "len %d pad_len %d", len, pad_len); + buf += alen; + if (pad_len >= (int)UBIFS_PAD_NODE_SZ) { + struct ubifs_ch *ch = buf; + struct ubifs_pad_node *pad_node = buf; + + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); + ch->node_type = UBIFS_PAD_NODE; + ch->group_type = UBIFS_NO_NODE_GROUP; + ch->padding[0] = ch->padding[1] = 0; + ch->sqnum = cpu_to_le64(0); + ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); + + pad_len -= UBIFS_PAD_NODE_SZ; + pad_node->pad_len = cpu_to_le32(pad_len); + + crc = ubifs_crc32(UBIFS_CRC32_INIT, buf + 8, + UBIFS_PAD_NODE_SZ - 8); + ch->crc = cpu_to_le32(crc); + + memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); + } else if (pad_len > 0) + memset(buf, UBIFS_PADDING_BYTE, pad_len); + + return wlen; +} + +/** + * write_node - write a node to a LEB. + * @node: node + * @len: node length + * @lnum: LEB number + */ +static int write_node(void *node, int len, int lnum) +{ + prepare_node(node, len); + + memcpy(leb_buf, node, len); + + len = do_pad(leb_buf, len); + + return write_leb(lnum, len, leb_buf); +} + +/** + * calc_dark - calculate LEB dark space size. + * @c: the UBIFS file-system description object + * @spc: amount of free and dirty space in the LEB + * + * This function calculates amount of dark space in an LEB which has @spc bytes + * of free and dirty space. Returns the calculations result. + * + * Dark space is the space which is not always usable - it depends on which + * nodes are written in which order. E.g., if an LEB has only 512 free bytes, + * it is dark space, because it cannot fit a large data node. So UBIFS cannot + * count on this LEB and treat these 512 bytes as usable because it is not true + * if, for example, only big chunks of uncompressible data will be written to + * the FS. + */ +static int calc_dark(struct ubifs_info *c, int spc) +{ + if (spc < c->dark_wm) + return spc; + + /* + * If we have slightly more space then the dark space watermark, we can + * anyway safely assume it we'll be able to write a node of the + * smallest size there. + */ + if (spc - c->dark_wm < (int)MIN_WRITE_SZ) + return spc - MIN_WRITE_SZ; + + return c->dark_wm; +} + +/** + * set_lprops - set the LEB property values for a LEB. + * @lnum: LEB number + * @offs: end offset of data in the LEB + * @flags: LEB property flags + */ +static void set_lprops(int lnum, int offs, int flags) +{ + int i = lnum - c->main_first, free, dirty; + int a = max_t(int, c->min_io_size, 8); + + free = c->leb_size - ALIGN(offs, a); + dirty = c->leb_size - free - ALIGN(offs, 8); + dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, + flags); + c->lpt[i].free = free; + c->lpt[i].dirty = dirty; + c->lpt[i].flags = flags; + c->lst.total_free += free; + c->lst.total_dirty += dirty; + if (flags & LPROPS_INDEX) + c->lst.idx_lebs += 1; + else { + int spc; + + spc = free + dirty; + if (spc < c->dead_wm) + c->lst.total_dead += spc; + else + c->lst.total_dark += calc_dark(c, spc); + c->lst.total_used += c->leb_size - spc; + } +} + +/** + * add_to_index - add a node key and position to the index. + * @key: node key + * @lnum: node LEB number + * @offs: node offset + * @len: node length + */ +static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, + int len) +{ + struct idx_entry *e; + + dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); + e = malloc(sizeof(struct idx_entry)); + if (!e) + return err_msg("out of memory"); + e->next = NULL; + e->prev = idx_list_last; + e->key = *key; + e->name = name; + e->lnum = lnum; + e->offs = offs; + e->len = len; + if (!idx_list_first) + idx_list_first = e; + if (idx_list_last) + idx_list_last->next = e; + idx_list_last = e; + idx_cnt += 1; + return 0; +} + +/** + * flush_nodes - write the current head and move the head to the next LEB. + */ +static int flush_nodes(void) +{ + int len, err; + + if (!head_offs) + return 0; + len = do_pad(leb_buf, head_offs); + err = write_leb(head_lnum, len, leb_buf); + if (err) + return err; + set_lprops(head_lnum, head_offs, head_flags); + head_lnum += 1; + head_offs = 0; + return 0; +} + +/** + * reserve_space - reserve space for a node on the head. + * @len: node length + * @lnum: LEB number is returned here + * @offs: offset is returned here + */ +static int reserve_space(int len, int *lnum, int *offs) +{ + int err; + + if (len > c->leb_size - head_offs) { + err = flush_nodes(); + if (err) + return err; + } + *lnum = head_lnum; + *offs = head_offs; + head_offs += ALIGN(len, 8); + return 0; +} + +/** + * add_node - write a node to the head. + * @key: node key + * @node: node + * @len: node length + */ +static int add_node(union ubifs_key *key, char *name, void *node, int len) +{ + int err, lnum, offs; + + prepare_node(node, len); + + err = reserve_space(len, &lnum, &offs); + if (err) + return err; + + memcpy(leb_buf + offs, node, len); + memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); + + add_to_index(key, name, lnum, offs, len); + + return 0; +} + +/** + * add_inode_with_data - write an inode. + * @st: stat information of source inode + * @inum: target inode number + * @data: inode data (for special inodes e.g. symlink path etc) + * @data_len: inode data length + * @flags: source inode flags + */ +static int add_inode_with_data(struct stat *st, ino_t inum, void *data, + unsigned int data_len, int flags) +{ + struct ubifs_ino_node *ino = node_buf; + union ubifs_key key; + int len, use_flags = 0; + + if (c->default_compr != UBIFS_COMPR_NONE) + use_flags |= UBIFS_COMPR_FL; + if (flags & FS_COMPR_FL) + use_flags |= UBIFS_COMPR_FL; + if (flags & FS_SYNC_FL) + use_flags |= UBIFS_SYNC_FL; + if (flags & FS_IMMUTABLE_FL) + use_flags |= UBIFS_IMMUTABLE_FL; + if (flags & FS_APPEND_FL) + use_flags |= UBIFS_APPEND_FL; + if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) + use_flags |= UBIFS_DIRSYNC_FL; + + memset(ino, 0, UBIFS_INO_NODE_SZ); + + ino_key_init(&key, inum); + ino->ch.node_type = UBIFS_INO_NODE; + key_write(&key, &ino->key); + ino->creat_sqnum = cpu_to_le64(creat_sqnum); + ino->size = cpu_to_le64(st->st_size); + ino->nlink = cpu_to_le32(st->st_nlink); + /* + * The time fields are updated assuming the default time granularity + * of 1 second. To support finer granularities, utime() would be needed. + */ + ino->atime_sec = cpu_to_le64(st->st_atime); + ino->ctime_sec = cpu_to_le64(st->st_ctime); + ino->mtime_sec = cpu_to_le64(st->st_mtime); + ino->atime_nsec = 0; + ino->ctime_nsec = 0; + ino->mtime_nsec = 0; + ino->uid = cpu_to_le32(st->st_uid); + ino->gid = cpu_to_le32(st->st_gid); + ino->uid = cpu_to_le32(st->st_uid); + ino->gid = cpu_to_le32(st->st_gid); + ino->mode = cpu_to_le32(st->st_mode); + ino->flags = cpu_to_le32(use_flags); + ino->data_len = cpu_to_le32(data_len); + ino->compr_type = cpu_to_le16(c->default_compr); + if (data_len) + memcpy(&ino->data, data, data_len); + + len = UBIFS_INO_NODE_SZ + data_len; + + return add_node(&key, NULL, ino, len); +} + +/** + * add_inode - write an inode. + * @st: stat information of source inode + * @inum: target inode number + * @flags: source inode flags + */ +static int add_inode(struct stat *st, ino_t inum, int flags) +{ + return add_inode_with_data(st, inum, NULL, 0, flags); +} + +/** + * add_dir_inode - write an inode for a directory. + * @dir: source directory + * @inum: target inode number + * @size: target directory size + * @nlink: target directory link count + * @st: struct stat object describing attributes (except size and nlink) of the + * target inode to create + * + * Note, this function may be called with %NULL @dir, when the directory which + * is being created does not exist at the host file system, but is defined by + * the device table. + */ +static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, + struct stat *st) +{ + int fd, flags = 0; + + st->st_size = size; + st->st_nlink = nlink; + + if (dir) { + fd = dirfd(dir); + if (fd == -1) + return sys_err_msg("dirfd failed"); + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) + flags = 0; + } + + return add_inode(st, inum, flags); +} + +/** + * add_dev_inode - write an inode for a character or block device. + * @st: stat information of source inode + * @inum: target inode number + * @flags: source inode flags + */ +static int add_dev_inode(struct stat *st, ino_t inum, int flags) +{ + union ubifs_dev_desc dev; + + dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); + return add_inode_with_data(st, inum, &dev, 8, flags); +} + +/** + * add_symlink_inode - write an inode for a symbolic link. + * @path_name: path name of symbolic link inode itself (not the link target) + * @st: stat information of source inode + * @inum: target inode number + * @flags: source inode flags + */ +static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, + int flags) +{ + char buf[UBIFS_MAX_INO_DATA + 2]; + ssize_t len; + + /* Take the symlink as is */ + len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); + if (len <= 0) + return sys_err_msg("readlink failed for %s", path_name); + if (len > UBIFS_MAX_INO_DATA) + return err_msg("symlink too long for %s", path_name); + + return add_inode_with_data(st, inum, buf, len, flags); +} + +/** + * add_dent_node - write a directory entry node. + * @dir_inum: target inode number of directory + * @name: directory entry name + * @inum: target inode number of the directory entry + * @type: type of the target inode + */ +static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, + unsigned char type) +{ + struct ubifs_dent_node *dent = node_buf; + union ubifs_key key; + struct qstr dname; + char *kname; + int len; + + dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, inum, + (unsigned)type, dir_inum); + memset(dent, 0, UBIFS_DENT_NODE_SZ); + + dname.name = (void *)name; + dname.len = strlen(name); + + dent->ch.node_type = UBIFS_DENT_NODE; + + dent_key_init(c, &key, dir_inum, &dname); + key_write(&key, dent->key); + dent->inum = cpu_to_le64(inum); + dent->padding1 = 0; + dent->type = type; + dent->nlen = cpu_to_le16(dname.len); + memcpy(dent->name, dname.name, dname.len); + dent->name[dname.len] = '\0'; + + len = UBIFS_DENT_NODE_SZ + dname.len + 1; + + kname = strdup(name); + if (!kname) + return err_msg("cannot allocate memory"); + + return add_node(&key, kname, dent, len); +} + +/** + * lookup_inum_mapping - add an inode mapping for link counting. + * @dev: source device on which source inode number resides + * @inum: source inode number + */ +static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) +{ + struct inum_mapping *im; + unsigned int k; + + k = inum % HASH_TABLE_SIZE; + im = hash_table[k]; + while (im) { + if (im->dev == dev && im->inum == inum) + return im; + im = im->next; + } + im = malloc(sizeof(struct inum_mapping)); + if (!im) + return NULL; + im->next = hash_table[k]; + im->prev = NULL; + im->dev = dev; + im->inum = inum; + im->use_inum = 0; + im->use_nlink = 0; + if (hash_table[k]) + hash_table[k]->prev = im; + hash_table[k] = im; + return im; +} + +/** + * all_zero - does a buffer contain only zero bytes. + * @buf: buffer + * @len: buffer length + */ +static int all_zero(void *buf, int len) +{ + unsigned char *p = buf; + + while (len--) + if (*p++ != 0) + return 0; + return 1; +} + +/** + * add_file - write the data of a file and its inode to the output file. + * @path_name: source path name + * @st: source inode stat information + * @inum: target inode number + * @flags: source inode flags + */ +static int add_file(const char *path_name, struct stat *st, ino_t inum, + int flags) +{ + struct ubifs_data_node *dn = node_buf; + void *buf = block_buf; + loff_t file_size = 0; + ssize_t ret, bytes_read; + union ubifs_key key; + int fd, dn_len, err, compr_type, use_compr; + unsigned int block_no = 0; + size_t out_len; + + fd = open(path_name, O_RDONLY | O_LARGEFILE); + if (fd == -1) + return sys_err_msg("failed to open file '%s'", path_name); + do { + /* Read next block */ + bytes_read = 0; + do { + ret = read(fd, buf + bytes_read, + UBIFS_BLOCK_SIZE - bytes_read); + if (ret == -1) { + sys_err_msg("failed to read file '%s'", + path_name); + close(fd); + return 1; + } + bytes_read += ret; + } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); + if (bytes_read == 0) + break; + file_size += bytes_read; + /* Skip holes */ + if (all_zero(buf, bytes_read)) { + block_no += 1; + continue; + } + /* Make data node */ + memset(dn, 0, UBIFS_DATA_NODE_SZ); + data_key_init(&key, inum, block_no++); + dn->ch.node_type = UBIFS_DATA_NODE; + key_write(&key, &dn->key); + dn->size = cpu_to_le32(bytes_read); + out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; + if (c->default_compr == UBIFS_COMPR_NONE && + (flags & FS_COMPR_FL)) + use_compr = UBIFS_COMPR_LZO; + else + use_compr = c->default_compr; + compr_type = compress_data(buf, bytes_read, &dn->data, + &out_len, use_compr); + dn->compr_type = cpu_to_le16(compr_type); + dn_len = UBIFS_DATA_NODE_SZ + out_len; + /* Add data node to file system */ + err = add_node(&key, NULL, dn, dn_len); + if (err) { + close(fd); + return err; + } + } while (ret != 0); + if (close(fd) == -1) + return sys_err_msg("failed to close file '%s'", path_name); + if (file_size != st->st_size) + return err_msg("file size changed during writing file '%s'", + path_name); + return add_inode(st, inum, flags); +} + +/** + * add_non_dir - write a non-directory to the output file. + * @path_name: source path name + * @inum: target inode number is passed and returned here (due to link counting) + * @nlink: number of links if known otherwise zero + * @type: UBIFS inode type is returned here + * @st: struct stat object containing inode attributes which should be use when + * creating the UBIFS inode + */ +static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, + unsigned char *type, struct stat *st) +{ + int fd, flags = 0; + + dbg_msg(2, "%s", path_name); + + if (S_ISREG(st->st_mode)) { + fd = open(path_name, O_RDONLY); + if (fd == -1) + return sys_err_msg("failed to open file '%s'", + path_name); + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) + flags = 0; + if (close(fd) == -1) + return sys_err_msg("failed to close file '%s'", + path_name); + *type = UBIFS_ITYPE_REG; + } else if (S_ISCHR(st->st_mode)) + *type = UBIFS_ITYPE_CHR; + else if (S_ISBLK(st->st_mode)) + *type = UBIFS_ITYPE_BLK; + else if (S_ISLNK(st->st_mode)) + *type = UBIFS_ITYPE_LNK; + else if (S_ISSOCK(st->st_mode)) + *type = UBIFS_ITYPE_SOCK; + else if (S_ISFIFO(st->st_mode)) + *type = UBIFS_ITYPE_FIFO; + else + return err_msg("file '%s' has unknown inode type", path_name); + + if (nlink) + st->st_nlink = nlink; + else if (st->st_nlink > 1) { + /* + * If the number of links is greater than 1, then add this file + * later when we know the number of links that we actually have. + * For now, we just put the inode mapping in the hash table. + */ + struct inum_mapping *im; + + im = lookup_inum_mapping(st->st_dev, st->st_ino); + if (!im) + return err_msg("out of memory"); + if (im->use_nlink == 0) { + /* New entry */ + im->use_inum = *inum; + im->use_nlink = 1; + im->path_name = malloc(strlen(path_name) + 1); + if (!im->path_name) + return err_msg("out of memory"); + strcpy(im->path_name, path_name); + } else { + /* Existing entry */ + *inum = im->use_inum; + im->use_nlink += 1; + /* Return unused inode number */ + c->highest_inum -= 1; + } + + memcpy(&im->st, st, sizeof(struct stat)); + return 0; + } else + st->st_nlink = 1; + + creat_sqnum = ++c->max_sqnum; + + if (S_ISREG(st->st_mode)) + return add_file(path_name, st, *inum, flags); + if (S_ISCHR(st->st_mode)) + return add_dev_inode(st, *inum, flags); + if (S_ISBLK(st->st_mode)) + return add_dev_inode(st, *inum, flags); + if (S_ISLNK(st->st_mode)) + return add_symlink_inode(path_name, st, *inum, flags); + if (S_ISSOCK(st->st_mode)) + return add_inode(st, *inum, flags); + if (S_ISFIFO(st->st_mode)) + return add_inode(st, *inum, flags); + + return err_msg("file '%s' has unknown inode type", path_name); +} + +/** + * add_directory - write a directory tree to the output file. + * @dir_name: directory path name + * @dir_inum: UBIFS inode number of directory + * @st: directory inode statistics + * @non_existing: non-zero if this function is called for a directory which + * does not exist on the host file-system and it is being + * created because it is defined in the device table file. + */ +static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, + int non_existing) +{ + struct dirent *entry; + DIR *dir = NULL; + int err = 0; + loff_t size = UBIFS_INO_NODE_SZ; + char *name = NULL; + unsigned int nlink = 2; + struct path_htbl_element *ph_elt; + struct name_htbl_element *nh_elt = NULL; + struct hashtable_itr *itr; + ino_t inum; + unsigned char type; + unsigned long long dir_creat_sqnum = ++c->max_sqnum; + + dbg_msg(2, "%s", dir_name); + if (!non_existing) { + dir = opendir(dir_name); + if (dir == NULL) + return sys_err_msg("cannot open directory '%s'", + dir_name); + } + + /* + * Check whether this directory contains files which should be + * added/changed because they were specified in the device table. + * @ph_elt will be non-zero if yes. + */ + ph_elt = devtbl_find_path(dir_name + root_len - 1); + + /* + * Before adding the directory itself, we have to iterate over all the + * entries the device table adds to this directory and create them. + */ + for (; !non_existing;) { + struct stat dent_st; + + errno = 0; + entry = readdir(dir); + if (!entry) { + if (errno == 0) + break; + sys_err_msg("error reading directory '%s'", dir_name); + err = -1; + break; + } + + if (strcmp(".", entry->d_name) == 0) + continue; + if (strcmp("..", entry->d_name) == 0) + continue; + + if (ph_elt) + /* + * This directory was referred to at the device table + * file. Check if this directory entry is referred at + * too. + */ + nh_elt = devtbl_find_name(ph_elt, entry->d_name); + + /* + * We are going to create the file corresponding to this + * directory entry (@entry->d_name). We use 'struct stat' + * object to pass information about file attributes (actually + * only about UID, GID, mode, major, and minor). Get attributes + * for this file from the UBIFS rootfs on the host. + */ + free(name); + name = make_path(dir_name, entry->d_name); + if (lstat(name, &dent_st) == -1) { + sys_err_msg("lstat failed for file '%s'", name); + goto out_free; + } + + if (squash_owner) + /* + * Squash UID/GID. But the device table may override + * this. + */ + dent_st.st_uid = dent_st.st_gid = 0; + + /* + * And if the device table describes the same file, override + * the attributes. However, this is not allowed for device node + * files. + */ + if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) + goto out_free; + + inum = ++c->highest_inum; + + if (S_ISDIR(dent_st.st_mode)) { + err = add_directory(name, inum, &dent_st, 0); + if (err) + goto out_free; + nlink += 1; + type = UBIFS_ITYPE_DIR; + } else { + err = add_non_dir(name, &inum, 0, &type, &dent_st); + if (err) + goto out_free; + } + + err = add_dent_node(dir_inum, entry->d_name, inum, type); + if (err) + goto out_free; + size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, + 8); + } + + /* + * OK, we have created all files in this directory (recursively), let's + * also create all files described in the device table. All t + */ + nh_elt = first_name_htbl_element(ph_elt, &itr); + while (nh_elt) { + struct stat fake_st; + + /* + * We prohibit creating regular files using the device table, + * the device table may only re-define attributes of regular + * files. + */ + if (S_ISREG(nh_elt->mode)) { + err_msg("Bad device table entry %s/%s - it is " + "prohibited to create regular files " + "via device table", + strcmp(ph_elt->path, "/") ? ph_elt->path : "", + nh_elt->name); + goto out_free; + } + + memcpy(&fake_st, &root_st, sizeof(struct stat)); + fake_st.st_uid = nh_elt->uid; + fake_st.st_uid = nh_elt->uid; + fake_st.st_mode = nh_elt->mode; + fake_st.st_rdev = nh_elt->dev; + fake_st.st_nlink = 1; + + free(name); + name = make_path(dir_name, nh_elt->name); + inum = ++c->highest_inum; + + if (S_ISDIR(nh_elt->mode)) { + err = add_directory(name, inum, &fake_st, 1); + if (err) + goto out_free; + nlink += 1; + type = UBIFS_ITYPE_DIR; + } else { + err = add_non_dir(name, &inum, 0, &type, &fake_st); + if (err) + goto out_free; + } + + err = add_dent_node(dir_inum, nh_elt->name, inum, type); + if (err) + goto out_free; + size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); + + nh_elt = next_name_htbl_element(ph_elt, &itr); + } + + creat_sqnum = dir_creat_sqnum; + + err = add_dir_inode(dir, dir_inum, size, nlink, st); + if (err) + goto out_free; + + free(name); + if (!non_existing && closedir(dir) == -1) + return sys_err_msg("error closing directory '%s'", dir_name); + + return 0; + +out_free: + free(name); + if (!non_existing) + closedir(dir); + return -1; +} + +/** + * add_multi_linked_files - write all the files for which we counted links. + */ +static int add_multi_linked_files(void) +{ + int i, err; + + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct inum_mapping *im; + unsigned char type = 0; + + for (im = hash_table[i]; im; im = im->next) { + dbg_msg(2, "%s", im->path_name); + err = add_non_dir(im->path_name, &im->use_inum, + im->use_nlink, &type, &im->st); + if (err) + return err; + } + } + return 0; +} + +/** + * write_data - write the files and directories. + */ +static int write_data(void) +{ + int err; + + err = stat(root, &root_st); + if (err) + return sys_err_msg("bad root file-system directory '%s'", root); + root_st.st_uid = root_st.st_gid = 0; + root_st.st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; + + head_flags = 0; + err = add_directory(root, UBIFS_ROOT_INO, &root_st, 0); + if (err) + return err; + err = add_multi_linked_files(); + if (err) + return err; + return flush_nodes(); +} + +static int namecmp(const char *name1, const char *name2) +{ + size_t len1 = strlen(name1), len2 = strlen(name2); + size_t clen = (len1 < len2) ? len1 : len2; + int cmp; + + cmp = memcmp(name1, name2, clen); + if (cmp) + return cmp; + return (len1 < len2) ? -1 : 1; +} + +static int cmp_idx(const void *a, const void *b) +{ + const struct idx_entry *e1 = *(const struct idx_entry **)a; + const struct idx_entry *e2 = *(const struct idx_entry **)b; + int cmp; + + cmp = keys_cmp(&e1->key, &e2->key); + if (cmp) + return cmp; + return namecmp(e1->name, e2->name); +} + +/** + * add_idx_node - write an index node to the head. + * @node: index node + * @child_cnt: number of children of this index node + */ +static int add_idx_node(void *node, int child_cnt) +{ + int err, lnum, offs, len; + + len = ubifs_idx_node_sz(c, child_cnt); + + prepare_node(node, len); + + err = reserve_space(len, &lnum, &offs); + if (err) + return err; + + memcpy(leb_buf + offs, node, len); + memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); + + c->old_idx_sz += ALIGN(len, 8); + + dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, + c->old_idx_sz); + + /* The last index node written will be the root */ + c->zroot.lnum = lnum; + c->zroot.offs = offs; + c->zroot.len = len; + + return 0; +} + +/** + * write_index - write out the index. + */ +static int write_index(void) +{ + size_t sz, i, cnt, idx_sz, pstep, bcnt; + struct idx_entry **idx_ptr, **p; + struct ubifs_idx_node *idx; + struct ubifs_branch *br; + int child_cnt, j, level, blnum, boffs, blen, blast_len, err; + + dbg_msg(1, "leaf node count: %zd", idx_cnt); + + /* Reset the head for the index */ + head_flags = LPROPS_INDEX; + /* Allocate index node */ + idx_sz = ubifs_idx_node_sz(c, c->fanout); + idx = malloc(idx_sz); + if (!idx) + return err_msg("out of memory"); + /* Make an array of pointers to sort the index list */ + sz = idx_cnt * sizeof(struct idx_entry *); + if (sz / sizeof(struct idx_entry *) != idx_cnt) { + free(idx); + return err_msg("index is too big (%zu entries)", idx_cnt); + } + idx_ptr = malloc(sz); + if (!idx_ptr) { + free(idx); + return err_msg("out of memory - needed %zu bytes for index", + sz); + } + idx_ptr[0] = idx_list_first; + for (i = 1; i < idx_cnt; i++) + idx_ptr[i] = idx_ptr[i - 1]->next; + qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); + /* Write level 0 index nodes */ + cnt = idx_cnt / c->fanout; + if (idx_cnt % c->fanout) + cnt += 1; + p = idx_ptr; + blnum = head_lnum; + boffs = head_offs; + for (i = 0; i < cnt; i++) { + /* + * Calculate the child count. All index nodes are created full + * except for the last index node on each row. + */ + if (i == cnt - 1) { + child_cnt = idx_cnt % c->fanout; + if (child_cnt == 0) + child_cnt = c->fanout; + } else + child_cnt = c->fanout; + memset(idx, 0, idx_sz); + idx->ch.node_type = UBIFS_IDX_NODE; + idx->child_cnt = cpu_to_le16(child_cnt); + idx->level = cpu_to_le16(0); + for (j = 0; j < child_cnt; j++, p++) { + br = ubifs_idx_branch(c, idx, j); + key_write_idx(&(*p)->key, &br->key); + br->lnum = cpu_to_le32((*p)->lnum); + br->offs = cpu_to_le32((*p)->offs); + br->len = cpu_to_le32((*p)->len); + } + add_idx_node(idx, child_cnt); + } + /* Write level 1 index nodes and above */ + level = 0; + pstep = 1; + while (cnt > 1) { + /* + * 'blast_len' is the length of the last index node in the level + * below. + */ + blast_len = ubifs_idx_node_sz(c, child_cnt); + /* 'bcnt' is the number of index nodes in the level below */ + bcnt = cnt; + /* 'cnt' is the number of index nodes in this level */ + cnt = (cnt + c->fanout - 1) / c->fanout; + if (cnt == 0) + cnt = 1; + level += 1; + /* + * The key of an index node is the same as the key of its first + * child. Thus we can get the key by stepping along the bottom + * level 'p' with an increasing large step 'pstep'. + */ + p = idx_ptr; + pstep *= c->fanout; + for (i = 0; i < cnt; i++) { + /* + * Calculate the child count. All index nodes are + * created full except for the last index node on each + * row. + */ + if (i == cnt - 1) { + child_cnt = bcnt % c->fanout; + if (child_cnt == 0) + child_cnt = c->fanout; + } else + child_cnt = c->fanout; + memset(idx, 0, idx_sz); + idx->ch.node_type = UBIFS_IDX_NODE; + idx->child_cnt = cpu_to_le16(child_cnt); + idx->level = cpu_to_le16(level); + for (j = 0; j < child_cnt; j++) { + size_t bn = i * c->fanout + j; + + /* + * The length of the index node in the level + * below is 'idx_sz' except when it is the last + * node on the row. i.e. all the others on the + * row are full. + */ + if (bn == bcnt - 1) + blen = blast_len; + else + blen = idx_sz; + /* + * 'blnum' and 'boffs' hold the position of the + * index node on the level below. + */ + if (boffs + blen > c->leb_size) { + blnum += 1; + boffs = 0; + } + /* + * Fill in the branch with the key and position + * of the index node from the level below. + */ + br = ubifs_idx_branch(c, idx, j); + key_write_idx(&(*p)->key, &br->key); + br->lnum = cpu_to_le32(blnum); + br->offs = cpu_to_le32(boffs); + br->len = cpu_to_le32(blen); + /* + * Step to the next index node on the level + * below. + */ + boffs += ALIGN(blen, 8); + p += pstep; + } + add_idx_node(idx, child_cnt); + } + } + + /* Free stuff */ + for (i = 0; i < idx_cnt; i++) + free(idx_ptr[i]); + free(idx_ptr); + free(idx); + + dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, + c->zroot.len); + + /* Set the index head */ + c->ihead_lnum = head_lnum; + c->ihead_offs = ALIGN(head_offs, c->min_io_size); + dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); + + /* Flush the last index LEB */ + err = flush_nodes(); + if (err) + return err; + + return 0; +} + +/** + * set_gc_lnum - set the LEB number reserved for the garbage collector. + */ +static int set_gc_lnum(void) +{ + int err; + + c->gc_lnum = head_lnum++; + err = write_empty_leb(c->gc_lnum); + if (err) + return err; + set_lprops(c->gc_lnum, 0, 0); + c->lst.empty_lebs += 1; + return 0; +} + +/** + * finalize_leb_cnt - now that we know how many LEBs we used. + */ +static int finalize_leb_cnt(void) +{ + c->leb_cnt = head_lnum; + if (c->leb_cnt > c->max_leb_cnt) + /* TODO: in this case it segfaults because buffer overruns - we + * somewhere allocate smaller buffers - fix */ + return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); + c->main_lebs = c->leb_cnt - c->main_first; + if (verbose) { + printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); + printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); + printf("\tlog_lebs: %d\n", c->log_lebs); + printf("\tlpt_lebs: %d\n", c->lpt_lebs); + printf("\torph_lebs: %d\n", c->orph_lebs); + printf("\tmain_lebs: %d\n", c->main_lebs); + printf("\tgc lebs: %d\n", 1); + printf("\tindex lebs: %d\n", c->lst.idx_lebs); + printf("\tleb_cnt: %d\n", c->leb_cnt); + } + dbg_msg(1, "total_free: %llu", c->lst.total_free); + dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); + dbg_msg(1, "total_used: %llu", c->lst.total_used); + dbg_msg(1, "total_dead: %llu", c->lst.total_dead); + dbg_msg(1, "total_dark: %llu", c->lst.total_dark); + dbg_msg(1, "index size: %llu", c->old_idx_sz); + dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); + return 0; +} + +/** + * write_super - write the super block. + */ +static int write_super(void) +{ + struct ubifs_sb_node sup; + + memset(&sup, 0, UBIFS_SB_NODE_SZ); + + sup.ch.node_type = UBIFS_SB_NODE; + sup.key_hash = c->key_hash_type; + sup.min_io_size = cpu_to_le32(c->min_io_size); + sup.leb_size = cpu_to_le32(c->leb_size); + sup.leb_cnt = cpu_to_le32(c->leb_cnt); + sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); + sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); + sup.log_lebs = cpu_to_le32(c->log_lebs); + sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); + sup.orph_lebs = cpu_to_le32(c->orph_lebs); + sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); + sup.fanout = cpu_to_le32(c->fanout); + sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); + sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); + sup.default_compr = cpu_to_le16(c->default_compr); + sup.rp_size = cpu_to_le64(c->rp_size); + sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); + uuid_generate_random(sup.uuid); + if (verbose) { + char s[40]; + + uuid_unparse_upper(sup.uuid, s); + printf("\tUUID: %s\n", s); + } + if (c->big_lpt) + sup.flags |= cpu_to_le32(UBIFS_FLG_BIGLPT); + + return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); +} + +/** + * write_master - write the master node. + */ +static int write_master(void) +{ + struct ubifs_mst_node mst; + int err; + + memset(&mst, 0, UBIFS_MST_NODE_SZ); + + mst.ch.node_type = UBIFS_MST_NODE; + mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); + mst.highest_inum = cpu_to_le64(c->highest_inum); + mst.cmt_no = cpu_to_le64(0); + mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); + mst.root_lnum = cpu_to_le32(c->zroot.lnum); + mst.root_offs = cpu_to_le32(c->zroot.offs); + mst.root_len = cpu_to_le32(c->zroot.len); + mst.gc_lnum = cpu_to_le32(c->gc_lnum); + mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); + mst.ihead_offs = cpu_to_le32(c->ihead_offs); + mst.index_size = cpu_to_le64(c->old_idx_sz); + mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); + mst.lpt_offs = cpu_to_le32(c->lpt_offs); + mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); + mst.nhead_offs = cpu_to_le32(c->nhead_offs); + mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); + mst.ltab_offs = cpu_to_le32(c->ltab_offs); + mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); + mst.lsave_offs = cpu_to_le32(c->lsave_offs); + mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); + mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); + mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); + mst.total_free = cpu_to_le64(c->lst.total_free); + mst.total_dirty = cpu_to_le64(c->lst.total_dirty); + mst.total_used = cpu_to_le64(c->lst.total_used); + mst.total_dead = cpu_to_le64(c->lst.total_dead); + mst.total_dark = cpu_to_le64(c->lst.total_dark); + mst.leb_cnt = cpu_to_le32(c->leb_cnt); + + err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); + if (err) + return err; + + err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); + if (err) + return err; + + return 0; +} + +/** + * write_log - write an empty log. + */ +static int write_log(void) +{ + struct ubifs_cs_node cs; + int err, i, lnum; + + lnum = UBIFS_LOG_LNUM; + + cs.ch.node_type = UBIFS_CS_NODE; + cs.cmt_no = cpu_to_le64(0); + + err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); + if (err) + return err; + + lnum += 1; + + for (i = 1; i < c->log_lebs; i++, lnum++) { + err = write_empty_leb(lnum); + if (err) + return err; + } + + return 0; +} + +/** + * write_lpt - write the LEB properties tree. + */ +static int write_lpt(void) +{ + int err, lnum; + + err = create_lpt(c); + if (err) + return err; + + lnum = c->nhead_lnum + 1; + while (lnum <= c->lpt_last) { + err = write_empty_leb(lnum++); + if (err) + return err; + } + + return 0; +} + +/** + * write_orphan_area - write an empty orphan area. + */ +static int write_orphan_area(void) +{ + int err, i, lnum; + + lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; + for (i = 0; i < c->orph_lebs; i++, lnum++) { + err = write_empty_leb(lnum); + if (err) + return err; + } + return 0; +} + +/** + * open_target - open the output file. + */ +static int open_target(void) +{ + 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 file. + */ +static int close_target(void) +{ + if (close(out_fd) == -1) + return sys_err_msg("cannot close output file '%s'", output); + return 0; +} + +/** + * init - initialize things. + */ +static int init(void) +{ + int err, i, main_lebs, big_lpt = 0, sz; + + c->highest_inum = UBIFS_FIRST_INO; + + c->jhead_cnt = 1; + + main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; + main_lebs -= c->log_lebs + c->orph_lebs; + + err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); + if (err) + return err; + + c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + + c->orph_lebs; + head_lnum = c->main_first; + head_offs = 0; + + c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; + c->lpt_last = c->lpt_first + c->lpt_lebs - 1; + + c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); + if (!c->lpt) + return err_msg("unable to allocate LPT"); + + c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); + if (!c->ltab) + return err_msg("unable to allocate LPT ltab"); + + /* Initialize LPT's own lprops */ + for (i = 0; i < c->lpt_lebs; i++) { + c->ltab[i].free = c->leb_size; + c->ltab[i].dirty = 0; + } + + c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); + c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); + dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); + + leb_buf = malloc(c->leb_size); + if (!leb_buf) + return err_msg("out of memory"); + + node_buf = malloc(NODE_BUFFER_SIZE); + if (!node_buf) + return err_msg("out of memory"); + + block_buf = malloc(UBIFS_BLOCK_SIZE); + if (!block_buf) + return err_msg("out of memory"); + + sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; + hash_table = malloc(sz); + if (!hash_table) + return err_msg("out of memory"); + memset(hash_table, 0, sz); + + err = init_compression(); + if (err) + return err; + + return 0; +} + +static void destroy_hash_table(void) +{ + int i; + + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct inum_mapping *im, *q; + + for (im = hash_table[i]; im; ) { + q = im; + im = im->next; + free(q->path_name); + free(q); + } + } +} + +/** + * deinit - deinitialize things. + */ +static void deinit(void) +{ + free(c->lpt); + free(c->ltab); + free(leb_buf); + free(node_buf); + free(block_buf); + destroy_hash_table(); + free(hash_table); + destroy_compression(); + free_devtable_info(); +} + +/** + * mkfs - make the file system. + * + * Each on-flash area has a corresponding function to create it. The order of + * the functions reflects what information must be known to complete each stage. + * As a consequence the output file is not written sequentially. No effort has + * been made to make efficient use of memory or to allow for the possibility of + * incremental updates to the output file. + */ +static int mkfs(void) +{ + int err = 0; + + err = init(); + if (err) + goto out; + + err = write_data(); + if (err) + goto out; + + err = set_gc_lnum(); + if (err) + goto out; + + err = write_index(); + if (err) + goto out; + + err = finalize_leb_cnt(); + if (err) + goto out; + + err = write_lpt(); + if (err) + goto out; + + err = write_super(); + if (err) + goto out; + + err = write_master(); + if (err) + goto out; + + err = write_log(); + if (err) + goto out; + + err = write_orphan_area(); + +out: + deinit(); + return err; +} + +int main(int argc, char *argv[]) +{ + int err; + + err = get_options(argc, argv); + if (err) + return err; + + err = open_target(); + if (err) + return err; + + err = mkfs(); + if (err) { + close_target(); + return err; + } + + err = close_target(); + if (err) + return err; + + if (verbose) + printf("Success!\n"); + + return 0; +} diff --git a/mkfs.ubifs/mkfs.ubifs.h b/mkfs.ubifs/mkfs.ubifs.h new file mode 100644 index 0000000..6460bd5 --- /dev/null +++ b/mkfs.ubifs/mkfs.ubifs.h @@ -0,0 +1,146 @@ +/* + * 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 "crc32.h" +#include "defs.h" +#include "crc16.h" +#include "ubifs-media.h" +#include "ubifs.h" +#include "key.h" +#include "lpt.h" +#include "compr.h" + +/* + * Compression flags are duplicated so that compr.c can compile without ubifs.h. + * Here we make sure they are the same. + */ +#if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE +#error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE +#endif +#if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO +#error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO +#endif +#if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB +#error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB +#endif + +extern int verbose; +extern int debug_level; + +#define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ + printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ +} while(0) + +#define err_msg(fmt, ...) ({ \ + fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ + -1; \ +}) + +#define sys_err_msg(fmt, ...) ({ \ + int err_ = errno; \ + fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ + fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ + -1; \ +}) + +/** + * struct path_htbl_element - an element of the path hash table. + * @path: the UBIFS path the element describes (the key of the element) + * @name_htbl: one more (nested) hash table containing names of all + * files/directories/device nodes which should be created at this + * path + * + * See device table handling for more information. + */ +struct path_htbl_element { + const char *path; + struct hashtable *name_htbl; +}; + +/** + * struct name_htbl_element - an element in the name hash table + * @name: name of the file/directory/device node (the key of the element) + * @mode: accsess rights and file type + * @uid: user ID + * @gid: group ID + * @major: device node major number + * @minor: device node minor number + * + * This is an element of the name hash table. Name hash table sits in the path + * hash table elements and describes file names which should be created/changed + * at this path. + */ +struct name_htbl_element { + const char *name; + unsigned int mode; + unsigned int uid; + unsigned int gid; + dev_t dev; +}; + +extern struct ubifs_info info_; + +struct hashtable_itr; + +int write_leb(int lnum, int len, void *buf); +int parse_devtable(const char *tbl_file); +struct path_htbl_element *devtbl_find_path(const char *path); +struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, + const char *name); +int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, + struct name_htbl_element *nh_elt); +struct name_htbl_element * +first_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr); +struct name_htbl_element * +next_name_htbl_element(struct path_htbl_element *ph_elt, + struct hashtable_itr **itr); +void free_devtable_info(void); + +#endif diff --git a/mkfs.ubifs/ubifs-media.h b/mkfs.ubifs/ubifs-media.h new file mode 100644 index 0000000..a9ecbd9 --- /dev/null +++ b/mkfs.ubifs/ubifs-media.h @@ -0,0 +1,745 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2006-2008 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Adrian Hunter + */ + +/* + * This file describes UBIFS on-flash format and contains definitions of all the + * relevant data structures and constants. + * + * All UBIFS on-flash objects are stored in the form of nodes. All nodes start + * with the UBIFS node magic number and have the same common header. Nodes + * always sit at 8-byte aligned positions on the media and node header sizes are + * also 8-byte aligned (except for the indexing node and the padding node). + */ + +#ifndef __UBIFS_MEDIA_H__ +#define __UBIFS_MEDIA_H__ + +/* UBIFS node magic number (must not have the padding byte first or last) */ +#define UBIFS_NODE_MAGIC 0x06101831 + +/* UBIFS on-flash format version */ +#define UBIFS_FORMAT_VERSION 4 + +/* Minimum logical eraseblock size in bytes */ +#define UBIFS_MIN_LEB_SZ (15*1024) + +/* Initial CRC32 value used when calculating CRC checksums */ +#define UBIFS_CRC32_INIT 0xFFFFFFFFU + +/* + * UBIFS does not try to compress data if its length is less than the below + * constant. + */ +#define UBIFS_MIN_COMPR_LEN 128 + +/* Root inode number */ +#define UBIFS_ROOT_INO 1 + +/* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */ +#define UBIFS_FIRST_INO 64 + +/* + * Maximum file name and extended attribute length (must be a multiple of 8, + * minus 1). + */ +#define UBIFS_MAX_NLEN 255 + +/* Maximum number of data journal heads */ +#define UBIFS_MAX_JHEADS 1 + +/* + * Size of UBIFS data block. Note, UBIFS is not a block oriented file-system, + * which means that it does not treat the underlying media as consisting of + * blocks like in case of hard drives. Do not be confused. UBIFS block is just + * the maximum amount of data which one data node can have or which can be + * attached to an inode node. + */ +#define UBIFS_BLOCK_SIZE 4096 +#define UBIFS_BLOCK_SHIFT 12 +#define UBIFS_BLOCK_MASK 0x00000FFF + +/* UBIFS padding byte pattern (must not be first or last byte of node magic) */ +#define UBIFS_PADDING_BYTE 0xCE + +/* Maximum possible key length */ +#define UBIFS_MAX_KEY_LEN 16 + +/* Key length ("simple" format) */ +#define UBIFS_SK_LEN 8 + +/* Minimum index tree fanout */ +#define UBIFS_MIN_FANOUT 3 + +/* Maximum number of levels in UBIFS indexing B-tree */ +#define UBIFS_MAX_LEVELS 512 + +/* Maximum amount of data attached to an inode in bytes */ +#define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE + +/* LEB Properties Tree fanout (must be power of 2) and fanout shift */ +#define UBIFS_LPT_FANOUT 4 +#define UBIFS_LPT_FANOUT_SHIFT 2 + +/* LEB Properties Tree bit field sizes */ +#define UBIFS_LPT_CRC_BITS 16 +#define UBIFS_LPT_CRC_BYTES 2 +#define UBIFS_LPT_TYPE_BITS 4 + +/* The key is always at the same position in all keyed nodes */ +#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key) + +/* + * LEB Properties Tree node types. + * + * UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties) + * UBIFS_LPT_NNODE: LPT internal node + * UBIFS_LPT_LTAB: LPT's own lprops table + * UBIFS_LPT_LSAVE: LPT's save table (big model only) + * UBIFS_LPT_NODE_CNT: count of LPT node types + * UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type + */ +enum { + UBIFS_LPT_PNODE, + UBIFS_LPT_NNODE, + UBIFS_LPT_LTAB, + UBIFS_LPT_LSAVE, + UBIFS_LPT_NODE_CNT, + UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1, +}; + +/* + * UBIFS inode types. + * + * UBIFS_ITYPE_REG: regular file + * UBIFS_ITYPE_DIR: directory + * UBIFS_ITYPE_LNK: soft link + * UBIFS_ITYPE_BLK: block device node + * UBIFS_ITYPE_CHR: character device node + * UBIFS_ITYPE_FIFO: fifo + * UBIFS_ITYPE_SOCK: socket + * UBIFS_ITYPES_CNT: count of supported file types + */ +enum { + UBIFS_ITYPE_REG, + UBIFS_ITYPE_DIR, + UBIFS_ITYPE_LNK, + UBIFS_ITYPE_BLK, + UBIFS_ITYPE_CHR, + UBIFS_ITYPE_FIFO, + UBIFS_ITYPE_SOCK, + UBIFS_ITYPES_CNT, +}; + +/* + * Supported key hash functions. + * + * UBIFS_KEY_HASH_R5: R5 hash + * UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name + */ +enum { + UBIFS_KEY_HASH_R5, + UBIFS_KEY_HASH_TEST, +}; + +/* + * Supported key formats. + * + * UBIFS_SIMPLE_KEY_FMT: simple key format + */ +enum { + UBIFS_SIMPLE_KEY_FMT, +}; + +/* + * The simple key format uses 29 bits for storing UBIFS block number and hash + * value. + */ +#define UBIFS_S_KEY_BLOCK_BITS 29 +#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF +#define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS +#define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK + +/* + * Key types. + * + * UBIFS_INO_KEY: inode node key + * UBIFS_DATA_KEY: data node key + * UBIFS_DENT_KEY: directory entry node key + * UBIFS_XENT_KEY: extended attribute entry key + * UBIFS_KEY_TYPES_CNT: number of supported key types + */ +enum { + UBIFS_INO_KEY, + UBIFS_DATA_KEY, + UBIFS_DENT_KEY, + UBIFS_XENT_KEY, + UBIFS_KEY_TYPES_CNT, +}; + +/* Count of LEBs reserved for the superblock area */ +#define UBIFS_SB_LEBS 1 +/* Count of LEBs reserved for the master area */ +#define UBIFS_MST_LEBS 2 + +/* First LEB of the superblock area */ +#define UBIFS_SB_LNUM 0 +/* First LEB of the master area */ +#define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS) +/* First LEB of the log area */ +#define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS) + +/* + * The below constants define the absolute minimum values for various UBIFS + * media areas. Many of them actually depend of flash geometry and the FS + * configuration (number of journal heads, orphan LEBs, etc). This means that + * the smallest volume size which can be used for UBIFS cannot be pre-defined + * by these constants. The file-system that meets the below limitation will not + * necessarily mount. UBIFS does run-time calculations and validates the FS + * size. + */ + +/* Minimum number of logical eraseblocks in the log */ +#define UBIFS_MIN_LOG_LEBS 2 +/* Minimum number of bud logical eraseblocks (one for each head) */ +#define UBIFS_MIN_BUD_LEBS 3 +/* Minimum number of journal logical eraseblocks */ +#define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS) +/* Minimum number of LPT area logical eraseblocks */ +#define UBIFS_MIN_LPT_LEBS 2 +/* Minimum number of orphan area logical eraseblocks */ +#define UBIFS_MIN_ORPH_LEBS 1 +/* + * Minimum number of main area logical eraseblocks (buds, 3 for the index, 1 + * for GC, 1 for deletions, and at least 1 for committed data). + */ +#define UBIFS_MIN_MAIN_LEBS (UBIFS_MIN_BUD_LEBS + 6) + +/* Minimum number of logical eraseblocks */ +#define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \ + UBIFS_MIN_LOG_LEBS + UBIFS_MIN_LPT_LEBS + \ + UBIFS_MIN_ORPH_LEBS + UBIFS_MIN_MAIN_LEBS) + +/* Node sizes (N.B. these are guaranteed to be multiples of 8) */ +#define UBIFS_CH_SZ sizeof(struct ubifs_ch) +#define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node) +#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node) +#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node) +#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node) +#define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node) +#define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node) +#define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node) +#define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node) +#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node) +#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node) +#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node) +/* Extended attribute entry nodes are identical to directory entry nodes */ +#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ +/* Only this does not have to be multiple of 8 bytes */ +#define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch) + +/* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */ +#define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE) +#define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA) +#define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1) +#define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ + +/* The largest UBIFS node */ +#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ + +/* + * On-flash inode flags. + * + * UBIFS_COMPR_FL: use compression for this inode + * UBIFS_SYNC_FL: I/O on this inode has to be synchronous + * UBIFS_IMMUTABLE_FL: inode is immutable + * UBIFS_APPEND_FL: writes to the inode may only append data + * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous + * UBIFS_XATTR_FL: this inode is the inode for an extended attribute value + * + * Note, these are on-flash flags which correspond to ioctl flags + * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not + * have to be the same. + */ +enum { + UBIFS_COMPR_FL = 0x01, + UBIFS_SYNC_FL = 0x02, + UBIFS_IMMUTABLE_FL = 0x04, + UBIFS_APPEND_FL = 0x08, + UBIFS_DIRSYNC_FL = 0x10, + UBIFS_XATTR_FL = 0x20, +}; + +/* Inode flag bits used by UBIFS */ +#define UBIFS_FL_MASK 0x0000001F + +/* + * UBIFS compression algorithms. + * + * UBIFS_COMPR_NONE: no compression + * UBIFS_COMPR_LZO: LZO compression + * UBIFS_COMPR_ZLIB: ZLIB compression + * UBIFS_COMPR_TYPES_CNT: count of supported compression types + */ +enum { + UBIFS_COMPR_NONE, + UBIFS_COMPR_LZO, + UBIFS_COMPR_ZLIB, + UBIFS_COMPR_TYPES_CNT, +}; + +/* + * UBIFS node types. + * + * UBIFS_INO_NODE: inode node + * UBIFS_DATA_NODE: data node + * UBIFS_DENT_NODE: directory entry node + * UBIFS_XENT_NODE: extended attribute node + * UBIFS_TRUN_NODE: truncation node + * UBIFS_PAD_NODE: padding node + * UBIFS_SB_NODE: superblock node + * UBIFS_MST_NODE: master node + * UBIFS_REF_NODE: LEB reference node + * UBIFS_IDX_NODE: index node + * UBIFS_CS_NODE: commit start node + * UBIFS_ORPH_NODE: orphan node + * UBIFS_NODE_TYPES_CNT: count of supported node types + * + * Note, we index arrays by these numbers, so keep them low and contiguous. + * Node type constants for inodes, direntries and so on have to be the same as + * corresponding key type constants. + */ +enum { + UBIFS_INO_NODE, + UBIFS_DATA_NODE, + UBIFS_DENT_NODE, + UBIFS_XENT_NODE, + UBIFS_TRUN_NODE, + UBIFS_PAD_NODE, + UBIFS_SB_NODE, + UBIFS_MST_NODE, + UBIFS_REF_NODE, + UBIFS_IDX_NODE, + UBIFS_CS_NODE, + UBIFS_ORPH_NODE, + UBIFS_NODE_TYPES_CNT, +}; + +/* + * Master node flags. + * + * UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty + * UBIFS_MST_NO_ORPHS: no orphan inodes present + * UBIFS_MST_RCVRY: written by recovery + */ +enum { + UBIFS_MST_DIRTY = 1, + UBIFS_MST_NO_ORPHS = 2, + UBIFS_MST_RCVRY = 4, +}; + +/* + * Node group type (used by recovery to recover whole group or none). + * + * UBIFS_NO_NODE_GROUP: this node is not part of a group + * UBIFS_IN_NODE_GROUP: this node is a part of a group + * UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group + */ +enum { + UBIFS_NO_NODE_GROUP = 0, + UBIFS_IN_NODE_GROUP, + UBIFS_LAST_OF_NODE_GROUP, +}; + +/* + * Superblock flags. + * + * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set + */ +enum { + UBIFS_FLG_BIGLPT = 0x02, +}; + +/** + * struct ubifs_ch - common header node. + * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) + * @crc: CRC-32 checksum of the node header + * @sqnum: sequence number + * @len: full node length + * @node_type: node type + * @group_type: node group type + * @padding: reserved for future, zeroes + * + * Every UBIFS node starts with this common part. If the node has a key, the + * key always goes next. + */ +struct ubifs_ch { + __le32 magic; + __le32 crc; + __le64 sqnum; + __le32 len; + __u8 node_type; + __u8 group_type; + __u8 padding[2]; +} __attribute__ ((packed)); + +/** + * union ubifs_dev_desc - device node descriptor. + * @new: new type device descriptor + * @huge: huge type device descriptor + * + * This data structure describes major/minor numbers of a device node. In an + * inode is a device node then its data contains an object of this type. UBIFS + * uses standard Linux "new" and "huge" device node encodings. + */ +union ubifs_dev_desc { + __le32 new; + __le64 huge; +} __attribute__ ((packed)); + +/** + * struct ubifs_ino_node - inode node. + * @ch: common header + * @key: node key + * @creat_sqnum: sequence number at time of creation + * @size: inode size in bytes (amount of uncompressed data) + * @atime_sec: access time seconds + * @ctime_sec: creation time seconds + * @mtime_sec: modification time seconds + * @atime_nsec: access time nanoseconds + * @ctime_nsec: creation time nanoseconds + * @mtime_nsec: modification time nanoseconds + * @nlink: number of hard links + * @uid: owner ID + * @gid: group ID + * @mode: access flags + * @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc) + * @data_len: inode data length + * @xattr_cnt: count of extended attributes this inode has + * @xattr_size: summarized size of all extended attributes in bytes + * @padding1: reserved for future, zeroes + * @xattr_names: sum of lengths of all extended attribute names belonging to + * this inode + * @compr_type: compression type used for this inode + * @padding2: reserved for future, zeroes + * @data: data attached to the inode + * + * Note, even though inode compression type is defined by @compr_type, some + * nodes of this inode may be compressed with different compressor - this + * happens if compression type is changed while the inode already has data + * nodes. But @compr_type will be use for further writes to the inode. + * + * Note, do not forget to amend 'zero_ino_node_unused()' function when changing + * the padding fields. + */ +struct ubifs_ino_node { + struct ubifs_ch ch; + __u8 key[UBIFS_MAX_KEY_LEN]; + __le64 creat_sqnum; + __le64 size; + __le64 atime_sec; + __le64 ctime_sec; + __le64 mtime_sec; + __le32 atime_nsec; + __le32 ctime_nsec; + __le32 mtime_nsec; + __le32 nlink; + __le32 uid; + __le32 gid; + __le32 mode; + __le32 flags; + __le32 data_len; + __le32 xattr_cnt; + __le32 xattr_size; + __u8 padding1[4]; /* Watch 'zero_ino_node_unused()' if changing! */ + __le32 xattr_names; + __le16 compr_type; + __u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */ + __u8 data[]; +} __attribute__ ((packed)); + +/** + * struct ubifs_dent_node - directory entry node. + * @ch: common header + * @key: node key + * @inum: target inode number + * @padding1: reserved for future, zeroes + * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc) + * @nlen: name length + * @padding2: reserved for future, zeroes + * @name: zero-terminated name + * + * Note, do not forget to amend 'zero_dent_node_unused()' function when + * changing the padding fields. + */ +struct ubifs_dent_node { + struct ubifs_ch ch; + __u8 key[UBIFS_MAX_KEY_LEN]; + __le64 inum; + __u8 padding1; + __u8 type; + __le16 nlen; + __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */ + __u8 name[]; +} __attribute__ ((packed)); + +/** + * struct ubifs_data_node - data node. + * @ch: common header + * @key: node key + * @size: uncompressed data size in bytes + * @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc) + * @padding: reserved for future, zeroes + * @data: data + * + * Note, do not forget to amend 'zero_data_node_unused()' function when + * changing the padding fields. + */ +struct ubifs_data_node { + struct ubifs_ch ch; + __u8 key[UBIFS_MAX_KEY_LEN]; + __le32 size; + __le16 compr_type; + __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */ + __u8 data[]; +} __attribute__ ((packed)); + +/** + * struct ubifs_trun_node - truncation node. + * @ch: common header + * @inum: truncated inode number + * @padding: reserved for future, zeroes + * @old_size: size before truncation + * @new_size: size after truncation + * + * This node exists only in the journal and never goes to the main area. Note, + * do not forget to amend 'zero_trun_node_unused()' function when changing the + * padding fields. + */ +struct ubifs_trun_node { + struct ubifs_ch ch; + __le32 inum; + __u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */ + __le64 old_size; + __le64 new_size; +} __attribute__ ((packed)); + +/** + * struct ubifs_pad_node - padding node. + * @ch: common header + * @pad_len: how many bytes after this node are unused (because padded) + * @padding: reserved for future, zeroes + */ +struct ubifs_pad_node { + struct ubifs_ch ch; + __le32 pad_len; +} __attribute__ ((packed)); + +/** + * struct ubifs_sb_node - superblock node. + * @ch: common header + * @padding: reserved for future, zeroes + * @key_hash: type of hash function used in keys + * @key_fmt: format of the key + * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) + * @min_io_size: minimal input/output unit size + * @leb_size: logical eraseblock size in bytes + * @leb_cnt: count of LEBs used by file-system + * @max_leb_cnt: maximum count of LEBs used by file-system + * @max_bud_bytes: maximum amount of data stored in buds + * @log_lebs: log size in logical eraseblocks + * @lpt_lebs: number of LEBs used for lprops table + * @orph_lebs: number of LEBs used for recording orphans + * @jhead_cnt: count of journal heads + * @fanout: tree fanout (max. number of links per indexing node) + * @lsave_cnt: number of LEB numbers in LPT's save table + * @fmt_version: UBIFS on-flash format version + * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc) + * @padding1: reserved for future, zeroes + * @rp_uid: reserve pool UID + * @rp_gid: reserve pool GID + * @rp_size: size of the reserved pool in bytes + * @padding2: reserved for future, zeroes + * @time_gran: time granularity in nanoseconds + * @uuid: UUID generated when the file system image was created + */ +struct ubifs_sb_node { + struct ubifs_ch ch; + __u8 padding[2]; + __u8 key_hash; + __u8 key_fmt; + __le32 flags; + __le32 min_io_size; + __le32 leb_size; + __le32 leb_cnt; + __le32 max_leb_cnt; + __le64 max_bud_bytes; + __le32 log_lebs; + __le32 lpt_lebs; + __le32 orph_lebs; + __le32 jhead_cnt; + __le32 fanout; + __le32 lsave_cnt; + __le32 fmt_version; + __le16 default_compr; + __u8 padding1[2]; + __le32 rp_uid; + __le32 rp_gid; + __le64 rp_size; + __le32 time_gran; + __u8 uuid[16]; + __u8 padding2[3972]; +} __attribute__ ((packed)); + +/** + * struct ubifs_mst_node - master node. + * @ch: common header + * @highest_inum: highest inode number in the committed index + * @cmt_no: commit number + * @flags: various flags (%UBIFS_MST_DIRTY, etc) + * @log_lnum: start of the log + * @root_lnum: LEB number of the root indexing node + * @root_offs: offset within @root_lnum + * @root_len: root indexing node length + * @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was + * not reserved and should be reserved on mount) + * @ihead_lnum: LEB number of index head + * @ihead_offs: offset of index head + * @index_size: size of index on flash + * @total_free: total free space in bytes + * @total_dirty: total dirty space in bytes + * @total_used: total used space in bytes (includes only data LEBs) + * @total_dead: total dead space in bytes (includes only data LEBs) + * @total_dark: total dark space in bytes (includes only data LEBs) + * @lpt_lnum: LEB number of LPT root nnode + * @lpt_offs: offset of LPT root nnode + * @nhead_lnum: LEB number of LPT head + * @nhead_offs: offset of LPT head + * @ltab_lnum: LEB number of LPT's own lprops table + * @ltab_offs: offset of LPT's own lprops table + * @lsave_lnum: LEB number of LPT's save table (big model only) + * @lsave_offs: offset of LPT's save table (big model only) + * @lscan_lnum: LEB number of last LPT scan + * @empty_lebs: number of empty logical eraseblocks + * @idx_lebs: number of indexing logical eraseblocks + * @leb_cnt: count of LEBs used by file-system + * @padding: reserved for future, zeroes + */ +struct ubifs_mst_node { + struct ubifs_ch ch; + __le64 highest_inum; + __le64 cmt_no; + __le32 flags; + __le32 log_lnum; + __le32 root_lnum; + __le32 root_offs; + __le32 root_len; + __le32 gc_lnum; + __le32 ihead_lnum; + __le32 ihead_offs; + __le64 index_size; + __le64 total_free; + __le64 total_dirty; + __le64 total_used; + __le64 total_dead; + __le64 total_dark; + __le32 lpt_lnum; + __le32 lpt_offs; + __le32 nhead_lnum; + __le32 nhead_offs; + __le32 ltab_lnum; + __le32 ltab_offs; + __le32 lsave_lnum; + __le32 lsave_offs; + __le32 lscan_lnum; + __le32 empty_lebs; + __le32 idx_lebs; + __le32 leb_cnt; + __u8 padding[344]; +} __attribute__ ((packed)); + +/** + * struct ubifs_ref_node - logical eraseblock reference node. + * @ch: common header + * @lnum: the referred logical eraseblock number + * @offs: start offset in the referred LEB + * @jhead: journal head number + * @padding: reserved for future, zeroes + */ +struct ubifs_ref_node { + struct ubifs_ch ch; + __le32 lnum; + __le32 offs; + __le32 jhead; + __u8 padding[28]; +} __attribute__ ((packed)); + +/** + * struct ubifs_branch - key/reference/length branch + * @lnum: LEB number of the target node + * @offs: offset within @lnum + * @len: target node length + * @key: key + */ +struct ubifs_branch { + __le32 lnum; + __le32 offs; + __le32 len; + __u8 key[]; +} __attribute__ ((packed)); + +/** + * struct ubifs_idx_node - indexing node. + * @ch: common header + * @child_cnt: number of child index nodes + * @level: tree level + * @branches: LEB number / offset / length / key branches + */ +struct ubifs_idx_node { + struct ubifs_ch ch; + __le16 child_cnt; + __le16 level; + __u8 branches[]; +} __attribute__ ((packed)); + +/** + * struct ubifs_cs_node - commit start node. + * @ch: common header + * @cmt_no: commit number + */ +struct ubifs_cs_node { + struct ubifs_ch ch; + __le64 cmt_no; +} __attribute__ ((packed)); + +/** + * struct ubifs_orph_node - orphan node. + * @ch: common header + * @cmt_no: commit number (also top bit is set on the last node of the commit) + * @inos: inode numbers of orphans + */ +struct ubifs_orph_node { + struct ubifs_ch ch; + __le64 cmt_no; + __le64 inos[]; +} __attribute__ ((packed)); + +#endif /* __UBIFS_MEDIA_H__ */ diff --git a/mkfs.ubifs/ubifs.h b/mkfs.ubifs/ubifs.h new file mode 100644 index 0000000..79c7192 --- /dev/null +++ b/mkfs.ubifs/ubifs.h @@ -0,0 +1,430 @@ +/* + * This file is part of UBIFS. + * + * Copyright (C) 2008 Nokia Corporation. + * Copyright (C) 2008 University of Szeged, Hungary + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Artem Bityutskiy + * Adrian Hunter + * Zoltan Sogor + */ + +#ifndef __UBIFS_H__ +#define __UBIFS_H__ + +/* Minimum amount of data UBIFS writes to the flash */ +#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) + +/* Largest key size supported in this implementation */ +#define CUR_MAX_KEY_LEN UBIFS_SK_LEN + +/* + * There is no notion of truncation key because truncation nodes do not exist + * in TNC. However, when replaying, it is handy to introduce fake "truncation" + * keys for truncation nodes because the code becomes simpler. So we define + * %UBIFS_TRUN_KEY type. + */ +#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT + +/* The below union makes it easier to deal with keys */ +union ubifs_key +{ + uint8_t u8[CUR_MAX_KEY_LEN]; + uint32_t u32[CUR_MAX_KEY_LEN/4]; + uint64_t u64[CUR_MAX_KEY_LEN/8]; + __le32 j32[CUR_MAX_KEY_LEN/4]; +}; + +/* + * LEB properties flags. + * + * LPROPS_UNCAT: not categorized + * LPROPS_DIRTY: dirty > 0, not index + * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index + * LPROPS_FREE: free > 0, not empty, not index + * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs + * LPROPS_EMPTY: LEB is empty, not taken + * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken + * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken + * LPROPS_CAT_MASK: mask for the LEB categories above + * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) + * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) + */ +enum { + LPROPS_UNCAT = 0, + LPROPS_DIRTY = 1, + LPROPS_DIRTY_IDX = 2, + LPROPS_FREE = 3, + LPROPS_HEAP_CNT = 3, + LPROPS_EMPTY = 4, + LPROPS_FREEABLE = 5, + LPROPS_FRDI_IDX = 6, + LPROPS_CAT_MASK = 15, + LPROPS_TAKEN = 16, + LPROPS_INDEX = 32, +}; + +/** + * struct ubifs_lprops - logical eraseblock properties. + * @free: amount of free space in bytes + * @dirty: amount of dirty space in bytes + * @flags: LEB properties flags (see above) + */ +struct ubifs_lprops +{ + int free; + int dirty; + int flags; +}; + +/** + * struct ubifs_lpt_lprops - LPT logical eraseblock properties. + * @free: amount of free space in bytes + * @dirty: amount of dirty space in bytes + */ +struct ubifs_lpt_lprops +{ + int free; + int dirty; +}; + +struct ubifs_nnode; + +/** + * struct ubifs_cnode - LEB Properties Tree common node. + * @parent: parent nnode + * @cnext: next cnode to commit + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) + * @iip: index in parent + * @level: level in the tree (zero for pnodes, greater than zero for nnodes) + * @num: node number + */ +struct ubifs_cnode +{ + struct ubifs_nnode *parent; + struct ubifs_cnode *cnext; + unsigned long flags; + int iip; + int level; + int num; +}; + +/** + * struct ubifs_pnode - LEB Properties Tree leaf node. + * @parent: parent nnode + * @cnext: next cnode to commit + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) + * @iip: index in parent + * @level: level in the tree (always zero for pnodes) + * @num: node number + * @lprops: LEB properties array + */ +struct ubifs_pnode +{ + struct ubifs_nnode *parent; + struct ubifs_cnode *cnext; + unsigned long flags; + int iip; + int level; + int num; + struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; +}; + +/** + * struct ubifs_nbranch - LEB Properties Tree internal node branch. + * @lnum: LEB number of child + * @offs: offset of child + * @nnode: nnode child + * @pnode: pnode child + * @cnode: cnode child + */ +struct ubifs_nbranch +{ + int lnum; + int offs; + union + { + struct ubifs_nnode *nnode; + struct ubifs_pnode *pnode; + struct ubifs_cnode *cnode; + }; +}; + +/** + * struct ubifs_nnode - LEB Properties Tree internal node. + * @parent: parent nnode + * @cnext: next cnode to commit + * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) + * @iip: index in parent + * @level: level in the tree (always greater than zero for nnodes) + * @num: node number + * @nbranch: branches to child nodes + */ +struct ubifs_nnode +{ + struct ubifs_nnode *parent; + struct ubifs_cnode *cnext; + unsigned long flags; + int iip; + int level; + int num; + struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; +}; + +/** + * struct ubifs_lp_stats - statistics of eraseblocks in the main area. + * @empty_lebs: number of empty LEBs + * @taken_empty_lebs: number of taken LEBs + * @idx_lebs: number of indexing LEBs + * @total_free: total free space in bytes + * @total_dirty: total dirty space in bytes + * @total_used: total used space in bytes (includes only data LEBs) + * @total_dead: total dead space in bytes (includes only data LEBs) + * @total_dark: total dark space in bytes (includes only data LEBs) + */ +struct ubifs_lp_stats { + int empty_lebs; + int taken_empty_lebs; + int idx_lebs; + long long total_free; + long long total_dirty; + long long total_used; + long long total_dead; + long long total_dark; +}; + +/** + * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. + * @key: key + * @znode: znode address in memory + * @lnum: LEB number of the indexing node + * @offs: offset of the indexing node within @lnum + * @len: target node length + */ +struct ubifs_zbranch +{ + union ubifs_key key; + struct ubifs_znode *znode; + int lnum; + int offs; + int len; +}; + +/** + * struct ubifs_znode - in-memory representation of an indexing node. + * @parent: parent znode or NULL if it is the root + * @cnext: next znode to commit + * @flags: flags + * @time: last access time (seconds) + * @level: level of the entry in the TNC tree + * @child_cnt: count of child znodes + * @iip: index in parent's zbranch array + * @alt: lower bound of key range has altered i.e. child inserted at slot 0 + * @zbranch: array of znode branches (@c->fanout elements) + */ +struct ubifs_znode +{ + struct ubifs_znode *parent; + struct ubifs_znode *cnext; + unsigned long flags; + unsigned long time; + int level; + int child_cnt; + int iip; + int alt; +#ifdef CONFIG_UBIFS_FS_DEBUG + int lnum, offs, len; +#endif + struct ubifs_zbranch zbranch[]; +}; + +/** + * struct ubifs_info - UBIFS file-system description data structure + * (per-superblock). + * + * @highest_inum: highest used inode number + * @max_sqnum: current global sequence number + * + * @jhead_cnt: count of journal heads + * @max_bud_bytes: maximum number of bytes allowed in buds + * + * @zroot: zbranch which points to the root index node and znode + * @ihead_lnum: LEB number of index head + * @ihead_offs: offset of index head + * + * @log_lebs: number of logical eraseblocks in the log + * @lpt_lebs: number of LEBs used for lprops table + * @lpt_first: first LEB of the lprops table area + * @lpt_last: last LEB of the lprops table area + * @main_lebs: count of LEBs in the main area + * @main_first: first LEB of the main area + * @default_compr: default compression type + * @favor_lzo: favor LZO compression method + * @favor_percent: lzo vs. zlib threshold used in case favor LZO + * + * @key_hash_type: type of the key hash + * @key_hash: direntry key hash function + * @key_fmt: key format + * @key_len: key length + * @fanout: fanout of the index tree (number of links per indexing node) + * + * @min_io_size: minimal input/output unit size + * @leb_size: logical eraseblock size in bytes + * @leb_cnt: count of logical eraseblocks + * @max_leb_cnt: maximum count of logical eraseblocks + * + * @old_idx_sz: size of index on flash + * @lst: lprops statistics + * + * @dead_wm: LEB dead space watermark + * @dark_wm: LEB dark space watermark + * + * @gc_lnum: LEB number used for garbage collection + * @rp_size: reserved pool size + * + * @space_bits: number of bits needed to record free or dirty space + * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT + * @lpt_offs_bits: number of bits needed to record an offset in the LPT + * @lpt_spc_bits: number of bits needed to space in the LPT + * @pcnt_bits: number of bits needed to record pnode or nnode number + * @lnum_bits: number of bits needed to record LEB number + * @nnode_sz: size of on-flash nnode + * @pnode_sz: size of on-flash pnode + * @ltab_sz: size of on-flash LPT lprops table + * @lsave_sz: size of on-flash LPT save table + * @pnode_cnt: number of pnodes + * @nnode_cnt: number of nnodes + * @lpt_hght: height of the LPT + * + * @lpt_lnum: LEB number of the root nnode of the LPT + * @lpt_offs: offset of the root nnode of the LPT + * @nhead_lnum: LEB number of LPT head + * @nhead_offs: offset of LPT head + * @big_lpt: flag that LPT is too big to write whole during commit + * @lpt_sz: LPT size + * + * @ltab_lnum: LEB number of LPT's own lprops table + * @ltab_offs: offset of LPT's own lprops table + * @lpt: lprops table + * @ltab: LPT's own lprops table + * @lsave_cnt: number of LEB numbers in LPT's save table + * @lsave_lnum: LEB number of LPT's save table + * @lsave_offs: offset of LPT's save table + * @lsave: LPT's save table + * @lscan_lnum: LEB number of last LPT scan + */ +struct ubifs_info +{ + ino_t highest_inum; + unsigned long long max_sqnum; + + int jhead_cnt; + long long max_bud_bytes; + + struct ubifs_zbranch zroot; + int ihead_lnum; + int ihead_offs; + + int log_lebs; + int lpt_lebs; + int lpt_first; + int lpt_last; + int orph_lebs; + int main_lebs; + int main_first; + int default_compr; + int favor_lzo; + int favor_percent; + + uint8_t key_hash_type; + uint32_t (*key_hash)(const char *str, int len); + int key_fmt; + int key_len; + int fanout; + + int min_io_size; + int leb_size; + int leb_cnt; + int max_leb_cnt; + + unsigned long long old_idx_sz; + struct ubifs_lp_stats lst; + + int dead_wm; + int dark_wm; + + int gc_lnum; + long long rp_size; + + int space_bits; + int lpt_lnum_bits; + int lpt_offs_bits; + int lpt_spc_bits; + int pcnt_bits; + int lnum_bits; + int nnode_sz; + int pnode_sz; + int ltab_sz; + int lsave_sz; + int pnode_cnt; + int nnode_cnt; + int lpt_hght; + + int lpt_lnum; + int lpt_offs; + int nhead_lnum; + int nhead_offs; + int big_lpt; + long long lpt_sz; + + int ltab_lnum; + int ltab_offs; + struct ubifs_lprops *lpt; + struct ubifs_lpt_lprops *ltab; + int lsave_cnt; + int lsave_lnum; + int lsave_offs; + int *lsave; + int lscan_lnum; + +}; + +/** + * ubifs_idx_node_sz - return index node size. + * @c: the UBIFS file-system description object + * @child_cnt: number of children of this index node + */ +static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) +{ + return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; +} + +/** + * ubifs_idx_branch - return pointer to an index branch. + * @c: the UBIFS file-system description object + * @idx: index node + * @bnum: branch number + */ +static inline +struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, + const struct ubifs_idx_node *idx, + int bnum) +{ + return (struct ubifs_branch *)((void *)idx->branches + + (UBIFS_BRANCH_SZ + c->key_len) * bnum); +} + +#endif /* __UBIFS_H__ */ diff --git a/new-utils/Android.mk b/new-utils/Android.mk new file mode 100644 index 0000000..251c6f8 --- /dev/null +++ b/new-utils/Android.mk @@ -0,0 +1,153 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/ubinize.c \ + src/common.c \ + src/crc32.c \ + src/libiniparser.c \ + src/dictionary.c \ + src/libubigen.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS+= -Wpointer-arith -Wwrite-strings -Wstrict-prototypes +LOCAL_CFLAGS+= -Wmissing-prototypes -Winline -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_MODULE := ubinize + +include $(BUILD_HOST_EXECUTABLE) + +# libubi +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/libubi.c \ + src/libmtd.c \ + src/libscan.c \ + src/libubigen.c \ + src/crc32.c \ + src/common.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_MODULE := libubi + +include $(BUILD_SHARED_LIBRARY) + +# ubinfo util +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := src/ubinfo.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_SHARED_LIBRARIES := libubi + +LOCAL_MODULE := ubinfo + +include $(BUILD_EXECUTABLE) + +# ubiformat util +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := src/ubiformat.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_SHARED_LIBRARIES := libubi + +LOCAL_MODULE := ubiformat + +include $(BUILD_EXECUTABLE) + +# ubimkvol util +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := src/ubimkvol.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_SHARED_LIBRARIES := libubi + +LOCAL_MODULE := ubimkvol + +include $(BUILD_EXECUTABLE) + +# ubiattach util +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := src/ubiattach.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_SHARED_LIBRARIES := libubi + +LOCAL_MODULE := ubiattach + +include $(BUILD_EXECUTABLE) + +# ubidetach util +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := src/ubidetach.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_SHARED_LIBRARIES := libubi + +LOCAL_MODULE := ubidetach + +include $(BUILD_EXECUTABLE) + +# ubiupdatevol util +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := src/ubiupdatevol.c + +LOCAL_CFLAGS = -O2 -Wall +LOCAL_CFLAGS += -Wall -Wextra -Wwrite-strings -Wno-sign-compare -D_FILE_OFFSET_BITS=64 + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/src + +LOCAL_SHARED_LIBRARIES := libubi + +LOCAL_MODULE := ubiupdatevol + +include $(BUILD_EXECUTABLE) diff --git a/new-utils/Makefile b/new-utils/Makefile new file mode 100644 index 0000000..6ae60b3 --- /dev/null +++ b/new-utils/Makefile @@ -0,0 +1,57 @@ +# +# Makefile for ubi-utils +# + +KERNELHDR := ../../include + +#CFLAGS += -Werror +CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR) + +LIBS = libubi libmtd libubigen libiniparser libscan +TARGETS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ + ubidetach ubinize ubiformat + +vpath %.c src + +include ../../common.mk + +# And the below is the rule to get final executable from its .o and common.o +$(TARGETS): $(addprefix $(BUILDDIR)/,\ + libubi.a common.o) +# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lubi -o $@ + +$(BUILDDIR)/ubicrc32: $(addprefix $(BUILDDIR)/,\ + ubicrc32.o crc32.o) +# $(CC) $(CFLAGS) -o $@ $^ + +$(BUILDDIR)/ubinize: $(addprefix $(BUILDDIR)/,\ + ubinize.o common.o crc32.o libiniparser.a libubigen.a) +# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -liniparser -lubigen -o $@ + +$(BUILDDIR)/ubiformat: $(addprefix $(BUILDDIR)/,\ + ubiformat.o common.o crc32.o libmtd.a libscan.a libubi.a libubigen.a) +# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@ + +$(BUILDDIR)/libubi.a: $(BUILDDIR)/libubi.o + +$(BUILDDIR)/libmtd.a: $(BUILDDIR)/libmtd.o + +$(BUILDDIR)/libubigen.a: $(BUILDDIR)/libubigen.o + +$(BUILDDIR)/libiniparser.a: $(addprefix $(BUILDDIR)/,\ + libiniparser.o dictionary.o) + +$(BUILDDIR)/libscan.a: $(addprefix $(BUILDDIR)/,\ + libscan.o crc32.o) + +clean:: + rm -f $(addsuffix .a, $(LIBS)) + +install:: + mkdir -p ${DESTDIR}/${SBINDIR} + install -m 0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/ + +uninstall: + for file in ${TARGETS}; do \ + $(RM) ${DESTDIR}/${SBINDIR}/$$file; \ + done diff --git a/new-utils/include/libiniparser.h b/new-utils/include/libiniparser.h new file mode 100644 index 0000000..be3c667 --- /dev/null +++ b/new-utils/include/libiniparser.h @@ -0,0 +1,280 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.h + @author N. Devillard + @date Sep 2007 + @version 3.0 + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: iniparser.h,v 1.24 2007-11-23 21:38:19 ndevilla Exp $ + $Revision: 1.24 $ +*/ + +#ifndef _INIPARSER_H_ +#define _INIPARSER_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include +#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 diff --git a/new-utils/include/libmtd.h b/new-utils/include/libmtd.h new file mode 100644 index 0000000..d3c6a63 --- /dev/null +++ b/new-utils/include/libmtd.h @@ -0,0 +1,73 @@ +/* + * 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 + * + * MTD library. + */ + +#ifndef __LIBMTD_H__ +#define __LIBMTD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * struct mtd_info - information about an MTD device. + * @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 + * @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 (not set by 'mtd_get_info()'!!!) + * @rdonly: non-zero if the device is read-only + * @allows_bb: non-zero if the MTD device may have bad eraseblocks + * @fd: descriptor of the opened MTD character device node + */ +struct mtd_info +{ + int num; + int major; + int minor; + int type; + const char *type_str; + long long size; + int eb_cnt; + int eb_size; + int min_io_size; + int subpage_size; + unsigned int rdonly:1; + unsigned int allows_bb:1; + int fd; +}; + +int mtd_get_info(const char *node, struct mtd_info *mtd); +int mtd_erase(const struct mtd_info *mtd, int eb); +int mtd_is_bad(const struct mtd_info *mtd, int eb); +int mtd_read(const struct mtd_info *mtd, int eb, int offs, void *buf, int len); +int mtd_write(const struct mtd_info *mtd, int eb, int offs, void *buf, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBMTD_H__ */ diff --git a/new-utils/include/libscan.h b/new-utils/include/libscan.h new file mode 100644 index 0000000..5afc93e --- /dev/null +++ b/new-utils/include/libscan.h @@ -0,0 +1,112 @@ +/* + * 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_info; + +/** + * ubi_scan - scan an MTD device. + * @mtd: information about the MTD device to scan + * @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_info *mtd, struct ubi_scan_info **info, int verbose); + +/** + * ubi_scan_free - free scanning information. + * @si: scanning information to free + */ +void ubi_scan_free(struct ubi_scan_info *si); + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBSCAN_H__ */ + diff --git a/new-utils/include/libubi.h b/new-utils/include/libubi.h new file mode 100644 index 0000000..2eeae03 --- /dev/null +++ b/new-utils/include/libubi.h @@ -0,0 +1,382 @@ +/* + * 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 + +/* UBI library descriptor */ +typedef void * libubi_t; + +/** + * struct ubi_attach_request - MTD device attachement request. + * @dev_num: number to assigne to the newly created UBI device + * (%UBI_DEV_NUM_AUTO should be used to automatically assign the + * number) + * @mtd_num: MTD device number to attach + * @vid_hdr_offset: VID header offset (%0 means default offset and this is what + * most of the users want) + */ +struct ubi_attach_request +{ + int dev_num; + int mtd_num; + int vid_hdr_offset; +}; + +/** + * struct ubi_mkvol_request - volume creation request. + * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to + * automatically assign ID) + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @name: volume name + */ +struct ubi_mkvol_request +{ + int vol_id; + int alignment; + long long bytes; + int vol_type; + const char *name; +}; + +/** + * struct ubi_info - general UBI information. + * @dev_count: count of UBI devices in system + * @lowest_dev_num: lowest UBI device number + * @highest_dev_num: highest UBI device number + * @version: UBI version + * @ctrl_major: major number of the UBI control device + * @ctrl_minor: minor number of the UBI control device + */ +struct ubi_info +{ + int dev_count; + int lowest_dev_num; + int highest_dev_num; + int version; + int ctrl_major; + int ctrl_minor; +}; + +/** + * struct ubi_dev_info - UBI device information. + * @vol_count: count of volumes on this UBI device + * @lowest_vol_id: lowest volume ID + * @highest_vol_id: highest volume ID + * @major: major number of corresponding character device + * @minor: minor number of corresponding character device + * @total_lebs: total number of logical eraseblocks on this UBI device + * @avail_lebs: how many logical eraseblocks are not used and available for new + * volumes + * @total_bytes: @total_lebs * @leb_size + * @avail_bytes: @avail_lebs * @leb_size + * @bad_count: count of bad physical eraseblocks + * @leb_size: logical eraseblock size + * @max_ec: current highest erase counter value + * @bad_rsvd: how many physical eraseblocks of the underlying flash device are + * reserved for bad eraseblocks handling + * @max_vol_count: maximum possible number of volumes on this UBI device + * @min_io_size: minimum input/output unit size of the UBI device + */ +struct ubi_dev_info +{ + int dev_num; + int vol_count; + int lowest_vol_id; + int highest_vol_id; + int major; + int minor; + int total_lebs; + int avail_lebs; + long long total_bytes; + long long avail_bytes; + int bad_count; + int leb_size; + long long max_ec; + int bad_rsvd; + int max_vol_count; + int min_io_size; +}; + +/** + * struct ubi_vol_info - UBI volume information. + * @dev_num: UBI device number the volume resides on + * @vol_id: ID of this volume + * @major: major number of corresponding volume character device + * @minor: minor number of corresponding volume character device + * @dev_major: major number of corresponding UBI device character device + * @dev_minor: minor number of corresponding UBI device character device + * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @alignment: alignemnt of this volume + * @data_bytes: how many data bytes are stored on this volume (equivalent to + * @rsvd_bytes for dynamic volumes) + * @rsvd_bytes: how many bytes are reserved for this volume + * @rsvd_lebs: how many logical eraseblocks are reserved for this volume + * @leb_size: logical eraseblock size of this volume (may be less then + * device's logical eraseblock size due to alignment) + * @corrupted: non-zero if the volume is corrupted + * @name: volume name (null-terminated) + */ +struct ubi_vol_info +{ + int dev_num; + int vol_id; + int major; + int minor; + int dev_major; + int dev_minor; + int type; + int alignment; + long long data_bytes; + long long rsvd_bytes; + int rsvd_lebs; + int leb_size; + int corrupted; + char name[UBI_VOL_NAME_MAX + 1]; +}; + +/** + * libubi_open - open UBI library. + * @required: if non-zero, libubi will print an error messages if this UBI is + * not present in the system + * + * This function initializes and opens the UBI library and returns UBI library + * descriptor in case of success and %NULL in case of failure. + */ +libubi_t libubi_open(int required); + +/** + * libubi_close - close UBI library. + * @desc UBI library descriptor + */ +void libubi_close(libubi_t desc); + +/** + * ubi_get_info - get general UBI information. + * @desc: UBI library descriptor + * @info: pointer to the &struct ubi_info object to fill + * + * This function fills the passed @info object with general UBI information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_info(libubi_t desc, struct ubi_info *info); + +/** + * mtd_num2ubi_dev - find UBI device by attached MTD device. + * @@desc: UBI library descriptor + * @mtd_num: MTD device number + * @dev_num: UBI device number is returned here + * + * This function finds UBI device to which MTD device @mtd_num is attached. + * Returns %0 if the UBI device was found and %-1 if not. + */ +int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num); + +/** + * ubi_attach_mtd - attach MTD device to UBI. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @req: MTD attach request. + * + * This function creates a new UBI device by attaching an MTD device as + * described by @req. Returns %0 in case of success and %-1 in case of failure. + * The newly created UBI device number is returned in @req->dev_num. + */ +int ubi_attach_mtd(libubi_t desc, const char *node, + struct ubi_attach_request *req); + +/** + * ubi_detach_mtd - detach an MTD device. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @mtd_num: MTD device number to detach + * + * This function detaches MTD device number @mtd_num from UBI, which means the + * corresponding UBI device is removed. Returns zero in case of success and %-1 + * in case of failure. + */ +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num); + +/** + * ubi_remove_dev - remove an UBI device. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @ubi_dev: UBI device number to remove + * + * This function removes UBI device number @ubi_dev and returns zero in case of + * success and %-1 in case of failure. + */ +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev); + +/** + * ubi_mkvol - create an UBI volume. + * @desc: UBI library descriptor + * @node: name of the UBI character device to create a volume at + * @req: UBI volume creation request + * + * This function creates a UBI volume as described at @req and returns %0 in + * case of success and %-1 in case of failure. The assigned volume ID is + * returned in @req->vol_id. + */ +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); + +/** + * ubi_rmvol - remove a UBI volume. + * @desc: UBI library descriptor + * @node: name of the UBI character device to remove a volume from + * @vol_id: ID of the volume to remove + * + * This function removes volume @vol_id from UBI device @node and returns %0 in + * case of success and %-1 in case of failure. + */ +int ubi_rmvol(libubi_t desc, const char *node, int vol_id); + +/** + * ubi_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_node_type - test UBI node type. + * @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 node or if an error occurred (the latter is indicated by + * a non-zero errno). + */ +int ubi_node_type(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. + */ +int ubi_get_dev_info(libubi_t desc, const char *node, + struct ubi_dev_info *info); + +/** + * ubi_get_dev_info1 - get UBI device information. + * @desc: UBI library descriptor + * @dev_num: UBI device number to fetch information about + * @info: pointer to the &struct ubi_dev_info object to fill + * + * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI + * device number, not UBI character device. + */ +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); + +/** + * ubi_get_vol_info - get UBI volume information. + * @desc: UBI library descriptor + * @node: name of the UBI volume character device to fetch information about + * @info: pointer to the &struct ubi_vol_info object to fill + * + * This function fills the passed @info object with UBI volume information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_vol_info(libubi_t desc, const char *node, + struct ubi_vol_info *info); + +/** + * ubi_get_vol_info1 - get UBI volume information. + * @desc: UBI library descriptor + * @dev_num: UBI device number + * @vol_id: ID of the UBI volume to fetch information about + * @info: pointer to the &struct ubi_vol_info object to fill + * + * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI + * volume ID, not UBI volume character device. + */ +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info); + +/** + * ubi_get_vol_info1_nm - get UBI volume information by volume name. + * @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 name, not UBI volume ID. + */ +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 devie file descriptor + * @bytes: how many bytes will be written to the volume + * + * This function initiates UBI volume update and returns %0 in case of success + * and %-1 in case of error. The caller is assumed to write @bytes data to the + * volume @fd afterwards. + */ +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 devie 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 afterwards. + */ +int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_H__ */ diff --git a/new-utils/include/libubigen.h b/new-utils/include/libubigen.h new file mode 100644 index 0000000..c2b95b0 --- /dev/null +++ b/new-utils/include/libubigen.h @@ -0,0 +1,110 @@ +/* + * 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 + +#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 + */ +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; +}; + +/** + * struct ubigen_vol_info - information about a volume. + * @id: volume id + * @type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @usable_leb_size: LEB size accessible for volume users + * @name: volume name + * @name_len: volume name length + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @used_ebs: total number of used logical eraseblocks in this volume (relevant + * for static volumes only) + * @bytes: size of the volume contents in bytes (relevant for static volumes + * only) + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + */ +struct ubigen_vol_info +{ + int id; + int type; + int alignment; + int data_pad; + int usable_leb_size; + const char *name; + int name_len; + int compat; + int used_ebs; + long long bytes; + uint8_t flags; +}; + +void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, + int subpage_size, int vid_hdr_offs, int ubi_ver); +struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui); +void ubigen_init_ec_hdr(const struct ubigen_info *ui, + struct ubi_ec_hdr *hdr, long long ec); +int ubigen_get_vtbl_size(const struct ubigen_info *ui); +int ubigen_add_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, + struct ubi_vtbl_record *vtbl); +int ubigen_write_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, long long ec, + long long bytes, int in, int out); +int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, + long long ec1, long long ec2, + struct ubi_vtbl_record *vtbl, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBIGEN_H__ */ diff --git a/new-utils/include/linux/jffs2.h b/new-utils/include/linux/jffs2.h new file mode 100644 index 0000000..c2f684a --- /dev/null +++ b/new-utils/include/linux/jffs2.h @@ -0,0 +1,218 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $ + * + */ + +#ifndef __LINUX_JFFS2_H__ +#define __LINUX_JFFS2_H__ + +/* You must include something which defines the C99 uintXX_t types. + We don't do it from here because this file is used in too many + different environments. */ + +#define JFFS2_SUPER_MAGIC 0x72b6 + +/* Values we may expect to find in the 'magic' field */ +#define JFFS2_OLD_MAGIC_BITMASK 0x1984 +#define JFFS2_MAGIC_BITMASK 0x1985 +#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ +#define JFFS2_EMPTY_BITMASK 0xffff +#define JFFS2_DIRTY_BITMASK 0x0000 + +/* Summary node MAGIC marker */ +#define JFFS2_SUM_MAGIC 0x02851885 + +/* We only allow a single char for length, and 0xFF is empty flash so + we don't want it confused with a real length. Hence max 254. +*/ +#define JFFS2_MAX_NAME_LEN 254 + +/* How small can we sensibly write nodes? */ +#define JFFS2_MIN_DATA_LEN 128 + +#define JFFS2_COMPR_NONE 0x00 +#define JFFS2_COMPR_ZERO 0x01 +#define JFFS2_COMPR_RTIME 0x02 +#define JFFS2_COMPR_RUBINMIPS 0x03 +#define JFFS2_COMPR_COPY 0x04 +#define JFFS2_COMPR_DYNRUBIN 0x05 +#define JFFS2_COMPR_ZLIB 0x06 +#define JFFS2_COMPR_LZO 0x07 +/* Compatibility flags. */ +#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ +#define JFFS2_NODE_ACCURATE 0x2000 +/* INCOMPAT: Fail to mount the filesystem */ +#define JFFS2_FEATURE_INCOMPAT 0xc000 +/* ROCOMPAT: Mount read-only */ +#define JFFS2_FEATURE_ROCOMPAT 0x8000 +/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */ +#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000 +/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */ +#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 + +#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) +#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) +#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) +#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) + +#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) + +#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) + +/* XATTR Related */ +#define JFFS2_XPREFIX_USER 1 /* for "user." */ +#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ +#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */ +#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ +#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */ + +#define JFFS2_ACL_VERSION 0x0001 + +// Maybe later... +//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) +//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) + + +#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at + mount time, don't wait for it to + happen later */ +#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific + compression type */ + + +/* These can go once we've made sure we've caught all uses without + byteswapping */ + +typedef struct { + uint32_t v32; +} __attribute__((packed)) jint32_t; + +typedef struct { + uint32_t m; +} __attribute__((packed)) jmode_t; + +typedef struct { + uint16_t v16; +} __attribute__((packed)) jint16_t; + +struct jffs2_unknown_node +{ + /* All start like this */ + jint16_t magic; + jint16_t nodetype; + jint32_t totlen; /* So we can skip over nodes we don't grok */ + jint32_t hdr_crc; +} __attribute__((packed)); + +struct jffs2_raw_dirent +{ + jint16_t magic; + jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t pino; + jint32_t version; + jint32_t ino; /* == zero for unlink */ + jint32_t mctime; + uint8_t nsize; + uint8_t type; + uint8_t unused[2]; + jint32_t node_crc; + jint32_t name_crc; + uint8_t name[0]; +} __attribute__((packed)); + +/* The JFFS2 raw inode structure: Used for storage on physical media. */ +/* The uid, gid, atime, mtime and ctime members could be longer, but + are left like this for space efficiency. If and when people decide + they really need them extended, it's simple enough to add support for + a new type of raw node. +*/ +struct jffs2_raw_inode +{ + jint16_t magic; /* A constant magic number. */ + jint16_t nodetype; /* == JFFS2_NODETYPE_INODE */ + jint32_t totlen; /* Total length of this node (inc data, etc.) */ + jint32_t hdr_crc; + jint32_t ino; /* Inode number. */ + jint32_t version; /* Version number. */ + jmode_t mode; /* The file's type or mode. */ + jint16_t uid; /* The file's owner. */ + jint16_t gid; /* The file's group. */ + jint32_t isize; /* Total resultant size of this inode (used for truncations) */ + jint32_t atime; /* Last access time. */ + jint32_t mtime; /* Last modification time. */ + jint32_t ctime; /* Change time. */ + jint32_t offset; /* Where to begin to write. */ + jint32_t csize; /* (Compressed) data size */ + jint32_t dsize; /* Size of the node's data. (after decompression) */ + uint8_t compr; /* Compression algorithm used */ + uint8_t usercompr; /* Compression algorithm requested by the user */ + jint16_t flags; /* See JFFS2_INO_FLAG_* */ + jint32_t data_crc; /* CRC for the (compressed) data. */ + jint32_t node_crc; /* CRC for the raw inode (excluding data) */ + uint8_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_xattr { + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t xid; /* XATTR identifier number */ + jint32_t version; + uint8_t xprefix; + uint8_t name_len; + jint16_t value_len; + jint32_t data_crc; + jint32_t node_crc; + uint8_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_xref +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t xseqno; /* xref sequencial number */ + jint32_t node_crc; +} __attribute__((packed)); + +struct jffs2_raw_summary +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_SUMMARY */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t sum_num; /* number of sum entries*/ + jint32_t cln_mkr; /* clean marker size, 0 = no cleanmarker */ + jint32_t padded; /* sum of the size of padding nodes */ + jint32_t sum_crc; /* summary information crc */ + jint32_t node_crc; /* node crc */ + jint32_t sum[0]; /* inode summary info */ +} __attribute__((packed)); + +union jffs2_node_union +{ + struct jffs2_raw_inode i; + struct jffs2_raw_dirent d; + struct jffs2_raw_xattr x; + struct jffs2_raw_xref r; + struct jffs2_raw_summary s; + struct jffs2_unknown_node u; +}; + +#endif /* __LINUX_JFFS2_H__ */ diff --git a/new-utils/include/mtd/ftl-user.h b/new-utils/include/mtd/ftl-user.h new file mode 100644 index 0000000..53e94c2 --- /dev/null +++ b/new-utils/include/mtd/ftl-user.h @@ -0,0 +1,76 @@ +/* + * $Id: ftl.h,v 1.7 2005/11/07 11:14:54 gleixner Exp $ + * + * Derived from (and probably identical to): + * ftl.h 1.7 1999/10/25 20:23:17 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * . Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use + * your version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#ifndef __MTD_FTL_USER_H__ +#define __MTD_FTL_USER_H__ + +typedef struct erase_unit_header_t { + u_int8_t LinkTargetTuple[5]; + u_int8_t DataOrgTuple[10]; + u_int8_t NumTransferUnits; + u_int32_t EraseCount; + u_int16_t LogicalEUN; + u_int8_t BlockSize; + u_int8_t EraseUnitSize; + u_int16_t FirstPhysicalEUN; + u_int16_t NumEraseUnits; + u_int32_t FormattedSize; + u_int32_t FirstVMAddress; + u_int16_t NumVMPages; + u_int8_t Flags; + u_int8_t Code; + u_int32_t SerialNumber; + u_int32_t AltEUHOffset; + u_int32_t BAMOffset; + u_int8_t Reserved[12]; + u_int8_t EndTuple[2]; +} erase_unit_header_t; + +/* Flags in erase_unit_header_t */ +#define HIDDEN_AREA 0x01 +#define REVERSE_POLARITY 0x02 +#define DOUBLE_BAI 0x04 + +/* Definitions for block allocation information */ + +#define BLOCK_FREE(b) ((b) == 0xffffffff) +#define BLOCK_DELETED(b) (((b) == 0) || ((b) == 0xfffffffe)) + +#define BLOCK_TYPE(b) ((b) & 0x7f) +#define BLOCK_ADDRESS(b) ((b) & ~0x7f) +#define BLOCK_NUMBER(b) ((b) >> 9) +#define BLOCK_CONTROL 0x30 +#define BLOCK_DATA 0x40 +#define BLOCK_REPLACEMENT 0x60 +#define BLOCK_BAD 0x70 + +#endif /* __MTD_FTL_USER_H__ */ diff --git a/new-utils/include/mtd/inftl-user.h b/new-utils/include/mtd/inftl-user.h new file mode 100644 index 0000000..9b1e252 --- /dev/null +++ b/new-utils/include/mtd/inftl-user.h @@ -0,0 +1,91 @@ +/* + * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ + * + * Parts of INFTL headers shared with userspace + * + */ + +#ifndef __MTD_INFTL_USER_H__ +#define __MTD_INFTL_USER_H__ + +#define OSAK_VERSION 0x5120 +#define PERCENTUSED 98 + +#define SECTORSIZE 512 + +/* Block Control Information */ + +struct inftl_bci { + uint8_t ECCsig[6]; + uint8_t Status; + uint8_t Status1; +} __attribute__((packed)); + +struct inftl_unithead1 { + uint16_t virtualUnitNo; + uint16_t prevUnitNo; + uint8_t ANAC; + uint8_t NACs; + uint8_t parityPerField; + uint8_t discarded; +} __attribute__((packed)); + +struct inftl_unithead2 { + uint8_t parityPerField; + uint8_t ANAC; + uint16_t prevUnitNo; + uint16_t virtualUnitNo; + uint8_t NACs; + uint8_t discarded; +} __attribute__((packed)); + +struct inftl_unittail { + uint8_t Reserved[4]; + uint16_t EraseMark; + uint16_t EraseMark1; +} __attribute__((packed)); + +union inftl_uci { + struct inftl_unithead1 a; + struct inftl_unithead2 b; + struct inftl_unittail c; +}; + +struct inftl_oob { + struct inftl_bci b; + union inftl_uci u; +}; + + +/* INFTL Media Header */ + +struct INFTLPartition { + __u32 virtualUnits; + __u32 firstUnit; + __u32 lastUnit; + __u32 flags; + __u32 spareUnits; + __u32 Reserved0; + __u32 Reserved1; +} __attribute__((packed)); + +struct INFTLMediaHeader { + char bootRecordID[8]; + __u32 NoOfBootImageBlocks; + __u32 NoOfBinaryPartitions; + __u32 NoOfBDTLPartitions; + __u32 BlockMultiplierBits; + __u32 FormatFlags; + __u32 OsakVersion; + __u32 PercentUsed; + struct INFTLPartition Partitions[4]; +} __attribute__((packed)); + +/* Partition flag types */ +#define INFTL_BINARY 0x20000000 +#define INFTL_BDTL 0x40000000 +#define INFTL_LAST 0x80000000 + +#endif /* __MTD_INFTL_USER_H__ */ + + diff --git a/new-utils/include/mtd/jffs2-user.h b/new-utils/include/mtd/jffs2-user.h new file mode 100644 index 0000000..bc5d99a --- /dev/null +++ b/new-utils/include/mtd/jffs2-user.h @@ -0,0 +1,82 @@ +/* + * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $ + * + * JFFS2 definitions for use in user space only + */ + +#ifndef __JFFS2_USER_H__ +#define __JFFS2_USER_H__ + +/* This file is blessed for inclusion by userspace */ +#include +#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__ */ diff --git a/new-utils/include/mtd/mtd-abi.h b/new-utils/include/mtd/mtd-abi.h new file mode 100644 index 0000000..86defe1 --- /dev/null +++ b/new-utils/include/mtd/mtd-abi.h @@ -0,0 +1,152 @@ +/* + * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $ + * + * Portions of MTD ABI definition which are shared by kernel and user space + */ + +#ifndef __MTD_ABI_H__ +#define __MTD_ABI_H__ + +struct erase_info_user { + uint32_t start; + uint32_t length; +}; + +struct mtd_oob_buf { + uint32_t start; + uint32_t length; + unsigned char *ptr; +}; + +#define MTD_ABSENT 0 +#define MTD_RAM 1 +#define MTD_ROM 2 +#define MTD_NORFLASH 3 +#define MTD_NANDFLASH 4 +#define MTD_DATAFLASH 6 +#define MTD_UBIVOLUME 7 + +#define MTD_WRITEABLE 0x400 /* Device is writeable */ +#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ +#define MTD_NO_ERASE 0x1000 /* No erase necessary */ +#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */ + +// Some common devices / combinations of capabilities +#define MTD_CAP_ROM 0 +#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) +#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) +#define MTD_CAP_NANDFLASH (MTD_WRITEABLE) + +/* ECC byte placement */ +#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) +#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) +#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme +#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) +#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default + +/* OTP mode selection */ +#define MTD_OTP_OFF 0 +#define MTD_OTP_FACTORY 1 +#define MTD_OTP_USER 2 + +struct mtd_info_user { + uint8_t type; + uint32_t flags; + uint32_t size; // Total size of the MTD + uint32_t erasesize; + uint32_t writesize; + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + /* The below two fields are obsolete and broken, do not use them + * (TODO: remove at some point) */ + uint32_t ecctype; + uint32_t eccsize; +}; + +struct region_info_user { + uint32_t offset; /* At which this region starts, + * from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks in this region */ + uint32_t regionindex; +}; + +struct otp_info { + uint32_t start; + uint32_t length; + uint32_t locked; +}; + +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETREGIONCOUNT _IOR('M', 7, int) +#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) +#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) +#define MEMGETBADBLOCK _IOW('M', 11, loff_t) +#define MEMSETBADBLOCK _IOW('M', 12, loff_t) +#define OTPSELECT _IOR('M', 13, int) +#define OTPGETREGIONCOUNT _IOW('M', 14, int) +#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) +#define OTPLOCK _IOR('M', 16, struct otp_info) +#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) +#define MTDFILEMODE _IO('M', 19) + +/* + * Obsolete legacy interface. Keep it in order not to break userspace + * interfaces + */ +struct nand_oobinfo { + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; + uint32_t eccpos[32]; +}; + +struct nand_oobfree { + uint32_t offset; + uint32_t length; +}; + +#define MTD_MAX_OOBFREE_ENTRIES 8 +/* + * ECC layout control structure. Exported to userspace for + * diagnosis and to allow creation of raw images + */ +struct nand_ecclayout { + uint32_t eccbytes; + uint32_t eccpos[64]; + uint32_t oobavail; + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; +}; + +/** + * struct mtd_ecc_stats - error correction stats + * + * @corrected: number of corrected bits + * @failed: number of uncorrectable errors + * @badblocks: number of bad blocks in this partition + * @bbtblocks: number of blocks reserved for bad block tables + */ +struct mtd_ecc_stats { + uint32_t corrected; + uint32_t failed; + uint32_t badblocks; + uint32_t bbtblocks; +}; + +/* + * Read/write file modes for access to MTD + */ +enum mtd_file_modes { + MTD_MODE_NORMAL = MTD_OTP_OFF, + MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, + MTD_MODE_OTP_USER = MTD_OTP_USER, + MTD_MODE_RAW, +}; + +#endif /* __MTD_ABI_H__ */ diff --git a/new-utils/include/mtd/mtd-user.h b/new-utils/include/mtd/mtd-user.h new file mode 100644 index 0000000..713f34d --- /dev/null +++ b/new-utils/include/mtd/mtd-user.h @@ -0,0 +1,21 @@ +/* + * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $ + * + * MTD ABI header for use by user space only. + */ + +#ifndef __MTD_USER_H__ +#define __MTD_USER_H__ + +#include + +/* 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 nand_ecclayout_t; + +#endif /* __MTD_USER_H__ */ diff --git a/new-utils/include/mtd/nftl-user.h b/new-utils/include/mtd/nftl-user.h new file mode 100644 index 0000000..b2bca18 --- /dev/null +++ b/new-utils/include/mtd/nftl-user.h @@ -0,0 +1,76 @@ +/* + * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ + * + * Parts of NFTL headers shared with userspace + * + */ + +#ifndef __MTD_NFTL_USER_H__ +#define __MTD_NFTL_USER_H__ + +/* Block Control Information */ + +struct nftl_bci { + unsigned char ECCSig[6]; + uint8_t Status; + uint8_t Status1; +}__attribute__((packed)); + +/* Unit Control Information */ + +struct nftl_uci0 { + uint16_t VirtUnitNum; + uint16_t ReplUnitNum; + uint16_t SpareVirtUnitNum; + uint16_t SpareReplUnitNum; +} __attribute__((packed)); + +struct nftl_uci1 { + uint32_t WearInfo; + uint16_t EraseMark; + uint16_t EraseMark1; +} __attribute__((packed)); + +struct nftl_uci2 { + uint16_t FoldMark; + uint16_t FoldMark1; + uint32_t unused; +} __attribute__((packed)); + +union nftl_uci { + struct nftl_uci0 a; + struct nftl_uci1 b; + struct nftl_uci2 c; +}; + +struct nftl_oob { + struct nftl_bci b; + union nftl_uci u; +}; + +/* NFTL Media Header */ + +struct NFTLMediaHeader { + char DataOrgID[6]; + uint16_t NumEraseUnits; + uint16_t FirstPhysicalEUN; + uint32_t FormattedSize; + unsigned char UnitSizeFactor; +} __attribute__((packed)); + +#define MAX_ERASE_ZONES (8192 - 512) + +#define ERASE_MARK 0x3c69 +#define SECTOR_FREE 0xff +#define SECTOR_USED 0x55 +#define SECTOR_IGNORE 0x11 +#define SECTOR_DELETED 0x00 + +#define FOLD_MARK_IN_PROGRESS 0x5555 + +#define ZONE_GOOD 0xff +#define ZONE_BAD_ORIGINAL 0 +#define ZONE_BAD_MARKED 7 + + +#endif /* __MTD_NFTL_USER_H__ */ diff --git a/new-utils/include/mtd/ubi-header.h b/new-utils/include/mtd/ubi-header.h new file mode 100644 index 0000000..386fa3c --- /dev/null +++ b/new-utils/include/mtd/ubi-header.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. May be included by user-space. + */ + +#ifndef __UBI_HEADER_H__ +#define __UBI_HEADER_H__ + +#include + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling unit to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(uint32_t)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(uint32_t)) + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @padding2: reserved for future, zeroes + * @hdr_crc: erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejected. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. + */ +struct ubi_ec_hdr { + uint32_t magic; + uint8_t version; + uint8_t padding1[3]; + uint64_t ec; /* Warning: the current limit is 31-bit anyway! */ + uint32_t vid_hdr_offset; + uint32_t data_offset; + uint8_t padding2[36]; + uint32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: ID of this volume + * @lnum: logical eraseblock number + * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be + * removed, kept only for not breaking older UBI users) + * @data_size: how many bytes of data this logical eraseblock contains + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock + * @padding1: reserved for future, zeroes + * @sqnum: sequence number + * @padding2: reserved for future, zeroes + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more then one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * Note, there is an obsolete @leb_ver field which was used instead of @sqnum + * in the past. But it is not used anymore and we keep it in order to be able + * to deal with old UBI images. It will be removed at some point. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * data of the physical eraseblock was moved by the wear-leveling unit, then + * the wear-leveling unit calculates the data CRC and stores it in the + * @data_crc field. And of course, the @copy_flag is %in this case. + * + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + uint32_t magic; + uint8_t version; + uint8_t vol_type; + uint8_t copy_flag; + uint8_t compat; + uint32_t vol_id; + uint32_t lnum; + uint32_t leb_ver; /* obsolete, to be removed, don't use */ + uint32_t data_size; + uint32_t used_ebs; + uint32_t data_pad; + uint32_t data_crc; + uint8_t padding1[4]; + uint64_t sqnum; + uint8_t padding2[12]; + uint32_t hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(uint32_t)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + uint32_t reserved_pebs; + uint32_t alignment; + uint32_t data_pad; + uint8_t vol_type; + uint8_t upd_marker; + uint16_t name_len; + uint8_t name[UBI_VOL_NAME_MAX+1]; + uint8_t flags; + uint8_t padding[23]; + uint32_t crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_HEADER_H__ */ diff --git a/new-utils/include/mtd/ubi-user.h b/new-utils/include/mtd/ubi-user.h new file mode 100644 index 0000000..a7421f1 --- /dev/null +++ b/new-utils/include/mtd/ubi-user.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 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 volume update + * ~~~~~~~~~~~~~~~~~ + * + * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * corresponding UBI volume character device. A pointer to a 64-bit update + * size should be passed to the IOCTL. After this, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + * + * Atomic eraseblock change + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL + * command of the corresponding UBI volume character device. A pointer to + * &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is + * expected to write the requested amount of bytes. This is similar to the + * "volume update" IOCTL. + */ + +/* + * When a new UBI volume or UBI device is created, users may either specify the + * volume/device number they want to create or to let UBI automatically assign + * the number using these constants. + */ +#define UBI_VOL_NUM_AUTO (-1) +#define UBI_DEV_NUM_AUTO (-1) + +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + +/* IOCTL commands of UBI character devices */ + +#define UBI_IOC_MAGIC 'o' + +/* Create an UBI volume */ +#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) +/* Remove an UBI volume */ +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + +/* IOCTL commands of the UBI control character device */ + +#define UBI_CTRL_IOC_MAGIC 'o' + +/* Attach an MTD device */ +#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) +/* Detach an MTD device */ +#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) + +/* IOCTL commands of UBI volume character devices */ + +#define UBI_VOL_IOC_MAGIC 'O' + +/* Start UBI volume update */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) +/* An eraseblock erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) +/* An atomic eraseblock change command */ +#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) + +/* Maximum MTD device name length supported by UBI */ +#define MAX_UBI_MTD_NAME_LEN 127 + +/* + * UBI data type hint constants. + * + * UBI_LONGTERM: long-term data + * UBI_SHORTTERM: short-term data + * UBI_UNKNOWN: data persistence is unknown + * + * These constants are used when data is written to UBI volumes in order to + * help the UBI wear-leveling unit to find more appropriate physical + * eraseblocks. + */ +enum { + UBI_LONGTERM = 1, + UBI_SHORTTERM = 2, + UBI_UNKNOWN = 3, +}; + +/* + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4, +}; + +/** + * struct ubi_attach_req - attach MTD device request. + * @ubi_num: UBI device number to create + * @mtd_num: MTD device number to attach + * @vid_hdr_offset: VID header offset (use defaults if %0) + * @padding: reserved for future, not used, has to be zeroed + * + * This data structure is used to specify MTD device UBI has to attach and the + * parameters it has to use. The number which should be assigned to the new UBI + * device is passed in @ubi_num. UBI may automatically assign the number if + * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in + * @ubi_num. + * + * Most applications should pass %0 in @vid_hdr_offset to make UBI use default + * offset of the VID header within physical eraseblocks. The default offset is + * the next min. I/O unit after the EC header. For example, it will be offset + * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or + * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. + * + * But in rare cases, if this optimizes things, the VID header may be placed to + * a different offset. For example, the boot-loader might do things faster if the + * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As + * the boot-loader would not normally need to read EC headers (unless it needs + * UBI in RW mode), it might be faster to calculate ECC. This is weird example, + * but it real-life example. So, in this example, @vid_hdr_offer would be + * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes + * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page + * of the first page and add needed padding. + */ +struct ubi_attach_req { + int32_t ubi_num; + int32_t mtd_num; + int32_t vid_hdr_offset; + uint8_t padding[12]; +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding1: reserved for future, not used, has to be zeroed + * @name_len: volume name length + * @padding2: reserved for future, not used, has to be zeroed + * @name: volume name + * + * This structure is used by user-space programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * BLOBs, without caring about how to properly align them. + */ +struct ubi_mkvol_req { + int32_t vol_id; + int32_t alignment; + int64_t bytes; + int8_t vol_type; + int8_t padding1; + int16_t name_len; + int8_t padding2[4]; + char name[UBI_MAX_VOLUME_NAME + 1]; +} __attribute__ ((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * Re-sizing is possible for both dynamic and static volumes. But while dynamic + * volumes may be re-sized arbitrarily, static volumes cannot be made to be + * smaller then the number of bytes they bear. To arbitrarily shrink a static + * volume, it must be wiped out first (by means of volume update operation with + * zero number of bytes). + */ +struct ubi_rsvol_req { + int64_t bytes; + int32_t vol_id; +} __attribute__ ((packed)); + +/** + * struct ubi_leb_change_req - a data structure used in atomic logical + * eraseblock change requests. + * @lnum: logical eraseblock number to change + * @bytes: how many bytes will be written to the logical eraseblock + * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_leb_change_req { + int32_t lnum; + int32_t bytes; + uint8_t dtype; + uint8_t padding[7]; +} __attribute__ ((packed)); + +#endif /* __UBI_USER_H__ */ diff --git a/new-utils/include/mtd_swab.h b/new-utils/include/mtd_swab.h new file mode 100644 index 0000000..c3340a6 --- /dev/null +++ b/new-utils/include/mtd_swab.h @@ -0,0 +1,51 @@ +#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 diff --git a/new-utils/src/common.c b/new-utils/src/common.c new file mode 100644 index 0000000..bcb775c --- /dev/null +++ b/new-utils/src/common.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2007, 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This file contains various common stuff used by UBI utilities. + * + * Authors: Artem Bityutskiy + * Adrian Hunter + */ + +#include +#include +#include +#include + +/** + * 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; + + /* Handle deprecated stuff */ + if (!strcmp(str, "KB") || !strcmp(str, "Kib") || !strcmp(str, "kib") || + !strcmp(str, "kiB")) { + fprintf(stderr, "Warning: use \"KiB\" instead of \"%s\" to " + "specify Kilobytes - support will be removed\n", str); + return 1024; + } + if (!strcmp(str, "MB") || !strcmp(str, "Mib") || !strcmp(str, "mb")) { + fprintf(stderr, "Warning: use \"MiB\" instead of \"%s\", " + "this support will be removed\n", str); + return 1024*1024; + } + if (!strcmp(str, "GB") || !strcmp(str, "Gib") || !strcmp(str, "gb")) { + fprintf(stderr, "Warning: use \"GiB\" instead of \"%s\", " + "this support will be removed\n", str); + 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; + } +} diff --git a/new-utils/src/common.h b/new-utils/src/common.h new file mode 100644 index 0000000..56fa020 --- /dev/null +++ b/new-utils/src/common.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) Artem Bityutskiy, 2007, 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __UBI_UTILS_COMMON_H__ +#define __UBI_UTILS_COMMON_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MIN(a ,b) ((a) < (b) ? (a) : (b)) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* Verbose messages */ +#define verbose(verbose, fmt, ...) do { \ + if (verbose) \ + printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +/* Normal messages */ +#define normsg(fmt, ...) do { \ + printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ +} while(0) +#define normsg_cont(fmt, ...) do { \ + printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \ +} while(0) +#define normsg_cont(fmt, ...) do { \ + printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \ +} while(0) + +/* Error messages */ +#define errmsg(fmt, ...) ({ \ + fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \ + -1; \ +}) + +/* System error messages */ +#define sys_errmsg(fmt, ...) ({ \ + int _err = errno; \ + size_t _i; \ + fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \ + for (_i = 0; _i < sizeof(PROGRAM_NAME) + 1; _i++) \ + fprintf(stderr, " "); \ + fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \ + -1; \ +}) + +/* Warnings */ +#define warnmsg(fmt, ...) do { \ + fprintf(stderr, PROGRAM_NAME ": warning!: " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +static inline int is_power_of_2(unsigned long long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +long long ubiutils_get_bytes(const char *str); +void ubiutils_print_bytes(long long bytes, int bracket); +void ubiutils_print_text(FILE *stream, const char *txt, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* !__UBI_UTILS_COMMON_H__ */ diff --git a/new-utils/src/crc32.c b/new-utils/src/crc32.c new file mode 100644 index 0000000..6b1e50c --- /dev/null +++ b/new-utils/src/crc32.c @@ -0,0 +1,95 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +#include + +const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; diff --git a/new-utils/src/crc32.h b/new-utils/src/crc32.h new file mode 100644 index 0000000..ee3145b --- /dev/null +++ b/new-utils/src/crc32.h @@ -0,0 +1,19 @@ +#ifndef CRC32_H +#define CRC32_H + +#include + +extern const uint32_t crc32_table[256]; + +/* Return a 32-bit CRC of the contents of the buffer. */ + + static inline uint32_t +crc32(uint32_t val, const void *ss, int len) +{ + const unsigned char *s = ss; + while (--len >= 0) + val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); + return val; +} + +#endif diff --git a/new-utils/src/dictionary.c b/new-utils/src/dictionary.c new file mode 100644 index 0000000..b7c9ebf --- /dev/null +++ b/new-utils/src/dictionary.c @@ -0,0 +1,405 @@ +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.c + @author N. Devillard + @date Sep 2007 + @version $Revision: 1.27 $ + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ + $Revision: 1.27 $ +*/ +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#include "dictionary.h" + +#include +#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 */ diff --git a/new-utils/src/dictionary.h b/new-utils/src/dictionary.h new file mode 100644 index 0000000..c7d1790 --- /dev/null +++ b/new-utils/src/dictionary.h @@ -0,0 +1,174 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.h + @author N. Devillard + @date Sep 2007 + @version $Revision: 1.12 $ + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $ + $Author: ndevilla $ + $Date: 2007-11-23 21:37:00 $ + $Revision: 1.12 $ +*/ + +#ifndef _DICTIONARY_H_ +#define _DICTIONARY_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include +#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 diff --git a/new-utils/src/libiniparser.c b/new-utils/src/libiniparser.c new file mode 100644 index 0000000..3bea51e --- /dev/null +++ b/new-utils/src/libiniparser.c @@ -0,0 +1,646 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.c + @author N. Devillard + @date Sep 2007 + @version 3.0 + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/* + $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $ + $Revision: 2.18 $ + $Date: 2008-01-03 18:35:39 $ +*/ +/*---------------------------- Includes ------------------------------------*/ +#include +#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 */ diff --git a/new-utils/src/libmtd.c b/new-utils/src/libmtd.c new file mode 100644 index 0000000..64efc55 --- /dev/null +++ b/new-utils/src/libmtd.c @@ -0,0 +1,316 @@ +/* + * 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 + * + * MTD library. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "common.h" + +#define PROGRAM_NAME "libmtd" +#define MTD_DEV_MAJOR 90 + +#define MTD_UBIVOLUME 7 + +/** + * mtd_get_info - get information about an MTD device. + * @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. + */ +int mtd_get_info(const char *node, struct mtd_info *mtd) +{ + struct stat st; + struct mtd_info_user ui; + int ret; + loff_t offs = 0; + + if (stat(node, &st)) + return sys_errmsg("cannot open \"%s\"", node); + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return errmsg("\"%s\" is not a character device", node); + } + + 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->num = mtd->minor / 2; + mtd->rdonly = mtd->minor & 1; + + mtd->fd = open(node, O_RDWR); + if (mtd->fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + if (ioctl(mtd->fd, MEMGETINFO, &ui)) { + sys_errmsg("MEMGETINFO ioctl request failed"); + goto out_close; + } + + ret = ioctl(mtd->fd, MEMGETBADBLOCK, &offs); + if (ret == -1) { + if (errno != EOPNOTSUPP) { + sys_errmsg("MEMGETBADBLOCK ioctl failed"); + goto out_close; + } + errno = 0; + mtd->allows_bb = 0; + } else + mtd->allows_bb = 1; + + mtd->type = ui.type; + mtd->size = ui.size; + mtd->eb_size = ui.erasesize; + mtd->min_io_size = ui.writesize; + + if (mtd->min_io_size <= 0) { + errmsg("mtd%d (%s) has insane min. I/O unit size %d", + mtd->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->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->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->num, node); + goto out_close; + case MTD_RAM: + mtd->type_str = "RAM-based"; + break; + case MTD_ROM: + mtd->type_str = "ROM"; + break; + case MTD_NORFLASH: + mtd->type_str = "NOR"; + break; + case MTD_NANDFLASH: + mtd->type_str = "NAND"; + break; + case MTD_DATAFLASH: + mtd->type_str = "DataFlash"; + break; + case MTD_UBIVOLUME: + mtd->type_str = "UBI-emulated MTD"; + break; + default: + mtd->type_str = "Unknown flash type"; + break; + } + + if (!(ui.flags & MTD_WRITEABLE)) + mtd->rdonly = 1; + + return 0; + +out_close: + close(mtd->fd); + return -1; +} + +/** + * mtd_erase - erase an eraseblock. + * @mtd: MTD device description object + * @eb: eraseblock to erase + * + * This function erases the eraseblock and returns %0 in case of success and + * %-1 in case of failure. + */ +int mtd_erase(const struct mtd_info *mtd, int eb) +{ + struct erase_info_user ei; + + ei.start = eb * mtd->eb_size;; + ei.length = mtd->eb_size; + return ioctl(mtd->fd, MEMERASE, &ei); +} + +/** + * mtd_is_bad - check if eraseblock is bad. + * @mtd: MTD device description object + * @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_info *mtd, int eb) +{ + int ret; + loff_t seek; + + if (eb < 0 || eb >= mtd->eb_cnt) { + errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", + eb, mtd->num, mtd->eb_cnt); + errno = EINVAL; + return -1; + } + + if (!mtd->allows_bb) + return 0; + + seek = (loff_t)eb * mtd->eb_size; + ret = ioctl(mtd->fd, MEMGETBADBLOCK, &seek); + if (ret == -1) { + sys_errmsg("MEMGETBADBLOCK ioctl failed for " + "eraseblock %d (mtd%d)", eb, mtd->num); + return -1; + } + + return ret; +} + +/** + * mtd_read - read data from an MTD device. + * @mtd: MTD device description object + * @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_info *mtd, int eb, int offs, void *buf, int len) +{ + int ret, rd = 0; + off_t seek; + + if (eb < 0 || eb >= mtd->eb_cnt) { + errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", + eb, mtd->num, mtd->eb_cnt); + errno = EINVAL; + return -1; + } + if (offs < 0 || offs + len > mtd->eb_size) { + errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", + offs, len, mtd->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(mtd->fd, seek, SEEK_SET) != seek) { + sys_errmsg("cannot seek mtd%d to offset %llu", + mtd->num, (unsigned long long)seek); + return -1; + } + + while (rd < len) { + ret = read(mtd->fd, buf, len); + if (ret < 0) { + sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)", + len, mtd->num, eb, offs); + return -1; + } + rd += ret; + } + + return 0; +} + +/** + * mtd_write - write data to an MTD device. + * @mtd: MTD device description object + * @eb: eraseblock to write to + * @offs: offset withing the eraseblock to write to + * @buf: buffer to write + * @len: how many bytes to write + * + * This function writes @len bytes of data to eraseblock @eb and offset @offs + * of the MTD device defined by @mtd. Returns %0 in case of success and %-1 in + * case of failure. + */ +int mtd_write(const struct mtd_info *mtd, int eb, int offs, void *buf, int len) +{ + int ret; + off_t seek; + + if (eb < 0 || eb >= mtd->eb_cnt) { + errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", + eb, mtd->num, mtd->eb_cnt); + errno = EINVAL; + return -1; + } + if (offs < 0 || offs + len > mtd->eb_size) { + errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", + offs, len, mtd->num, mtd->eb_size); + errno = EINVAL; + return -1; + } +#if 0 + if (offs % mtd->subpage_size) { + errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", + offs, 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->num, mtd->subpage_size); + errno = EINVAL; + return -1; + } +#endif + + /* Seek to the beginning of the eraseblock */ + seek = (off_t)eb * mtd->eb_size + offs; + if (lseek(mtd->fd, seek, SEEK_SET) != seek) { + sys_errmsg("cannot seek mtd%d to offset %llu", + mtd->num, (unsigned long long)seek); + return -1; + } + + ret = write(mtd->fd, buf, len); + if (ret != len) { + sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)", + len, mtd->num, eb, offs); + return -1; + } + + return 0; +} diff --git a/new-utils/src/libscan.c b/new-utils/src/libscan.c new file mode 100644 index 0000000..da13b1b --- /dev/null +++ b/new-utils/src/libscan.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI scanning library. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "common.h" +#include "crc32.h" + +#define PROGRAM_NAME "libscan" + +static int all_ff(const void *buf, int len) +{ + int i; + const uint8_t *p = buf; + + for (i = 0; i < len; i++) + if (p[i] != 0xFF) + return 0; + return 1; +} + +int ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info, int verbose) +{ + int eb, v = (verbose == 2), pr = (verbose == 1); + struct ubi_scan_info *si; + unsigned long long sum = 0; + + si = calloc(1, sizeof(struct ubi_scan_info)); + if (!si) + return sys_errmsg("cannot allocate %zd bytes of memory", + sizeof(struct ubi_scan_info)); + + si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); + if (!si->ec) { + sys_errmsg("cannot allocate %zd bytes of memory", + sizeof(struct ubi_scan_info)); + goto out_si; + } + + si->vid_hdr_offs = si->data_offs = -1; + + verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); + for (eb = 0; eb < mtd->eb_cnt; eb++) { + int ret; + uint32_t crc; + struct ubi_ec_hdr hdr; + unsigned long long ec; + + if (v) { + normsg_cont("scanning eraseblock %d", eb); + fflush(stdout); + } + if (pr) { + printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete ", + eb, (long long)(eb + 1) * 100 / mtd->eb_cnt); + fflush(stdout); + } + + ret = mtd_is_bad(mtd, 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, eb, 0, &hdr, sizeof(struct ubi_ec_hdr));; + if (ret < 0) + goto out_ec; + + /* Check the EC header */ + if (be32_to_cpu(hdr.magic) != UBI_EC_HDR_MAGIC) { + if (all_ff(&hdr, sizeof(struct ubi_ec_hdr))) { + si->empty_cnt += 1; + si->ec[eb] = EB_EMPTY; + if (v) + printf(": empty\n"); + } else { + si->alien_cnt += 1; + si->ec[eb] = EB_ALIEN; + if (v) + printf(": alien\n"); + } + continue; + } + + crc = crc32(UBI_CRC32_INIT, &hdr, UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(hdr.hdr_crc) != crc) { + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + if (v) + printf(": bad CRC %#08x, should be %#08x\n", + crc, be32_to_cpu(hdr.hdr_crc)); + continue; + } + + ec = be64_to_cpu(hdr.ec); + if (ec > EC_MAX) { + if (pr) + printf("\n"); + errmsg("erase counter in EB %d is %llu, while this " + "program expects them to be less than %u", + eb, ec, EC_MAX); + goto out_ec; + } + + if (si->vid_hdr_offs == -1) { + si->vid_hdr_offs = be32_to_cpu(hdr.vid_hdr_offset); + si->data_offs = be32_to_cpu(hdr.data_offset); + if (si->data_offs % mtd->min_io_size) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("bad data offset %d at eraseblock %d (n" + "of multiple of min. I/O unit size %d)", + si->data_offs, eb, mtd->min_io_size); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + + } + } else { + if ((int)be32_to_cpu(hdr.vid_hdr_offset) != si->vid_hdr_offs) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("inconsistent VID header offset: was " + "%d, but is %d in eraseblock %d", + si->vid_hdr_offs, + be32_to_cpu(hdr.vid_hdr_offset), eb); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + } + if ((int)be32_to_cpu(hdr.data_offset) != si->data_offs) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("inconsistent data offset: was %d, but" + " is %d in eraseblock %d", + si->data_offs, + be32_to_cpu(hdr.data_offset), eb); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + } + } + + si->ok_cnt += 1; + si->ec[eb] = ec; + if (v) + printf(": OK, erase counter %u\n", si->ec[eb]); + } + + if (si->ok_cnt != 0) { + /* Calculate mean erase counter */ + for (eb = 0; eb < mtd->eb_cnt; eb++) { + if (si->ec[eb] > EC_MAX) + continue; + sum += si->ec[eb]; + } + si->mean_ec = sum / si->ok_cnt; + } + + si->good_cnt = mtd->eb_cnt - si->bad_cnt; + verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " + "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, + si->empty_cnt, si->alien_cnt, si->bad_cnt); + + *info = si; + if (pr) + printf("\n"); + return 0; + +out_ec: + free(si->ec); +out_si: + free(si); + *info = NULL; + return -1; +} + +void ubi_scan_free(struct ubi_scan_info *si) +{ + free(si->ec); + free(si); +} diff --git a/new-utils/src/libubi.c b/new-utils/src/libubi.c new file mode 100644 index 0000000..8c4f13d --- /dev/null +++ b/new-utils/src/libubi.c @@ -0,0 +1,1187 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#include "libubi_int.h" +#include "common.h" +#include +#include + +#define PROGRAM_NAME "libubi" + +/** + * mkpath - compose full path from 2 given components. + * @path: the first component + * @name: the second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) +{ + char *n; + int len1 = strlen(path); + int len2 = strlen(name); + + n = malloc(len1 + len2 + 2); + if (!n) { + sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); + return NULL; + } + + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; + + memcpy(n + len1, name, len2 + 1); + return n; +} + +/** + * read_positive_ll - read a positive 'long long' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function reads file @file and interprets its contents as a positive + * 'long long' integer. If this is not true, it fails with %EINVAL error code. + * Returns %0 in case of success and %-1 in case of failure. + */ +static int read_positive_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, 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) { + /* This must be a UBI bug */ + 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; + } + + /* 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_v, minor_v; + 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_v = major(st.st_rdev); + minor_v = minor(st.st_rdev); + + if (minor_v == 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_v) + break; + } + + if (i > info.highest_dev_num) { + errno = ENODEV; + return -1; + } + + /* Make sure this UBI volume exists */ + sprintf(file, lib->ubi_vol, i, minor_v - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + errno = ENODEV; + return -1; + } + + *dev_num = i; + *vol_id = minor_v - 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 + * + * This function returns positive UBI device number 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_v, minor_v; + + 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_v = major(st.st_rdev); + minor_v = minor(st.st_rdev); + + if (minor_v != 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_v) { + 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(int required) +{ + int fd, version; + struct libubi *lib; + + lib = calloc(1, sizeof(struct libubi)); + if (!lib) + return NULL; + + /* TODO: this must be discovered instead */ + lib->sysfs = strdup("/sys"); + if (!lib->sysfs) + goto out_error; + + lib->sysfs_ctrl = mkpath(lib->sysfs, 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(lib->sysfs, SYSFS_UBI); + if (!lib->sysfs_ubi) + goto out_error; + + /* Make sure UBI is present */ + fd = open(lib->sysfs_ubi, O_RDONLY); + if (fd == -1) { + if (required) + errmsg("cannot open \"%s\", UBI does not seem to " + "exist in system", lib->sysfs_ubi); + 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->sysfs); + free(lib); +} + +int ubi_attach_mtd(libubi_t desc, const char *node, + struct ubi_attach_request *req) +{ + int fd, ret; + struct ubi_attach_req r; + + memset(&r, sizeof(struct ubi_attach_req), '\0'); + + desc = desc; + r.ubi_num = req->dev_num; + r.mtd_num = req->mtd_num; + r.vid_hdr_offset = req->vid_hdr_offset; + + fd = open(node, O_RDONLY); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + ret = ioctl(fd, UBI_IOCATT, &r); + close(fd); + if (ret == -1) + return -1; + + req->dev_num = r.ubi_num; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + + return ret; +} + +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) +{ + int ret, ubi_dev; + + ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); + if (ret == -1) { + errno = ENODEV; + return ret; + } + + return ubi_remove_dev(desc, node, ubi_dev); +} + +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) +{ + int fd, ret; + + desc = desc; + + fd = open(node, O_RDONLY); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + ret = ioctl(fd, UBI_IOCDET, &ubi_dev); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_node_type(libubi_t desc, const char *node) +{ + struct stat st; + struct ubi_info info; + int i, fd, major_v, minor_v; + 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_v = major(st.st_rdev); + minor_v = 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_v) + break; + } + + if (i > info.highest_dev_num) + goto out_not_ubi; + + if (minor_v == 0) + return 1; + + /* This is supposdely an UBI volume device node */ + sprintf(file, lib->ubi_vol, i, minor_v - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", node); + sys_errmsg("cannot access \"%s\"", file); + return -1; + } + + return 2; + +out_not_ubi: + errmsg("\"%s\" has major:minor %d:%d, but this does not correspond to " + "any UBI device or volume", node, major_v, minor_v); + errno = 0; + 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) > 256) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_ubi, dirent->d_name); + 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, sizeof(struct ubi_mkvol_req), '\0'); + + desc = desc; + r.vol_id = req->vol_id; + r.alignment = req->alignment; + r.bytes = req->bytes; + r.vol_type = req->vol_type; + + n = strlen(req->name); + if (n > UBI_MAX_VOLUME_NAME) + return -1; + + strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); + r.name_len = n; + + fd = open(node, O_RDONLY); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + ret = ioctl(fd, UBI_IOCMKVOL, &r); + if (ret == -1) + goto out_close; + + req->vol_id = r.vol_id; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +{ + int fd, ret; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ + int fd, ret; + struct ubi_rsvol_req req; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return 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_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; + + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_vol_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) > 256) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_ubi, dirent->d_name); + goto out_close; + } + + ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); + if (ret == 2 && devno == dev_num) { + info->vol_count += 1; + if (vol_id > info->highest_vol_id) + info->highest_vol_id = vol_id; + if (vol_id < info->lowest_vol_id) + info->lowest_vol_id = vol_id; + } + } + + if (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + goto out_close; + } + + if (closedir(sysfs_ubi)) + return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + + if (info->lowest_vol_id == INT_MAX) + info->lowest_vol_id = 0; + + if (dev_get_major(lib, dev_num, &info->major, &info->minor)) + return -1; + + if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) + return -1; + if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) + return -1; + if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) + return -1; + if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size)) + return -1; + if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) + return -1; + if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) + return -1; + if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) + return -1; + if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) + return -1; + + info->avail_bytes = info->avail_lebs * info->leb_size; + info->total_bytes = 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 dev_num; + struct libubi *lib = (struct libubi *)desc; + + if (dev_node2num(lib, node, &dev_num)) + return -1; + + return ubi_get_dev_info1(desc, dev_num, info); +} + +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info) +{ + int ret; + struct libubi *lib = (struct libubi *)desc; + char buf[50]; + + memset(info, '\0', sizeof(struct ubi_vol_info)); + info->dev_num = dev_num; + info->vol_id = vol_id; + + if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor)) + return -1; + if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) + return -1; + + ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); + if (ret < 0) + return -1; + + if (strncmp(buf, "static\n", ret) == 0) + info->type = UBI_STATIC_VOLUME; + else if (strncmp(buf, "dynamic\n", ret) == 0) + info->type = UBI_DYNAMIC_VOLUME; + else { + errmsg("bad value at \"%s\"", buf); + errno = EINVAL; + return -1; + } + + ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, + &info->alignment); + if (ret) + return -1; + ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, + &info->data_bytes); + if (ret) + return -1; + ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs); + if (ret) + return -1; + ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size); + if (ret) + return -1; + ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, + &info->corrupted); + if (ret) + return -1; + info->rsvd_bytes = 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 vol_id, dev_num; + struct libubi *lib = (struct libubi *)desc; + + 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; +} diff --git a/new-utils/src/libubi_int.h b/new-utils/src/libubi_int.h new file mode 100644 index 0000000..2e664b8 --- /dev/null +++ b/new-utils/src/libubi_int.h @@ -0,0 +1,133 @@ +/* + * 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__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The below are pre-define UBI file and directory names. + * + * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. + * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is + * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' + * directories to '/sys/class/ubi/'. For now libubi assumes old layout. + */ + +#define SYSFS_UBI "class/ubi" +#define SYSFS_CTRL "class/misc/ubi_ctrl/" + +#define CTRL_DEV "dev" + +#define UBI_VER "version" +#define UBI_DEV_NAME_PATT "ubi%d" + +#define DEV_DEV "dev" +#define DEV_AVAIL_EBS "avail_eraseblocks" +#define DEV_TOTAL_EBS "total_eraseblocks" +#define DEV_BAD_COUNT "bad_peb_count" +#define DEV_EB_SIZE "eraseblock_size" +#define DEV_MAX_EC "max_ec" +#define DEV_MAX_RSVD "reserved_for_bad" +#define DEV_MAX_VOLS "max_vol_count" +#define DEV_MIN_IO_SIZE "min_io_size" +#define DEV_MTD_NUM "mtd_num" + +#define UBI_VOL_NAME_PATT "ubi%d_%d" +#define VOL_TYPE "type" +#define VOL_DEV "dev" +#define VOL_ALIGNMENT "alignment" +#define VOL_DATA_BYTES "data_bytes" +#define VOL_RSVD_EBS "reserved_ebs" +#define VOL_EB_SIZE "usable_eb_size" +#define VOL_CORRUPTED "corrupted" +#define VOL_NAME "name" + +/** + * libubi - UBI library description data structure. + * @sysfs: sysfs file system path + * @sysfs_ctrl: UBI control device directory in sysfs + * @ctrl_dev: UBI control device major/minor numbers sysfs file + * @sysfs_ubi: UBI directory in sysfs + * @ubi_dev: UBI device sysfs directory pattern + * @ubi_version: UBI version file sysfs path + * @dev_dev: UBI device major/minor numbers file pattern + * @dev_avail_ebs: count of available eraseblocks sysfs path pattern + * @dev_total_ebs: total eraseblocks count sysfs path pattern + * @dev_bad_count: count of bad eraseblocks sysfs path pattern + * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern + * @dev_max_ec: maximum erase counter sysfs path pattern + * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks + * handling + * @dev_max_vols: maximum volumes number count sysfs path pattern + * @dev_min_io_size: minimum I/O unit size sysfs path pattern + * @ubi_vol: UBI volume sysfs directory pattern + * @vol_type: volume type sysfs path pattern + * @vol_dev: volume major/minor numbers file pattern + * @vol_alignment: volume alignment sysfs path pattern + * @vol_data_bytes: volume data size sysfs path pattern + * @vol_rsvd_ebs: volume reserved size sysfs path pattern + * @vol_eb_size: volume eraseblock size sysfs path pattern + * @vol_corrupted: volume corruption flag sysfs path pattern + * @vol_name: volume name sysfs path pattern + */ +struct libubi +{ + char *sysfs; + char *sysfs_ctrl; + char *ctrl_dev; + char *sysfs_ubi; + char *ubi_dev; + char *ubi_version; + char *dev_dev; + char *dev_avail_ebs; + char *dev_total_ebs; + char *dev_bad_count; + char *dev_eb_size; + char *dev_max_ec; + char *dev_bad_rsvd; + char *dev_max_vols; + char *dev_min_io_size; + char *dev_mtd_num; + char *ubi_vol; + char *vol_type; + char *vol_dev; + char *vol_alignment; + char *vol_data_bytes; + char *vol_rsvd_ebs; + char *vol_eb_size; + char *vol_corrupted; + char *vol_name; + char *vol_max_count; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_INT_H__ */ diff --git a/new-utils/src/libubigen.c b/new-utils/src/libubigen.c new file mode 100644 index 0000000..70ef0ca --- /dev/null +++ b/new-utils/src/libubigen.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Generating UBI images. + * + * Authors: Oliver Lohmann + * Artem Bityutskiy + */ + +#include +#include +#include +#include + +#include +#include +#include +#include "crc32.h" +#include "common.h" + +#define PROGRAM_NAME "libubigen" + +/** + * ubigen_info_init - initialize libubigen. + * @ui: libubigen information + * @peb_size: flash physical eraseblock size + * @min_io_size: flash minimum input/output unit size + * @subpage_size: flash sub-page, if present (has to be equivalent to + * @min_io_size if does not exist) + * @vid_hdr_offs: offset of the VID header + * @ubi_ver: UBI version + */ +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) +{ + 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->max_volumes = ui->leb_size / UBI_VTBL_RECORD_SIZE; + if (ui->max_volumes > UBI_MAX_VOLUMES) + ui->max_volumes = UBI_MAX_VOLUMES; + ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE; +} + +/** + * ubigen_create_empty_vtbl - creates empty volume table. + * + * This function creates an empty volume table and returns a pointer to it in + * case of success and %NULL in case of failure. The returned object has to be + * freed with 'free()' call. + */ +struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui) +{ + struct ubi_vtbl_record *vtbl; + int i; + + vtbl = calloc(1, ui->vtbl_size); + if (!vtbl) { + sys_errmsg("cannot allocate %d bytes of memory", ui->vtbl_size); + return NULL; + } + + for (i = 0; i < ui->max_volumes; i++) { + uint32_t crc = crc32(UBI_CRC32_INIT, &vtbl[i], + UBI_VTBL_RECORD_SIZE_CRC); + vtbl[i].crc = cpu_to_be32(crc); + } + + return vtbl; +} + +/** + * ubigen_add_volume - add a volume to the volume table. + * @ui: libubigen information + * @vi: volume information + * @vtbl: volume table to add to + * + * This function adds volume described by input parameters to the volume table + * @vtbl. + */ +int ubigen_add_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, + struct ubi_vtbl_record *vtbl) +{ + struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id]; + uint32_t tmp; + + if (vi->id >= ui->max_volumes) + return errmsg("too high volume id %d, max. volumes is %d", + vi->id, ui->max_volumes); + + if (vi->alignment >= ui->leb_size) + return errmsg("too large alignment %d, max is %d (LEB size)", + vi->alignment, ui->leb_size); + + memset(vtbl_rec, '\0', sizeof(struct ubi_vtbl_record)); + tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size; + vtbl_rec->reserved_pebs = cpu_to_be32(tmp); + vtbl_rec->alignment = cpu_to_be32(vi->alignment); + vtbl_rec->vol_type = vi->type; + tmp = ui->leb_size % vi->alignment; + vtbl_rec->data_pad = cpu_to_be32(tmp); + vtbl_rec->flags = vi->flags; + + memcpy(vtbl_rec->name, vi->name, vi->name_len); + vtbl_rec->name[vi->name_len] = '\0'; + vtbl_rec->name_len = cpu_to_be16(vi->name_len); + + tmp = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_be32(tmp); + return 0; +} + +/** + * ubigen_init_ec_hdr - initialize EC header. + * @ui: libubigen information + * @hdr: the EC header to initialize + * @ec: erase counter value + */ +void ubigen_init_ec_hdr(const struct ubigen_info *ui, + struct ubi_ec_hdr *hdr, long long ec) +{ + uint32_t crc; + + memset(hdr, '\0', sizeof(struct ubi_ec_hdr)); + + hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); + hdr->version = ui->ubi_ver; + hdr->ec = cpu_to_be64(ec); + hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs); + + hdr->data_offset = cpu_to_be32(ui->data_offs); + + crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); + hdr->hdr_crc = cpu_to_be32(crc); +} + +/** + * init_vid_hdr - initialize VID header. + * @ui: libubigen information + * @vi: volume information + * @hdr: the VID header to initialize + * @lnum: logical eraseblock number + * @data: the contents of the LEB (static volumes only) + * @data_size: amount of data in this LEB (static volumes only) + * + * Note, @used_ebs, @data and @data_size are ignored in case of dynamic + * volumes. + */ +static void init_vid_hdr(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, + struct ubi_vid_hdr *hdr, int lnum, + const void *data, int data_size) +{ + uint32_t crc; + + memset(hdr, '\0', sizeof(struct ubi_vid_hdr)); + + hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); + hdr->version = ui->ubi_ver; + hdr->vol_type = vi->type; + hdr->vol_id = cpu_to_be32(vi->id); + hdr->lnum = cpu_to_be32(lnum); + hdr->data_pad = cpu_to_be32(vi->data_pad); + hdr->compat = vi->compat; + + if (vi->type == UBI_VID_STATIC) { + hdr->data_size = cpu_to_be32(data_size); + hdr->used_ebs = cpu_to_be32(vi->used_ebs); + crc = crc32(UBI_CRC32_INIT, data, data_size); + hdr->data_crc = cpu_to_be32(crc); + } + + crc = crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC); + hdr->hdr_crc = cpu_to_be32(crc); +} + +/** + * ubigen_write_volume - write UBI volume. + * @ui: libubigen information + * @vi: volume information + * @ec: erase coutner value to put to EC headers + * @bytes: volume size in bytes + * @in: input file descriptor (has to be properly seeked) + * @out: output file descriptor + * + * This function reads the contents of the volume from the input file @in and + * writes the UBI volume to the output file @out. Returns zero on success and + * %-1 on failure. + */ +int ubigen_write_volume(const struct ubigen_info *ui, + const struct ubigen_vol_info *vi, long long ec, + long long bytes, int in, int out) +{ + int len = vi->usable_leb_size, rd, lnum = 0; + char inbuf[ui->leb_size], outbuf[ui->peb_size]; + + if (vi->id >= ui->max_volumes) + return errmsg("too high volume id %d, max. volumes is %d", + vi->id, ui->max_volumes); + + if (vi->alignment >= ui->leb_size) + return errmsg("too large alignment %d, max is %d (LEB size)", + vi->alignment, ui->leb_size); + + memset(outbuf, 0xFF, ui->data_offs); + ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec); + + while (bytes) { + int l; + struct ubi_vid_hdr *vid_hdr; + + if (bytes < len) + len = bytes; + bytes -= len; + + l = len; + do { + rd = read(in, inbuf + len - l, l); + if (rd != l) + return sys_errmsg("cannot read %d bytes from the input file", l); + + l -= rd; + } while (l); + + vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); + init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf, len); + + memcpy(outbuf + ui->data_offs, inbuf, len); + memset(outbuf + ui->data_offs + len, 0xFF, + ui->peb_size - ui->data_offs - len); + + if (write(out, outbuf, ui->peb_size) != ui->peb_size) + return sys_errmsg("cannot write %d bytes to the output file", ui->peb_size); + + lnum += 1; + } + + return 0; +} + +/** + * ubigen_write_layout_vol - write UBI layout volume + * @ui: libubigen information + * @peb1: physical eraseblock number to write the first volume table copy + * @peb2: physical eraseblock number to write the second volume table copy + * @ec1: erase counter value for @peb1 + * @ec2: erase counter value for @peb1 + * @vtbl: volume table + * @fd: output file descriptor seeked to the proper position + * + * This function creates the UBI layout volume which contains 2 copies of the + * volume table. Returns zero in case of success and %-1 in case of failure. + */ +int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, + long long ec1, long long ec2, + struct ubi_vtbl_record *vtbl, int fd) +{ + int ret; + struct ubigen_vol_info vi; + char outbuf[ui->peb_size]; + 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; + + 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) + return sys_errmsg("cannot seek output file"); + ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1); + init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0); + ret = write(fd, outbuf, ui->peb_size); + if (ret != ui->peb_size) + return sys_errmsg("cannot write %d bytes", ui->peb_size); + + seek = peb2 * ui->peb_size; + if (lseek(fd, seek, SEEK_SET) != seek) + return sys_errmsg("cannot seek output file"); + ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2); + init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0); + ret = write(fd, outbuf, ui->peb_size); + if (ret != ui->peb_size) + return sys_errmsg("cannot write %d bytes", ui->peb_size); + + return 0; +} diff --git a/new-utils/src/ubiattach.c b/new-utils/src/ubiattach.c new file mode 100644 index 0000000..1f72620 --- /dev/null +++ b/new-utils/src/ubiattach.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * An utility to attach MTD devices to UBI. + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubiattach" + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int mtdn; + int vidoffs; + const char *node; +}; + +static struct args args = { + .devn = UBI_DEV_NUM_AUTO, + .mtdn = -1, + .vidoffs = 0, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to attach MTD device to UBI."; + +static const char *optionsstr = +"-d, --devn= the number to assign to the newly created UBI device\n" +" (the number is assigned automatically if this is not\n" +" specified\n" +"-m, --mtdn= MTD device number to attach\n" +"-O, --vid-hdr-offset VID header offset (do not specify this unless you\n" +" really know what you do and the optimal defaults will\n" +" be used)\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-m ] [-d ]\n" +"\t\t[--mtdn=] [--devn ]\n" +"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - attach MTD device 0 (mtd0) to UBI\n" +"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI and\n" +" and create UBI device number 3 (ubi3)"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "m:d:O:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'm': + args.mtdn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.mtdn < 0) + return errmsg("bad MTD device number: \"%s\"", optarg); + + break; + + case 'O': + args.vidoffs = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vidoffs <= 0) + return errmsg("bad VID header offset: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("UBI control device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one UBI control device specified (use -h for help)"); + + if (args.mtdn == -1) + return errmsg("MTD device number was not specified (use -h for help)"); + + args.node = argv[optind]; + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_info ubi_info; + struct ubi_dev_info dev_info; + struct ubi_attach_request req; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + /* + * Make sure the kernel is fresh enough and this feature is supported. + */ + err = ubi_get_info(libubi, &ubi_info); + if (err) { + sys_errmsg("cannot get UBI information"); + goto out_libubi; + } + + if (ubi_info.ctrl_major == -1) { + errmsg("MTD attach/detach feature is not supported by your kernel"); + goto out_libubi; + } + + req.dev_num = args.devn; + req.mtd_num = args.mtdn; + req.vid_hdr_offset = args.vidoffs; + + err = ubi_attach_mtd(libubi, args.node, &req); + if (err) { + sys_errmsg("cannot attach mtd%d", args.mtdn); + goto out_libubi; + } + + /* Print some information about the new UBI device */ + err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info); + if (err) { + sys_errmsg("cannot get information about newly created UBI device"); + goto out_libubi; + } + + printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs); + ubiutils_print_bytes(dev_info.total_bytes, 0); + printf("), available %d LEBs (", dev_info.avail_lebs); + ubiutils_print_bytes(dev_info.avail_bytes, 0); + printf("), LEB size "); + ubiutils_print_bytes(dev_info.leb_size, 1); + printf("\n"); + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} diff --git a/new-utils/src/ubicrc32.c b/new-utils/src/ubicrc32.c new file mode 100644 index 0000000..4f5b32e --- /dev/null +++ b/new-utils/src/ubicrc32.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image. + * + * Author: Oliver Lohmann + */ + +#include +#include +#include +#include +#include +#include + +#include "crc32.h" +#include "common.h" + +#define BUFSIZE 4096 + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubicrc32" + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)"; + +static const char *optionsstr = +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-h] [--help]"; + +static const struct option long_options[] = { + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err = 0; + uint32_t crc = UBI_CRC32_INIT; + char buf[BUFSIZE]; + FILE *fp; + + if (argc > 1) { + fp = fopen(argv[1], "r"); + if (!fp) + return sys_errmsg("cannot open \"%s\"", argv[1]); + } else + fp = stdin; + + err = parse_opt(argc, argv); + if (err) + return err; + + while (!feof(fp)) { + size_t read; + + read = fread(buf, 1, BUFSIZE, fp); + if (ferror(fp)) { + sys_errmsg("cannot read input file"); + err = -1; + goto out_close; + } + crc = crc32(crc, buf, read); + } + + printf("0x%08x\n", crc); + +out_close: + if (fp != stdin) + fclose(fp); + return err; +} diff --git a/new-utils/src/ubidetach.c b/new-utils/src/ubidetach.c new file mode 100644 index 0000000..50670d0 --- /dev/null +++ b/new-utils/src/ubidetach.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * An utility to delete UBI devices (detach MTD devices from UBI). + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubidetach" + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int mtdn; + const char *node; +}; + +static struct args args = { + .devn = UBI_DEV_NUM_AUTO, + .mtdn = -1, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION +" - a tool to remove UBI devices (detach MTD devices from UBI)"; + +static const char *optionsstr = +"-d, --devn= UBI device number to delete\n" +"-m, --mtdn= or altrnatively, MTD device number to detach -\n" +" this will delete corresponding UBI device\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-d ] [-m ]\n" +"\t\t[--devn ] [--mtdn=]\n" +"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -d 2 - delete UBI device 2 (ubi2)\n" +"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - detach MTD device 0 (mtd0)"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "m:d:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'm': + args.mtdn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.mtdn < 0) + return errmsg("bad MTD device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("UBI control device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one UBI control device specified (use -h for help)"); + + if (args.mtdn == -1 && args.devn == -1) + return errmsg("neither MTD nor UBI devices were specified (use -h for help)"); + + if (args.mtdn != -1 && args.devn != -1) + return errmsg("specify either MTD or UBI device (use -h for help)"); + + args.node = argv[optind]; + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + struct ubi_info ubi_info; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + /* + * Make sure the kernel is fresh enough and this feature is supported. + */ + err = ubi_get_info(libubi, &ubi_info); + if (err) { + sys_errmsg("cannot get UBI information"); + goto out_libubi; + } + + if (ubi_info.ctrl_major == -1) { + errmsg("MTD detach/detach feature is not supported by your kernel"); + goto out_libubi; + } + + if (args.devn != -1) { + err = ubi_remove_dev(libubi, args.node, args.devn); + if (err) { + sys_errmsg("cannot remove ubi%d", args.devn); + goto out_libubi; + } + } else { + err = ubi_detach_mtd(libubi, args.node, args.mtdn); + if (err) { + sys_errmsg("cannot detach mtd%d", args.mtdn); + goto out_libubi; + } + } + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} + diff --git a/new-utils/src/ubiformat.c b/new-utils/src/ubiformat.c new file mode 100644 index 0000000..0074c7a --- /dev/null +++ b/new-utils/src/ubiformat.c @@ -0,0 +1,780 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "crc32.h" +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubiformat" + +/* The variables below are set by command line arguments */ +struct args { + unsigned int yes:1; + unsigned int quiet:1; + unsigned int verbose:1; + unsigned int override_ec:1; + unsigned int novtbl:1; + int subpage_size; + int vid_hdr_offs; + int ubi_ver; + off_t image_sz; + long long ec; + const char *image; + const char *node; +}; + +static struct args args = +{ + .ubi_ver = 1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to format MTD devices and flash UBI images"; + +static const char *optionsstr = +"-s, --sub-page-size= 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" +"-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" +"-x, --ubi-ver= UBI version number to put to EC headers\n" +" (default is 1)\n" +"-h, -?, --help print help message\n" +"-V, --version print program version\n"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-h] [-V] [-y] [-q] [-v]\n" +"\t\t\t[-x ] [-E ] [-s ] [-O ] [-n]\n" +"\t\t\t[--help] [--version] [--yes] [--verbose] [--quiet]\n" +"\t\t\t[--ec=] [--vid-hdr-offset=]\n" +"\t\t\t[--ubi-ver=] [--no-volume-table]\n" +"\t\t\t[--flash-image=] [--image-size=]\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[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "nh?Vyqve:x:s:O:f:S:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 's': + args.subpage_size = ubiutils_get_bytes(optarg); + if (args.subpage_size <= 0) + return errmsg("bad sub-page size: \"%s\"", optarg); + if (!is_power_of_2(args.subpage_size)) + return errmsg("sub-page size should be power of 2"); + break; + + case 'O': + args.vid_hdr_offs = strtoul(optarg, &endp, 0); + if (args.vid_hdr_offs <= 0 || *endp != '\0' || endp == optarg) + return errmsg("bad VID header offset: \"%s\"", optarg); + break; + + case 'e': + args.ec = strtoull(optarg, &endp, 0); + if (args.ec <= 0 || *endp != '\0' || endp == optarg) + return errmsg("bad erase counter value: \"%s\"", optarg); + if (args.ec >= EC_MAX) + return errmsg("too high erase %llu, counter, max is %u", args.ec, EC_MAX); + args.override_ec = 1; + break; + + case 'f': + args.image = optarg; + break; + + case 'S': + args.image_sz = ubiutils_get_bytes(optarg); + if (args.image_sz <= 0) + return errmsg("bad image-size: \"%s\"", optarg); + break; + + case 'n': + args.novtbl = 1; + break; + + case 'y': + args.yes = 1; + break; + + case 'q': + args.quiet = 1; + break; + + case 'x': + args.ubi_ver = strtoul(optarg, &endp, 0); + if (args.ubi_ver < 0 || *endp != '\0' || endp == optarg) + return errmsg("bad UBI version: \"%s\"", optarg); + break; + + case 'v': + args.verbose = 1; + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (args.quiet && args.verbose) + return errmsg("using \"-q\" and \"-v\" at the same time does not make sense"); + + if (optind == argc) + return errmsg("MTD device name was not specified (use -h for help)"); + else if (optind != argc - 1) + return errmsg("more then one MTD device specified (use -h for help)"); + + if (args.image && args.novtbl) + return errmsg("-n cannot be used together with -f"); + + args.node = argv[optind]; + return 0; +} + +static int want_exit(void) +{ + char buf[4]; + + while (1) { + normsg_cont("continue? (yes/no) "); + if (scanf("%3s", buf) == EOF) { + sys_errmsg("scanf returned unexpected EOF, assume \"yes\""); + return 1; + } + if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) + return 0; + if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) + return 1; + } +} + +static int answer_is_yes(void) +{ + char buf[4]; + + while (1) { + if (scanf("%3s", buf) == EOF) { + sys_errmsg("scanf returned unexpected EOF, assume \"no\""); + return 0; + } + if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) + return 1; + if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) + return 0; + } +} + +static void print_bad_eraseblocks(const struct mtd_info *mtd, + const struct ubi_scan_info *si) +{ + int first = 1, eb; + + if (si->bad_cnt == 0) + return; + + normsg_cont("bad eraseblocks: "); + 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_ec(struct ubi_ec_hdr *hdr, long long ec) +{ + uint32_t crc; + + /* Check the EC header */ + if (be32_to_cpu(hdr->magic) != UBI_EC_HDR_MAGIC) + return errmsg("bad UBI magic %#08x, should be %#08x", + be32_to_cpu(hdr->magic), UBI_EC_HDR_MAGIC); + + crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(hdr->hdr_crc) != crc) + return errmsg("bad CRC %#08x, should be %#08x\n", + crc, be32_to_cpu(hdr->hdr_crc)); + + hdr->ec = cpu_to_be64(ec); + crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); + hdr->hdr_crc = cpu_to_be32(crc); + + return 0; +} + +static int drop_ffs(const struct mtd_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; +} + +static int flash_image(const struct mtd_info *mtd, struct ubi_scan_info *si) +{ + int fd, img_ebs, eb, written_ebs = 0, divisor; + off_t st_size; + + fd = open_file(&st_size); + if (fd < 0) + return fd; + + img_ebs = st_size / mtd->eb_size; + + if (img_ebs > si->good_cnt) { + sys_errmsg("file \"%s\" is too large (%lld bytes)", + args.image, (long long)st_size); + goto out_close; + } + + if (st_size % mtd->eb_size) { + return sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of eraseblock size (%d bytes)", + args.image, (long long)st_size, mtd->eb_size); + goto out_close; + } + + verbose(args.verbose, "will write %d eraseblocks", img_ebs); + divisor = img_ebs; + for (eb = 0; eb < mtd->eb_cnt; eb++) { + int err, new_len; + char buf[mtd->eb_size]; + long long ec; + + if (!args.quiet && !args.verbose) { + printf("\r" PROGRAM_NAME ": flashing eraseblock %d -- %2lld %% complete ", + eb, (long long)(eb + 1) * 100 / divisor); + fflush(stdout); + } + + if (si->ec[eb] == EB_BAD) { + divisor += 1; + continue; + } + + if (args.verbose) { + normsg_cont("eraseblock %d: erase", eb); + fflush(stdout); + } + + err = mtd_erase(mtd, eb); + if (err) { + sys_errmsg("failed to erase eraseblock %d", eb); + goto out_close; + } + + err = read_all(fd, buf, mtd->eb_size); + if (err) { + sys_errmsg("failed to read eraseblock %d from \"%s\"", + written_ebs, args.image); + goto out_close; + } + + + if (si->ec[eb] <= EC_MAX) + ec = si->ec[eb] + 1; + else if (!args.override_ec) + ec = si->mean_ec; + else + ec = args.ec; + + if (args.verbose) { + printf(", change EC to %lld", ec); + fflush(stdout); + } + + err = change_ec((struct ubi_ec_hdr *)buf, ec); + if (err) { + errmsg("bad EC header at eraseblock %d of \"%s\"", + written_ebs, args.image); + goto out_close; + } + + if (args.verbose) { + printf(", write data\n"); + fflush(stdout); + } + + new_len = drop_ffs(mtd, buf, mtd->eb_size); + + err = mtd_write(mtd, eb, 0, buf, new_len); + if (err) { + sys_errmsg("cannot write eraseblock %d", eb); + goto out_close; + } + if (++written_ebs >= img_ebs) + break; + } + + if (!args.quiet && !args.verbose) + printf("\n"); + close(fd); + return eb + 1; + +out_close: + close(fd); + return -1; +} + +static int format(const struct mtd_info *mtd, const struct ubigen_info *ui, + const 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 (si->ec[eb] <= EC_MAX) + ec = si->ec[eb] + 1; + else if (!args.override_ec) + ec = si->mean_ec; + else + ec = args.ec; + ubigen_init_ec_hdr(ui, hdr, ec); + + if (args.verbose) { + normsg_cont("eraseblock %d: erase", eb); + fflush(stdout); + } + + err = mtd_erase(mtd, eb); + if (err) { + if (!args.quiet) + printf("\n"); + sys_errmsg("failed to erase eraseblock %d", eb); + goto out_free; + } + + if ((eb1 == -1 || eb2 == -1) && !novtbl) { + if (eb1 == -1) { + eb1 = eb; + ec1 = ec; + } else if (eb2 == -1) { + eb2 = eb; + ec2 = ec; + } + if (args.verbose) + printf(", do not write EC, leave for vtbl\n"); + continue; + } + + if (args.verbose) { + printf(", write EC %lld\n", ec); + fflush(stdout); + } + + err = mtd_write(mtd, eb, 0, hdr, write_size); + if (err) { + if (!args.quiet && !args.verbose) + printf("\n"); + sys_errmsg("cannot write EC header (%d bytes buffer) to eraseblock %d", + write_size, eb); + if (args.subpage_size != mtd->min_io_size) + normsg("may be %d is incorrect?", args.subpage_size); + goto out_free; + } + } + + 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, mtd->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; + struct mtd_info mtd; + libubi_t libubi; + struct ubigen_info ui; + struct ubi_scan_info *si; + + err = parse_opt(argc, argv); + if (err) + return -1; + + err = mtd_get_info(args.node, &mtd); + if (err) + return errmsg("cannot get information about \"%s\"", args.node); + + if (args.subpage_size == 0) + args.subpage_size = mtd.min_io_size; + else { + 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; + } + } + + /* 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; + } + } + + /* + * Because of MTD interface limitations 'mtd_get_info()' cannot get + * sub-page so we force the user to pass it via the command line. Let's + * hope the user passed us something sane. + */ + mtd.subpage_size = args.subpage_size; + + if (mtd.rdonly) { + errmsg("mtd%d (%s) is a read-only device", mtd.num, args.node); + goto out_close; + } + + /* Make sure this MTD device is not attached to UBI */ + libubi = libubi_open(0); + if (libubi) { + int ubi_dev_num; + + err = mtd_num2ubi_dev(libubi, mtd.num, &ubi_dev_num); + libubi_close(libubi); + if (!err) { + errmsg("please, first detach mtd%d (%s) from ubi%d", + mtd.num, args.node, ubi_dev_num); + goto out_close; + } + } + + if (!args.quiet) { + normsg_cont("mtd%d (%s), size ", mtd.num, mtd.type_str); + ubiutils_print_bytes(mtd.size, 1); + printf(", %d eraseblocks of ", mtd.eb_size); + 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, &si, verbose); + if (err) { + errmsg("failed to scan mtd%d (%s)", 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.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, args.subpage_size, + args.vid_hdr_offs, args.ubi_ver); + + 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 calculated 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) ", + si->vid_hdr_offs, si->data_offs); + } + if (args.yes || answer_is_yes()) { + if (args.yes && !args.quiet) + printf("yes\n"); + } else { + ui.vid_hdr_offs = si->vid_hdr_offs; + ui.data_offs = si->data_offs; + } + } + + if (args.image) { + err = flash_image(&mtd, si); + if (err < 0) + goto out_free; + + err = format(&mtd, &ui, si, err, 1); + if (err) + goto out_free; + } else { + err = format(&mtd, &ui, si, 0, args.novtbl); + if (err) + goto out_free; + } + + ubi_scan_free(si); + close(mtd.fd); + return 0; + +out_free: + ubi_scan_free(si); +out_close: + close(mtd.fd); + return -1; +} diff --git a/new-utils/src/ubimkvol.c b/new-utils/src/ubimkvol.c new file mode 100644 index 0000000..820c9d8 --- /dev/null +++ b/new-utils/src/ubimkvol.c @@ -0,0 +1,313 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubimkvol" + +/* The variables below are set by command line arguments */ +struct args { + int vol_id; + int vol_type; + long long bytes; + int lebs; + int alignment; + const char *name; + const char *node; + int maxavs; + /* For deprecated -d option handling */ + int devn; + char dev_name[256]; +}; + +static struct args args = { + .vol_type = UBI_DYNAMIC_VOLUME, + .bytes = -1, + .lebs = -1, + .alignment = 1, + .vol_id = UBI_VOL_NUM_AUTO, + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_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\n\n" +"The following is a compatibility option which is deprecated, do not use it\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\""; + + +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' }, + /* Deprecated -d option */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { NULL, 0, NULL, 0}, +}; + +static int param_sanity_check(void) +{ + int len; + + if (args.bytes == -1 && !args.maxavs && args.lebs == -1) + return errmsg("volume size was not specified (use -h for help)"); + + if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) || + (args.lebs != -1 && (args.maxavs || args.bytes != -1)) || + (args.maxavs && (args.bytes != -1 || args.lebs != -1))) + return errmsg("size specified with more then one option"); + + if (args.name == NULL) + return errmsg("volume name was not specified (use -h for help)"); + + len = strlen(args.name); + if (len > UBI_MAX_VOLUME_NAME) + return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME); + + return 0; +} + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "a:n:N:s:S:t:h?Vmd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 't': + if (!strcmp(optarg, "dynamic")) + args.vol_type = UBI_DYNAMIC_VOLUME; + else if (!strcmp(optarg, "static")) + args.vol_type = UBI_STATIC_VOLUME; + else + return errmsg("bad volume type: \"%s\"", optarg); + break; + + case 's': + args.bytes = ubiutils_get_bytes(optarg); + if (args.bytes <= 0) + return errmsg("bad volume size: \"%s\"", optarg); + break; + + case 'S': + args.lebs = strtoull(optarg, &endp, 0); + if (endp == optarg || args.lebs <= 0 || *endp != '\0') + return errmsg("bad LEB count: \"%s\"", optarg); + break; + + case 'a': + args.alignment = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.alignment <= 0) + return errmsg("bad volume alignment: \"%s\"", optarg); + break; + + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) + return errmsg("bad volume ID: " "\"%s\"", optarg); + break; + + case 'd': + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + + case 'N': + args.name = optarg; + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case 'm': + args.maxavs = 1; + break; + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + 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(1); + if (!libubi) + return sys_errmsg("cannot open libubi"); + + err = ubi_node_type(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) { + errmsg("\"%s\" is not an UBI device node", args.node); + goto out_libubi; + } + + err = ubi_get_dev_info(libubi, args.node, &dev_info); + if (err) { + sys_errmsg("cannot get information about UBI device \"%s\"", + args.node); + goto out_libubi; + } + + if (dev_info.avail_bytes == 0) { + errmsg("UBI device does not have free logical eraseblocks"); + goto out_libubi; + } + + if (args.maxavs) { + args.bytes = dev_info.avail_bytes; + printf("Set volume size to %lld\n", args.bytes); + } + + if (args.lebs != -1) { + args.bytes = dev_info.leb_size; + args.bytes -= dev_info.leb_size % args.alignment; + args.bytes *= args.lebs; + } + + req.vol_id = args.vol_id; + req.alignment = args.alignment; + req.bytes = args.bytes; + req.vol_type = args.vol_type; + req.name = args.name; + + err = ubi_mkvol(libubi, args.node, &req); + if (err < 0) { + sys_errmsg("cannot UBI create volume"); + goto out_libubi; + } + + args.vol_id = req.vol_id; + + /* Print information about the created device */ + err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info); + if (err) { + sys_errmsg("cannot get information about newly created UBI volume"); + goto out_libubi; + } + + printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf("), LEB size "); + ubiutils_print_bytes(vol_info.leb_size, 1); + printf(", %s, name \"%s\", alignment %d\n", + req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", + vol_info.name, vol_info.alignment); + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} diff --git a/new-utils/src/ubinfo.c b/new-utils/src/ubinfo.c new file mode 100644 index 0000000..536ec01 --- /dev/null +++ b/new-utils/src/ubinfo.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2007, 2008 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * An utility to get UBI information. + * + * Author: Artem Bityutskiy + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubinfo" + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int vol_id; + int all; + const char *node; +}; + +static struct args args = { + .vol_id = -1, + .devn = -1, + .all = 0, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to print UBI information."; + +static const char *optionsstr = +"-d, --devn= UBI device number to get information about\n" +"-n, --vol_id= ID of UBI volume to print information about\n" +"-a, --all print information about all devices and volumes,\n" +" or about all volumes if the UBI device was\n" +" specified\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage 1: " PROGRAM_NAME " [-d ] [-n ] [-a] [-h] [-V] [--vol_id=]\n" +"\t\t[--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 = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "an:d:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'a': + args.all = 1; + break; + + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) + return errmsg("bad volume ID: " "\"%s\"", optarg); + break; + + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc - 1) + args.node = argv[optind]; + else if (optind < argc) + return errmsg("more then one UBI devices specified (use -h for help)"); + + return 0; +} + +static int translate_dev(libubi_t libubi, const char *node) +{ + int err; + + err = ubi_node_type(libubi, node); + if (err == -1) { + if (errno) + return errmsg("unrecognized device node \"%s\"", node); + return errmsg("\"%s\" does not correspond to any UBI device or volume", node); + } + + if (err == 1) { + struct ubi_dev_info dev_info; + + err = ubi_get_dev_info(libubi, node, &dev_info); + if (err) + return sys_errmsg("cannot get information about UBI device \"%s\"", node); + + args.devn = dev_info.dev_num; + } else { + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info(libubi, node, &vol_info); + if (err) + return sys_errmsg("cannot get information about UBI volume \"%s\"", node); + + if (args.vol_id != -1) + return errmsg("both volume character device node (\"%s\") and " + "volume ID (%d) are specify, use only one of them" + "(use -h for help)", node, args.vol_id); + + args.devn = vol_info.dev_num; + args.vol_id = vol_info.vol_id; + } + + return 0; +} + +static int print_vol_info(libubi_t libubi, int dev_num, int vol_id) +{ + int err; + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info); + if (err) + return sys_errmsg("cannot get information about UBI volume %d on ubi%d", + vol_id, dev_num); + + printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num); + printf("Type: %s\n", + vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"); + printf("Alignment: %d\n", vol_info.alignment); + + printf("Size: %d LEBs (", vol_info.rsvd_lebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf(")\n"); + + if (vol_info.type == UBI_STATIC_VOLUME) { + printf("Data bytes: "); + ubiutils_print_bytes(vol_info.data_bytes, 1); + printf("\n"); + } + printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK"); + printf("Name: %s\n", vol_info.name); + printf("Character device major/minor: %d:%d\n", + vol_info.major, vol_info.minor); + + return 0; +} + +static int print_dev_info(libubi_t libubi, int dev_num, int all) +{ + int i, err, first = 1; + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + + err = ubi_get_dev_info1(libubi, dev_num, &dev_info); + if (err) + return sys_errmsg("cannot get information about UBI device %d", dev_num); + + printf("ubi%d:\n", dev_info.dev_num); + printf("Volumes count: %d\n", dev_info.vol_count); + printf("Logical eraseblock size: %d\n", dev_info.leb_size); + + 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 bytes\n", dev_info.min_io_size); + 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; + + return sys_errmsg("libubi failed to probe UBI device %d", i); + } + + if (!first) + printf(", ubi%d", i); + else { + printf("ubi%d", i); + first = 0; + } + } + printf("\n"); + + if (!all) + return 0; + + first = 1; + printf("\n"); + + for (i = ubi_info.lowest_dev_num; + i <= ubi_info.highest_dev_num; i++) { + if(!first) + printf("\n===================================\n\n"); + err = ubi_get_dev_info1(libubi, i, &dev_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + return sys_errmsg("libubi failed to probe UBI device %d", i); + } + 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; + + if (!args.node && args.devn != -1) + return errmsg("specify either device number or node file (use -h for help)"); + + libubi = libubi_open(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + if (args.node) { + /* + * A character device was specified, translate this into UBI + * device number and volume ID. + */ + err = translate_dev(libubi, args.node); + if (err) + goto out_libubi; + } + + if (args.vol_id != -1 && args.devn == -1) { + errmsg("volume ID is specified, but UBI device number is not " + "(use -h for help)\n"); + goto out_libubi; + } + + if (args.devn != -1 && args.vol_id != -1) { + print_vol_info(libubi, args.devn, args.vol_id); + goto out; + } + + if (args.devn == -1 && args.vol_id == -1) + err = print_general_info(libubi, args.all); + else if (args.devn != -1 && args.vol_id == -1) + err = print_dev_info(libubi, args.devn, args.all); + + if (err) + goto out_libubi; + +out: + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} diff --git a/new-utils/src/ubinize.c b/new-utils/src/ubinize.c new file mode 100644 index 0000000..00dc508 --- /dev/null +++ b/new-utils/src/ubinize.c @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Generate UBI images. + * + * Authors: Artem Bityutskiy + * Oliver Lohmann + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "common.h" + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubinize" + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION +" - a tool to generate UBI images. An UBI image may contain one or more UBI " +"volumes which have to be defined in the input configuration ini-file. The " +"ini file defines all the UBI volumes - their characteristics and the and the " +"contents, but it does not define the characteristics of the flash the UBI " +"image is generated for. Instead, the flash characteristics are defined via " +"the command-line options. Note, if not sure about some of the command-line " +"parameters, do not specify them and let the utility to use default values."; + +static const char *optionsstr = +"-o, --output= 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" +"-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] [-h] [-V] [--output=] [--help]\n" +"\t\t[--version] ini-file\n" +"Example: " PROGRAM_NAME " -o ubi.img -p 16KiB -m 512 -s 256 cfg.ini - create UBI image\n" +" 'ubi.img' as described by configuration file 'cfg.ini'"; + +static const char *ini_doc = "INI-file format.\n" +"The input configuration ini-file describes all the volumes which have to\n" +"be included to the output UBI image. Each volume is described in its own\n" +"section which may be named arbitrarily. The section consists on\n" +"\"key=value\" pairs, for example:\n\n" +"[jffs2-volume]\n" +"mode=ubi\n" +"image=../jffs2.img\n" +"vol_id=1\n" +"vol_size=30MiB\n" +"vol_type=dynamic\n" +"vol_name=jffs2_volume\n" +"vol_flags=autoresize\n" +"vol_alignment=1\n\n" +"This example configuration file tells the utility to create an UBI image\n" +"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n" +"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n" +"\"image=../jffs2.img\" line tells the utility to take the contents of the\n" +"volume from the \"../jffs2.img\" file. The size of the image file has to be\n" +"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n" +"mandatory and just tells that the section describes an UBI volume - other\n" +"section modes may be added in the future.\n" +"Notes:\n" +" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n" +" gigabytes (GiB) or bytes (no modifier);\n" +" * if \"vol_size\" key is absent, the volume size is assumed to be\n" +" equivalent to the size of the image file (defined by \"image\" key);\n" +" * if the \"image\" is absent, the volume is assumed to be empty;\n" +" * volume alignment must not be greater than the logical eraseblock size;\n" +" * one ini file may contain arbitrary number of sections, the utility will\n" +" put all the volumes which are described by these section to the output\n" +" UBI image file."; + +struct option long_options[] = { + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, + { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, + { .name = "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; + 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[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "o:p:m:s:O:e:x:vhV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'o': + args.out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, + S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (args.out_fd == -1) + return sys_errmsg("cannot open file \"%s\"", optarg); + args.f_out = optarg; + break; + + case 'p': + args.peb_size = ubiutils_get_bytes(optarg); + if (args.peb_size <= 0) + return errmsg("bad physical eraseblock size: \"%s\"", optarg); + break; + + case 'm': + args.min_io_size = ubiutils_get_bytes(optarg); + if (args.min_io_size <= 0) + return errmsg("bad min. I/O unit size: \"%s\"", optarg); + if (!is_power_of_2(args.min_io_size)) + return errmsg("min. I/O unit size should be power of 2"); + break; + + case 's': + args.subpage_size = ubiutils_get_bytes(optarg); + if (args.subpage_size <= 0) + return errmsg("bad sub-page size: \"%s\"", optarg); + if (!is_power_of_2(args.subpage_size)) + return errmsg("sub-page size should be power of 2"); + break; + + case 'O': + args.vid_hdr_offs = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vid_hdr_offs < 0) + return errmsg("bad VID header offset: \"%s\"", optarg); + break; + + case 'e': + args.ec = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.ec < 0) + return errmsg("bad erase counter value: \"%s\"", optarg); + break; + + case 'x': + args.ubi_ver = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.ubi_ver < 0) + return errmsg("bad UBI version: \"%s\"", optarg); + break; + + case 'v': + args.verbose = 1; + break; + + case 'h': + ubiutils_print_text(stderr, doc, 80); + fprintf(stderr, "\n%s\n\n", ini_doc); + fprintf(stderr, "%s\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc) + return errmsg("input configuration file was not specified (use -h for help)"); + + if (optind != argc - 1) + return errmsg("more then one configuration file was specified (use -h for help)"); + + args.f_in = argv[optind]; + + if (args.peb_size < 0) + return errmsg("physical eraseblock size was not specified (use -h for help)"); + + if (args.peb_size > 1024*1024) + return errmsg("too high physical eraseblock size %d", args.peb_size); + + if (args.min_io_size < 0) + return errmsg("min. I/O unit size was not specified (use -h for help)"); + + if (args.subpage_size < 0) + args.subpage_size = args.min_io_size; + + if (args.subpage_size > args.min_io_size) + return errmsg("sub-page cannot be larger then min. I/O unit"); + + if (args.peb_size % args.min_io_size) + return errmsg("physical eraseblock should be multiple of min. I/O units"); + + if (args.min_io_size % args.subpage_size) + return errmsg("min. I/O unit size should be multiple of sub-page size"); + + if (!args.f_out) + return errmsg("output file was not specified (use -h for help)"); + + if (args.vid_hdr_offs) { + if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE >= args.peb_size) + return errmsg("bad VID header position"); + if (args.vid_hdr_offs % 8) + return errmsg("VID header offset has to be multiple of min. I/O unit size"); + } + + return 0; +} + +static int read_section(const struct ubigen_info *ui, const char *sname, + struct ubigen_vol_info *vi, const char **img, + struct stat *st) +{ + char buf[256]; + const char *p; + + *img = NULL; + + if (strlen(sname) > 128) + return errmsg("too long section name \"%s\"", sname); + + /* Make sure mode is UBI, otherwise ignore this section */ + sprintf(buf, "%s:mode", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (!p) { + errmsg("\"mode\" key not found in section \"%s\"", sname); + errmsg("the \"mode\" key is mandatory and has to be " + "\"mode=ubi\" if the section describes an UBI volume"); + return -1; + } + + /* If mode is not UBI, skip this section */ + if (strcmp(p, "ubi")) { + verbose(args.verbose, "skip non-ubi section \"%s\"", sname); + return 1; + } + + verbose(args.verbose, "mode=ubi, keep parsing"); + + /* Fetch volume type */ + sprintf(buf, "%s:vol_type", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (!p) { + normsg("volume type was not specified in " + "section \"%s\", assume \"dynamic\"\n", sname); + vi->type = UBI_VID_DYNAMIC; + } else { + if (!strcmp(p, "static")) + vi->type = UBI_VID_STATIC; + else if (!strcmp(p, "dynamic")) + vi->type = UBI_VID_DYNAMIC; + else + return errmsg("invalid volume type \"%s\" in section \"%s\"", + p, sname); + } + + verbose(args.verbose, "volume type: %s", + vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static"); + + /* Fetch the name of the volume image file */ + sprintf(buf, "%s:image", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (p) { + *img = p; + if (stat(p, st)) + return sys_errmsg("cannot stat \"%s\" referred from section \"%s\"", + p, sname); + if (st->st_size == 0) + return errmsg("empty file \"%s\" referred from section \"%s\"", + p, sname); + } else if (vi->type == UBI_VID_STATIC) + return errmsg("image is not specified for static volume in section \"%s\"", + sname); + + /* Fetch volume id */ + sprintf(buf, "%s:vol_id", sname); + vi->id = iniparser_getint(args.dict, buf, -1); + if (vi->id == -1) + return errmsg("\"vol_id\" key not found in section \"%s\"", sname); + if (vi->id < 0) + return errmsg("negative volume ID %d in section \"%s\"", + vi->id, sname); + if (vi->id >= ui->max_volumes) + return errmsg("too high volume ID %d in section \"%s\", max. is %d", + vi->id, sname, ui->max_volumes); + + verbose(args.verbose, "volume ID: %d", vi->id); + + /* Fetch volume size */ + sprintf(buf, "%s:vol_size", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (p) { + vi->bytes = ubiutils_get_bytes(p); + if (vi->bytes <= 0) + return errmsg("bad \"vol_size\" key value \"%s\" (section \"%s\")", + p, sname); + + /* Make sure the image size is not larger than volume size */ + if (*img && st->st_size > vi->bytes) + return errmsg("error in section \"%s\": size of the image file " + "\"%s\" is %lld, which is larger than volume size %lld", + sname, *img, (long long)st->st_size, vi->bytes); + verbose(args.verbose, "volume size: %lld bytes", vi->bytes); + } else { + struct stat st; + + if (!*img) + return errmsg("neither image file (\"image=\") nor volume size " + "(\"vol_size=\") specified in section \"%s\"", sname); + + if (stat(*img, &st)) + return sys_errmsg("cannot stat \"%s\"", *img); + + vi->bytes = st.st_size; + + if (vi->bytes == 0) + return errmsg("file \"%s\" referred from section \"%s\" is empty", + *img, sname); + + normsg_cont("volume size was not specified in section \"%s\", assume" + " minimum to fit image \"%s\"", sname, *img); + ubiutils_print_bytes(vi->bytes, 1); + printf("\n"); + } + + /* Fetch volume name */ + sprintf(buf, "%s:vol_name", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (!p) + return errmsg("\"vol_name\" key not found in section \"%s\"", sname); + + vi->name = p; + vi->name_len = strlen(p); + if (vi->name_len > UBI_VOL_NAME_MAX) + return errmsg("too long volume name in section \"%s\", max. is %d characters", + vi->name, UBI_VOL_NAME_MAX); + + verbose(args.verbose, "volume name: %s", p); + + /* Fetch volume alignment */ + sprintf(buf, "%s:vol_alignment", sname); + vi->alignment = iniparser_getint(args.dict, buf, -1); + if (vi->alignment == -1) + vi->alignment = 1; + else if (vi->id < 0) + return errmsg("negative volume alignement %d in section \"%s\"", + vi->alignment, sname); + + verbose(args.verbose, "volume alignment: %d", vi->alignment); + + /* Fetch volume flags */ + sprintf(buf, "%s:vol_flags", sname); + p = iniparser_getstring(args.dict, buf, NULL); + if (p) { + if (!strcmp(p, "autoresize")) { + verbose(args.verbose, "autoresize flags found"); + vi->flags |= UBI_VTBL_AUTORESIZE_FLG; + } else { + return errmsg("unknown flags \"%s\" in section \"%s\"", + p, sname); + } + } + + /* Initialize the rest of the volume information */ + vi->data_pad = ui->leb_size % vi->alignment; + vi->usable_leb_size = ui->leb_size - vi->data_pad; + if (vi->type == UBI_VID_DYNAMIC) + vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size; + else + vi->used_ebs = (st->st_size + vi->usable_leb_size - 1) / vi->usable_leb_size; + vi->compat = 0; + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err = -1, sects, i, autoresize_was_already = 0; + struct ubigen_info ui; + struct ubi_vtbl_record *vtbl; + struct ubigen_vol_info *vi; + off_t seek; + + err = parse_opt(argc, argv); + if (err) + return -1; + + ubigen_info_init(&ui, args.peb_size, args.min_io_size, + args.subpage_size, args.vid_hdr_offs, + args.ubi_ver); + + 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); + + vtbl = ubigen_create_empty_vtbl(&ui); + if (!vtbl) + goto out; + + args.dict = iniparser_load(args.f_in); + if (!args.dict) { + errmsg("cannot load the input ini file \"%s\"", args.f_in); + goto out_vtbl; + } + + verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in); + + /* Each section describes one volume */ + sects = iniparser_getnsec(args.dict); + if (sects == -1) { + errmsg("ini-file parsing error (iniparser_getnsec)"); + goto out_dict; + } + + verbose(args.verbose, "count of sections: %d", sects); + if (sects == 0) { + errmsg("no sections found the ini-file \"%s\"", args.f_in); + goto out_dict; + } + + if (sects > ui.max_volumes) { + errmsg("too many sections (%d) in the ini-file \"%s\"", + sects, args.f_in); + normsg("each section corresponds to an UBI volume, maximum " + "count of volumes is %d", ui.max_volumes); + goto out_dict; + } + + vi = calloc(sizeof(struct ubigen_vol_info), sects); + if (!vi) { + errmsg("cannot allocate memory"); + goto out_dict; + } + + /* + * Skip 2 PEBs at the beginning of the file for the volume table which + * will be written later. + */ + seek = ui.peb_size * 2; + if (lseek(args.out_fd, seek, SEEK_SET) != seek) { + sys_errmsg("cannot seek file \"%s\"", args.f_out); + goto out_free; + } + + for (i = 0; i < sects; i++) { + const char *sname = iniparser_getsecname(args.dict, i); + const char *img = NULL; + struct stat st; + int fd, j; + + if (!sname) { + errmsg("ini-file parsing error (iniparser_getsecname)"); + goto out_free; + } + + if (args.verbose) + printf("\n"); + verbose(args.verbose, "parsing section \"%s\"", sname); + + err = read_section(&ui, sname, &vi[i], &img, &st); + if (err == -1) + goto out_free; + + verbose(args.verbose, "adding volume %d", vi[i].id); + + /* + * Make sure that volume ID and name is unique and that only + * one volume has auto-resize flag + */ + for (j = 0; j < i; j++) { + if (vi[i].id == vi[j].id) { + errmsg("volume IDs must be unique, but ID %d " + "in section \"%s\" is not", + vi[i].id, sname); + goto out_free; + } + + if (!strcmp(vi[i].name, vi[j].name)) { + errmsg("volume name must be unique, but name " + "\"%s\" in section \"%s\" is not", + vi[i].name, sname); + goto out_free; + } + } + + if (vi[i].flags & UBI_VTBL_AUTORESIZE_FLG) { + if (autoresize_was_already) + return errmsg("only one volume is allowed " + "to have auto-resize flag"); + autoresize_was_already = 1; + } + + err = ubigen_add_volume(&ui, &vi[i], vtbl); + if (err) { + errmsg("cannot add volume for section \"%s\"", sname); + goto out_free; + } + + if (img) { + fd = open(img, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", img); + goto out_free; + } + + verbose(args.verbose, "writing volume %d", vi[i].id); + verbose(args.verbose, "image file: %s", img); + + err = ubigen_write_volume(&ui, &vi[i], args.ec, st.st_size, fd, args.out_fd); + close(fd); + if (err) { + errmsg("cannot write volume for section \"%s\"", sname); + goto out_free; + } + } + + if (args.verbose) + printf("\n"); + } + + verbose(args.verbose, "writing layout volume"); + + err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl, args.out_fd); + if (err) { + errmsg("cannot write layout volume"); + goto out_free; + } + + verbose(args.verbose, "done"); + + free(vi); + iniparser_freedict(args.dict); + free(vtbl); + close(args.out_fd); + return 0; + +out_free: + free(vi); +out_dict: + iniparser_freedict(args.dict); +out_vtbl: + free(vtbl); +out: + close(args.out_fd); + remove(args.f_out); + return err; +} diff --git a/new-utils/src/ubirmvol.c b/new-utils/src/ubirmvol.c new file mode 100644 index 0000000..a4cf1df --- /dev/null +++ b/new-utils/src/ubirmvol.c @@ -0,0 +1,230 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubirmvol" + +/* The variables below are set by command line arguments */ +struct args { + int vol_id; + const char *node; + const char *name; + /* For deprecated -d option handling */ + int devn; + char dev_name[256]; +}; + +static struct args args = { + .vol_id = -1, + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to remove UBI volumes."; + +static const char *optionsstr = +"-n, --vol_id= volume ID to remove\n" +"-N, --name= volume name to remove\n" +"-h, -?, --help print help message\n" +"-V, --version print program version\n\n" +"The following is a compatibility option which is deprecated, do not use it\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\""; + +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' }, + /* Deprecated -d option */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { NULL, 0, NULL, 0}, +}; + +static int param_sanity_check(void) +{ + if (args.vol_id == -1 && !args.name) { + errmsg("please, specify either volume ID or volume name"); + return -1; + } + + if (args.vol_id != -1 && args.name) { + errmsg("please, specify either volume ID or volume name, not both"); + return -1; + } + + return 0; +} + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "n:N:h?Vd:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) { + errmsg("bad volume ID: " "\"%s\"", optarg); + return -1; + } + break; + + case 'N': + args.name = optarg; + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'd': + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + errmsg("parameter is missing"); + return -1; + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + 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(1); + if (libubi == NULL) + return sys_errmsg("cannot open libubi"); + + err = ubi_node_type(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) { + errmsg("\"%s\" is not an UBI device node", args.node); + goto out_libubi; + } + + if (args.name) { + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + + err = ubi_get_dev_info(libubi, args.node, &dev_info); + if (err) { + sys_errmsg("cannot get information about UBI device \"%s\"", + args.node); + goto out_libubi; + } + + err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num, + args.name, &vol_info); + if (err) { + sys_errmsg("cannot find UBI volume \"%s\"", args.name); + goto out_libubi; + } + + args.vol_id = vol_info.vol_id; + } + + err = ubi_rmvol(libubi, args.node, args.vol_id); + if (err) { + sys_errmsg("cannot UBI remove volume"); + goto out_libubi; + } + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} diff --git a/new-utils/src/ubiupdatevol.c b/new-utils/src/ubiupdatevol.c new file mode 100644 index 0000000..c83731c --- /dev/null +++ b/new-utils/src/ubiupdatevol.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to update UBI volumes. + * + * Authors: Frank Haverkamp + * Joshua W. Boyer + * Artem Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.1" +#define PROGRAM_NAME "ubiupdatevol" + +struct args { + int truncate; + const char *node; + const char *img; + /* For deprecated -d and -B options handling */ + int devn; + char dev_name[256]; + int broken_update; + int size; + int use_stdin; +}; + +static struct args args = { + .devn = -1, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to write data to UBI volumes."; + +static const char *optionsstr = +"-t, --truncate truncate volume (wipe it out)\n" +"-h, --help print help message\n" +"-V, --version print program version\n\n" +"-s, --size= bytes in input, if not reading from file\n" +"The following are compatibility options which are deprecated, do not use them\n" +"-d, --devn= UBI device number - may be used instead of the UBI\n" +" device node name in which case the utility assumes\n" +" that the device node is \"/dev/ubi\"\n" +"-B, --broken-update broken update, this is for testing"; + +static const char *usage = +"Usage: " PROGRAM_NAME " [-t] [-h] [-V] [--truncate] [--size=x] [--help]\n" +"\t\t\t[--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"; + +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' }, + /* Deprecated -d and -B options */ + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'B' }, + { NULL, 0, NULL, 0} +}; + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "n:th?Vd:s:", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 't': + args.truncate = 1; + break; + + case 's': + args.size = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.size < 0) + return errmsg("bad size: " "\"%s\"", optarg); + break; + + case 'h': + case '?': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'd': + /* Handle deprecated -d option */ + warnmsg("-d is depricated and will be removed, do not use it"); + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: " "\"%s\"", optarg); + break; + + case 'B': + /* Handle deprecated -B option */ + warnmsg("-B is depricated and will be removed, do not use it"); + args.broken_update = 1; + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + /* Handle deprecated -d option */ + if (args.devn != -1) { + sprintf(args.dev_name, "/dev/ubi%d", args.devn); + args.node = args.dev_name; + } else { + 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; + } + + /* A hack to handle deprecated -B option */ + if (args.broken_update) + bytes = 1; + + 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(1); + if (libubi == NULL) { + sys_errmsg("cannot open libubi"); + goto out_libubi; + } + + err = ubi_node_type(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) { + errmsg("\"%s\" is not an UBI volume node", 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; +} -- cgit v1.2.3