diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..0a0c65bc3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# +# Kbuild ignores +# +.* +*.o +*.o.* +*.a +*.s +Kbuild +Config.in + +# +# Never ignore these +# +!.gitignore + +# +# Normal output +# +/busybox +/busybox_old +/busybox_unstripped* + +# +# Backups / patches +# +*~ +*.orig +*.rej +/*.patch + +# +# debugging stuff +# +core +.gdb_history +.gdbinit diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..0908fc719 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,176 @@ +List of the authors of code contained in BusyBox. + +If you have code in BusyBox, you should be listed here. If you should be +listed, or the description of what you have done needs more detail, or is +incorrect, _please_ let me know. + + -Erik + +----------- + +Peter Willis + eject + +Emanuele Aina + run-parts + +Erik Andersen + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + Lots of tedious effort writing these boring docs that + nobody is going to actually read. + +Laurence Anderson + rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm + +Jeff Angielski + ftpput, ftpget + +Enrik Berkhan + setconsole + +Jim Bauer + modprobe shell dependency + +Edward Betts + expr, hostid, logname, whoami + +John Beppu + du, nslookup, sort + +David Brownell + zcip + +Brian Candler + tiny-ls(ls) + +Randolph Chung + fbset, ping, hostname + +Dave Cinege + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +Jordan Crouse + ipcalc + +Magnus Damm + tftp client + insmod powerpc support + +Larry Doolittle + pristine source directory compilation, lots of patches and fixes. + +Glenn Engel + httpd + +Gennady Feldman + Sysklogd (single threaded syslogd, IPC Circular buffer support, + logread), various fixes. + +Robert Griebl + modprobe, hwclock, suid/sgid handling, tinylogin integration + many bugfixes and enhancements + +Karl M. Hegbloom + cp_mv.c, the test suite, various fixes to utility.c, &c. + +Daniel Jacobowitz + mktemp.c + +Matt Kraai + documentation, bugfixes, test suite + +Rob Landley + Became busybox maintainer in 2006. + + sed (major rewrite in 2003, and I now maintain the thing) + bunzip2 (complete from-scratch rewrite, then mjn3 optimized the result) + sort (more or less from scratch rewrite in 2004, I now maintain it) + mount (rewrite in 2005, I maintain the new one) + +Stephan Linz + ipcalc, Red Hat equivalence + +John Lombardo + tr + +Glenn McGrath + Common unarchiving code and unarchiving applets, ifupdown, ftpgetput, + nameif, sed, patch, fold, install, uudecode. + Various bugfixes, review and apply numerous patches. + +Manuel Novoa III + cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, + mesg, vconfig, nice, renice, + make_directory, parse_mode, dirname, mode_string, + get_last_path_component, simplify_path, and a number trivial libbb routines + + also bug fixes, partial rewrites, and size optimizations in + ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, + mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, + interface, dutmp, ifconfig, route + +Vladimir Oleynik + cmdedit; bb_mkdep, xargs(current), httpd(current); + ports: ash, crond, fdisk (initial, unmaintained now), inetd, stty, traceroute, + top; + locale, various fixes + and irreconcilable critic of everything not perfect. + +Bruce Perens + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + +Rodney Radford + ipcs, ipcrm + +Tim Riker + bug fixes, member of fan club + +Kent Robotti + reset, tons and tons of bug reports and patches. + +Chip Rosenthal , + wget - Contributed by permission of Covad Communications + +Pavel Roskin + Lots of bugs fixes and patches. + +Gyepi Sam + Remote logging feature for syslogd + +Rob Sullivan + comm + +Linus Torvalds + mkswap, fsck.minix, mkfs.minix + +Mark Whitley + grep, sed, cut, xargs(previous), + style-guide, new-applet-HOWTO, bug fixes, etc. + +Charles P. Wright + gzip, mini-netcat(nc) + +Enrique Zanardi + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +Tito Ragusa + devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm, + fdformat, lsattr, chattr, id and eject. + +Paul Fox + vi editing mode for ash, various other patches/fixes + +Roberto A. Foglietta + port: dnsd + +Bernhard Reutner-Fischer + misc + +Mike Frysinger + initial e2fsprogs, printenv, setarch, sum, misc + +Jie Zhang + fixed two bugs in msh and hush (exitcode of killed processes) diff --git a/INSTALL b/INSTALL new file mode 100644 index 000000000..f93e5fb25 --- /dev/null +++ b/INSTALL @@ -0,0 +1,139 @@ +Building: +========= + +The BusyBox build process is similar to the Linux kernel build: + + make menuconfig # This creates a file called ".config" + make # This creates the "busybox" executable + make install # or make CONFIG_PREFIX=/path/from/root install + +The full list of configuration and install options is available by typing: + + make help + +Quick Start: +============ + +The easy way to try out BusyBox for the first time, without having to install +it, is to enable all features and then use "standalone shell" mode with a +blank command $PATH. + +To enable all features, use "make defconfig", which produces the largest +general-purpose configuration. It's allyesconfig minus debugging options, +optional packaging choices, and a few special-purpose features requiring +extra configuration to use. Then enable "standalone shell" feature: + + make defconfig + make menuconfig + # select Busybox Settings + # then General Configuration + # then exec prefers applets + # exit back to top level menu + # select Shells + # then Standalone shell + # exit back to top level menu + # exit and save new configuration + # OR + # use these commands to modify .config directly: + sed -e 's/.*FEATURE_PREFER_APPLETS.*/CONFIG_FEATURE_PREFER_APPLETS=y/' -i .config + sed -e 's/.*FEATURE_SH_STANDALONE.*/CONFIG_FEATURE_SH_STANDALONE=y/' -i .config + make + PATH= ./busybox ash + +Standalone shell mode causes busybox's built-in command shell to run +any built-in busybox applets directly, without looking for external +programs by that name. Supplying an empty command path (as above) means +the only commands busybox can find are the built-in ones. + +Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH +to be set appropriately, depending on whether or not /proc/self/exe is +available or not. If you do not have /proc, then point that config option +to the location of your busybox binary, usually /bin/busybox. + +Configuring Busybox: +==================== + +Busybox is optimized for size, but enabling the full set of functionality +still results in a fairly large executable -- more than 1 megabyte when +statically linked. To save space, busybox can be configured with only the +set of applets needed for each environment. The minimal configuration, with +all applets disabled, produces a 4k executable. (It's useless, but very small.) + +The manual configurator "make menuconfig" modifies the existing configuration. +(For systems without ncurses, try "make config" instead.) The two most +interesting starting configurations are "make allnoconfig" (to start with +everything disabled and add just what you need), and "make defconfig" (to +start with everything enabled and remove what you don't need). If menuconfig +is run without an existing configuration, make defconfig will run first to +create a known starting point. + +Other starting configurations (mostly used for testing purposes) include +"make allbareconfig" (enables all applets but disables all optional features), +"make allyesconfig" (enables absolutely everything including debug features), +and "make randconfig" (produce a random configuration). The configs/ directory +contains a number of additional configuration files ending in _defconfig which +are useful in specific cases. "make help" will list them. + +Configuring BusyBox produces a file ".config", which can be saved for future +use. Run "make oldconfig" to bring a .config file from an older version of +busybox up to date. + +Installing Busybox: +=================== + +Busybox is a single executable that can behave like many different commands, +and BusyBox uses the name it was invoked under to determine the desired +behavior. (Try "mv busybox ls" and then "./ls -l".) + +Installing busybox consists of creating symlinks (or hardlinks) to the busybox +binary for each applet enabled in busybox, and making sure these symlinks are +in the shell's command $PATH. Running "make install" creates these symlinks, +or "make install-hardlinks" creates hardlinks instead (useful on systems with +a limited number of inodes). This install process uses the file +"busybox.links" (created by make), which contains the list of enabled applets +and the path at which to install them. + +Installing links to busybox is not always necessary. The special applet name +"busybox" (or with any optional suffix, such as "busybox-static") uses the +first argument to determine which applet to behave as, for example +"./busybox cat LICENSE". (Running the busybox applet with no arguments gives +a list of all enabled applets.) The standalone shell can also call busybox +applets without links to busybox under other names in the filesystem. You can +also configure a standalone install capability into the busybox base applet, +and then install such links at runtime with one of "busybox --install" (for +hardlinks) or "busybox --install -s" (for symlinks). + +If you enabled the busybox shared library feature (libbusybox.so) and want +to run tests without installing, set your LD_LIBRARY_PATH accordingly when +running the executable: + + LD_LIBRARY_PATH=`pwd` ./busybox + +Building out-of-tree: +===================== + +By default, the BusyBox build puts its temporary files in the source tree. +Building from a read-only source tree, or building multiple configurations from +the same source directory, requires the ability to put the temporary files +somewhere else. + +To build out of tree, cd to an empty directory and configure busybox from there: + + make KBUILD_SRC=/path/to/source -f /path/to/source/Makefile defconfig + make + make install + +Alternately, use the O=$BUILDPATH option (with an absolute path) during the +configuration step, as in: + + make O=/some/empty/directory allyesconfig + cd /some/empty/directory + make + make CONFIG_PREFIX=. install + +More Information: +================= + +Se also the busybox FAQ, under the questions "How can I get started using +BusyBox" and "How do I build a BusyBox-based system?" The BusyBox FAQ is +available from http://www.busybox.net/FAQ.html diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..6f50a7164 --- /dev/null +++ b/LICENSE @@ -0,0 +1,348 @@ +--- A note on GPL versions + +BusyBox is distributed under version 2 of the General Public License (included +in its entirety, below). Version 2 is the only version of this license which +this version of BusyBox (or modified versions derived from this one) may be +distributed under. + +------------------------------------------------------------------------ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +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) year 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/Makefile b/Makefile new file mode 100644 index 000000000..35d2e1e00 --- /dev/null +++ b/Makefile @@ -0,0 +1,1341 @@ +VERSION = 1 +PATCHLEVEL = 20 +SUBLEVEL = 0 +EXTRAVERSION = .git +NAME = Unnamed + +# *DOCUMENTATION* +# To see a list of typical targets execute "make help" +# More info can be located in ./README +# Comments in this file are targeted only to the developer, do not +# expect to learn how to build the kernel reading this file. + +# Do not print "Entering directory ..." +MAKEFLAGS += --no-print-directory + +# We are using a recursive build, so we need to do a little thinking +# to get the ordering right. +# +# Most importantly: sub-Makefiles should only ever modify files in +# their own directory. If in some directory we have a dependency on +# a file in another dir (which doesn't happen often, but it's often +# unavoidable when linking the built-in.o targets which finally +# turn into busybox), we will call a sub make in that other dir, and +# after that we are sure that everything which is in that other dir +# is now up to date. +# +# The only cases where we need to modify files which have global +# effects are thus separated out and done before the recursive +# descending is started. They are now explicitly listed as the +# prepare rule. + +# To put more focus on warnings, be less verbose as default +# Use 'make V=1' to see the full commands + +ifdef V + ifeq ("$(origin V)", "command line") + KBUILD_VERBOSE = $(V) + endif +endif +ifndef KBUILD_VERBOSE + KBUILD_VERBOSE = 0 +endif + +# Call sparse as part of compilation of C files +# Use 'make C=1' to enable sparse checking + +ifdef C + ifeq ("$(origin C)", "command line") + KBUILD_CHECKSRC = $(C) + endif +endif +ifndef KBUILD_CHECKSRC + KBUILD_CHECKSRC = 0 +endif + +# Use make M=dir to specify directory of external module to build +# Old syntax make ... SUBDIRS=$PWD is still supported +# Setting the environment variable KBUILD_EXTMOD take precedence +ifdef SUBDIRS + KBUILD_EXTMOD ?= $(SUBDIRS) +endif +ifdef M + ifeq ("$(origin M)", "command line") + KBUILD_EXTMOD := $(M) + endif +endif + + +# kbuild supports saving output files in a separate directory. +# To locate output files in a separate directory two syntaxes are supported. +# In both cases the working directory must be the root of the kernel src. +# 1) O= +# Use "make O=dir/to/store/output/files/" +# +# 2) Set KBUILD_OUTPUT +# Set the environment variable KBUILD_OUTPUT to point to the directory +# where the output files shall be placed. +# export KBUILD_OUTPUT=dir/to/store/output/files/ +# make +# +# The O= assignment takes precedence over the KBUILD_OUTPUT environment +# variable. + + +# KBUILD_SRC is set on invocation of make in OBJ directory +# KBUILD_SRC is not intended to be used by the regular user (for now) +ifeq ($(KBUILD_SRC),) + +# OK, Make called in directory where kernel src resides +# Do we want to locate output files in a separate directory? +ifdef O + ifeq ("$(origin O)", "command line") + KBUILD_OUTPUT := $(O) + endif +endif + +# That's our default target when none is given on the command line +PHONY := _all +_all: + +ifneq ($(KBUILD_OUTPUT),) +# Invoke a second make in the output directory, passing relevant variables +# check that the output directory actually exists +saved-output := $(KBUILD_OUTPUT) +KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd) +$(if $(KBUILD_OUTPUT),, \ + $(error output directory "$(saved-output)" does not exist)) + +PHONY += $(MAKECMDGOALS) + +$(filter-out _all,$(MAKECMDGOALS)) _all: + $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \ + KBUILD_SRC=$(CURDIR) \ + KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@ + +# Leave processing to above invocation of make +skip-makefile := 1 +endif # ifneq ($(KBUILD_OUTPUT),) +endif # ifeq ($(KBUILD_SRC),) + +# We process the rest of the Makefile if this is the final invocation of make +ifeq ($(skip-makefile),) + +# If building an external module we do not care about the all: rule +# but instead _all depend on modules +PHONY += all +ifeq ($(KBUILD_EXTMOD),) +_all: all +else +_all: modules +endif + +srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR)) +TOPDIR := $(srctree) +# FIXME - TOPDIR is obsolete, use srctree/objtree +objtree := $(CURDIR) +src := $(srctree) +obj := $(objtree) + +VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) + +export srctree objtree VPATH TOPDIR + + +# Cross compiling and selecting different set of gcc/bin-utils +# --------------------------------------------------------------------------- +# +# When performing cross compilation for other architectures ARCH shall be set +# to the target architecture. (See arch/* for the possibilities). +# ARCH can be set during invocation of make: +# make ARCH=ia64 +# Another way is to have ARCH set in the environment. +# The default ARCH is the host where make is executed. + +# CROSS_COMPILE specify the prefix used for all executables used +# during compilation. Only gcc and related bin-utils executables +# are prefixed with $(CROSS_COMPILE). +# CROSS_COMPILE can be set on the command line +# make CROSS_COMPILE=ia64-linux- +# Alternatively CROSS_COMPILE can be set in the environment. +# Default value for CROSS_COMPILE is not to prefix executables +# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile + +CROSS_COMPILE ?= +# bbox: we may have CONFIG_CROSS_COMPILER_PREFIX in .config, +# and it has not been included yet... thus using an awkward syntax. +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := $(shell grep ^CONFIG_CROSS_COMPILER_PREFIX .config 2>/dev/null) +CROSS_COMPILE := $(subst CONFIG_CROSS_COMPILER_PREFIX=,,$(CROSS_COMPILE)) +CROSS_COMPILE := $(subst ",,$(CROSS_COMPILE)) +#") +endif + +# SUBARCH tells the usermode build what the underlying arch is. That is set +# first, and if a usermode build is happening, the "ARCH=um" on the command +# line overrides the setting of ARCH below. If a native build is happening, +# then ARCH is assigned, getting whatever value it gets normally, and +# SUBARCH is subsequently ignored. + +ifneq ($(CROSS_COMPILE),) +SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1) +else +SUBARCH := $(shell uname -m) +endif +SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ -e s/sa110/arm/ \ + -e s/s390x/s390/ -e s/parisc64/parisc/ \ + -e s/ppc.*/powerpc/ -e s/mips.*/mips/ ) + +ARCH ?= $(SUBARCH) + +# Architecture as present in compile.h +UTS_MACHINE := $(ARCH) + +# SHELL used by kbuild +CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ + else if [ -x /bin/bash ]; then echo /bin/bash; \ + else echo sh; fi ; fi) + +# Decide whether to build built-in, modular, or both. +# Normally, just do built-in. + +KBUILD_MODULES := +KBUILD_BUILTIN := 1 + +# If we have only "make modules", don't compile built-in objects. +# When we're building modules with modversions, we need to consider +# the built-in objects during the descend as well, in order to +# make sure the checksums are uptodate before we record them. + +ifeq ($(MAKECMDGOALS),modules) + KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) +endif + +# If we have "make modules", compile modules +# in addition to whatever we do anyway. +# Just "make" or "make all" shall build modules as well + +ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) + KBUILD_MODULES := 1 +endif + +ifeq ($(MAKECMDGOALS),) + KBUILD_MODULES := 1 +endif + +export KBUILD_MODULES KBUILD_BUILTIN +export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD + +# Beautify output +# --------------------------------------------------------------------------- +# +# Normally, we echo the whole command before executing it. By making +# that echo $($(quiet)$(cmd)), we now have the possibility to set +# $(quiet) to choose other forms of output instead, e.g. +# +# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@ +# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< +# +# If $(quiet) is empty, the whole command will be printed. +# If it is set to "quiet_", only the short version will be printed. +# If it is set to "silent_", nothing wil be printed at all, since +# the variable $(silent_cmd_cc_o_c) doesn't exist. +# +# A simple variant is to prefix commands with $(Q) - that's useful +# for commands that shall be hidden in non-verbose mode. +# +# $(Q)ln $@ :< +# +# If KBUILD_VERBOSE equals 0 then the above command will be hidden. +# If KBUILD_VERBOSE equals 1 then the above command is displayed. + +ifeq ($(KBUILD_VERBOSE),1) + quiet = + Q = +else + quiet=quiet_ + Q = @ +endif + +# If the user is running make -s (silent mode), suppress echoing of +# commands + +ifneq ($(findstring s,$(MAKEFLAGS)),) + quiet=silent_ +endif + +export quiet Q KBUILD_VERBOSE + + +# Look for make include files relative to root of kernel src +MAKEFLAGS += --include-dir=$(srctree) + +HOSTCC = gcc +HOSTCXX = g++ +HOSTCFLAGS := +HOSTCXXFLAGS := +# We need some generic definitions +include $(srctree)/scripts/Kbuild.include + +HOSTCFLAGS += $(call hostcc-option,-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer,) +HOSTCXXFLAGS += -O2 + +# For maximum performance (+ possibly random breakage, uncomment +# the following) + +MAKEFLAGS += -rR + +# Make variables (CC, etc...) + +AS = $(CROSS_COMPILE)as +CC = $(CROSS_COMPILE)gcc +LD = $(CC) -nostdlib +CPP = $(CC) -E +AR = $(CROSS_COMPILE)ar +NM = $(CROSS_COMPILE)nm +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump +AWK = awk +GENKSYMS = scripts/genksyms/genksyms +DEPMOD = /sbin/depmod +KALLSYMS = scripts/kallsyms +PERL = perl +CHECK = sparse + +CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF) +MODFLAGS = -DMODULE +CFLAGS_MODULE = $(MODFLAGS) +AFLAGS_MODULE = $(MODFLAGS) +LDFLAGS_MODULE = -r +CFLAGS_KERNEL = +AFLAGS_KERNEL = + + +# Use LINUXINCLUDE when you must reference the include/ directory. +# Needed to be compatible with the O= option +CFLAGS := $(CFLAGS) +# Added only to final link stage of busybox binary +CFLAGS_busybox := $(CFLAGS_busybox) +CPPFLAGS := $(CPPFLAGS) +AFLAGS := $(AFLAGS) +LDFLAGS := $(LDFLAGS) +LDLIBS := + +# Read KERNELRELEASE from .kernelrelease (if it exists) +KERNELRELEASE = $(shell cat .kernelrelease 2> /dev/null) +KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) + +export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \ + ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ + CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \ + HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS + +export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS +export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE +export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE +export FLTFLAGS + +# When compiling out-of-tree modules, put MODVERDIR in the module +# tree rather than in the kernel tree. The kernel tree might +# even be read-only. +export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions + +# Files to ignore in find ... statements + +RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o +export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git + +# =========================================================================== +# Rules shared between *config targets and build targets + +# Basic helpers built in scripts/ +PHONY += scripts_basic +scripts_basic: + $(Q)$(MAKE) $(build)=scripts/basic + +# To avoid any implicit rule to kick in, define an empty command. +scripts/basic/%: scripts_basic ; + +# This target generates Kbuild's and Config.in's from *.c files +PHONY += gen_build_files +gen_build_files: $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) + $(Q)$(srctree)/scripts/gen_build_files.sh $(srctree) $(objtree) + +# bbox: we have helpers in applets/ +# we depend on scripts_basic, since scripts/basic/fixdep +# must be built before any other host prog +PHONY += applets_dir +applets_dir: scripts_basic gen_build_files + $(Q)$(MAKE) $(build)=applets + +applets/%: applets_dir ; + +PHONY += outputmakefile +# outputmakefile generates a Makefile in the output directory, if using a +# separate output directory. This allows convenient use of make in the +# output directory. +outputmakefile: +ifneq ($(KBUILD_SRC),) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ + $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) +endif + +# To make sure we do not include .config for any of the *config targets +# catch them early, and hand them over to scripts/kconfig/Makefile +# It is allowed to specify more targets when calling make, including +# mixing *config targets and build targets. +# For example 'make oldconfig all'. +# Detect when mixed targets is specified, and make a second invocation +# of make so .config is not included in this case either (for *config). + +no-dot-config-targets := clean mrproper distclean \ + cscope TAGS tags help %docs +#bbox# check% is removed from above + +config-targets := 0 +mixed-targets := 0 +dot-config := 1 + +ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) + ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) + dot-config := 0 + endif +endif + +ifeq ($(KBUILD_EXTMOD),) + ifneq ($(filter config %config,$(MAKECMDGOALS)),) + config-targets := 1 + ifneq ($(filter-out config %config,$(MAKECMDGOALS)),) + mixed-targets := 1 + endif + endif +endif + +ifeq ($(mixed-targets),1) +# =========================================================================== +# We're called with mixed targets (*config and build targets). +# Handle them one by one. + +%:: FORCE + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@ + +else +ifeq ($(config-targets),1) +# =========================================================================== +# *config targets only - make sure prerequisites are updated, and descend +# in scripts/kconfig to make the *config target + +# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed. +# KBUILD_DEFCONFIG may point out an alternative default configuration +# used for 'make defconfig' +-include $(srctree)/arch/$(ARCH)/Makefile +export KBUILD_DEFCONFIG + +config: scripts_basic outputmakefile gen_build_files FORCE + $(Q)mkdir -p include + $(Q)$(MAKE) $(build)=scripts/kconfig $@ + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease + +%config: scripts_basic outputmakefile gen_build_files FORCE + $(Q)mkdir -p include + $(Q)$(MAKE) $(build)=scripts/kconfig $@ + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease + +else +# =========================================================================== +# Build targets only - this includes busybox, arch specific targets, clean +# targets and others. In general all targets except *config targets. + +ifeq ($(KBUILD_EXTMOD),) +# Additional helpers built in scripts/ +# Carefully list dependencies so we do not try to build scripts twice +# in parallel +PHONY += scripts +scripts: gen_build_files scripts_basic include/config/MARKER + $(Q)$(MAKE) $(build)=$(@) + +scripts_basic: include/autoconf.h + +# Objects we will link into busybox / subdirs we need to visit +core-y := \ + applets/ \ + +libs-y := \ + archival/ \ + archival/libarchive/ \ + console-tools/ \ + coreutils/ \ + coreutils/libcoreutils/ \ + debianutils/ \ + e2fsprogs/ \ + editors/ \ + findutils/ \ + init/ \ + libbb/ \ + libpwdgrp/ \ + loginutils/ \ + mailutils/ \ + miscutils/ \ + modutils/ \ + networking/ \ + networking/libiproute/ \ + networking/udhcp/ \ + printutils/ \ + procps/ \ + runit/ \ + selinux/ \ + shell/ \ + sysklogd/ \ + util-linux/ \ + util-linux/volume_id/ \ + +endif # KBUILD_EXTMOD + +ifeq ($(dot-config),1) +# In this section, we need .config + +# Read in dependencies to all Kconfig* files, make sure to run +# oldconfig if changes are detected. +-include .kconfig.d + +-include .config + +# If .config needs to be updated, it will be done via the dependency +# that autoconf has on .config. +# To avoid any implicit rule to kick in, define an empty command +.config .kconfig.d: ; + +# Now we can define CFLAGS etc according to .config +include $(srctree)/Makefile.flags + +# If .config is newer than include/autoconf.h, someone tinkered +# with it and forgot to run make oldconfig. +# If kconfig.d is missing then we are probarly in a cleaned tree so +# we execute the config step to be sure to catch updated Kconfig files +include/autoconf.h: .kconfig.d .config $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) | gen_build_files + $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig + +include/usage.h: gen_build_files + +else +# Dummy target needed, because used as prerequisite +include/autoconf.h: ; +endif + +# The all: target is the default when no target is given on the +# command line. +# This allow a user to issue only 'make' to build a kernel including modules +# Defaults busybox but it is usually overridden in the arch makefile +all: busybox doc + +-include $(srctree)/arch/$(ARCH)/Makefile + +# arch Makefile may override CC so keep this after arch Makefile is included +#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) +CHECKFLAGS += $(NOSTDINC_FLAGS) + +# Default kernel image to build when no specific target is given. +# KBUILD_IMAGE may be overruled on the commandline or +# set in the environment +# Also any assignments in arch/$(ARCH)/Makefile take precedence over +# this default value +export KBUILD_IMAGE ?= busybox + +# +# INSTALL_PATH specifies where to place the updated kernel and system map +# images. Default is /boot, but you can set it to other values +export INSTALL_PATH ?= /boot + +# +# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory +# relocations required by build roots. This is not defined in the +# makefile but the arguement can be passed to make if needed. +# + +MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) +export MODLIB + + +ifeq ($(KBUILD_EXTMOD),) +busybox-dirs := $(patsubst %/,%,$(filter %/, $(core-y) $(core-m) $(libs-y) $(libs-m))) + +busybox-alldirs := $(sort $(busybox-dirs) $(patsubst %/,%,$(filter %/, \ + $(core-n) $(core-) $(libs-n) $(libs-) \ + ))) + +core-y := $(patsubst %/, %/built-in.o, $(core-y)) +libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) +libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) +libs-y := $(libs-y1) $(libs-y2) + +# Build busybox +# --------------------------------------------------------------------------- +# busybox is build from the objects selected by $(busybox-init) and +# $(busybox-main). Most are built-in.o files from top-level directories +# in the kernel tree, others are specified in arch/$(ARCH)Makefile. +# Ordering when linking is important, and $(busybox-init) must be first. +# +# busybox +# ^ +# | +# +-< $(busybox-init) +# | +--< init/version.o + more +# | +# +--< $(busybox-main) +# | +--< driver/built-in.o mm/built-in.o + more +# | +# +-< kallsyms.o (see description in CONFIG_KALLSYMS section) +# +# busybox version (uname -v) cannot be updated during normal +# descending-into-subdirs phase since we do not yet know if we need to +# update busybox. +# Therefore this step is delayed until just before final link of busybox - +# except in the kallsyms case where it is done just before adding the +# symbols to the kernel. +# +# System.map is generated to document addresses of all kernel symbols + +busybox-all := $(core-y) $(libs-y) + +# Rule to link busybox - also used during CONFIG_KALLSYMS +# May be overridden by arch/$(ARCH)/Makefile +quiet_cmd_busybox__ ?= LINK $@ + cmd_busybox__ ?= $(srctree)/scripts/trylink \ + "$@" \ + "$(CC)" \ + "$(CFLAGS) $(CFLAGS_busybox)" \ + "$(LDFLAGS) $(EXTRA_LDFLAGS)" \ + "$(core-y)" \ + "$(libs-y)" \ + "$(LDLIBS)" + +# Generate System.map +quiet_cmd_sysmap = SYSMAP + cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap + +# Link of busybox +# If CONFIG_KALLSYMS is set .version is already updated +# Generate System.map and verify that the content is consistent +# Use + in front of the busybox_version rule to silent warning with make -j2 +# First command is ':' to allow us to use + in front of the rule +define rule_busybox__ + : + $(call cmd,busybox__) + $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd +endef + + +ifdef CONFIG_KALLSYMS +# Generate section listing all symbols and add it into busybox $(kallsyms.o) +# It's a three stage process: +# o .tmp_busybox1 has all symbols and sections, but __kallsyms is +# empty +# Running kallsyms on that gives us .tmp_kallsyms1.o with +# the right size - busybox version (uname -v) is updated during this step +# o .tmp_busybox2 now has a __kallsyms section of the right size, +# but due to the added section, some addresses have shifted. +# From here, we generate a correct .tmp_kallsyms2.o +# o The correct .tmp_kallsyms2.o is linked into the final busybox. +# o Verify that the System.map from busybox matches the map from +# .tmp_busybox2, just in case we did not generate kallsyms correctly. +# o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using +# .tmp_busybox3 and .tmp_kallsyms3.o. This is only meant as a +# temporary bypass to allow the kernel to be built while the +# maintainers work out what went wrong with kallsyms. + +ifdef CONFIG_KALLSYMS_EXTRA_PASS +last_kallsyms := 3 +else +last_kallsyms := 2 +endif + +kallsyms.o := .tmp_kallsyms$(last_kallsyms).o + +define verify_kallsyms + $(Q)$(if $($(quiet)cmd_sysmap), \ + echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \ + $(cmd_sysmap) .tmp_busybox$(last_kallsyms) .tmp_System.map + $(Q)cmp -s System.map .tmp_System.map || \ + (echo Inconsistent kallsyms data; \ + echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \ + rm .tmp_kallsyms* ; /bin/false ) +endef + +# Update busybox version before link +# Use + in front of this rule to silent warning about make -j1 +# First command is ':' to allow us to use + in front of this rule +cmd_ksym_ld = $(cmd_busybox__) +define rule_ksym_ld + : + +$(call cmd,busybox_version) + $(call cmd,busybox__) + $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd +endef + +# Generate .S file with all kernel symbols +quiet_cmd_kallsyms = KSYM $@ + cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \ + $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@ + +.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE + $(call if_changed_dep,as_o_S) + +.tmp_kallsyms%.S: .tmp_busybox% $(KALLSYMS) + $(call cmd,kallsyms) + +# .tmp_busybox1 must be complete except kallsyms, so update busybox version +.tmp_busybox1: $(busybox-lds) $(busybox-all) FORCE + $(call if_changed_rule,ksym_ld) + +.tmp_busybox2: $(busybox-lds) $(busybox-all) .tmp_kallsyms1.o FORCE + $(call if_changed,busybox__) + +.tmp_busybox3: $(busybox-lds) $(busybox-all) .tmp_kallsyms2.o FORCE + $(call if_changed,busybox__) + +# Needs to visit scripts/ before $(KALLSYMS) can be used. +$(KALLSYMS): scripts ; + +# Generate some data for debugging strange kallsyms problems +debug_kallsyms: .tmp_map$(last_kallsyms) + +.tmp_map%: .tmp_busybox% FORCE + ($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@ + +.tmp_map3: .tmp_map2 + +.tmp_map2: .tmp_map1 + +endif # ifdef CONFIG_KALLSYMS + +# busybox image - including updated kernel symbols +busybox_unstripped: $(busybox-all) FORCE + $(call if_changed_rule,busybox__) + $(Q)rm -f .old_version + +busybox: busybox_unstripped +ifeq ($(SKIP_STRIP),y) + $(Q)cp $< $@ +else + $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ + busybox_unstripped -o $@ +# strip is confused by PIE executable and does not set exec bits + $(Q)chmod a+x $@ +endif + +# The actual objects are generated when descending, +# make sure no implicit rule kicks in +$(sort $(busybox-all)): $(busybox-dirs) ; + +# Handle descending into subdirectories listed in $(busybox-dirs) +# Preset locale variables to speed up the build process. Limit locale +# tweaks to this spot to avoid wrong language settings when running +# make menuconfig etc. +# Error messages still appears in the original language + +PHONY += $(busybox-dirs) +$(busybox-dirs): prepare scripts + $(Q)$(MAKE) $(build)=$@ + +# Build the kernel release string +# The KERNELRELEASE is stored in a file named .kernelrelease +# to be used when executing for example make install or make modules_install +# +# Take the contents of any files called localversion* and the config +# variable CONFIG_LOCALVERSION and append them to KERNELRELEASE. +# LOCALVERSION from the command line override all of this + +nullstring := +space := $(nullstring) # end of line + +___localver = $(objtree)/localversion* $(srctree)/localversion* +__localver = $(sort $(wildcard $(___localver))) +# skip backup files (containing '~') +_localver = $(foreach f, $(__localver), $(if $(findstring ~, $(f)),,$(f))) + +localver = $(subst $(space),, \ + $(shell cat /dev/null $(_localver)) \ + $(patsubst "%",%,$(CONFIG_LOCALVERSION))) + +# If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called +# and if the SCM is know a tag from the SCM is appended. +# The appended tag is determinded by the SCM used. +# +# Currently, only git is supported. +# Other SCMs can edit scripts/setlocalversion and add the appropriate +# checks as needed. +ifdef _BB_DISABLED_CONFIG_LOCALVERSION_AUTO + _localver-auto = $(shell $(CONFIG_SHELL) \ + $(srctree)/scripts/setlocalversion $(srctree)) + localver-auto = $(LOCALVERSION)$(_localver-auto) +endif + +localver-full = $(localver)$(localver-auto) + +# Store (new) KERNELRELASE string in .kernelrelease +kernelrelease = $(KERNELVERSION)$(localver-full) +.kernelrelease: FORCE + $(Q)rm -f $@ + $(Q)echo $(kernelrelease) > $@ + + +# Things we need to do before we recursively start building the kernel +# or the modules are listed in "prepare". +# A multi level approach is used. prepareN is processed before prepareN-1. +# archprepare is used in arch Makefiles and when processed asm symlink, +# version.h and scripts_basic is processed / created. + +# Listed in dependency order +PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 + +# prepare-all is deprecated, use prepare as valid replacement +PHONY += prepare-all + +# prepare3 is used to check if we are building in a separate output directory, +# and if so do: +# 1) Check that make has not been executed in the kernel src $(srctree) +# 2) Create the include2 directory, used for the second asm symlink +prepare3: .kernelrelease +ifneq ($(KBUILD_SRC),) + @echo ' Using $(srctree) as source for busybox' + $(Q)if [ -f $(srctree)/.config ]; then \ + echo " $(srctree) is not clean, please run 'make mrproper'";\ + echo " in the '$(srctree)' directory.";\ + /bin/false; \ + fi; + $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi; + $(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm +endif + +# prepare2 creates a makefile if using a separate output directory +prepare2: prepare3 outputmakefile + +prepare1: prepare2 include/config/MARKER +ifneq ($(KBUILD_MODULES),) + $(Q)mkdir -p $(MODVERDIR) + $(Q)rm -f $(MODVERDIR)/* +endif + +archprepare: prepare1 scripts_basic applets_dir + +prepare0: archprepare FORCE + $(Q)$(MAKE) $(build)=. + +# All the preparing.. +prepare prepare-all: prepare0 + +# Leave this as default for preprocessing busybox.lds.S, which is now +# done in arch/$(ARCH)/kernel/Makefile + +export CPPFLAGS_busybox.lds += -P -C -U$(ARCH) + +# FIXME: The asm symlink changes when $(ARCH) changes. That's +# hard to detect, but I suppose "make mrproper" is a good idea +# before switching between archs anyway. + +#bbox# include/asm: +#bbox# @echo ' SYMLINK $@ -> include/asm-$(ARCH)' +#bbox# $(Q)if [ ! -d include ]; then mkdir -p include; fi; +#bbox# @ln -fsn asm-$(ARCH) $@ + +# Split autoconf.h into include/linux/config/* +quiet_cmd_gen_bbconfigopts = GEN include/bbconfigopts.h + cmd_gen_bbconfigopts = $(srctree)/scripts/mkconfigs include/bbconfigopts.h include/bbconfigopts_bz2.h +quiet_cmd_split_autoconf = SPLIT include/autoconf.h -> include/config/* + cmd_split_autoconf = scripts/basic/split-include include/autoconf.h include/config +#bbox# piggybacked generation of few .h files +include/config/MARKER: scripts/basic/split-include include/autoconf.h + $(call cmd,split_autoconf) + $(call cmd,gen_bbconfigopts) + @touch $@ + +# Generate some files +# --------------------------------------------------------------------------- + +# KERNELRELEASE can change from a few different places, meaning version.h +# needs to be updated, so this check is forced on all builds + +uts_len := 64 + +define filechk_version.h + if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \ + echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \ + exit 1; \ + fi; \ + (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \ + echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)`; \ + echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))'; \ + ) +endef + +# --------------------------------------------------------------------------- + +PHONY += depend dep +depend dep: + @echo '*** Warning: make $@ is unnecessary now.' + +# --------------------------------------------------------------------------- +# Modules + +ifdef _BB_DISABLED_CONFIG_MODULES + +# By default, build modules as well + +all: modules + +# Build modules + +PHONY += modules +modules: $(busybox-dirs) $(if $(KBUILD_BUILTIN),busybox) + @echo ' Building modules, stage 2.'; + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + + +# Target to prepare building external modules +PHONY += modules_prepare +modules_prepare: prepare scripts + +# Target to install modules +PHONY += modules_install +modules_install: _modinst_ _modinst_post + +PHONY += _modinst_ +_modinst_: + @if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \ + echo "Warning: you may need to install module-init-tools"; \ + echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\ + sleep 1; \ + fi + @rm -rf $(MODLIB)/kernel + @rm -f $(MODLIB)/source + @mkdir -p $(MODLIB)/kernel + @ln -s $(srctree) $(MODLIB)/source + @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ + rm -f $(MODLIB)/build ; \ + ln -s $(objtree) $(MODLIB)/build ; \ + fi + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst + +# If System.map exists, run depmod. This deliberately does not have a +# dependency on System.map since that would run the dependency tree on +# busybox. This depmod is only for convenience to give the initial +# boot a modules.dep even before / is mounted read-write. However the +# boot script depmod is the master version. +ifeq "$(strip $(INSTALL_MOD_PATH))" "" +depmod_opts := +else +depmod_opts := -b $(INSTALL_MOD_PATH) -r +endif +PHONY += _modinst_post +_modinst_post: _modinst_ + if [ -r System.map -a -x $(DEPMOD) ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi + +else # CONFIG_MODULES + +# Modules not configured +# --------------------------------------------------------------------------- + +modules modules_install: FORCE + @echo + @echo "The present busybox configuration has modules disabled." + @echo "Type 'make config' and enable loadable module support." + @echo "Then build a kernel with module support enabled." + @echo + @exit 1 + +endif # CONFIG_MODULES + +### +# Cleaning is done on three levels. +# make clean Delete most generated files +# Leave enough to build external modules +# make mrproper Delete the current configuration, and all generated files +# make distclean Remove editor backup files, patch leftover files and the like + +# Directories & files removed with 'make clean' +CLEAN_DIRS += $(MODVERDIR) _install 0_lib +CLEAN_FILES += busybox busybox_unstripped* busybox.links \ + System.map .kernelrelease \ + .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map + +# Directories & files removed with 'make mrproper' +MRPROPER_DIRS += include/config include2 +MRPROPER_FILES += .config .config.old include/asm .version .old_version \ + include/NUM_APPLETS.h \ + include/autoconf.h \ + include/bbconfigopts.h \ + include/bbconfigopts_bz2.h \ + include/usage_compressed.h \ + include/applet_tables.h \ + include/applets.h \ + include/usage.h \ + applets/usage \ + .kernelrelease Module.symvers tags TAGS cscope* \ + busybox_old + +# clean - Delete most, but leave enough to build external modules +# +clean: rm-dirs := $(CLEAN_DIRS) +clean: rm-files := $(CLEAN_FILES) +clean-dirs := $(addprefix _clean_,$(srctree) $(busybox-alldirs)) + +PHONY += $(clean-dirs) clean archclean +$(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +clean: archclean $(clean-dirs) + $(call cmd,rmdirs) + $(call cmd,rmfiles) + @find . $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ + -type f -print | xargs rm -f + +PHONY += doc-clean +doc-clean: rm-files := docs/busybox.pod \ + docs/BusyBox.html docs/busybox.1 docs/BusyBox.txt +doc-clean: + $(call cmd,rmfiles) + +# mrproper - Delete all generated files, including .config +# +mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) +mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) +mrproper-dirs := $(addprefix _mrproper_,scripts) + +PHONY += $(mrproper-dirs) mrproper archmrproper +$(mrproper-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@) + +mrproper: clean archmrproper $(mrproper-dirs) + $(call cmd,rmdirs) + $(call cmd,rmfiles) + @find . -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f + @find . -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f + +# distclean +# +PHONY += distclean + +distclean: mrproper + @find $(srctree) $(RCS_FIND_IGNORE) \ + \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ + -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ + -o -name '.*.rej' -o -name '*.tmp' -o -size 0 \ + -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ + -type f -print | xargs rm -f + + +# Packaging of the kernel to various formats +# --------------------------------------------------------------------------- +# rpm target kept for backward compatibility +package-dir := $(srctree)/scripts/package + +%pkg: FORCE + $(Q)$(MAKE) $(build)=$(package-dir) $@ +rpm: FORCE + $(Q)$(MAKE) $(build)=$(package-dir) $@ + + +# Brief documentation of the typical targets used +# --------------------------------------------------------------------------- + +boards := $(wildcard $(srctree)/configs/*_defconfig) +boards := $(notdir $(boards)) + +-include $(srctree)/Makefile.help + +# Documentation targets +# --------------------------------------------------------------------------- +%docs: scripts_basic FORCE + $(Q)$(MAKE) $(build)=Documentation/DocBook $@ + +else # KBUILD_EXTMOD + +### +# External module support. +# When building external modules the kernel used as basis is considered +# read-only, and no consistency checks are made and the make +# system is not used on the basis kernel. If updates are required +# in the basis kernel ordinary make commands (without M=...) must +# be used. +# +# The following are the only valid targets when building external +# modules. +# make M=dir clean Delete all automatically generated files +# make M=dir modules Make all modules in specified dir +# make M=dir Same as 'make M=dir modules' +# make M=dir modules_install +# Install the modules build in the module directory +# Assumes install directory is already created + +# We are always building modules +KBUILD_MODULES := 1 +PHONY += crmodverdir +crmodverdir: + $(Q)mkdir -p $(MODVERDIR) + $(Q)rm -f $(MODVERDIR)/* + +PHONY += $(objtree)/Module.symvers +$(objtree)/Module.symvers: + @test -e $(objtree)/Module.symvers || ( \ + echo; \ + echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \ + echo " is missing; modules will have no dependencies and modversions."; \ + echo ) + +module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD)) +PHONY += $(module-dirs) modules +$(module-dirs): crmodverdir $(objtree)/Module.symvers + $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@) + +modules: $(module-dirs) + @echo ' Building modules, stage 2.'; + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + +PHONY += modules_install +modules_install: _emodinst_ _emodinst_post + +install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra) +PHONY += _emodinst_ +_emodinst_: + $(Q)mkdir -p $(MODLIB)/$(install-dir) + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst + +# Run depmod only is we have System.map and depmod is executable +quiet_cmd_depmod = DEPMOD $(KERNELRELEASE) + cmd_depmod = if [ -r System.map -a -x $(DEPMOD) ]; then \ + $(DEPMOD) -ae -F System.map \ + $(if $(strip $(INSTALL_MOD_PATH)), \ + -b $(INSTALL_MOD_PATH) -r) \ + $(KERNELRELEASE); \ + fi + +PHONY += _emodinst_post +_emodinst_post: _emodinst_ + $(call cmd,depmod) + +clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD)) + +PHONY += $(clean-dirs) clean +$(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +clean: rm-dirs := $(MODVERDIR) +clean: $(clean-dirs) + $(call cmd,rmdirs) + @find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ + -type f -print | xargs rm -f + +# Dummies... +PHONY += prepare scripts +prepare: ; +scripts: ; +endif # KBUILD_EXTMOD + +# Generate tags for editors +# --------------------------------------------------------------------------- + +#We want __srctree to totally vanish out when KBUILD_OUTPUT is not set +#(which is the most common case IMHO) to avoid unneeded clutter in the big tags file. +#Adding $(srctree) adds about 20M on i386 to the size of the output file! + +ifeq ($(src),$(obj)) +__srctree = +else +__srctree = $(srctree)/ +endif + +ifeq ($(ALLSOURCE_ARCHS),) +ifeq ($(ARCH),um) +ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH) +else +ALLINCLUDE_ARCHS := $(ARCH) +endif +else +#Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour. +ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS) +endif + +ALLSOURCE_ARCHS := $(ARCH) + +define all-sources + ( find $(__srctree) $(RCS_FIND_IGNORE) \ + \( -name include -o -name arch \) -prune -o \ + -name '*.[chS]' -print; \ + for ARCH in $(ALLSOURCE_ARCHS) ; do \ + find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + done ; \ + find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + find $(__srctree)include $(RCS_FIND_IGNORE) \ + \( -name config -o -name 'asm-*' \) -prune \ + -o -name '*.[chS]' -print; \ + for ARCH in $(ALLINCLUDE_ARCHS) ; do \ + find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + done ; \ + find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print ) +endef + +quiet_cmd_cscope-file = FILELST cscope.files + cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files + +quiet_cmd_cscope = MAKE cscope.out + cmd_cscope = cscope -b + +cscope: FORCE + $(call cmd,cscope-file) + $(call cmd,cscope) + +quiet_cmd_TAGS = MAKE $@ +define cmd_TAGS + rm -f $@; \ + ETAGSF=`etags --version | grep -i exuberant >/dev/null && \ + echo "-I __initdata,__exitdata,__acquires,__releases \ + -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ + --extra=+f --c-kinds=+px"`; \ + $(all-sources) | xargs etags $$ETAGSF -a +endef + +TAGS: FORCE + $(call cmd,TAGS) + + +quiet_cmd_tags = MAKE $@ +define cmd_tags + rm -f $@; \ + CTAGSF=`ctags --version | grep -i exuberant >/dev/null && \ + echo "-I __initdata,__exitdata,__acquires,__releases \ + -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ + --extra=+f --c-kinds=+px"`; \ + $(all-sources) | xargs ctags $$CTAGSF -a +endef + +tags: FORCE + $(call cmd,tags) + + +# Scripts to check various things for consistency +# --------------------------------------------------------------------------- + +includecheck: + find * $(RCS_FIND_IGNORE) \ + -name '*.[hcS]' -type f -print | sort \ + | xargs $(PERL) -w scripts/checkincludes.pl + +versioncheck: + find * $(RCS_FIND_IGNORE) \ + -name '*.[hcS]' -type f -print | sort \ + | xargs $(PERL) -w scripts/checkversion.pl + +namespacecheck: + $(PERL) $(srctree)/scripts/namespace.pl + +endif #ifeq ($(config-targets),1) +endif #ifeq ($(mixed-targets),1) + +PHONY += checkstack +checkstack: + $(OBJDUMP) -d busybox $$(find . -name '*.ko') | \ + $(PERL) $(src)/scripts/checkstack.pl $(ARCH) + +kernelrelease: + $(if $(wildcard .kernelrelease), $(Q)echo $(KERNELRELEASE), \ + $(error kernelrelease not valid - run 'make *config' to update it)) +kernelversion: + @echo $(KERNELVERSION) + +# Single targets +# --------------------------------------------------------------------------- +# Single targets are compatible with: +# - build whith mixed source and output +# - build with separate output dir 'make O=...' +# - external modules +# +# target-dir => where to store outputfile +# build-dir => directory in kernel source tree to use + +ifeq ($(KBUILD_EXTMOD),) + build-dir = $(patsubst %/,%,$(dir $@)) + target-dir = $(dir $@) +else + zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@))) + build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash)) + target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@)) +endif + +%.s: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.i: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.lst: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.s: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) + +# Modules +%/: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) +/: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) + +%.ko: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) $(@:.ko=.o) + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + +# FIXME Should go into a make.lib or something +# =========================================================================== + +quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) + cmd_rmdirs = rm -rf $(rm-dirs) + +quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) + cmd_rmfiles = rm -f $(rm-files) + + +a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \ + $(NOSTDINC_FLAGS) $(CPPFLAGS) \ + $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) + +quiet_cmd_as_o_S = AS $@ +cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< + +# read all saved command lines + +targets := $(wildcard $(sort $(targets))) +cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + $(cmd_files): ; # Do not try to update included dependency files + include $(cmd_files) +endif + +# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir +# Usage: +# $(Q)$(MAKE) $(clean)=dir +clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj + +endif # skip-makefile + +PHONY += FORCE +FORCE: + +-include $(srctree)/Makefile.custom + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable se we can use it in if_changed and friends. +.PHONY: $(PHONY) diff --git a/Makefile.custom b/Makefile.custom new file mode 100644 index 000000000..6da79e6e4 --- /dev/null +++ b/Makefile.custom @@ -0,0 +1,178 @@ +# ========================================================================== +# Build system +# ========================================================================== + +busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h include/applets.h + $(Q)-$(SHELL) $^ >$@ + +.PHONY: install +ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y) +INSTALL_OPTS:= --symlinks +endif +ifeq ($(CONFIG_INSTALL_APPLET_HARDLINKS),y) +INSTALL_OPTS:= --hardlinks +endif +ifeq ($(CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS),y) +ifeq ($(CONFIG_INSTALL_SH_APPLET_SYMLINK),y) +INSTALL_OPTS:= --sw-sh-sym +endif +ifeq ($(CONFIG_INSTALL_SH_APPLET_HARDLINK),y) +INSTALL_OPTS:= --sw-sh-hard +endif +ifeq ($(CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER),y) +INSTALL_OPTS:= --scriptwrapper +endif +endif +install: $(srctree)/applets/install.sh busybox busybox.links + $(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \ + $(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS) +ifeq ($(strip $(CONFIG_FEATURE_SUID)),y) + @echo + @echo + @echo -------------------------------------------------- + @echo You will probably need to make your busybox binary + @echo setuid root to ensure all configured applets will + @echo work properly. + @echo -------------------------------------------------- + @echo +endif + +uninstall: busybox.links + rm -f $(CONFIG_PREFIX)/bin/busybox + for i in `cat busybox.links` ; do rm -f $(CONFIG_PREFIX)$$i; done +ifneq ($(strip $(DO_INSTALL_LIBS)),n) + for i in $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS); do \ + rm -f $(CONFIG_PREFIX)$$i; \ + done +endif + +# Not very elegant: copies testsuite to objdir... +# (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be)) +.PHONY: check +.PHONY: test +check test: busybox busybox.links + test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree) + bindir=$(objtree) srcdir=$(srctree)/testsuite \ + $(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)" + +.PHONY: release +release: distclean + cd ..; \ + rm -r -f busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION); \ + cp -pPR busybox busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) && { \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \ + -name .svn \ + -print \ + -exec rm -r -f {} \; ; \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \ + -name .git \ + -print \ + -exec rm -r -f {} \; ; \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \ + -name .\#* \ + -print \ + -exec rm -f {} \; ; \ + tar -czf busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION).tar.gz \ + busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ ; } + +.PHONY: checkhelp +checkhelp: + $(Q)$(srctree)/scripts/checkhelp.awk \ + $(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./))) + +.PHONY: sizes +sizes: busybox_unstripped + $(NM) --size-sort $(<) + +.PHONY: bloatcheck +bloatcheck: busybox_old busybox_unstripped + @$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped + @$(CROSS_COMPILE)size busybox_old busybox_unstripped + +.PHONY: baseline +baseline: busybox_unstripped + @mv busybox_unstripped busybox_old + +.PHONY: objsizes +objsizes: busybox_unstripped + $(srctree)/scripts/objsizes + +.PHONY: stksizes +stksizes: busybox_unstripped + $(CROSS_COMPILE)objdump -d busybox_unstripped | $(srctree)/scripts/checkstack.pl $(ARCH) | uniq + +.PHONY: bigdata +bigdata: busybox_unstripped + $(CROSS_COMPILE)nm --size-sort busybox_unstripped | grep -vi ' [trw] ' + +# Documentation Targets +.PHONY: doc +doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html + +# FIXME: Doesn't belong here + cmd_doc = + quiet_cmd_doc = $(Q)echo " DOC $(@F)" +silent_cmd_doc = +disp_doc = $($(quiet)cmd_doc) + +# sed adds newlines after "Options:" etc, +# this is needed in order to get good BusyBox.{1,txt,html} +docs/busybox.pod: $(srctree)/docs/busybox_header.pod \ + include/usage.h \ + $(srctree)/docs/busybox_footer.pod \ + applets/usage_pod + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-( \ + cat $(srctree)/docs/busybox_header.pod; \ + echo; \ + applets/usage_pod | sed 's/^[A-Za-z][A-Za-z ]*[a-z]:$$/&\n/'; \ + cat $(srctree)/docs/busybox_footer.pod; \ + ) > docs/busybox.pod + +docs/BusyBox.txt: docs/busybox.pod + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-pod2text $< > $@ + +docs/busybox.1: docs/busybox.pod + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@ + +docs/BusyBox.html: docs/busybox.net/BusyBox.html + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-rm -f docs/BusyBox.html + $(Q)-cp docs/busybox.net/BusyBox.html docs/BusyBox.html + +docs/busybox.net/BusyBox.html: docs/busybox.pod + $(Q)-mkdir -p docs/busybox.net + $(Q)-pod2html --noindex $< > $@ + $(Q)-rm -f pod2htm* + +# documentation, cross-reference +# Modern distributions already ship synopsis packages (e.g. debian) +# If you have an old distribution go to http://synopsis.fresco.org/ +syn_tgt = $(wildcard $(patsubst %,%/*.c,$(busybox-alldirs))) +syn = $(patsubst %.c, %.syn, $(syn_tgt)) + +comma:= , +brace_open:= ( +brace_close:= ) + +SYN_CPPFLAGS := $(strip $(CPPFLAGS) $(EXTRA_CPPFLAGS)) +SYN_CPPFLAGS := $(subst $(brace_open),\$(brace_open),$(SYN_CPPFLAGS)) +SYN_CPPFLAGS := $(subst $(brace_close),\$(brace_close),$(SYN_CPPFLAGS)) +#SYN_CPPFLAGS := $(subst ",\",$(SYN_CPPFLAGS)) +#") +#SYN_CPPFLAGS := [$(patsubst %,'%'$(comma),$(SYN_CPPFLAGS))''] + +%.syn: %.c + synopsis -p C -l Comments.SSDFilter,Comments.Previous -Wp,preprocess=True,cppflags="'$(SYN_CPPFLAGS)'" -o $@ $< + +.PHONY: html +html: $(syn) + synopsis -f HTML -Wf,title="'BusyBox Documentation'" -o $@ $^ + +-include $(srctree)/Makefile.local diff --git a/Makefile.flags b/Makefile.flags new file mode 100644 index 000000000..7e1c6030c --- /dev/null +++ b/Makefile.flags @@ -0,0 +1,137 @@ +# ========================================================================== +# Build system +# ========================================================================== + +BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) +export BB_VER +SKIP_STRIP ?= n + +# -std=gnu99 needed for [U]LLONG_MAX on some systems +CPPFLAGS += $(call cc-option,-std=gnu99,) + +CPPFLAGS += \ + -Iinclude -Ilibbb \ + $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include -I$(srctree)/libbb) \ + -include include/autoconf.h \ + -D_GNU_SOURCE -DNDEBUG \ + $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \ + -D"BB_VER=KBUILD_STR($(BB_VER))" -DBB_BT=AUTOCONF_TIMESTAMP + +CFLAGS += $(call cc-option,-Wall,) +CFLAGS += $(call cc-option,-Wshadow,) +CFLAGS += $(call cc-option,-Wwrite-strings,) +CFLAGS += $(call cc-option,-Wundef,) +CFLAGS += $(call cc-option,-Wstrict-prototypes,) +CFLAGS += $(call cc-option,-Wunused -Wunused-parameter,) +CFLAGS += $(call cc-option,-Wunused-function -Wunused-value,) +CFLAGS += $(call cc-option,-Wmissing-prototypes -Wmissing-declarations,) +# warn about C99 declaration after statement +CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) +# If you want to add more -Wsomething above, make sure that it is +# still possible to build bbox without warnings. + +ifeq ($(CONFIG_WERROR),y) +CFLAGS += $(call cc-option,-Werror,) +## TODO: +## gcc version 4.4.0 20090506 (Red Hat 4.4.0-4) (GCC) is a PITA: +## const char *ptr; ... off_t v = *(off_t*)ptr; -> BOOM +## and no easy way to convince it to shut the hell up. +## We have a lot of such things all over the place. +## Classic *(off_t*)(void*)ptr does not work, +## and I am unwilling to do crazy gcc specific ({ void *ppp = ...; }) +## stuff in macros. This would obfuscate the code too much. +## Maybe try __attribute__((__may_alias__))? +#CFLAGS += $(call cc-ifversion, -eq, 0404, -fno-strict-aliasing) +endif +# gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action() +CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition) + +CFLAGS += $(call cc-option,-fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer -ffunction-sections -fdata-sections,) +# -fno-guess-branch-probability: prohibit pseudo-random guessing +# of branch probabilities (hopefully makes bloatcheck more stable): +CFLAGS += $(call cc-option,-fno-guess-branch-probability,) +CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,) +CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1,) + +# FIXME: These warnings are at least partially to be concerned about and should +# be fixed.. +#CFLAGS += $(call cc-option,-Wconversion,) + +ifneq ($(CONFIG_DEBUG),y) +CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) +else +CFLAGS += $(call cc-option,-g,) +#CFLAGS += "-D_FORTIFY_SOURCE=2" +ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) +CFLAGS += $(call cc-option,-O0,) +else +CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) +endif +endif + +# If arch/$(ARCH)/Makefile did not override it (with, say, -fPIC)... +ARCH_FPIC ?= -fpic +ARCH_FPIE ?= -fpie +ARCH_PIE ?= -pie + +ifeq ($(CONFIG_BUILD_LIBBUSYBOX),y) +# on i386: 14% smaller libbusybox.so +# (code itself is 9% bigger, we save on relocs/PLT/GOT) +CFLAGS += $(ARCH_FPIC) +# and another 4% reduction of libbusybox.so: +# (external entry points must be marked EXTERNALLY_VISIBLE) +CFLAGS += $(call cc-option,-fvisibility=hidden) +endif + +ifeq ($(CONFIG_STATIC),y) +CFLAGS_busybox += -static +endif + +ifeq ($(CONFIG_PIE),y) +CFLAGS_busybox += $(ARCH_PIE) +CFLAGS += $(ARCH_FPIE) +endif + +ifneq ($(CONFIG_EXTRA_CFLAGS),) +CFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_CFLAGS))) +#")) +endif + +ifneq ($(CONFIG_CROSS_COMPILER_PREFIX),"arm-linux-androideabi-") +LDLIBS += m crypt +else +# Android libc has no crypt. TODO: make a generic CONFIG_LINK_WITH_CRYPT option? +LDLIBS += m +endif + +ifeq ($(CONFIG_PAM),y) +LDLIBS += pam pam_misc +endif + +ifeq ($(CONFIG_SELINUX),y) +LDLIBS += selinux sepol +endif + +ifeq ($(CONFIG_EFENCE),y) +LDLIBS += efence +endif + +ifeq ($(CONFIG_DMALLOC),y) +LDLIBS += dmalloc +endif + +# If a flat binary should be built, CFLAGS_busybox="-elf2flt" +# env var should be set for make invocation. +# Here we check whether CFLAGS_busybox indeed contains that flag. +# (For historical reasons, we also check LDFLAGS, which doesn't +# seem to be entirely correct variable to put "-elf2flt" into). +W_ELF2FLT = -elf2flt +ifneq (,$(findstring $(W_ELF2FLT),$(LDFLAGS) $(CFLAGS_busybox))) +SKIP_STRIP = y +endif + +# Busybox is a stack-fatty so make sure we increase default size +# TODO: use "make stksizes" to find & fix big stack users +# (we stole scripts/checkstack.pl from the kernel... thanks guys!) +# Reduced from 20k to 16k in 1.9.0. +FLTFLAGS += -s 16000 diff --git a/Makefile.help b/Makefile.help new file mode 100644 index 000000000..119dd6f89 --- /dev/null +++ b/Makefile.help @@ -0,0 +1,48 @@ +# ========================================================================== +# Build system +# ========================================================================== + +help: + @echo 'Cleaning:' + @echo ' clean - delete temporary files created by build' + @echo ' distclean - delete all non-source files (including .config)' + @echo ' doc-clean - delete all generated documentation' + @echo + @echo 'Build:' + @echo ' all - Executable and documentation' + @echo ' busybox - the swiss-army executable' + @echo ' doc - docs/BusyBox.{txt,html,1}' + @echo ' html - create html-based cross-reference' + @echo + @echo 'Configuration:' + @echo ' allnoconfig - disable all symbols in .config' + @echo ' allyesconfig - enable all symbols in .config (see defconfig)' + @echo ' config - text based configurator (of last resort)' + @echo ' defconfig - set .config to largest generic configuration' + @echo ' menuconfig - interactive curses-based configurator' + @echo ' oldconfig - resolve any unresolved symbols in .config' + @echo ' hosttools - build sed for the host.' + @echo ' You can use these commands if the commands on the host' + @echo ' is unusable. Afterwards use it like:' + @echo ' make SED="$(objtree)/sed"' + @$(if $(boards), \ + $(foreach b, $(boards), \ + printf " %-21s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \ + echo '') + @echo + @echo 'Installation:' + @echo ' install - install busybox into CONFIG_PREFIX' + @echo ' uninstall' + @echo + @echo 'Development:' + @echo ' baseline - create busybox_old for bloatcheck.' + @echo ' bloatcheck - show size difference between old and new versions' + @echo ' check - run the test suite for all applets' + @echo ' checkhelp - check for missing help-entries in Config.in' + @echo ' randconfig - generate a random configuration' + @echo ' release - create a distribution tarball' + @echo ' sizes - show size of all enabled busybox symbols' + @echo ' objsizes - show size of each .o object built' + @echo ' bigdata - show data objects, biggest first' + @echo ' stksizes - show stack users, biggest first' + @echo diff --git a/README b/README new file mode 100644 index 000000000..b940e357a --- /dev/null +++ b/README @@ -0,0 +1,204 @@ +Please see the LICENSE file for details on copying and usage. +Please refer to the INSTALL file for instructions on how to build. + +What is busybox: + + BusyBox combines tiny versions of many common UNIX utilities into a single + small executable. It provides minimalist replacements for most of the + utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs, + file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps, + sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The utilities + in BusyBox often have fewer options than their full-featured cousins; + however, the options that are included provide the expected functionality + and behave very much like their larger counterparts. + + BusyBox has been written with size-optimization and limited resources in + mind, both to produce small binaries and to reduce run-time memory usage. + Busybox is also extremely modular so you can easily include or exclude + commands (or features) at compile time. This makes it easy to customize + embedded systems; to create a working system, just add /dev, /etc, and a + Linux kernel. Busybox (usually together with uClibc) has also been used as + a component of "thin client" desktop systems, live-CD distributions, rescue + disks, installers, and so on. + + BusyBox provides a fairly complete POSIX environment for any small system, + both embedded environments and more full featured systems concerned about + space. Busybox is slowly working towards implementing the full Single Unix + Specification V3 (http://www.opengroup.org/onlinepubs/009695399/), but isn't + there yet (and for size reasons will probably support at most UTF-8 for + internationalization). We are also interested in passing the Linux Test + Project (http://ltp.sourceforge.net). + +---------------- + +Using busybox: + + BusyBox is extremely configurable. This allows you to include only the + components and options you need, thereby reducing binary size. Run 'make + config' or 'make menuconfig' to select the functionality that you wish to + enable. (See 'make help' for more commands.) + + The behavior of busybox is determined by the name it's called under: as + "cp" it behaves like cp, as "sed" it behaves like sed, and so on. Called + as "busybox" it takes the second argument as the name of the applet to + run (I.E. "./busybox ls -l /proc"). + + The "standalone shell" mode is an easy way to try out busybox; this is a + command shell that calls the built-in applets without needing them to be + installed in the path. (Note that this requires /proc to be mounted, if + testing from a boot floppy or in a chroot environment.) + + The build automatically generates a file "busybox.links", which is used by + 'make install' to create symlinks to the BusyBox binary for all compiled in + commands. This uses the CONFIG_PREFIX environment variable to specify + where to install, and installs hardlinks or symlinks depending + on the configuration preferences. (You can also manually run + the install script at "applets/install.sh"). + +---------------- + +Downloading the current source code: + + Source for the latest released version, as well as daily snapshots, can always + be downloaded from + + http://busybox.net/downloads/ + + You can browse the up to the minute source code and change history online. + + http://git.busybox.net/busybox/ + + Anonymous GIT access is available. For instructions, check out: + + http://www.busybox.net/source.html + + For those that are actively contributing and would like to check files in, + see: + + http://busybox.net/developer.html + + The developers also have a bug and patch tracking system + (https://bugs.busybox.net) although posting a bug/patch to the mailing list + is generally a faster way of getting it fixed, and the complete archive of + what happened is the git changelog. + + Note: if you want to compile busybox in a busybox environment you must + select CONFIG_DESKTOP. + +---------------- + +Getting help: + + when you find you need help, you can check out the busybox mailing list + archives at http://busybox.net/lists/busybox/ or even join + the mailing list if you are interested. + +---------------- + +Bugs: + + if you find bugs, please submit a detailed bug report to the busybox mailing + list at busybox@busybox.net. a well-written bug report should include a + transcript of a shell session that demonstrates the bad behavior and enables + anyone else to duplicate the bug on their own machine. the following is such + an example: + + to: busybox@busybox.net + from: diligent@testing.linux.org + subject: /bin/date doesn't work + + package: busybox + version: 1.00 + + when i execute busybox 'date' it produces unexpected results. + with gnu date i get the following output: + + $ date + fri oct 8 14:19:41 mdt 2004 + + but when i use busybox date i get this instead: + + $ date + illegal instruction + + i am using debian unstable, kernel version 2.4.25-vrs2 on a netwinder, + and the latest uclibc from cvs. + + -diligent + + note the careful description and use of examples showing not only what + busybox does, but also a counter example showing what an equivalent app + does (or pointing to the text of a relevant standard). Bug reports lacking + such detail may never be fixed... Thanks for understanding. + +---------------- + +Portability: + + Busybox is developed and tested on Linux 2.4 and 2.6 kernels, compiled + with gcc (the unit-at-a-time optimizations in version 3.4 and later are + worth upgrading to get, but older versions should work), and linked against + uClibc (0.9.27 or greater) or glibc (2.2 or greater). In such an + environment, the full set of busybox features should work, and if + anything doesn't we want to know about it so we can fix it. + + There are many other environments out there, in which busybox may build + and run just fine. We just don't test them. Since busybox consists of a + large number of more or less independent applets, portability is a question + of which features work where. Some busybox applets (such as cat and rm) are + highly portable and likely to work just about anywhere, while others (such as + insmod and losetup) require recent Linux kernels with recent C libraries. + + Earlier versions of Linux and glibc may or may not work, for any given + configuration. Linux 2.2 or earlier should mostly work (there's still + some support code in things like mount.c) but this is no longer regularly + tested, and inherently won't support certain features (such as long files + and --bind mounts). The same is true for glibc 2.0 and 2.1: expect a higher + testing and debugging burden using such old infrastructure. (The busybox + developers are not very interested in supporting these older versions, but + will probably accept small self-contained patches to fix simple problems.) + + Some environments are not recommended. Early versions of uClibc were buggy + and missing many features: upgrade. Linking against libc5 or dietlibc is + not supported and not interesting to the busybox developers. (The first is + obsolete and has no known size or feature advantages over uClibc, the second + has known bugs that its developers have actively refused to fix.) Ancient + Linux kernels (2.0.x and earlier) are similarly uninteresting. + + In theory it's possible to use Busybox under other operating systems (such as + MacOS X, Solaris, Cygwin, or the BSD Fork Du Jour). This generally involves + a different kernel and a different C library at the same time. While it + should be possible to port the majority of the code to work in one of + these environments, don't be surprised if it doesn't work out of the box. If + you're into that sort of thing, start small (selecting just a few applets) + and work your way up. + + In 2005 Shaun Jackman has ported busybox to a combination of newlib + and libgloss, and some of his patches have been integrated. + +Supported hardware: + + BusyBox in general will build on any architecture supported by gcc. We + support both 32 and 64 bit platforms, and both big and little endian + systems. + + Under 2.4 Linux kernels, kernel module loading was implemented in a + platform-specific manner. Busybox's insmod utility has been reported to + work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390, + SH3/4/5, Sparc, v850e, and x86_64. Anything else probably won't work. + + The module loading mechanism for the 2.6 kernel is much more generic, and + we believe 2.6.x kernel module loading support should work on all + architectures supported by the kernel. + +---------------- + +Please feed suggestions, bug reports, insults, and bribes back to the busybox +mailing list: + + busybox@busybox.net + +and/or maintainer: + + Denys Vlasenko + diff --git a/TODO b/TODO new file mode 100644 index 000000000..8b9f87f79 --- /dev/null +++ b/TODO @@ -0,0 +1,276 @@ +Busybox TODO + +Harvest patches from +http://git.openembedded.org/cgit.cgi/openembedded/tree/recipes/busybox/ + +Stuff that needs to be done. This is organized by who plans to get around to +doing it eventually, but that doesn't mean they "own" the item. If you want to +do one of these bounce an email off the person it's listed under to see if they +have any suggestions how they plan to go about it, and to minimize conflicts +between your work and theirs. But otherwise, all of these are fair game. + +Rob Landley suggested this: + Implement bb_realpath() that can handle NULL on non-glibc. + + sh + The command shell situation is a mess. We have two different + shells that don't really share any code, and the "standalone shell" doesn't + work all that well (especially not in a chroot environment), due to apps not + being reentrant. + + Do a SUSv3 audit + Look at the full Single Unix Specification version 3 (available online at + "http://www.opengroup.org/onlinepubs/009695399/nfindex.html") and + figure out which of our apps are compliant, and what we're missing that + we might actually care about. + + Even better would be some kind of automated compliance test harness that + exercises each command line option and the various corner cases. + + Internationalization + How much internationalization should we do? + + The low hanging fruit is UTF-8 character set support. We should do this. + See TODO_unicode file. + + We also have lots of hardwired english text messages. Consolidating this + into some kind of message table not only makes translation easier, but + also allows us to consolidate redundant (or close) strings. + + We probably don't want to be bloated with locale support. (Not unless we + can cleanly export it from our underlying C library without having to + concern ourselves with it directly. Perhaps a few specific things like a + config option for "date" are low hanging fruit here?) + + What level should things happen at? How much do we care about + internationalizing the text console when X11 and xterms are so much better + at it? (There's some infrastructure here we don't implement: The + "unicode_start" and "unicode_stop" shell scripts need "vt-is-UTF8" and a + --unicode option to loadkeys. That implies a real loadkeys/dumpkeys + implementation to replace loadkmap/dumpkmap. Plus messing with console font + loading. Is it worth it, or do we just say "use X"?) + + Individual compilation of applets. + It would be nice if busybox had the option to compile to individual applets, + for people who want an alternate implementation less bloated than the gnu + utils (or simply with less political baggage), but without it being one big + executable. + + Turning libbb into a real dll is another possibility, especially if libbb + could export some of the other library interfaces we've already more or less + got the code for (like zlib). + + buildroot - Make a "dogfood" option + Busybox 1.1 will be capable of replacing most gnu packages for real world + use, such as developing software or in a live CD. It needs wider testing. + + Busybox should now be able to replace bzip2, coreutils, e2fsprogs, file, + findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps, + sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The resulting + system should be self-hosting (I.E. able to rebuild itself from source + code). This means it would need (at least) binutils, gcc, and make, or + equivalents. + + It would be a good "eating our own dogfood" test if buildroot had the option + of using a "make allyesconfig" busybox instead of the all of the above + packages. Anything that's wrong with the resulting system, we can fix. (It + would be nice to be able to upgrade busybox to be able to replace bash and + diffutils as well, but we're not there yet.) + + One example of an existing system that does this already is Firmware Linux: + http://www.landley.net/code/firmware + + initramfs + Busybox should have a sample initramfs build script. This depends on + shell, mdev, and switch_root. + + mkdep + Write a mkdep that doesn't segfault if there's a directory it doesn't + have permission to read, isn't based on manually editing the output of + lexx and yacc, doesn't make such a mess under include/config, etc. + + Group globals into unions of structures. + Go through and turn all the global and static variables into structures, + and have all those structures be in a big union shared between processes, + so busybox uses less bss. (This is a big win on nommu machines.) See + sed.c and mdev.c for examples. + + Go through bugs.busybox.net and close out all of that somehow. + This one's open to everybody, but I'll wind up doing it... + +Bernhard Reutner-Fischer suggests to look at these: + New debug options: + -Wlarger-than-127 + Cleanup any big users + Collate BUFSIZ IOBUF_SIZE MY_BUF_SIZE PIPE_PROGRESS_SIZE BUFSIZE PIPESIZE + make bb_common_bufsiz1 configurable, size wise. + make pipesize configurable, size wise. + Use bb_common_bufsiz1 throughout applets! + +As yet unclaimed: + +---- +diff + Make sure we handle empty files properly: + From the patch man page: + + you can remove a file by sending out a context diff that compares + the file to be deleted with an empty file dated the Epoch. The + file will be removed unless patch is conforming to POSIX and the + -E or --remove-empty-files option is not given. +--- +patch + Should have simple fuzz factor support to apply patches at an offset which + shouldn't take up too much space. + + And while we're at it, a new patch filename quoting format is apparently + coming soon: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 +--- +stty / catv + stty's visible() function and catv's guts are identical. Merge them into + an appropriate libbb function. +--- +struct suffix_mult + Several duplicate users of: grep -r "1024\*1024" * -B2 -A1 + Merge to a single size_suffixes[] in libbb. + Users: head tail od_bloaty hexdump and (partially as it wouldn't hurt) svlogd +--- +tail + ./busybox tail -f foo.c~ TODO + should not print fmt=header_fmt for subsequent date >> TODO; i.e. only + fmt+ if another (not the current) file did change + +Architectural issues: + +bb_close() with fsync() + We should have a bb_close() in place of normal close, with a CONFIG_ option + to not just check the return value of close() for an error, but fsync(). + Close can't reliably report anything useful because if write() accepted the + data then it either went out to the network or it's in cache or a pipe + buffer. Either way, there's no guarantee it'll make it to its final + destination before close() gets called, so there's no guarantee that any + error will be reported. + + You need to call fsync() if you care about errors that occur after write(), + but that can have a big performance impact. So make it a config option. +--- +Unify archivers + Lots of archivers have the same general infrastructure. The directory + traversal code should be factored out, and the guts of each archiver could + be some setup code and a series of callbacks for "add this file", + "add this directory", "add this symlink" and so on. + + This could clean up tar and zip, and make it cheaper to add cpio and ar + write support, and possibly even cheaply add things like mkisofs or + mksquashfs someday, if they become relevant. +--- +Text buffer support. + Several existing applets (sort, vi, less...) read + a whole file into memory and act on it. Use open_read_close(). +--- +Memory Allocation + We have a CONFIG_BUFFER mechanism that lets us select whether to do memory + allocation on the stack or the heap. Unfortunately, we're not using it much. + We need to audit our memory allocations and turn a lot of malloc/free calls + into RESERVE_CONFIG_BUFFER/RELEASE_CONFIG_BUFFER. + For a start, see e.g. make EXTRA_CFLAGS=-Wlarger-than-64 + + And while we're at it, many of the CONFIG_FEATURE_CLEAN_UP #ifdefs will be + optimized out by the compiler in the stack allocation case (since there's no + free for an alloca()), and this means that various cleanup loops that just + call free might also be optimized out by the compiler if written right, so + we can yank those #ifdefs too, and generally clean up the code. +--- +FEATURE_CLEAN_UP + This is more an unresolved issue than a to-do item. More thought is needed. + + Normally we rely on exit() to free memory, close files and unmap segments + for us. This makes most calls to free(), close(), and unmap() optional in + busybox applets that don't intend to run for very long, and optional stuff + can be omitted to save size. + + The idea was raised that we could simulate fork/exit with setjmp/longjmp + for _really_ brainless embedded systems, or speed up the standalone shell + by not forking. Doing so would require a reliable FEATURE_CLEAN_UP. + Unfortunately, this isn't as easy as it sounds. + + The problem is, lots of things exit(), sometimes unexpectedly (xmalloc()) + and sometimes reliably (bb_perror_msg_and_die() or show_usage()). This + jumps out of the normal flow control and bypasses any cleanup code we + put at the end of our applets. + + It's possible to add hooks to libbb functions like xmalloc() and xopen() + to add their entries to a linked list, which could be traversed and + freed/closed automatically. (This would need to be able to free just the + entries after a checkpoint to be usable for a forkless standalone shell. + You don't want to free the shell's own resources.) + + Right now, FEATURE_CLEAN_UP is more or less a debugging aid, to make things + like valgrind happy. It's also documentation of _what_ we're trusting + exit() to clean up for us. But new infrastructure to auto-free stuff would + render the existing FEATURE_CLEAN_UP code redundant. + + For right now, exit() handles it just fine. + + +Minor stuff: + watchdog.c could autodetect the timer duration via: + if(!ioctl (fd, WDIOC_GETTIMEOUT, &tmo)) timer_duration = 1 + (tmo / 2); + Unfortunately, that needs linux/watchdog.h and that contains unfiltered + kernel types on some distros, which breaks the build. +--- + use bb_error_msg where appropriate: See + egrep "(printf.*\([[:space:]]*(stderr|2)|[^_]write.*\([[:space:]]*(stderr|2))" +--- + use bb_perror_msg where appropriate: See + egrep "[^_]perror" +--- + possible code duplication ingroup() and is_a_group_member() +--- + Move __get_hz() to a better place and (re)use it in route.c, ash.c +--- + See grep -r strtod + Alot of duplication that wants cleanup. +--- + in_ether duplicated in network/{interface,ifconfig}.c +--- + unify progress_meter. wget, flash_eraseall, pipe_progress, fbsplash, setfiles. +--- + support start-stop-daemon -d +--- +vdprintf() -> similar sized functionality +--- + +(TODO list after discussion 11.05.2009) + +* shrink tc/brctl/ip + tc/brctl seem like fairly large things to try and tackle in your timeframe, + and i think people have posted attempts in the past. Adding additional + options to ip though seems reasonable. + +* add tests for some applets + +* implement POSIX utilities and audit them for POSIX conformance. then + audit them for GNU conformance. then document all your findings in a new + doc/conformance.txt file while perhaps implementing some of the missing + features. + you can find the latest POSIX documentation (1003.1-2008) here: + http://www.opengroup.org/onlinepubs/9699919799/ + and the complete list of all utilities that POSIX covers: + http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html + The first step would to generate a file/matrix what is already archived + (also IPV6) + +* implement 'at' + +* rpcbind (former portmap) or equivalent + so that we don't have to use -o nolock on nfs mounts + +* check IPV6 compliance + +* generate a mini example using kernel+busybox only (+libc) for example + +* more support for advanced linux 2.6.x features, see: iotop + most likely there is more + +* even more support for statistics: mpstat, iostat, powertop.... diff --git a/TODO_unicode b/TODO_unicode new file mode 100644 index 000000000..b310e8d4d --- /dev/null +++ b/TODO_unicode @@ -0,0 +1,45 @@ +Already fixed applets: +cal +lsmod +df +dumpleases + +Applets which may need unicode handling (more extensive than sanitizing +of filenames in error messages): + +ls - work in progress +expand, unexpand - uses unicode_strlen, not scrlen +ash, hush through lineedit - uses unicode_strlen, not scrlen +top - need to sanitize process args +ps - need to sanitize process args +less +more +vi +ed +cut +awk +sed +tr +grep egrep fgrep +fold +sort +head, tail +catv - "display nonprinting chars" - what this could mean for unicode? +wc +chat +dumpkmap +last - just line up columns +man +microcom +strings +watch + +Unsure, may need fixing: + +hostname - do we really want to protect against bad chars in it? +patch +addgroup, adduser, delgroup, deluser +telnet +telnetd +od +printf diff --git a/applets/.gitignore b/applets/.gitignore new file mode 100644 index 000000000..459938d67 --- /dev/null +++ b/applets/.gitignore @@ -0,0 +1,3 @@ +/applet_tables +/usage +/usage_pod diff --git a/applets/Kbuild.src b/applets/Kbuild.src new file mode 100644 index 000000000..b61239948 --- /dev/null +++ b/applets/Kbuild.src @@ -0,0 +1,47 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +obj-y := +obj-y += applets.o + +hostprogs-y:= +hostprogs-y += usage usage_pod applet_tables + +always:= $(hostprogs-y) + +# Generated files need additional love + +# This trick decreases amount of rebuilds +# if tree is merely renamed/copied +ifeq ($(srctree),$(objtree)) +srctree_slash = +else +srctree_slash = $(srctree)/ +endif + +HOSTCFLAGS_usage.o = -I$(srctree_slash)include -Iinclude +HOSTCFLAGS_usage_pod.o = -I$(srctree_slash)include -Iinclude + +applets/applets.o: include/usage_compressed.h include/applet_tables.h + +applets/applet_tables: .config include/applets.h +applets/usage: .config include/applets.h +applets/usage_pod: .config include/applet_tables.h include/applets.h + +quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h + cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets + +include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed + $(call cmd,gen_usage_compressed) + +quiet_cmd_gen_applet_tables = GEN include/applet_tables.h + cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h + +include/applet_tables.h: applets/applet_tables + $(call cmd,gen_applet_tables) + +include/NUM_APPLETS.h: applets/applet_tables + $(call cmd,gen_applet_tables) diff --git a/applets/applet_tables.c b/applets/applet_tables.c new file mode 100644 index 000000000..3859d7326 --- /dev/null +++ b/applets/applet_tables.c @@ -0,0 +1,157 @@ +/* vi: set sw=4 ts=4: */ +/* + * Applet table generator. + * Runs on host and produces include/applet_tables.h + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include +#include +#include +#include +#include +#include + +#undef ARRAY_SIZE +#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0]))) + +#include "../include/autoconf.h" +#include "../include/applet_metadata.h" + +struct bb_applet { + const char *name; + const char *main; + enum bb_install_loc_t install_loc; + enum bb_suid_t need_suid; + /* true if instead of fork(); exec("applet"); waitpid(); + * one can do fork(); exit(applet_main(argc,argv)); waitpid(); */ + unsigned char noexec; + /* Even nicer */ + /* true if instead of fork(); exec("applet"); waitpid(); + * one can simply call applet_main(argc,argv); */ + unsigned char nofork; +}; + +/* Define struct bb_applet applets[] */ +#include "../include/applets.h" + +enum { NUM_APPLETS = ARRAY_SIZE(applets) }; + +static int offset[NUM_APPLETS]; + +static int cmp_name(const void *a, const void *b) +{ + const struct bb_applet *aa = a; + const struct bb_applet *bb = b; + return strcmp(aa->name, bb->name); +} + +int main(int argc, char **argv) +{ + int i; + int ofs; + unsigned MAX_APPLET_NAME_LEN = 1; + + qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name); + + ofs = 0; + for (i = 0; i < NUM_APPLETS; i++) { + offset[i] = ofs; + ofs += strlen(applets[i].name) + 1; + } + /* We reuse 4 high-order bits of offset array for other purposes, + * so if they are indeed needed, refuse to proceed */ + if (ofs > 0xfff) + return 1; + if (!argv[1]) + return 1; + + i = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (i < 0) + return 1; + dup2(i, 1); + + /* Keep in sync with include/busybox.h! */ + + printf("/* This is a generated file, don't edit */\n\n"); + + printf("#define NUM_APPLETS %u\n", NUM_APPLETS); + if (NUM_APPLETS == 1) { + printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name); + printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].name); + } + printf("\n"); + + //printf("#ifndef SKIP_definitions\n"); + printf("const char applet_names[] ALIGN1 = \"\"\n"); + for (i = 0; i < NUM_APPLETS; i++) { + printf("\"%s\" \"\\0\"\n", applets[i].name); + if (MAX_APPLET_NAME_LEN < strlen(applets[i].name)) + MAX_APPLET_NAME_LEN = strlen(applets[i].name); + } + printf(";\n\n"); + + printf("#ifndef SKIP_applet_main\n"); + printf("int (*const applet_main[])(int argc, char **argv) = {\n"); + for (i = 0; i < NUM_APPLETS; i++) { + printf("%s_main,\n", applets[i].main); + } + printf("};\n"); + printf("#endif\n\n"); + + printf("const uint16_t applet_nameofs[] ALIGN2 = {\n"); + for (i = 0; i < NUM_APPLETS; i++) { + printf("0x%04x,\n", + offset[i] +#if ENABLE_FEATURE_PREFER_APPLETS + + (applets[i].nofork << 12) + + (applets[i].noexec << 13) +#endif +#if ENABLE_FEATURE_SUID + + (applets[i].need_suid << 14) /* 2 bits */ +#endif + ); + } + printf("};\n\n"); + +#if ENABLE_FEATURE_INSTALLER + printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); + i = 0; + while (i < NUM_APPLETS) { + int v = applets[i].install_loc; /* 3 bits */ + if (++i < NUM_APPLETS) + v |= applets[i].install_loc << 4; /* 3 bits */ + printf("0x%02x,\n", v); + i++; + } + printf("};\n"); +#endif + //printf("#endif /* SKIP_definitions */\n"); + printf("\n"); + printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN); + + if (argv[2]) { + char line_old[80]; + char line_new[80]; + FILE *fp; + + line_old[0] = 0; + fp = fopen(argv[2], "r"); + if (fp) { + fgets(line_old, sizeof(line_old), fp); + fclose(fp); + } + sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS); + if (strcmp(line_old, line_new) != 0) { + fp = fopen(argv[2], "w"); + if (!fp) + return 1; + fputs(line_new, fp); + } + } + + return 0; +} diff --git a/applets/applets.c b/applets/applets.c new file mode 100644 index 000000000..98c2b44f5 --- /dev/null +++ b/applets/applets.c @@ -0,0 +1,16 @@ +/* vi: set sw=4 ts=4: */ +/* + * Stub for linking busybox binary against libbusybox. + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "busybox.h" + +#if ENABLE_BUILD_LIBBUSYBOX +int main(int argc UNUSED_PARAM, char **argv) +{ + return lbb_main(argv); +} +#endif diff --git a/applets/busybox.mkll b/applets/busybox.mkll new file mode 100755 index 000000000..68dbf2162 --- /dev/null +++ b/applets/busybox.mkll @@ -0,0 +1,24 @@ +#!/bin/sh +# Make busybox links list file. + +# input $1: full path to Config.h +# input $2: full path to applets.h +# output (stdout): list of pathnames that should be linked to busybox + +# Maintainer: Larry Doolittle + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +CONFIG_H=${1:-include/autoconf.h} +APPLETS_H=${2:-include/applets.h} +$HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H | + awk '/^[ \t]*LINK/{ + dir=substr($2,7) + gsub("_","/",dir) + if(dir=="/ROOT") dir="" + file=$3 + gsub("\"","",file) + if (file=="busybox") next + print tolower(dir) "/" file + }' diff --git a/applets/individual.c b/applets/individual.c new file mode 100644 index 000000000..1e74e4ce5 --- /dev/null +++ b/applets/individual.c @@ -0,0 +1,24 @@ +/* Minimal wrapper to build an individual busybox applet. + * + * Copyright 2005 Rob Landley +#include +#include "usage.h" + +int main(int argc, char **argv) +{ + applet_name = argv[0]; + return APPLET_main(argc,argv); +} + +void bb_show_usage(void) +{ + fputs(APPLET_full_usage "\n", stdout); + exit(EXIT_FAILURE); +} diff --git a/applets/install.sh b/applets/install.sh new file mode 100755 index 000000000..95b4719d4 --- /dev/null +++ b/applets/install.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +prefix=$1 +if [ -z "$prefix" ]; then + echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks/--scriptwrapper]" + exit 1 +fi + +h=`sort busybox.links | uniq` + +linkopts="" +scriptwrapper="n" +cleanup="0" +noclobber="0" +case "$2" in + --hardlinks) linkopts="-f";; + --symlinks) linkopts="-fs";; + --scriptwrapper) scriptwrapper="y";swrapall="y";; + --sw-sh-hard) scriptwrapper="y";linkopts="-f";; + --sw-sh-sym) scriptwrapper="y";linkopts="-fs";; + --cleanup) cleanup="1";; + --noclobber) noclobber="1";; + "") h="";; + *) echo "Unknown install option: $2"; exit 1;; +esac + +if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then + # get the target dir for the libs + # assume it starts with lib + libdir=$($CC -print-file-name=libc.so | \ + sed -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p') + if test -z "$libdir"; then + libdir=/lib + fi + + mkdir -p "$prefix/$libdir" || exit 1 + for i in $DO_INSTALL_LIBS; do + rm -f "$prefix/$libdir/$i" || exit 1 + if [ -f "$i" ]; then + cp -pPR "$i" "$prefix/$libdir/" || exit 1 + chmod 0644 "$prefix/$libdir/$i" || exit 1 + fi + done +fi + +if [ "$cleanup" = "1" ] && [ -e "$prefix/bin/busybox" ]; then + inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'` + sub_shell_it=` + cd "$prefix" + for d in usr/sbin usr/bin sbin bin; do + pd=$PWD + if [ -d "$d" ]; then + cd "$d" + ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f + fi + cd "$pd" + done + ` + exit 0 +fi + +rm -f "$prefix/bin/busybox" || exit 1 +mkdir -p "$prefix/bin" || exit 1 +install -m 755 busybox "$prefix/bin/busybox" || exit 1 + +for i in $h; do + appdir=`dirname "$i"` + mkdir -p "$prefix/$appdir" || exit 1 + if [ "$scriptwrapper" = "y" ]; then + if [ "$swrapall" != "y" ] && [ "$i" = "/bin/sh" ]; then + ln $linkopts busybox "$prefix/$i" || exit 1 + else + rm -f "$prefix/$i" + echo "#!/bin/busybox" >"$prefix/$i" + chmod +x "$prefix/$i" + fi + echo " $prefix/$i" + else + if [ "$2" = "--hardlinks" ]; then + bb_path="$prefix/bin/busybox" + else + case "$appdir" in + /) + bb_path="bin/busybox" + ;; + /bin) + bb_path="busybox" + ;; + /sbin) + bb_path="../bin/busybox" + ;; + /usr/bin | /usr/sbin) + bb_path="../../bin/busybox" + ;; + *) + echo "Unknown installation directory: $appdir" + exit 1 + ;; + esac + fi + if [ "$noclobber" = "0" ] || [ ! -e "$prefix/$i" ]; then + echo " $prefix/$i -> $bb_path" + ln $linkopts "$bb_path" "$prefix/$i" || exit 1 + else + echo " $prefix/$i already exists" + fi + fi +done + +exit 0 diff --git a/applets/usage.c b/applets/usage.c new file mode 100644 index 000000000..94520ff66 --- /dev/null +++ b/applets/usage.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 Denys Vlasenko. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include +#include + +#include "autoconf.h" + +/* Since we can't use platform.h, have to do this again by hand: */ +#if ENABLE_NOMMU +# define BB_MMU 0 +# define USE_FOR_NOMMU(...) __VA_ARGS__ +# define USE_FOR_MMU(...) +#else +# define BB_MMU 1 +# define USE_FOR_NOMMU(...) +# define USE_FOR_MMU(...) __VA_ARGS__ +#endif + +#include "usage.h" +#define MAKE_USAGE(aname, usage) { aname, usage }, +static struct usage_data { + const char *aname; + const char *usage; +} usage_array[] = { +#include "applets.h" +}; + +static int compare_func(const void *a, const void *b) +{ + const struct usage_data *ua = a; + const struct usage_data *ub = b; + return strcmp(ua->aname, ub->aname); +} + +int main(void) +{ + int i; + int num_messages = sizeof(usage_array) / sizeof(usage_array[0]); + + if (num_messages == 0) + return 0; + + qsort(usage_array, + num_messages, sizeof(usage_array[0]), + compare_func); + for (i = 0; i < num_messages; i++) + write(STDOUT_FILENO, usage_array[i].usage, strlen(usage_array[i].usage) + 1); + + return 0; +} diff --git a/applets/usage_compressed b/applets/usage_compressed new file mode 100755 index 000000000..af66bc5dc --- /dev/null +++ b/applets/usage_compressed @@ -0,0 +1,50 @@ +#!/bin/sh + +target="$1" +loc="$2" + +test "$target" || exit 1 +test "$loc" || loc=. +test -x "$loc/usage" || exit 1 +test "$SED" || SED=sed +test "$DD" || DD=dd + +# Some people were bitten by their system lacking a (proper) od +od -v -t x1 /dev/null +if test $? != 0; then + echo 'od tool is not installed or cannot accept "-v -t x1" options' + exit 1 +fi + +exec >"$target.$$" + +echo '#define UNPACKED_USAGE "" \' +"$loc/usage" | od -v -t x1 \ +| $SED -e 's/^[^ ]*//' \ + -e 's/ //g' \ + -e '/^$/d' \ + -e 's/\(..\)/\\x\1/g' \ + -e 's/^/"/' \ + -e 's/$/" \\/' +echo '' + +echo '#define PACKED_USAGE \' +## Breaks on big-endian systems! +## # Extra effort to avoid using "od -t x1": -t is not available +## # in non-CONFIG_DESKTOPed busybox od +## +## "$loc/usage" | bzip2 -1 | od -v -x \ +## | $SED -e 's/^[^ ]*//' \ +## -e 's/ //g' \ +## -e '/^$/d' \ +## -e 's/\(..\)\(..\)/0x\2,0x\1,/g' +## -e 's/$/ \\/' +"$loc/usage" | bzip2 -1 | $DD bs=2 skip=1 2>/dev/null | od -v -t x1 \ +| $SED -e 's/^[^ ]*//' \ + -e 's/ //g' \ + -e '/^$/d' \ + -e 's/\(..\)/0x\1,/g' \ + -e 's/$/ \\/' +echo '' + +mv -- "$target.$$" "$target" diff --git a/applets/usage_pod.c b/applets/usage_pod.c new file mode 100644 index 000000000..0b1c4aadb --- /dev/null +++ b/applets/usage_pod.c @@ -0,0 +1,111 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2009 Denys Vlasenko. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include +#include +#include +#include + +#include "autoconf.h" + +#define SKIP_applet_main +#define ALIGN1 /* nothing, just to placate applet_tables.h */ +#define ALIGN2 /* nothing, just to placate applet_tables.h */ +#include "applet_tables.h" + +/* Since we can't use platform.h, have to do this again by hand: */ +#if ENABLE_NOMMU +# define BB_MMU 0 +# define USE_FOR_NOMMU(...) __VA_ARGS__ +# define USE_FOR_MMU(...) +#else +# define BB_MMU 1 +# define USE_FOR_NOMMU(...) +# define USE_FOR_MMU(...) __VA_ARGS__ +#endif + +#include "usage.h" +#define MAKE_USAGE(aname, usage) { aname, usage }, +static struct usage_data { + const char *aname; + const char *usage; +} usage_array[] = { +#include "applets.h" +}; + +static int compare_func(const void *a, const void *b) +{ + const struct usage_data *ua = a; + const struct usage_data *ub = b; + return strcmp(ua->aname, ub->aname); +} + +int main(void) +{ + int col, len2; + + int i; + int num_messages = sizeof(usage_array) / sizeof(usage_array[0]); + + if (num_messages == 0) + return 0; + + qsort(usage_array, + num_messages, sizeof(usage_array[0]), + compare_func); + + col = 0; + for (i = 0; i < num_messages; i++) { + len2 = strlen(usage_array[i].aname) + 2; + if (col >= 76 - len2) { + printf(",\n"); + col = 0; + } + if (col == 0) { + col = 6; + printf("\t"); + } else { + printf(", "); + } + printf(usage_array[i].aname); + col += len2; + } + printf("\n\n"); + + printf("=head1 COMMAND DESCRIPTIONS\n\n"); + printf("=over 4\n\n"); + + for (i = 0; i < num_messages; i++) { + if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z' + && usage_array[i].usage[0] != NOUSAGE_STR[0] + ) { + printf("=item B<%s>\n\n", usage_array[i].aname); + if (usage_array[i].usage[0]) + printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage); + else + printf("%s\n\n", usage_array[i].aname); + } + } + return 0; +} + +/* TODO: we used to make options bold with B<> and output an example too: + +=item B + +cat [B<-u>] [FILE]... + +Concatenate FILE(s) and print them to stdout + +Options: + -u Use unbuffered i/o (ignored) + +Example: + $ cat /proc/uptime + 110716.72 17.67 + +*/ diff --git a/arch/i386/Makefile b/arch/i386/Makefile new file mode 100644 index 000000000..e6c99c67d --- /dev/null +++ b/arch/i386/Makefile @@ -0,0 +1,7 @@ +# ========================================================================== +# Build system +# ========================================================================== + +# -mpreferred-stack-boundary=2 is essential in preventing gcc 4.2.x +# from aligning stack to 16 bytes. (Which is gcc's way of supporting SSE). +CFLAGS += $(call cc-option,-march=i386 -mpreferred-stack-boundary=2,) diff --git a/archival/Config.src b/archival/Config.src new file mode 100644 index 000000000..885cb5bcc --- /dev/null +++ b/archival/Config.src @@ -0,0 +1,388 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Archival Utilities" + +INSERT + +config FEATURE_SEAMLESS_XZ + bool "Make tar, rpm, modprobe etc understand .xz data" + default y + help + Make tar, rpm, modprobe etc understand .xz data. + +config FEATURE_SEAMLESS_LZMA + bool "Make tar, rpm, modprobe etc understand .lzma data" + default y + help + Make tar, rpm, modprobe etc understand .lzma data. + +config FEATURE_SEAMLESS_BZ2 + bool "Make tar, rpm, modprobe etc understand .bz2 data" + default y + help + Make tar, rpm, modprobe etc understand .bz2 data. + +config FEATURE_SEAMLESS_GZ + bool "Make tar, rpm, modprobe etc understand .gz data" + default y + help + Make tar, rpm, modprobe etc understand .gz data. + +config FEATURE_SEAMLESS_Z + bool "Make tar and gunzip understand .Z data" + default n + help + Make tar and gunzip understand .Z data. + +config AR + bool "ar" + default n # needs to be improved to be able to replace binutils ar + help + ar is an archival utility program used to create, modify, and + extract contents from archives. An archive is a single file holding + a collection of other files in a structure that makes it possible to + retrieve the original individual files (called archive members). + The original files' contents, mode (permissions), timestamp, owner, + and group are preserved in the archive, and can be restored on + extraction. + + The stored filename is limited to 15 characters. (for more information + see long filename support). + ar has 60 bytes of overheads for every stored file. + + This implementation of ar can extract archives, it cannot create or + modify them. + On an x86 system, the ar applet adds about 1K. + + Unless you have a specific application which requires ar, you should + probably say N here. + +config FEATURE_AR_LONG_FILENAMES + bool "Support for long filenames (not needed for debs)" + default y + depends on AR + help + By default the ar format can only store the first 15 characters + of the filename, this option removes that limitation. + It supports the GNU ar long filename method which moves multiple long + filenames into a the data section of a new ar entry. + +config FEATURE_AR_CREATE + bool "Support archive creation" + default y + depends on AR + help + This enables archive creation (-c and -r) with busybox ar. + +config BUNZIP2 + bool "bunzip2" + default y + help + bunzip2 is a compression utility using the Burrows-Wheeler block + sorting text compression algorithm, and Huffman coding. Compression + is generally considerably better than that achieved by more + conventional LZ77/LZ78-based compressors, and approaches the + performance of the PPM family of statistical compressors. + + Unless you have a specific application which requires bunzip2, you + should probably say N here. + +config BZIP2 + bool "bzip2" + default y + help + bzip2 is a compression utility using the Burrows-Wheeler block + sorting text compression algorithm, and Huffman coding. Compression + is generally considerably better than that achieved by more + conventional LZ77/LZ78-based compressors, and approaches the + performance of the PPM family of statistical compressors. + + Unless you have a specific application which requires bzip2, you + should probably say N here. + +config CPIO + bool "cpio" + default y + help + cpio is an archival utility program used to create, modify, and + extract contents from archives. + cpio has 110 bytes of overheads for every stored file. + + This implementation of cpio can extract cpio archives created in the + "newc" or "crc" format, it cannot create or modify them. + + Unless you have a specific application which requires cpio, you + should probably say N here. + +config FEATURE_CPIO_O + bool "Support for archive creation" + default y + depends on CPIO + help + This implementation of cpio can create cpio archives in the "newc" + format only. + +config FEATURE_CPIO_P + bool "Support for passthrough mode" + default y + depends on FEATURE_CPIO_O + help + Passthrough mode. Rarely used. + +config DPKG + bool "dpkg" + default n + select FEATURE_SEAMLESS_GZ + help + dpkg is a medium-level tool to install, build, remove and manage + Debian packages. + + This implementation of dpkg has a number of limitations, + you should use the official dpkg if possible. + +config DPKG_DEB + bool "dpkg_deb" + default n + select FEATURE_SEAMLESS_GZ + help + dpkg-deb unpacks and provides information about Debian archives. + + This implementation of dpkg-deb cannot pack archives. + + Unless you have a specific application which requires dpkg-deb, + say N here. + +config FEATURE_DPKG_DEB_EXTRACT_ONLY + bool "Extract only (-x)" + default n + depends on DPKG_DEB + help + This reduces dpkg-deb to the equivalent of + "ar -p data.tar.gz | tar -zx". However it saves space as none + of the extra dpkg-deb, ar or tar options are needed, they are linked + to internally. + +config GUNZIP + bool "gunzip" + default y + help + gunzip is used to decompress archives created by gzip. + You can use the `-t' option to test the integrity of + an archive, without decompressing it. + +config GZIP + bool "gzip" + default y + help + gzip is used to compress files. + It's probably the most widely used UNIX compression program. + +config FEATURE_GZIP_LONG_OPTIONS + bool "Enable long options" + default y + depends on GZIP && LONG_OPTS + help + Enable use of long options, increases size by about 106 Bytes + +config GZIP_FAST + int "Trade memory for gzip speed (0:small,slow - 2:fast,big)" + default 0 + range 0 2 + depends on GZIP + help + Enable big memory options for gzip. + 0: small buffers, small hash-tables + 1: larger buffers, larger hash-tables + 2: larger buffers, largest hash-tables + Larger models may give slightly better compression + +config LZOP + bool "lzop" + default y + help + Lzop compression/decompresion. + +config LZOP_COMPR_HIGH + bool "lzop compression levels 7,8,9 (not very useful)" + default n + depends on LZOP + help + High levels (7,8,9) of lzop compression. These levels + are actually slower than gzip at equivalent compression ratios + and take up 3.2K of code. + +config RPM2CPIO + bool "rpm2cpio" + default y + help + Converts a RPM file into a CPIO archive. + +config RPM + bool "rpm" + default y + help + Mini RPM applet - queries and extracts RPM packages. + +config TAR + bool "tar" + default y + help + tar is an archiving program. It's commonly used with gzip to + create compressed archives. It's probably the most widely used + UNIX archive program. + +config FEATURE_TAR_CREATE + bool "Enable archive creation" + default y + depends on TAR + help + If you enable this option you'll be able to create + tar archives using the `-c' option. + +config FEATURE_TAR_AUTODETECT + bool "Autodetect compressed tarballs" + default y + depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ) + help + With this option tar can automatically detect compressed + tarballs. Currently it works only on files (not pipes etc). + +config FEATURE_TAR_FROM + bool "Enable -X (exclude from) and -T (include from) options)" + default y + depends on TAR + help + If you enable this option you'll be able to specify + a list of files to include or exclude from an archive. + +config FEATURE_TAR_OLDGNU_COMPATIBILITY + bool "Support for old tar header format" + default y + depends on TAR || DPKG + help + This option is required to unpack archives created in + the old GNU format; help to kill this old format by + repacking your ancient archives with the new format. + +config FEATURE_TAR_OLDSUN_COMPATIBILITY + bool "Enable untarring of tarballs with checksums produced by buggy Sun tar" + default y + depends on TAR || DPKG + help + This option is required to unpack archives created by some old + version of Sun's tar (it was calculating checksum using signed + arithmetic). It is said to be fixed in newer Sun tar, but "old" + tarballs still exist. + +config FEATURE_TAR_GNU_EXTENSIONS + bool "Support for GNU tar extensions (long filenames)" + default y + depends on TAR || DPKG + help + With this option busybox supports GNU long filenames and + linknames. + +config FEATURE_TAR_LONG_OPTIONS + bool "Enable long options" + default y + depends on TAR && LONG_OPTS + help + Enable use of long options, increases size by about 400 Bytes + +config FEATURE_TAR_TO_COMMAND + bool "Support for writing to an external program" + default y + depends on TAR && FEATURE_TAR_LONG_OPTIONS + help + If you enable this option you'll be able to instruct tar to send + the contents of each extracted file to the standard input of an + external program. + +config FEATURE_TAR_UNAME_GNAME + bool "Enable use of user and group names" + default y + depends on TAR + help + Enables use of user and group names in tar. This affects contents + listings (-t) and preserving permissions when unpacking (-p). + +200 bytes. + +config FEATURE_TAR_NOPRESERVE_TIME + bool "Enable -m (do not preserve time) option" + default y + depends on TAR + help + With this option busybox supports GNU tar -m + (do not preserve time) option. + +config FEATURE_TAR_SELINUX + bool "Support for extracting SELinux labels" + default n + depends on TAR && SELINUX + help + With this option busybox supports restoring SELinux labels + when extracting files from tar archives. + +config UNCOMPRESS + bool "uncompress" + default n + help + uncompress is used to decompress archives created by compress. + Not much used anymore, replaced by gzip/gunzip. + +config UNLZMA + bool "unlzma" + default y + help + unlzma is a compression utility using the Lempel-Ziv-Markov chain + compression algorithm, and range coding. Compression + is generally considerably better than that achieved by the bzip2 + compressors. + + The BusyBox unlzma applet is limited to decompression only. + On an x86 system, this applet adds about 4K. + +config FEATURE_LZMA_FAST + bool "Optimize unlzma for speed" + default n + depends on UNLZMA + help + This option reduces decompression time by about 25% at the cost of + a 1K bigger binary. + +config LZMA + bool "Provide lzma alias which supports only unpacking" + default y + depends on UNLZMA + help + Enable this option if you want commands like "lzma -d" to work. + IOW: you'll get lzma applet, but it will always require -d option. + +config UNXZ + bool "unxz" + default y + help + unxz is a unlzma successor. + +config XZ + bool "Provide xz alias which supports only unpacking" + default y + depends on UNXZ + help + Enable this option if you want commands like "xz -d" to work. + IOW: you'll get xz applet, but it will always require -d option. + +config UNZIP + bool "unzip" + default y + help + unzip will list or extract files from a ZIP archive, + commonly found on DOS/WIN systems. The default behavior + (with no options) is to extract the archive into the + current directory. Use the `-d' option to extract to a + directory of your choice. + +endmenu diff --git a/archival/Kbuild.src b/archival/Kbuild.src new file mode 100644 index 000000000..3466452f7 --- /dev/null +++ b/archival/Kbuild.src @@ -0,0 +1,30 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +libs-y += libarchive/ + +lib-y:= + +INSERT + +lib-$(CONFIG_AR) += ar.o +lib-$(CONFIG_CPIO) += cpio.o +lib-$(CONFIG_DPKG) += dpkg.o +lib-$(CONFIG_DPKG_DEB) += dpkg_deb.o +lib-$(CONFIG_RPM2CPIO) += rpm2cpio.o +lib-$(CONFIG_RPM) += rpm.o +lib-$(CONFIG_TAR) += tar.o +lib-$(CONFIG_UNZIP) += unzip.o + +lib-$(CONFIG_LZOP) += lzop.o bbunzip.o +lib-$(CONFIG_GZIP) += gzip.o bbunzip.o +lib-$(CONFIG_BZIP2) += bzip2.o bbunzip.o + +lib-$(CONFIG_UNXZ) += bbunzip.o +lib-$(CONFIG_UNLZMA) += bbunzip.o +lib-$(CONFIG_BUNZIP2) += bbunzip.o +lib-$(CONFIG_GUNZIP) += bbunzip.o +lib-$(CONFIG_UNCOMPRESS) += bbunzip.o diff --git a/archival/ar.c b/archival/ar.c new file mode 100644 index 000000000..acad20ff2 --- /dev/null +++ b/archival/ar.c @@ -0,0 +1,261 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ar implementation for busybox + * + * Copyright (C) 2000 by Glenn McGrath + * + * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Archive creation support: + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * Written by Alexander Shishkin. + * + * There is no single standard to adhere to so ar may not portable + * between different systems + * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html + */ + +//usage:#define ar_trivial_usage +//usage: "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES" +//usage:#define ar_full_usage "\n\n" +//usage: "Extract or list FILES from an ar archive\n" +//usage: "\n -o Preserve original dates" +//usage: "\n -p Extract to stdout" +//usage: "\n -t List" +//usage: "\n -x Extract" +//usage: "\n -v Verbose" + +#include "libbb.h" +#include "archive.h" +#include "ar.h" + +#if ENABLE_FEATURE_AR_CREATE +/* filter out entries with same names as specified on the command line */ +static char FAST_FUNC filter_replaceable(archive_handle_t *handle) +{ + if (find_list_entry(handle->accept, handle->file_header->name)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static void output_ar_header(archive_handle_t *handle) +{ + /* GNU ar 2.19.51.0.14 creates malformed archives + * if input files are >10G. It also truncates files >4GB + * (uses "size mod 4G"). We abort in this case: + * We could add support for up to 10G files, but this is unlikely to be useful. + * Note that unpacking side limits all fields to "unsigned int" data type, + * and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1. + */ + enum { + /* for 2nd field: mtime */ + MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1, + /* for last field: filesize */ + MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1, + }; + + struct file_header_t *fh = handle->file_header; + + if (handle->offset & 1) { + xwrite(handle->src_fd, "\n", 1); + handle->offset++; + } + + /* Careful! The widths should be exact. Fields must be separated */ + if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) { + bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name); + } + fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n", + fh->name, + (sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime, + fh->uid > 99999 ? 0 : (int)fh->uid, + fh->gid > 99999 ? 0 : (int)fh->gid, + (int)fh->mode & 07777777, + fh->size + ); + + handle->offset += AR_HEADER_LEN; +} + +/* + * when replacing files in an existing archive, copy from the + * original archive those files that are to be left intact + */ +static void FAST_FUNC copy_data(archive_handle_t *handle) +{ + archive_handle_t *out_handle = handle->ar__out; + struct file_header_t *fh = handle->file_header; + + out_handle->file_header = fh; + output_ar_header(out_handle); + + bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size); + out_handle->offset += fh->size; +} + +static int write_ar_header(archive_handle_t *handle) +{ + char *fn; + char fn_h[17]; /* 15 + "/" + NUL */ + struct stat st; + int fd; + + fn = llist_pop(&handle->accept); + if (!fn) + return -1; + + xstat(fn, &st); + + handle->file_header->mtime = st.st_mtime; + handle->file_header->uid = st.st_uid; + handle->file_header->gid = st.st_gid; + handle->file_header->mode = st.st_mode; + handle->file_header->size = st.st_size; + handle->file_header->name = fn_h; +//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES... + sprintf(fn_h, "%.15s/", bb_basename(fn)); + + output_ar_header(handle); + + fd = xopen(fn, O_RDONLY); + bb_copyfd_exact_size(fd, handle->src_fd, st.st_size); + close(fd); + handle->offset += st.st_size; + + return 0; +} + +static int write_ar_archive(archive_handle_t *handle) +{ + struct stat st; + archive_handle_t *out_handle; + + xfstat(handle->src_fd, &st, handle->ar__name); + + /* if archive exists, create a new handle for output. + * we create it in place of the old one. + */ + if (st.st_size != 0) { + out_handle = init_handle(); + xunlink(handle->ar__name); + out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); + out_handle->accept = handle->accept; + } else { + out_handle = handle; + } + + handle->ar__out = out_handle; + + xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1); + out_handle->offset += AR_MAGIC_LEN + 1; + + /* skip to the end of the archive if we have to append stuff */ + if (st.st_size != 0) { + handle->filter = filter_replaceable; + handle->action_data = copy_data; + unpack_ar_archive(handle); + } + + while (write_ar_header(out_handle) == 0) + continue; + + /* optional, since we exit right after we return */ + if (ENABLE_FEATURE_CLEAN_UP) { + close(handle->src_fd); + if (out_handle->src_fd != handle->src_fd) + close(out_handle->src_fd); + } + + return EXIT_SUCCESS; +} +#endif /* FEATURE_AR_CREATE */ + +static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header) +{ + const char *mode = bb_mode_string(file_header->mode); + char *mtime; + + mtime = ctime(&file_header->mtime); + mtime[16] = ' '; + memmove(&mtime[17], &mtime[20], 4); + mtime[21] = '\0'; + printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1], + (int)file_header->uid, (int)file_header->gid, + file_header->size, + &mtime[4], file_header->name + ); +} + +#define AR_OPT_VERBOSE (1 << 0) +#define AR_OPT_PRESERVE_DATE (1 << 1) +/* "ar r" implies create, but warns about it. c suppresses warning. + * bbox accepts but ignores it: */ +#define AR_OPT_CREATE (1 << 2) + +#define AR_CMD_PRINT (1 << 3) +#define FIRST_CMD AR_CMD_PRINT +#define AR_CMD_LIST (1 << 4) +#define AR_CMD_EXTRACT (1 << 5) +#define AR_CMD_INSERT (1 << 6) + +int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ar_main(int argc UNUSED_PARAM, char **argv) +{ + archive_handle_t *archive_handle; + unsigned opt, t; + + archive_handle = init_handle(); + + /* --: prepend '-' to the first argument if required */ + /* -1: at least one param is reqd */ + /* one of p,t,x[,r] is required */ + opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r"); + opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r")); + argv += optind; + + t = opt / FIRST_CMD; + if (t & (t-1)) /* more than one of p,t,x[,r] are specified */ + bb_show_usage(); + + if (opt & AR_CMD_PRINT) { + archive_handle->action_data = data_extract_to_stdout; + } + if (opt & AR_CMD_LIST) { + archive_handle->action_header = header_list; + } + if (opt & AR_CMD_EXTRACT) { + archive_handle->action_data = data_extract_all; + } + if (opt & AR_OPT_PRESERVE_DATE) { + archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE; + } + if (opt & AR_OPT_VERBOSE) { + archive_handle->action_header = header_verbose_list_ar; + } +#if ENABLE_FEATURE_AR_CREATE + archive_handle->ar__name = *argv; +#endif + archive_handle->src_fd = xopen(*argv++, + (opt & AR_CMD_INSERT) + ? O_RDWR | O_CREAT + : O_RDONLY + ); + + if (*argv) + archive_handle->filter = filter_accept_list; + while (*argv) { + llist_add_to_end(&archive_handle->accept, *argv++); + } + +#if ENABLE_FEATURE_AR_CREATE + if (opt & AR_CMD_INSERT) + return write_ar_archive(archive_handle); +#endif + + unpack_ar_archive(archive_handle); + + return EXIT_SUCCESS; +} diff --git a/archival/bbunzip.c b/archival/bbunzip.c new file mode 100644 index 000000000..bb1ec0e37 --- /dev/null +++ b/archival/bbunzip.c @@ -0,0 +1,472 @@ +/* vi: set sw=4 ts=4: */ +/* + * Common code for gunzip-like applets + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "archive.h" + +enum { + OPT_STDOUT = 1 << 0, + OPT_FORCE = 1 << 1, + /* only some decompressors: */ + OPT_VERBOSE = 1 << 2, + OPT_DECOMPRESS = 1 << 3, + OPT_TEST = 1 << 4, +}; + +static +int open_to_or_warn(int to_fd, const char *filename, int flags, int mode) +{ + int fd = open3_or_warn(filename, flags, mode); + if (fd < 0) { + return 1; + } + xmove_fd(fd, to_fd); + return 0; +} + +char* FAST_FUNC append_ext(char *filename, const char *expected_ext) +{ + return xasprintf("%s.%s", filename, expected_ext); +} + +int FAST_FUNC bbunpack(char **argv, + IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(unpack_info_t *info), + char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext), + const char *expected_ext +) +{ + struct stat stat_buf; + IF_DESKTOP(long long) int status; + char *filename, *new_name; + smallint exitcode = 0; + unpack_info_t info; + + do { + /* NB: new_name is *maybe* malloc'ed! */ + new_name = NULL; + filename = *argv; /* can be NULL - 'streaming' bunzip2 */ + + if (filename && LONE_DASH(filename)) + filename = NULL; + + /* Open src */ + if (filename) { + if (stat(filename, &stat_buf) != 0) { + bb_simple_perror_msg(filename); + err: + exitcode = 1; + goto free_name; + } + if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0)) + goto err; + } + + /* Special cases: test, stdout */ + if (option_mask32 & (OPT_STDOUT|OPT_TEST)) { + if (option_mask32 & OPT_TEST) + if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0)) + goto err; + filename = NULL; + } + + /* Open dst if we are going to unpack to file */ + if (filename) { + new_name = make_new_name(filename, expected_ext); + if (!new_name) { + bb_error_msg("%s: unknown suffix - ignored", filename); + goto err; + } + + /* -f: overwrite existing output files */ + if (option_mask32 & OPT_FORCE) { + unlink(new_name); + } + + /* O_EXCL: "real" bunzip2 doesn't overwrite files */ + /* GNU gunzip does not bail out, but goes to next file */ + if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL, + stat_buf.st_mode)) + goto err; + } + + /* Check that the input is sane */ + if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) { + bb_error_msg_and_die("compressed data not read from terminal, " + "use -f to force it"); + } + + /* memset(&info, 0, sizeof(info)); */ + info.mtime = 0; /* so far it has one member only */ + status = unpacker(&info); + if (status < 0) + exitcode = 1; + xclose(STDOUT_FILENO); /* with error check! */ + + if (filename) { + char *del = new_name; + if (status >= 0) { + /* TODO: restore other things? */ + if (info.mtime) { + struct timeval times[2]; + + times[1].tv_sec = times[0].tv_sec = info.mtime; + times[1].tv_usec = times[0].tv_usec = 0; + /* Note: we closed it first. + * On some systems calling utimes + * then closing resets the mtime + * back to current time. */ + utimes(new_name, times); /* ignoring errors */ + } + + /* Delete _compressed_ file */ + del = filename; + /* restore extension (unless tgz -> tar case) */ + if (new_name == filename) + filename[strlen(filename)] = '.'; + } + xunlink(del); + +#if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */ + /* Extreme bloat for gunzip compat */ + if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) { + fprintf(stderr, "%s: %u%% - replaced with %s\n", + filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name); + } +#endif + + free_name: + if (new_name != filename) + free(new_name); + } + } while (*argv && *++argv); + + return exitcode; +} + +#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ +static +char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext) +{ + char *extension = strrchr(filename, '.'); + if (!extension || strcmp(extension + 1, expected_ext) != 0) { + /* Mimic GNU gunzip - "real" bunzip2 tries to */ + /* unpack file anyway, to file.out */ + return NULL; + } + *extension = '\0'; + return filename; +} +#endif + + +/* + * Uncompress applet for busybox (c) 2002 Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define uncompress_trivial_usage +//usage: "[-cf] [FILE]..." +//usage:#define uncompress_full_usage "\n\n" +//usage: "Decompress .Z file[s]\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Overwrite" + +#if ENABLE_UNCOMPRESS +static +IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(unpack_info_t *info UNUSED_PARAM) +{ + IF_DESKTOP(long long) int status = -1; + + if ((xread_char(STDIN_FILENO) != 0x1f) || (xread_char(STDIN_FILENO) != 0x9d)) { + bb_error_msg("invalid magic"); + } else { + status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO); + } + return status; +} +int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uncompress_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "cf"); + argv += optind; + + return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z"); +} +#endif + + +/* + * Gzip implementation for busybox + * + * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Sven Rudolph + * based on gzip sources + * + * Adjusted further by Erik Andersen to support files as + * well as stdin/stdout, and to generally behave itself wrt command line + * handling. + * + * General cleanup to better adhere to the style guide and make use of standard + * busybox functions by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +//usage:#define gunzip_trivial_usage +//usage: "[-cft] [FILE]..." +//usage:#define gunzip_full_usage "\n\n" +//usage: "Decompress FILEs (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -t Test file integrity" +//usage: +//usage:#define gunzip_example_usage +//usage: "$ ls -la /tmp/BusyBox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" +//usage: "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" +//usage: "$ ls -la /tmp/BusyBox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n" +//usage: +//usage:#define zcat_trivial_usage +//usage: "FILE" +//usage:#define zcat_full_usage "\n\n" +//usage: "Decompress to stdout" + +#if ENABLE_GUNZIP +static +char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM) +{ + char *extension = strrchr(filename, '.'); + + if (!extension) + return NULL; + + extension++; + if (strcmp(extension, "tgz" + 1) == 0 +#if ENABLE_FEATURE_SEAMLESS_Z + || (extension[0] == 'Z' && extension[1] == '\0') +#endif + ) { + extension[-1] = '\0'; + } else if (strcmp(extension, "tgz") == 0) { + filename = xstrdup(filename); + extension = strrchr(filename, '.'); + extension[2] = 'a'; + extension[3] = 'r'; + } else { + return NULL; + } + return filename; +} +static +IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(unpack_info_t *info) +{ + IF_DESKTOP(long long) int status = -1; + + /* do the decompression, and cleanup */ + if (xread_char(STDIN_FILENO) == 0x1f) { + unsigned char magic2; + + magic2 = xread_char(STDIN_FILENO); + if (ENABLE_FEATURE_SEAMLESS_Z && magic2 == 0x9d) { + status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO); + } else if (magic2 == 0x8b) { + status = unpack_gz_stream_with_info(STDIN_FILENO, STDOUT_FILENO, info); + } else { + goto bad_magic; + } + if (status < 0) { + bb_error_msg("error inflating"); + } + } else { + bad_magic: + bb_error_msg("invalid magic"); + /* status is still == -1 */ + } + return status; +} +/* + * Linux kernel build uses gzip -d -n. We accept and ignore it. + * Man page says: + * -n --no-name + * gzip: do not save the original file name and time stamp. + * (The original name is always saved if the name had to be truncated.) + * gunzip: do not restore the original file name/time even if present + * (remove only the gzip suffix from the compressed file name). + * This option is the default when decompressing. + * -N --name + * gzip: always save the original file name and time stamp (this is the default) + * gunzip: restore the original file name and time stamp if present. + */ +int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int gunzip_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "cfvdtn"); + argv += optind; + /* if called as zcat */ + if (applet_name[1] == 'c') + option_mask32 |= OPT_STDOUT; + + return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL); +} +#endif + + +/* + * Modified for busybox by Glenn McGrath + * Added support output to stdout by Thomas Lundquist + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//usage:#define bunzip2_trivial_usage +//usage: "[-cf] [FILE]..." +//usage:#define bunzip2_full_usage "\n\n" +//usage: "Decompress FILEs (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage:#define bzcat_trivial_usage +//usage: "FILE" +//usage:#define bzcat_full_usage "\n\n" +//usage: "Decompress to stdout" +//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) +//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat)) +#if ENABLE_BUNZIP2 +static +IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(unpack_info_t *info UNUSED_PARAM) +{ + return unpack_bz2_stream_prime(STDIN_FILENO, STDOUT_FILENO); +} +int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int bunzip2_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "cfvdt"); + argv += optind; + if (applet_name[2] == 'c') /* bzcat */ + option_mask32 |= OPT_STDOUT; + + return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2"); +} +#endif + + +/* + * Small lzma deflate implementation. + * Copyright (C) 2006 Aurelien Jacobs + * + * Based on bunzip.c from busybox + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +//usage:#define unlzma_trivial_usage +//usage: "[-cf] [FILE]..." +//usage:#define unlzma_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: +//usage:#define lzma_trivial_usage +//usage: "-d [-cf] [FILE]..." +//usage:#define lzma_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: +//usage:#define lzcat_trivial_usage +//usage: "FILE" +//usage:#define lzcat_full_usage "\n\n" +//usage: "Decompress to stdout" +//usage: +//usage:#define unxz_trivial_usage +//usage: "[-cf] [FILE]..." +//usage:#define unxz_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: +//usage:#define xz_trivial_usage +//usage: "-d [-cf] [FILE]..." +//usage:#define xz_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: +//usage:#define xzcat_trivial_usage +//usage: "FILE" +//usage:#define xzcat_full_usage "\n\n" +//usage: "Decompress to stdout" + +#if ENABLE_UNLZMA +static +IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(unpack_info_t *info UNUSED_PARAM) +{ + return unpack_lzma_stream(STDIN_FILENO, STDOUT_FILENO); +} +int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int unlzma_main(int argc UNUSED_PARAM, char **argv) +{ + IF_LZMA(int opts =) getopt32(argv, "cfvdt"); +# if ENABLE_LZMA + /* lzma without -d or -t? */ + if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST))) + bb_show_usage(); +# endif + /* lzcat? */ + if (applet_name[2] == 'c') + option_mask32 |= OPT_STDOUT; + + argv += optind; + return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma"); +} +#endif + + +#if ENABLE_UNXZ +static +IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(unpack_info_t *info UNUSED_PARAM) +{ + struct { + uint32_t v1; + uint16_t v2; + } magic; + xread(STDIN_FILENO, &magic, 6); + if (magic.v1 != XZ_MAGIC1a || magic.v2 != XZ_MAGIC2a) { + bb_error_msg("invalid magic"); + return -1; + } + return unpack_xz_stream(STDIN_FILENO, STDOUT_FILENO); +} +int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int unxz_main(int argc UNUSED_PARAM, char **argv) +{ + IF_XZ(int opts =) getopt32(argv, "cfvdt"); +# if ENABLE_XZ + /* xz without -d or -t? */ + if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST))) + bb_show_usage(); +# endif + /* xzcat? */ + if (applet_name[2] == 'c') + option_mask32 |= OPT_STDOUT; + + argv += optind; + return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz"); +} +#endif diff --git a/archival/bbunzip_test.sh b/archival/bbunzip_test.sh new file mode 100644 index 000000000..b8e31bf97 --- /dev/null +++ b/archival/bbunzip_test.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Test that concatenated gz files are unpacking correctly. +# It also tests that unpacking in general is working right. +# Since zip code has many corner cases, run it for a few hours +# to get a decent coverage (200000 tests or more). + +gzip="gzip" +gunzip="../busybox gunzip" +# Or the other way around: +#gzip="../busybox gzip" +#gunzip="gunzip" + +c=0 +i=$PID +while true; do + c=$((c+1)) + + # RANDOM is not very random on some shells. Spice it up. + # 100003 is prime + len1=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 )) + i=$((i * 1664525 + 1013904223)) + len2=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 )) + + # Just using urandom will make gzip use method 0 (store) - + # not good for test coverage! + cat /dev/urandom | while true; do read junk; echo "junk $c $i $junk"; done \ + | dd bs=$len1 count=1 >z1 2>/dev/null + cat /dev/urandom | while true; do read junk; echo "junk $c $i $junk"; done \ + | dd bs=$len2 count=1 >z2 2>/dev/null + + $gzip zz.gz + $gzip >zz.gz + $gunzip -c zz.gz >z9 || { + echo "Exitcode $?" + exit + } + sum=`cat z1 z2 | md5sum` + sum9=`md5sum /dev/null + $gzip z1 + $gzip z2 + cat z1.gz z2.gz z1.gz z2.gz >zz.gz + $gunzip -c zz.gz >z9 || { + echo "Exitcode $? (2)" + exit + } + sum9=`md5sum /dev/null diff --git a/archival/bbunzip_test3.sh b/archival/bbunzip_test3.sh new file mode 100644 index 000000000..2dc4afda1 --- /dev/null +++ b/archival/bbunzip_test3.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Leak test for gunzip. Watch top for growing process size. +# In this case we look for leaks in "concatenated .gz" code - +# we feed gunzip with a stream of .gz files. + +i=$PID +c=0 +while true; do + c=$((c + 1)) + echo "Block# $c" >&2 + # RANDOM is not very random on some shells. Spice it up. + i=$((i * 1664525 + 1013904223)) + # 100003 is prime + len=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 )) + + # Just using urandom will make gzip use method 0 (store) - + # not good for test coverage! + cat /dev/urandom \ + | while true; do read junk; echo "junk $c $i $junk"; done \ + | dd bs=$len count=1 2>/dev/null \ + | gzip >xxx.gz + cat xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz +done | ../busybox gunzip -c >/dev/null diff --git a/archival/bzip2.c b/archival/bzip2.c new file mode 100644 index 000000000..3dde970f1 --- /dev/null +++ b/archival/bzip2.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2007 Denys Vlasenko + * + * This file uses bzip2 library code which is written + * by Julian Seward . + * See README and LICENSE files in bz/ directory for more information + * about bzip2 library code. + */ + +//usage:#define bzip2_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define bzip2_full_usage "\n\n" +//usage: "Compress FILEs (or stdin) with bzip2 algorithm\n" +//usage: "\n -1..9 Compression level" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" + +#include "libbb.h" +#include "archive.h" + +#define CONFIG_BZIP2_FAST 1 + +/* Speed test: + * Compiled with gcc 4.2.1, run on Athlon 64 1800 MHz (512K L2 cache). + * Stock bzip2 is 26.4% slower than bbox bzip2 at SPEED 1 + * (time to compress gcc-4.2.1.tar is 126.4% compared to bbox). + * At SPEED 5 difference is 32.7%. + * + * Test run of all CONFIG_BZIP2_FAST values on a 11Mb text file: + * Size Time (3 runs) + * 0: 10828 4.145 4.146 4.148 + * 1: 11097 3.845 3.860 3.861 + * 2: 11392 3.763 3.767 3.768 + * 3: 11892 3.722 3.724 3.727 + * 4: 12740 3.637 3.640 3.644 + * 5: 17273 3.497 3.509 3.509 + */ + + +#define BZ_DEBUG 0 +/* Takes ~300 bytes, detects corruption caused by bad RAM etc */ +#define BZ_LIGHT_DEBUG 0 + +#include "libarchive/bz/bzlib.h" + +#include "libarchive/bz/bzlib_private.h" + +#include "libarchive/bz/blocksort.c" +#include "libarchive/bz/bzlib.c" +#include "libarchive/bz/compress.c" +#include "libarchive/bz/huffman.c" + +/* No point in being shy and having very small buffer here. + * bzip2 internal buffers are much bigger anyway, hundreds of kbytes. + * If iobuf is several pages long, malloc() may use mmap, + * making iobuf is page aligned and thus (maybe) have one memcpy less + * if kernel is clever enough. + */ +enum { + IOBUF_SIZE = 8 * 1024 +}; + +static uint8_t level; + +/* NB: compressStream() has to return -1 on errors, not die. + * bbunpack() will correctly clean up in this case + * (delete incomplete .bz2 file) + */ + +/* Returns: + * -1 on errors + * total written bytes so far otherwise + */ +static +IF_DESKTOP(long long) int bz_write(bz_stream *strm, void* rbuf, ssize_t rlen, void *wbuf) +{ + int n, n2, ret; + + strm->avail_in = rlen; + strm->next_in = rbuf; + while (1) { + strm->avail_out = IOBUF_SIZE; + strm->next_out = wbuf; + + ret = BZ2_bzCompress(strm, rlen ? BZ_RUN : BZ_FINISH); + if (ret != BZ_RUN_OK /* BZ_RUNning */ + && ret != BZ_FINISH_OK /* BZ_FINISHing, but not done yet */ + && ret != BZ_STREAM_END /* BZ_FINISHed */ + ) { + bb_error_msg_and_die("internal error %d", ret); + } + + n = IOBUF_SIZE - strm->avail_out; + if (n) { + n2 = full_write(STDOUT_FILENO, wbuf, n); + if (n2 != n) { + if (n2 >= 0) + errno = 0; /* prevent bogus error message */ + bb_perror_msg(n2 >= 0 ? "short write" : bb_msg_write_error); + return -1; + } + } + + if (ret == BZ_STREAM_END) + break; + if (rlen && strm->avail_in == 0) + break; + } + return 0 IF_DESKTOP( + strm->total_out ); +} + +static +IF_DESKTOP(long long) int FAST_FUNC compressStream(unpack_info_t *info UNUSED_PARAM) +{ + IF_DESKTOP(long long) int total; + ssize_t count; + bz_stream bzs; /* it's small */ +#define strm (&bzs) + char *iobuf; +#define rbuf iobuf +#define wbuf (iobuf + IOBUF_SIZE) + + iobuf = xmalloc(2 * IOBUF_SIZE); + BZ2_bzCompressInit(strm, level); + + while (1) { + count = full_read(STDIN_FILENO, rbuf, IOBUF_SIZE); + if (count < 0) { + bb_perror_msg(bb_msg_read_error); + total = -1; + break; + } + /* if count == 0, bz_write finalizes compression */ + total = bz_write(strm, rbuf, count, wbuf); + if (count == 0 || total < 0) + break; + } + + /* Can't be conditional on ENABLE_FEATURE_CLEAN_UP - + * we are called repeatedly + */ + BZ2_bzCompressEnd(strm); + free(iobuf); + + return total; +} + +int bzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int bzip2_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opt; + + /* standard bzip2 flags + * -d --decompress force decompression + * -z --compress force compression + * -k --keep keep (don't delete) input files + * -f --force overwrite existing output files + * -t --test test compressed file integrity + * -c --stdout output to standard out + * -q --quiet suppress noncritical error messages + * -v --verbose be verbose (a 2nd -v gives more) + * -s --small use less memory (at most 2500k) + * -1 .. -9 set block size to 100k .. 900k + * --fast alias for -1 + * --best alias for -9 + */ + + opt_complementary = "s2"; /* -s means -2 (compatibility) */ + /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ + opt = getopt32(argv, "cfv" IF_BUNZIP2("dt") "123456789qzs"); +#if ENABLE_BUNZIP2 /* bunzip2_main may not be visible... */ + if (opt & 0x18) // -d and/or -t + return bunzip2_main(argc, argv); + opt >>= 5; +#else + opt >>= 3; +#endif + opt = (uint8_t)opt; /* isolate bits for -1..-8 */ + opt |= 0x100; /* if nothing else, assume -9 */ + level = 1; + while (!(opt & 1)) { + level++; + opt >>= 1; + } + + argv += optind; + option_mask32 &= 0x7; /* ignore all except -cfv */ + return bbunpack(argv, compressStream, append_ext, "bz2"); +} diff --git a/archival/cpio.c b/archival/cpio.c new file mode 100644 index 000000000..9674a046b --- /dev/null +++ b/archival/cpio.c @@ -0,0 +1,458 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cpio implementation for busybox + * + * Copyright (C) 2001 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Limitations: + * Doesn't check CRC's + * Only supports new ASCII and CRC formats + * + */ +#include "libbb.h" +#include "archive.h" + +//usage:#define cpio_trivial_usage +//usage: "[-dmvu] [-F FILE]" IF_FEATURE_CPIO_O(" [-H newc]") +//usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]") +//usage: " [EXTR_FILE]..." +//usage:#define cpio_full_usage "\n\n" +//usage: "Extract or list files from a cpio archive" +//usage: IF_FEATURE_CPIO_O(", or" +//usage: "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)") +//usage: " using file list on stdin" +//usage: ) +//usage: "\n" +//usage: "\nMain operation mode:" +//usage: "\n -t List" +//usage: "\n -i Extract EXTR_FILEs (or all)" +//usage: IF_FEATURE_CPIO_O( +//usage: "\n -o Create (requires -H newc)" +//usage: ) +//usage: IF_FEATURE_CPIO_P( +//usage: "\n -p DIR Copy files to DIR" +//usage: ) +//usage: "\n -d Make leading directories" +//usage: "\n -m Preserve mtime" +//usage: "\n -v Verbose" +//usage: "\n -u Overwrite" +//usage: "\n -F FILE Input (-t,-i,-p) or output (-o) file" +//usage: IF_FEATURE_CPIO_O( +//usage: "\n -H newc Archive format" +//usage: ) + +/* GNU cpio 2.9 --help (abridged): + + Modes: + -t, --list List the archive + -i, --extract Extract files from an archive + -o, --create Create the archive + -p, --pass-through Copy-pass mode + + Options valid in any mode: + --block-size=SIZE I/O block size = SIZE * 512 bytes + -B I/O block size = 5120 bytes + -c Use the old portable (ASCII) archive format + -C, --io-size=NUMBER I/O block size in bytes + -f, --nonmatching Only copy files that do not match given pattern + -F, --file=FILE Use FILE instead of standard input or output + -H, --format=FORMAT Use given archive FORMAT + -M, --message=STRING Print STRING when the end of a volume of the + backup media is reached + -n, --numeric-uid-gid If -v, show numeric UID and GID + --quiet Do not print the number of blocks copied + --rsh-command=COMMAND Use remote COMMAND instead of rsh + -v, --verbose Verbosely list the files processed + -V, --dot Print a "." for each file processed + -W, --warning=FLAG Control warning display: 'none','truncate','all'; + multiple options accumulate + + Options valid only in --extract mode: + -b, --swap Swap both halfwords of words and bytes of + halfwords in the data (equivalent to -sS) + -r, --rename Interactively rename files + -s, --swap-bytes Swap the bytes of each halfword in the files + -S, --swap-halfwords Swap the halfwords of each word (4 bytes) + --to-stdout Extract files to standard output + -E, --pattern-file=FILE Read additional patterns specifying filenames to + extract or list from FILE + --only-verify-crc Verify CRC's, don't actually extract the files + + Options valid only in --create mode: + -A, --append Append to an existing archive + -O FILE File to use instead of standard output + + Options valid only in --pass-through mode: + -l, --link Link files instead of copying them, when possible + + Options valid in --extract and --create modes: + --absolute-filenames Do not strip file system prefix components from + the file names + --no-absolute-filenames Create all files relative to the current dir + + Options valid in --create and --pass-through modes: + -0, --null A list of filenames is terminated by a NUL + -a, --reset-access-time Reset the access times of files after reading them + -I FILE File to use instead of standard input + -L, --dereference Dereference symbolic links (copy the files + that they point to instead of copying the links) + -R, --owner=[USER][:.][GROUP] Set owner of created files + + Options valid in --extract and --pass-through modes: + -d, --make-directories Create leading directories where needed + -m, --preserve-modification-time Retain mtime when creating files + --no-preserve-owner Do not change the ownership of the files + --sparse Write files with blocks of zeros as sparse files + -u, --unconditional Replace all files unconditionally + */ + +enum { + OPT_EXTRACT = (1 << 0), + OPT_TEST = (1 << 1), + OPT_NUL_TERMINATED = (1 << 2), + OPT_UNCONDITIONAL = (1 << 3), + OPT_VERBOSE = (1 << 4), + OPT_CREATE_LEADING_DIR = (1 << 5), + OPT_PRESERVE_MTIME = (1 << 6), + OPT_DEREF = (1 << 7), + OPT_FILE = (1 << 8), + OPTBIT_FILE = 8, + IF_FEATURE_CPIO_O(OPTBIT_CREATE ,) + IF_FEATURE_CPIO_O(OPTBIT_FORMAT ,) + IF_FEATURE_CPIO_P(OPTBIT_PASSTHROUGH,) + IF_LONG_OPTS( OPTBIT_QUIET ,) + IF_LONG_OPTS( OPTBIT_2STDOUT ,) + OPT_CREATE = IF_FEATURE_CPIO_O((1 << OPTBIT_CREATE )) + 0, + OPT_FORMAT = IF_FEATURE_CPIO_O((1 << OPTBIT_FORMAT )) + 0, + OPT_PASSTHROUGH = IF_FEATURE_CPIO_P((1 << OPTBIT_PASSTHROUGH)) + 0, + OPT_QUIET = IF_LONG_OPTS( (1 << OPTBIT_QUIET )) + 0, + OPT_2STDOUT = IF_LONG_OPTS( (1 << OPTBIT_2STDOUT )) + 0, +}; + +#define OPTION_STR "it0uvdmLF:" + +#if ENABLE_FEATURE_CPIO_O +static off_t cpio_pad4(off_t size) +{ + int i; + + i = (- size) & 3; + size += i; + while (--i >= 0) + bb_putchar('\0'); + return size; +} + +/* Return value will become exit code. + * It's ok to exit instead of return. */ +static NOINLINE int cpio_o(void) +{ + static const char trailer[] ALIGN1 = "TRAILER!!!"; + struct name_s { + struct name_s *next; + char name[1]; + }; + struct inodes_s { + struct inodes_s *next; + struct name_s *names; + struct stat st; + }; + + struct inodes_s *links = NULL; + off_t bytes = 0; /* output bytes count */ + + while (1) { + const char *name; + char *line; + struct stat st; + + line = (option_mask32 & OPT_NUL_TERMINATED) + ? bb_get_chunk_from_file(stdin, NULL) + : xmalloc_fgetline(stdin); + + if (line) { + /* Strip leading "./[./]..." from the filename */ + name = line; + while (name[0] == '.' && name[1] == '/') { + while (*++name == '/') + continue; + } + if (!*name) { /* line is empty */ + free(line); + continue; + } + if ((option_mask32 & OPT_DEREF) + ? stat(name, &st) + : lstat(name, &st) + ) { + abort_cpio_o: + bb_simple_perror_msg_and_die(name); + } + + if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode))) + st.st_size = 0; /* paranoia */ + + /* Store hardlinks for later processing, dont output them */ + if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) { + struct name_s *n; + struct inodes_s *l; + + /* Do we have this hardlink remembered? */ + l = links; + while (1) { + if (l == NULL) { + /* Not found: add new item to "links" list */ + l = xzalloc(sizeof(*l)); + l->st = st; + l->next = links; + links = l; + break; + } + if (l->st.st_ino == st.st_ino) { + /* found */ + break; + } + l = l->next; + } + /* Add new name to "l->names" list */ + n = xmalloc(sizeof(*n) + strlen(name)); + strcpy(n->name, name); + n->next = l->names; + l->names = n; + + free(line); + continue; + } + + } else { /* line == NULL: EOF */ + next_link: + if (links) { + /* Output hardlink's data */ + st = links->st; + name = links->names->name; + links->names = links->names->next; + /* GNU cpio is reported to emit file data + * only for the last instance. Mimic that. */ + if (links->names == NULL) + links = links->next; + else + st.st_size = 0; + /* NB: we leak links->names and/or links, + * this is intended (we exit soon anyway) */ + } else { + /* If no (more) hardlinks to output, + * output "trailer" entry */ + name = trailer; + /* st.st_size == 0 is a must, but for uniformity + * in the output, we zero out everything */ + memset(&st, 0, sizeof(st)); + /* st.st_nlink = 1; - GNU cpio does this */ + } + } + + bytes += printf("070701" + "%08X%08X%08X%08X%08X%08X%08X" + "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ + /* strlen+1: */ "%08X" + /* chksum: */ "00000000" /* (only for "070702" files) */ + /* name,NUL: */ "%s%c", + (unsigned)(uint32_t) st.st_ino, + (unsigned)(uint32_t) st.st_mode, + (unsigned)(uint32_t) st.st_uid, + (unsigned)(uint32_t) st.st_gid, + (unsigned)(uint32_t) st.st_nlink, + (unsigned)(uint32_t) st.st_mtime, + (unsigned)(uint32_t) st.st_size, + (unsigned)(uint32_t) major(st.st_dev), + (unsigned)(uint32_t) minor(st.st_dev), + (unsigned)(uint32_t) major(st.st_rdev), + (unsigned)(uint32_t) minor(st.st_rdev), + (unsigned)(strlen(name) + 1), + name, '\0'); + bytes = cpio_pad4(bytes); + + if (st.st_size) { + if (S_ISLNK(st.st_mode)) { + char *lpath = xmalloc_readlink_or_warn(name); + if (!lpath) + goto abort_cpio_o; + bytes += printf("%s", lpath); + free(lpath); + } else { /* S_ISREG */ + int fd = xopen(name, O_RDONLY); + fflush_all(); + /* We must abort if file got shorter too! */ + bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size); + bytes += st.st_size; + close(fd); + } + bytes = cpio_pad4(bytes); + } + + if (!line) { + if (name != trailer) + goto next_link; + /* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */ + return EXIT_SUCCESS; + } + + free(line); + } /* end of "while (1)" */ +} +#endif + +int cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cpio_main(int argc UNUSED_PARAM, char **argv) +{ + archive_handle_t *archive_handle; + char *cpio_filename; + IF_FEATURE_CPIO_O(const char *cpio_fmt = "";) + unsigned opt; + +#if ENABLE_LONG_OPTS + applet_long_options = + "extract\0" No_argument "i" + "list\0" No_argument "t" +#if ENABLE_FEATURE_CPIO_O + "create\0" No_argument "o" + "format\0" Required_argument "H" +#if ENABLE_FEATURE_CPIO_P + "pass-through\0" No_argument "p" +#endif +#endif + "verbose\0" No_argument "v" + "quiet\0" No_argument "\xff" + "to-stdout\0" No_argument "\xfe" + ; +#endif + + archive_handle = init_handle(); + /* archive_handle->src_fd = STDIN_FILENO; - done by init_handle */ + archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER; + + /* As of now we do not enforce this: */ + /* -i,-t,-o,-p are mutually exclusive */ + /* -u,-d,-m make sense only with -i or -p */ + /* -L makes sense only with -o or -p */ + +#if !ENABLE_FEATURE_CPIO_O + opt = getopt32(argv, OPTION_STR, &cpio_filename); + argv += optind; + if (opt & OPT_FILE) { /* -F */ + xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO); + } +#else + opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt); + argv += optind; + if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */ + xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO); + } + if (opt & OPT_PASSTHROUGH) { + pid_t pid; + struct fd_pair pp; + + if (argv[0] == NULL) + bb_show_usage(); + if (opt & OPT_CREATE_LEADING_DIR) + mkdir(argv[0], 0777); + /* Crude existence check: + * close(xopen(argv[0], O_RDONLY | O_DIRECTORY)); + * We can also xopen, fstat, IS_DIR, later fchdir. + * This would check for existence earlier and cleaner. + * As it stands now, if we fail xchdir later, + * child dies on EPIPE, unless it caught + * a diffrerent problem earlier. + * This is good enough for now. + */ +#if !BB_MMU + pp.rd = 3; + pp.wr = 4; + if (!re_execed) { + close(3); + close(4); + xpiped_pair(pp); + } +#else + xpiped_pair(pp); +#endif + pid = fork_or_rexec(argv - optind); + if (pid == 0) { /* child */ + close(pp.rd); + xmove_fd(pp.wr, STDOUT_FILENO); + goto dump; + } + /* parent */ + xchdir(*argv++); + close(pp.wr); + xmove_fd(pp.rd, STDIN_FILENO); + //opt &= ~OPT_PASSTHROUGH; + opt |= OPT_EXTRACT; + goto skip; + } + /* -o */ + if (opt & OPT_CREATE) { + if (cpio_fmt[0] != 'n') /* we _require_ "-H newc" */ + bb_show_usage(); + if (opt & OPT_FILE) { + xmove_fd(xopen(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); + } + dump: + return cpio_o(); + } + skip: +#endif + + /* One of either extract or test options must be given */ + if ((opt & (OPT_TEST | OPT_EXTRACT)) == 0) { + bb_show_usage(); + } + + if (opt & OPT_TEST) { + /* if both extract and test options are given, ignore extract option */ + opt &= ~OPT_EXTRACT; + archive_handle->action_header = header_list; + } + if (opt & OPT_EXTRACT) { + archive_handle->action_data = data_extract_all; + if (opt & OPT_2STDOUT) + archive_handle->action_data = data_extract_to_stdout; + } + if (opt & OPT_UNCONDITIONAL) { + archive_handle->ah_flags |= ARCHIVE_UNLINK_OLD; + archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER; + } + if (opt & OPT_VERBOSE) { + if (archive_handle->action_header == header_list) { + archive_handle->action_header = header_verbose_list; + } else { + archive_handle->action_header = header_list; + } + } + if (opt & OPT_CREATE_LEADING_DIR) { + archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS; + } + if (opt & OPT_PRESERVE_MTIME) { + archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE; + } + + while (*argv) { + archive_handle->filter = filter_accept_list; + llist_add_to(&archive_handle->accept, *argv); + argv++; + } + + /* see get_header_cpio */ + archive_handle->cpio__blocks = (off_t)-1; + while (get_header_cpio(archive_handle) == EXIT_SUCCESS) + continue; + + if (archive_handle->cpio__blocks != (off_t)-1 + && !(opt & OPT_QUIET) + ) { + fprintf(stderr, "%"OFF_FMT"u blocks\n", archive_handle->cpio__blocks); + } + + return EXIT_SUCCESS; +} diff --git a/archival/dpkg.c b/archival/dpkg.c new file mode 100644 index 000000000..2a6a7b3bf --- /dev/null +++ b/archival/dpkg.c @@ -0,0 +1,1920 @@ +/* vi: set sw=4 ts=4: */ +/* + * mini dpkg implementation for busybox. + * this is not meant as a replacement for dpkg + * + * written by glenn mcgrath with the help of others + * copyright (c) 2001 by glenn mcgrath + * + * parts of the version comparison code is plucked from the real dpkg + * application which is licensed GPLv2 and + * copyright (c) 1995 Ian Jackson + * + * started life as a busybox implementation of udpkg + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* + * known difference between busybox dpkg and the official dpkg that i don't + * consider important, its worth keeping a note of differences anyway, just to + * make it easier to maintain. + * - the first value for the confflile: field isnt placed on a new line. + * - when installing a package the status: field is placed at the end of the + * section, rather than just after the package: field. + * + * bugs that need to be fixed + * - (unknown, please let me know when you find any) + * + */ + +//usage:#define dpkg_trivial_usage +//usage: "[-ilCPru] [-F OPT] PACKAGE" +//usage:#define dpkg_full_usage "\n\n" +//usage: "Install, remove and manage Debian packages\n" +//usage: IF_LONG_OPTS( +//usage: "\n -i,--install Install the package" +//usage: "\n -l,--list List of installed packages" +//usage: "\n --configure Configure an unpackaged package" +//usage: "\n -P,--purge Purge all files of a package" +//usage: "\n -r,--remove Remove all but the configuration files for a package" +//usage: "\n --unpack Unpack a package, but don't configure it" +//usage: "\n --force-depends Ignore dependency problems" +//usage: "\n --force-confnew Overwrite existing config files when installing" +//usage: "\n --force-confold Keep old config files when installing" +//usage: ) +//usage: IF_NOT_LONG_OPTS( +//usage: "\n -i Install the package" +//usage: "\n -l List of installed packages" +//usage: "\n -C Configure an unpackaged package" +//usage: "\n -P Purge all files of a package" +//usage: "\n -r Remove all but the configuration files for a package" +//usage: "\n -u Unpack a package, but don't configure it" +//usage: "\n -F depends Ignore dependency problems" +//usage: "\n -F confnew Overwrite existing config files when installing" +//usage: "\n -F confold Keep old config files when installing" +//usage: ) + +#include "libbb.h" +#include +#include "archive.h" + +/* note: if you vary hash_prime sizes be aware, + * 1) tweaking these will have a big effect on how much memory this program uses. + * 2) for computational efficiency these hash tables should be at least 20% + * larger than the maximum number of elements stored in it. + * 3) all _hash_prime's must be a prime number or chaos is assured, if your looking + * for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt + * 4) if you go bigger than 15 bits you may get into trouble (untested) as its + * sometimes cast to an unsigned, if you go to 16 bit you will overlap + * int's and chaos is assured, 16381 is the max prime for 14 bit field + */ + +/* NAME_HASH_PRIME, Stores package names and versions, + * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME, + * as there a lot of duplicate version numbers */ +#define NAME_HASH_PRIME 16381 + +/* PACKAGE_HASH_PRIME, Maximum number of unique packages, + * It must not be smaller than STATUS_HASH_PRIME, + * Currently only packages from status_hashtable are stored in here, but in + * future this may be used to store packages not only from a status file, + * but an available_hashtable, and even multiple packages files. + * Package can be stored more than once if they have different versions. + * e.g. The same package may have different versions in the status file + * and available file */ +#define PACKAGE_HASH_PRIME 10007 +typedef struct edge_s { + unsigned operator:4; /* was:3 */ + unsigned type:4; + unsigned name:16; /* was:14 */ + unsigned version:16; /* was:14 */ +} edge_t; + +typedef struct common_node_s { + unsigned name:16; /* was:14 */ + unsigned version:16; /* was:14 */ + unsigned num_of_edges:16; /* was:14 */ + edge_t **edge; +} common_node_t; + +/* Currently it doesnt store packages that have state-status of not-installed + * So it only really has to be the size of the maximum number of packages + * likely to be installed at any one time, so there is a bit of leeway here */ +#define STATUS_HASH_PRIME 8191 +typedef struct status_node_s { + unsigned package:16; /* was:14 */ /* has to fit PACKAGE_HASH_PRIME */ + unsigned status:16; /* was:14 */ /* has to fit STATUS_HASH_PRIME */ +} status_node_t; + + +/* Globals */ +struct globals { + char *name_hashtable[NAME_HASH_PRIME + 1]; + common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1]; + status_node_t *status_hashtable[STATUS_HASH_PRIME + 1]; +}; +#define G (*ptr_to_globals) +#define name_hashtable (G.name_hashtable ) +#define package_hashtable (G.package_hashtable) +#define status_hashtable (G.status_hashtable ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ +} while (0) + + +/* Even numbers are for 'extras', like ored dependencies or null */ +enum edge_type_e { + EDGE_NULL = 0, + EDGE_PRE_DEPENDS = 1, + EDGE_OR_PRE_DEPENDS = 2, + EDGE_DEPENDS = 3, + EDGE_OR_DEPENDS = 4, + EDGE_REPLACES = 5, + EDGE_PROVIDES = 7, + EDGE_CONFLICTS = 9, + EDGE_SUGGESTS = 11, + EDGE_RECOMMENDS = 13, + EDGE_ENHANCES = 15 +}; +enum operator_e { + VER_NULL = 0, + VER_EQUAL = 1, + VER_LESS = 2, + VER_LESS_EQUAL = 3, + VER_MORE = 4, + VER_MORE_EQUAL = 5, + VER_ANY = 6 +}; + +typedef struct deb_file_s { + char *control_file; + char *filename; + unsigned package:16; /* was:14 */ +} deb_file_t; + + +static void make_hash(const char *key, unsigned *start, unsigned *decrement, const int hash_prime) +{ + unsigned long hash_num = key[0]; + int len = strlen(key); + int i; + + /* Maybe i should have uses a "proper" hashing algorithm here instead + * of making one up myself, seems to be working ok though. */ + for (i = 1; i < len; i++) { + /* shifts the ascii based value and adds it to previous value + * shift amount is mod 24 because long int is 32 bit and data + * to be shifted is 8, don't want to shift data to where it has + * no effect */ + hash_num += (key[i] + key[i-1]) << ((key[i] * i) % 24); + } + *start = (unsigned) hash_num % hash_prime; + *decrement = (unsigned) 1 + (hash_num % (hash_prime - 1)); +} + +/* this adds the key to the hash table */ +static int search_name_hashtable(const char *key) +{ + unsigned probe_address; + unsigned probe_decrement; + + make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME); + while (name_hashtable[probe_address] != NULL) { + if (strcmp(name_hashtable[probe_address], key) == 0) { + return probe_address; + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += NAME_HASH_PRIME; + } + } + name_hashtable[probe_address] = xstrdup(key); + return probe_address; +} + +/* this DOESNT add the key to the hashtable + * TODO make it consistent with search_name_hashtable + */ +static unsigned search_status_hashtable(const char *key) +{ + unsigned probe_address; + unsigned probe_decrement; + + make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME); + while (status_hashtable[probe_address] != NULL) { + if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) { + break; + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += STATUS_HASH_PRIME; + } + } + return probe_address; +} + +static int order(char x) +{ + return (x == '~' ? -1 + : x == '\0' ? 0 + : isdigit(x) ? 0 + : isalpha(x) ? x + : (unsigned char)x + 256 + ); +} + +/* This code is taken from dpkg and modified slightly to work with busybox */ +static int version_compare_part(const char *val, const char *ref) +{ + if (!val) val = ""; + if (!ref) ref = ""; + + while (*val || *ref) { + int first_diff; + + while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) { + int vc = order(*val); + int rc = order(*ref); + if (vc != rc) + return vc - rc; + val++; + ref++; + } + + while (*val == '0') + val++; + while (*ref == '0') + ref++; + + first_diff = 0; + while (isdigit(*val) && isdigit(*ref)) { + if (first_diff == 0) + first_diff = *val - *ref; + val++; + ref++; + } + if (isdigit(*val)) + return 1; + if (isdigit(*ref)) + return -1; + if (first_diff) + return first_diff; + } + return 0; +} + +/* if ver1 < ver2 return -1, + * if ver1 = ver2 return 0, + * if ver1 > ver2 return 1, + */ +static int version_compare(const unsigned ver1, const unsigned ver2) +{ + char *ch_ver1 = name_hashtable[ver1]; + char *ch_ver2 = name_hashtable[ver2]; + unsigned epoch1 = 0, epoch2 = 0; + char *colon; + char *deb_ver1, *deb_ver2; + char *upstream_ver1; + char *upstream_ver2; + int result; + + /* Compare epoch */ + colon = strchr(ch_ver1, ':'); + if (colon) { + epoch1 = atoi(ch_ver1); + ch_ver1 = colon + 1; + } + colon = strchr(ch_ver2, ':'); + if (colon) { + epoch2 = atoi(ch_ver2); + ch_ver2 = colon + 1; + } + if (epoch1 < epoch2) { + return -1; + } + if (epoch1 > epoch2) { + return 1; + } + + /* Compare upstream version */ + upstream_ver1 = xstrdup(ch_ver1); + upstream_ver2 = xstrdup(ch_ver2); + + /* Chop off debian version, and store for later use */ + deb_ver1 = strrchr(upstream_ver1, '-'); + deb_ver2 = strrchr(upstream_ver2, '-'); + if (deb_ver1) { + *deb_ver1++ = '\0'; + } + if (deb_ver2) { + *deb_ver2++ = '\0'; + } + result = version_compare_part(upstream_ver1, upstream_ver2); + if (result == 0) { + /* Compare debian versions */ + result = version_compare_part(deb_ver1, deb_ver2); + } + + free(upstream_ver1); + free(upstream_ver2); + return result; +} + +static int test_version(const unsigned version1, const unsigned version2, const unsigned operator) +{ + const int version_result = version_compare(version1, version2); + switch (operator) { + case VER_ANY: + return TRUE; + case VER_EQUAL: + return (version_result == 0); + case VER_LESS: + return (version_result < 0); + case VER_LESS_EQUAL: + return (version_result <= 0); + case VER_MORE: + return (version_result > 0); + case VER_MORE_EQUAL: + return (version_result >= 0); + } + return FALSE; +} + +static int search_package_hashtable(const unsigned name, const unsigned version, const unsigned operator) +{ + unsigned probe_address; + unsigned probe_decrement; + + make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME); + while (package_hashtable[probe_address] != NULL) { + if (package_hashtable[probe_address]->name == name) { + if (operator == VER_ANY) { + return probe_address; + } + if (test_version(package_hashtable[probe_address]->version, version, operator)) { + return probe_address; + } + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += PACKAGE_HASH_PRIME; + } + } + return probe_address; +} + +/* + * This function searches through the entire package_hashtable looking + * for a package which provides "needle". It returns the index into + * the package_hashtable for the providing package. + * + * needle is the index into name_hashtable of the package we are + * looking for. + * + * start_at is the index in the package_hashtable to start looking + * at. If start_at is -1 then start at the beginning. This is to allow + * for repeated searches since more than one package might provide + * needle. + * + * FIXME: I don't think this is very efficient, but I thought I'd keep + * it simple for now until it proves to be a problem. + */ +static int search_for_provides(int needle, int start_at) +{ + int i, j; + common_node_t *p; + for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) { + p = package_hashtable[i]; + if (p == NULL) + continue; + for (j = 0; j < p->num_of_edges; j++) + if (p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle) + return i; + } + return -1; +} + +/* + * Add an edge to a node + */ +static void add_edge_to_node(common_node_t *node, edge_t *edge) +{ + node->edge = xrealloc_vector(node->edge, 2, node->num_of_edges); + node->edge[node->num_of_edges++] = edge; +} + +/* + * Create one new node and one new edge for every dependency. + * + * Dependencies which contain multiple alternatives are represented as + * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a + * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of + * the OR edge contains the full dependency string while the version + * field contains the number of EDGE nodes which follow as part of + * this alternative. + */ +static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned edge_type) +{ + char *line = xstrdup(whole_line); + char *line2; + char *line_ptr1 = NULL; + char *line_ptr2 = NULL; + char *field; + char *field2; + char *version; + edge_t *edge; + edge_t *or_edge; + int offset_ch; + + field = strtok_r(line, ",", &line_ptr1); + do { + /* skip leading spaces */ + field += strspn(field, " "); + line2 = xstrdup(field); + field2 = strtok_r(line2, "|", &line_ptr2); + or_edge = NULL; + if ((edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS) + && (strcmp(field, field2) != 0) + ) { + or_edge = xzalloc(sizeof(edge_t)); + or_edge->type = edge_type + 1; + or_edge->name = search_name_hashtable(field); + //or_edge->version = 0; // tracks the number of alternatives + add_edge_to_node(parent_node, or_edge); + } + + do { + edge = xmalloc(sizeof(edge_t)); + edge->type = edge_type; + + /* Skip any extra leading spaces */ + field2 += strspn(field2, " "); + + /* Get dependency version info */ + version = strchr(field2, '('); + if (version == NULL) { + edge->operator = VER_ANY; + /* Get the versions hash number, adding it if the number isnt already in there */ + edge->version = search_name_hashtable("ANY"); + } else { + /* Skip leading ' ' or '(' */ + version += strspn(version, " ("); + /* Calculate length of any operator characters */ + offset_ch = strspn(version, "<=>"); + /* Determine operator */ + if (offset_ch > 0) { + if (strncmp(version, "=", offset_ch) == 0) { + edge->operator = VER_EQUAL; + } else if (strncmp(version, "<<", offset_ch) == 0) { + edge->operator = VER_LESS; + } else if (strncmp(version, "<=", offset_ch) == 0) { + edge->operator = VER_LESS_EQUAL; + } else if (strncmp(version, ">>", offset_ch) == 0) { + edge->operator = VER_MORE; + } else if (strncmp(version, ">=", offset_ch) == 0) { + edge->operator = VER_MORE_EQUAL; + } else { + bb_error_msg_and_die("illegal operator"); + } + } + /* skip to start of version numbers */ + version += offset_ch; + version += strspn(version, " "); + + /* Truncate version at trailing ' ' or ')' */ + version[strcspn(version, " )")] = '\0'; + /* Get the versions hash number, adding it if the number isnt already in there */ + edge->version = search_name_hashtable(version); + } + + /* Get the dependency name */ + field2[strcspn(field2, " (")] = '\0'; + edge->name = search_name_hashtable(field2); + + if (or_edge) + or_edge->version++; + + add_edge_to_node(parent_node, edge); + field2 = strtok_r(NULL, "|", &line_ptr2); + } while (field2 != NULL); + + free(line2); + field = strtok_r(NULL, ",", &line_ptr1); + } while (field != NULL); + + free(line); +} + +static void free_package(common_node_t *node) +{ + unsigned i; + if (node) { + for (i = 0; i < node->num_of_edges; i++) { + free(node->edge[i]); + } + free(node->edge); + free(node); + } +} + +/* + * Gets the next package field from package_buffer, separated into the field name + * and field value, it returns the int offset to the first character of the next field + */ +static int read_package_field(const char *package_buffer, char **field_name, char **field_value) +{ + int offset_name_start = 0; + int offset_name_end = 0; + int offset_value_start = 0; + int offset_value_end = 0; + int offset = 0; + int next_offset; + int name_length; + int value_length; + int exit_flag = FALSE; + + if (package_buffer == NULL) { + *field_name = NULL; + *field_value = NULL; + return -1; + } + while (1) { + next_offset = offset + 1; + switch (package_buffer[offset]) { + case '\0': + exit_flag = TRUE; + break; + case ':': + if (offset_name_end == 0) { + offset_name_end = offset; + offset_value_start = next_offset; + } + /* TODO: Name might still have trailing spaces if ':' isnt + * immediately after name */ + break; + case '\n': + /* TODO: The char next_offset may be out of bounds */ + if (package_buffer[next_offset] != ' ') { + exit_flag = TRUE; + break; + } + case '\t': + case ' ': + /* increment the value start point if its a just filler */ + if (offset_name_start == offset) { + offset_name_start++; + } + if (offset_value_start == offset) { + offset_value_start++; + } + break; + } + if (exit_flag) { + /* Check that the names are valid */ + offset_value_end = offset; + name_length = offset_name_end - offset_name_start; + value_length = offset_value_end - offset_value_start; + if (name_length == 0) { + break; + } + if ((name_length > 0) && (value_length > 0)) { + break; + } + + /* If not valid, start fresh with next field */ + exit_flag = FALSE; + offset_name_start = offset + 1; + offset_name_end = 0; + offset_value_start = offset + 1; + offset_value_end = offset + 1; + offset++; + } + offset++; + } + *field_name = NULL; + if (name_length) { + *field_name = xstrndup(&package_buffer[offset_name_start], name_length); + } + *field_value = NULL; + if (value_length > 0) { + *field_value = xstrndup(&package_buffer[offset_value_start], value_length); + } + return next_offset; +} + +static unsigned fill_package_struct(char *control_buffer) +{ + static const char field_names[] ALIGN1 = + "Package\0""Version\0" + "Pre-Depends\0""Depends\0""Replaces\0""Provides\0" + "Conflicts\0""Suggests\0""Recommends\0""Enhances\0"; + + common_node_t *new_node = xzalloc(sizeof(common_node_t)); + char *field_name; + char *field_value; + int field_start = 0; + int num = -1; + int buffer_length = strlen(control_buffer); + + new_node->version = search_name_hashtable("unknown"); + while (field_start < buffer_length) { + unsigned field_num; + + field_start += read_package_field(&control_buffer[field_start], + &field_name, &field_value); + + if (field_name == NULL) { + goto fill_package_struct_cleanup; + } + + field_num = index_in_strings(field_names, field_name); + switch (field_num) { + case 0: /* Package */ + new_node->name = search_name_hashtable(field_value); + break; + case 1: /* Version */ + new_node->version = search_name_hashtable(field_value); + break; + case 2: /* Pre-Depends */ + add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS); + break; + case 3: /* Depends */ + add_split_dependencies(new_node, field_value, EDGE_DEPENDS); + break; + case 4: /* Replaces */ + add_split_dependencies(new_node, field_value, EDGE_REPLACES); + break; + case 5: /* Provides */ + add_split_dependencies(new_node, field_value, EDGE_PROVIDES); + break; + case 6: /* Conflicts */ + add_split_dependencies(new_node, field_value, EDGE_CONFLICTS); + break; + case 7: /* Suggests */ + add_split_dependencies(new_node, field_value, EDGE_SUGGESTS); + break; + case 8: /* Recommends */ + add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS); + break; + case 9: /* Enhances */ + add_split_dependencies(new_node, field_value, EDGE_ENHANCES); + break; + } + fill_package_struct_cleanup: + free(field_name); + free(field_value); + } + + if (new_node->version == search_name_hashtable("unknown")) { + free_package(new_node); + return -1; + } + num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL); + free_package(package_hashtable[num]); + package_hashtable[num] = new_node; + return num; +} + +/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */ +static unsigned get_status(const unsigned status_node, const int num) +{ + char *status_string = name_hashtable[status_hashtable[status_node]->status]; + char *state_sub_string; + unsigned state_sub_num; + int len; + int i; + + /* set tmp_string to point to the start of the word number */ + for (i = 1; i < num; i++) { + /* skip past a word */ + status_string += strcspn(status_string, " "); + /* skip past the separating spaces */ + status_string += strspn(status_string, " "); + } + len = strcspn(status_string, " \n"); + state_sub_string = xstrndup(status_string, len); + state_sub_num = search_name_hashtable(state_sub_string); + free(state_sub_string); + return state_sub_num; +} + +static void set_status(const unsigned status_node_num, const char *new_value, const int position) +{ + const unsigned new_value_num = search_name_hashtable(new_value); + unsigned want = get_status(status_node_num, 1); + unsigned flag = get_status(status_node_num, 2); + unsigned status = get_status(status_node_num, 3); + char *new_status; + + switch (position) { + case 1: + want = new_value_num; + break; + case 2: + flag = new_value_num; + break; + case 3: + status = new_value_num; + break; + default: + bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen"); + } + + new_status = xasprintf("%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]); + status_hashtable[status_node_num]->status = search_name_hashtable(new_status); + free(new_status); +} + +static const char *describe_status(int status_num) +{ + int status_want, status_state; + if (status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0) + return "is not installed or flagged to be installed"; + + status_want = get_status(status_num, 1); + status_state = get_status(status_num, 3); + + if (status_state == search_name_hashtable("installed")) { + if (status_want == search_name_hashtable("install")) + return "is installed"; + if (status_want == search_name_hashtable("deinstall")) + return "is marked to be removed"; + if (status_want == search_name_hashtable("purge")) + return "is marked to be purged"; + } + if (status_want == search_name_hashtable("unknown")) + return "is in an indeterminate state"; + if (status_want == search_name_hashtable("install")) + return "is marked to be installed"; + + return "is not installed or flagged to be installed"; +} + +static void index_status_file(const char *filename) +{ + FILE *status_file; + char *control_buffer; + char *status_line; + status_node_t *status_node = NULL; + unsigned status_num; + + status_file = xfopen_for_read(filename); + while ((control_buffer = xmalloc_fgetline_str(status_file, "\n\n")) != NULL) { + const unsigned package_num = fill_package_struct(control_buffer); + if (package_num != -1) { + status_node = xmalloc(sizeof(status_node_t)); + /* fill_package_struct doesnt handle the status field */ + status_line = strstr(control_buffer, "Status:"); + if (status_line != NULL) { + status_line += 7; + status_line += strspn(status_line, " \n\t"); + status_line = xstrndup(status_line, strcspn(status_line, "\n")); + status_node->status = search_name_hashtable(status_line); + free(status_line); + } + status_node->package = package_num; + status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]); + status_hashtable[status_num] = status_node; + } + free(control_buffer); + } + fclose(status_file); +} + +static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer) +{ + char *name; + char *value; + int start = 0; + while (1) { + start += read_package_field(&control_buffer[start], &name, &value); + if (name == NULL) { + break; + } + if (strcmp(name, "Status") != 0) { + fprintf(new_status_file, "%s: %s\n", name, value); + } + } +} + +/* This could do with a cleanup */ +static void write_status_file(deb_file_t **deb_file) +{ + FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status"); + FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb"); + char *package_name; + char *status_from_file; + char *control_buffer = NULL; + char *tmp_string; + int status_num; + int field_start = 0; + int write_flag; + int i = 0; + + /* Update previously known packages */ + while ((control_buffer = xmalloc_fgetline_str(old_status_file, "\n\n")) != NULL) { + tmp_string = strstr(control_buffer, "Package:"); + if (tmp_string == NULL) { + continue; + } + + tmp_string += 8; + tmp_string += strspn(tmp_string, " \n\t"); + package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n")); + write_flag = FALSE; + tmp_string = strstr(control_buffer, "Status:"); + if (tmp_string != NULL) { + /* Separate the status value from the control buffer */ + tmp_string += 7; + tmp_string += strspn(tmp_string, " \n\t"); + status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n")); + } else { + status_from_file = NULL; + } + + /* Find this package in the status hashtable */ + status_num = search_status_hashtable(package_name); + if (status_hashtable[status_num] != NULL) { + const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status]; + if (strcmp(status_from_file, status_from_hashtable) != 0) { + /* New status isnt exactly the same as old status */ + const int state_status = get_status(status_num, 3); + if ((strcmp("installed", name_hashtable[state_status]) == 0) + || (strcmp("unpacked", name_hashtable[state_status]) == 0) + ) { + /* We need to add the control file from the package */ + i = 0; + while (deb_file[i] != NULL) { + if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) { + /* Write a status file entry with a modified status */ + /* remove trailing \n's */ + write_buffer_no_status(new_status_file, deb_file[i]->control_file); + set_status(status_num, "ok", 2); + fprintf(new_status_file, "Status: %s\n\n", + name_hashtable[status_hashtable[status_num]->status]); + write_flag = TRUE; + break; + } + i++; + } + /* This is temperary, debugging only */ + if (deb_file[i] == NULL) { + bb_error_msg_and_die("ALERT: cannot find a control file, " + "your status file may be broken, status may be " + "incorrect for %s", package_name); + } + } + else if (strcmp("not-installed", name_hashtable[state_status]) == 0) { + /* Only write the Package, Status, Priority and Section lines */ + fprintf(new_status_file, "Package: %s\n", package_name); + fprintf(new_status_file, "Status: %s\n", status_from_hashtable); + + while (1) { + char *field_name; + char *field_value; + field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); + if (field_name == NULL) { + break; + } + if ((strcmp(field_name, "Priority") == 0) + || (strcmp(field_name, "Section") == 0) + ) { + fprintf(new_status_file, "%s: %s\n", field_name, field_value); + } + } + write_flag = TRUE; + fputs("\n", new_status_file); + } + else if (strcmp("config-files", name_hashtable[state_status]) == 0) { + /* only change the status line */ + while (1) { + char *field_name; + char *field_value; + field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); + if (field_name == NULL) { + break; + } + /* Setup start point for next field */ + if (strcmp(field_name, "Status") == 0) { + fprintf(new_status_file, "Status: %s\n", status_from_hashtable); + } else { + fprintf(new_status_file, "%s: %s\n", field_name, field_value); + } + } + write_flag = TRUE; + fputs("\n", new_status_file); + } + } + } + /* If the package from the status file wasnt handle above, do it now*/ + if (!write_flag) { + fprintf(new_status_file, "%s\n\n", control_buffer); + } + + free(status_from_file); + free(package_name); + free(control_buffer); + } + + /* Write any new packages */ + for (i = 0; deb_file[i] != NULL; i++) { + status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]); + if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) { + write_buffer_no_status(new_status_file, deb_file[i]->control_file); + set_status(status_num, "ok", 2); + fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]); + } + } + fclose(old_status_file); + fclose(new_status_file); + + /* Create a separate backfile to dpkg */ + if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) { + if (errno != ENOENT) + bb_error_msg_and_die("can't create backup status file"); + /* Its ok if renaming the status file fails because status + * file doesnt exist, maybe we are starting from scratch */ + bb_error_msg("no status file found, creating new one"); + } + + xrename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status"); +} + +/* This function returns TRUE if the given package can satisfy a + * dependency of type depend_type. + * + * A pre-depends is satisfied only if a package is already installed, + * which a regular depends can be satisfied by a package which we want + * to install. + */ +static int package_satisfies_dependency(int package, int depend_type) +{ + int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]); + + /* status could be unknown if package is a pure virtual + * provides which cannot satisfy any dependency by itself. + */ + if (status_hashtable[status_num] == NULL) + return 0; + + switch (depend_type) { + case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed"); + case EDGE_DEPENDS: return get_status(status_num, 1) == search_name_hashtable("install"); + } + return 0; +} + +static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count - ?? */) +{ + int *conflicts = NULL; + int conflicts_num = 0; + int i = deb_start; + int j; + + /* Check for conflicts + * TODO: TEST if conflicts with other packages to be installed + * + * Add install packages and the packages they provide + * to the list of files to check conflicts for + */ + + /* Create array of package numbers to check against + * installed package for conflicts*/ + while (deb_file[i] != NULL) { + const unsigned package_num = deb_file[i]->package; + conflicts = xrealloc_vector(conflicts, 2, conflicts_num); + conflicts[conflicts_num] = package_num; + conflicts_num++; + /* add provides to conflicts list */ + for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) { + if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) { + const int conflicts_package_num = search_package_hashtable( + package_hashtable[package_num]->edge[j]->name, + package_hashtable[package_num]->edge[j]->version, + package_hashtable[package_num]->edge[j]->operator); + if (package_hashtable[conflicts_package_num] == NULL) { + /* create a new package */ + common_node_t *new_node = xzalloc(sizeof(common_node_t)); + new_node->name = package_hashtable[package_num]->edge[j]->name; + new_node->version = package_hashtable[package_num]->edge[j]->version; + package_hashtable[conflicts_package_num] = new_node; + } + conflicts = xrealloc_vector(conflicts, 2, conflicts_num); + conflicts[conflicts_num] = conflicts_package_num; + conflicts_num++; + } + } + i++; + } + + /* Check conflicts */ + i = 0; + while (deb_file[i] != NULL) { + const common_node_t *package_node = package_hashtable[deb_file[i]->package]; + int status_num = 0; + status_num = search_status_hashtable(name_hashtable[package_node->name]); + + if (get_status(status_num, 3) == search_name_hashtable("installed")) { + i++; + continue; + } + + for (j = 0; j < package_node->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + + if (package_edge->type == EDGE_CONFLICTS) { + const unsigned package_num = + search_package_hashtable(package_edge->name, + package_edge->version, + package_edge->operator); + int result = 0; + if (package_hashtable[package_num] != NULL) { + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + + if (get_status(status_num, 1) == search_name_hashtable("install")) { + result = test_version(package_hashtable[deb_file[i]->package]->version, + package_edge->version, package_edge->operator); + } + } + + if (result) { + bb_error_msg_and_die("package %s conflicts with %s", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + } + } + i++; + } + + + /* Check dependendcies */ + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + int status_num = 0; + int number_of_alternatives = 0; + const edge_t * root_of_alternatives = NULL; + const common_node_t *package_node = package_hashtable[i]; + + /* If the package node does not exist then this + * package is a virtual one. In which case there are + * no dependencies to check. + */ + if (package_node == NULL) continue; + + status_num = search_status_hashtable(name_hashtable[package_node->name]); + + /* If there is no status then this package is a + * virtual one provided by something else. In which + * case there are no dependencies to check. + */ + if (status_hashtable[status_num] == NULL) continue; + + /* If we don't want this package installed then we may + * as well ignore it's dependencies. + */ + if (get_status(status_num, 1) != search_name_hashtable("install")) { + continue; + } + + /* This code is tested only for EDGE_DEPENDS, since I + * have no suitable pre-depends available. There is no + * reason that it shouldn't work though :-) + */ + for (j = 0; j < package_node->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + unsigned package_num; + + if (package_edge->type == EDGE_OR_PRE_DEPENDS + || package_edge->type == EDGE_OR_DEPENDS + ) { + /* start an EDGE_OR_ list */ + number_of_alternatives = package_edge->version; + root_of_alternatives = package_edge; + continue; + } + if (number_of_alternatives == 0) { /* not in the middle of an EDGE_OR_ list */ + number_of_alternatives = 1; + root_of_alternatives = NULL; + } + + package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator); + + if (package_edge->type == EDGE_PRE_DEPENDS + || package_edge->type == EDGE_DEPENDS + ) { + int result=1; + status_num = 0; + + /* If we are inside an alternative then check + * this edge is the right type. + * + * EDGE_DEPENDS == OR_DEPENDS -1 + * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1 + */ + if (root_of_alternatives && package_edge->type != root_of_alternatives->type - 1) + bb_error_msg_and_die("fatal error, package dependencies corrupt: %d != %d - 1", + package_edge->type, root_of_alternatives->type); + + if (package_hashtable[package_num] != NULL) + result = !package_satisfies_dependency(package_num, package_edge->type); + + if (result) { /* check for other package which provide what we are looking for */ + int provider = -1; + + while ((provider = search_for_provides(package_edge->name, provider)) > -1) { + if (package_hashtable[provider] == NULL) { + puts("Have a provider but no package information for it"); + continue; + } + result = !package_satisfies_dependency(provider, package_edge->type); + + if (result == 0) + break; + } + } + + /* It must be already installed, or to be installed */ + number_of_alternatives--; + if (result && number_of_alternatives == 0) { + if (root_of_alternatives) + bb_error_msg_and_die( + "package %s %sdepends on %s, " + "which cannot be satisfied", + name_hashtable[package_node->name], + package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "", + name_hashtable[root_of_alternatives->name]); + bb_error_msg_and_die( + "package %s %sdepends on %s, which %s\n", + name_hashtable[package_node->name], + package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "", + name_hashtable[package_edge->name], + describe_status(status_num)); + } + if (result == 0 && number_of_alternatives) { + /* we've found a package which + * satisfies the dependency, + * so skip over the rest of + * the alternatives. + */ + j += number_of_alternatives; + number_of_alternatives = 0; + } + } + } + } + free(conflicts); + return TRUE; +} + +static char **create_list(const char *filename) +{ + FILE *list_stream; + char **file_list; + char *line; + int count; + + /* don't use [xw]fopen here, handle error ourself */ + list_stream = fopen_for_read(filename); + if (list_stream == NULL) { + return NULL; + } + + file_list = NULL; + count = 0; + while ((line = xmalloc_fgetline(list_stream)) != NULL) { + file_list = xrealloc_vector(file_list, 2, count); + file_list[count++] = line; + /*file_list[count] = NULL; - xrealloc_vector did it */ + } + fclose(list_stream); + + return file_list; +} + +/* maybe i should try and hook this into remove_file.c somehow */ +static int remove_file_array(char **remove_names, char **exclude_names) +{ + struct stat path_stat; + int remove_flag = 1; /* not removed anything yet */ + int i, j; + + if (remove_names == NULL) { + return 0; + } + for (i = 0; remove_names[i] != NULL; i++) { + if (exclude_names != NULL) { + for (j = 0; exclude_names[j] != NULL; j++) { + if (strcmp(remove_names[i], exclude_names[j]) == 0) { + goto skip; + } + } + } + /* TODO: why we are checking lstat? we can just try rm/rmdir */ + if (lstat(remove_names[i], &path_stat) < 0) { + continue; + } + if (S_ISDIR(path_stat.st_mode)) { + remove_flag &= rmdir(remove_names[i]); /* 0 if no error */ + } else { + remove_flag &= unlink(remove_names[i]); /* 0 if no error */ + } + skip: + continue; + } + return (remove_flag == 0); +} + +static void run_package_script_or_die(const char *package_name, const char *script_type) +{ + char *script_path; + int result; + + script_path = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, script_type); + + /* If the file doesnt exist is isnt fatal */ + result = access(script_path, F_OK) ? EXIT_SUCCESS : system(script_path); + free(script_path); + if (result) + bb_error_msg_and_die("%s failed, exit code %d", script_type, result); +} + +/* +The policy manual defines what scripts get called when and with +what arguments. I realize that busybox does not support all of +these scenarios, but it does support some of them; it does not, +however, run them with any parameters in run_package_script_or_die(). +Here are the scripts: + +preinst install +preinst install +preinst upgrade +preinst abort_upgrade +postinst configure +postinst abort-upgade +postinst abort-remove +postinst abort-remove in-favour +postinst abort-deconfigure in-favor removing +prerm remove +prerm upgrade +prerm failed-upgrade +prerm remove in-favor +prerm deconfigure in-favour removing +postrm remove +postrm purge +postrm upgrade +postrm failed-upgrade +postrm abort-install +postrm abort-install +postrm abort-upgrade +postrm disappear +*/ +static const char *const all_control_files[] = { + "preinst", "postinst", "prerm", "postrm", + "list", "md5sums", "shlibs", "conffiles", + "config", "templates" +}; + +static char **all_control_list(const char *package_name) +{ + unsigned i = 0; + char **remove_files; + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = xzalloc(sizeof(all_control_files) + sizeof(char*)); + while (i < ARRAY_SIZE(all_control_files)) { + remove_files[i] = xasprintf("/var/lib/dpkg/info/%s.%s", + package_name, all_control_files[i]); + i++; + } + + return remove_files; +} + +static void free_array(char **array) +{ + if (array) { + unsigned i = 0; + while (array[i]) { + free(array[i]); + i++; + } + free(array); + } +} + +/* This function lists information on the installed packages. It loops through + * the status_hashtable to retrieve the info. This results in smaller code than + * scanning the status file. The resulting list, however, is unsorted. + */ +static void list_packages(const char *pattern) +{ + int i; + + puts(" Name Version"); + puts("+++-==============-=============="); + + /* go through status hash, dereference package hash and finally strings */ + for (i = 0; i < STATUS_HASH_PRIME+1; i++) { + if (status_hashtable[i]) { + const char *stat_str; /* status string */ + const char *name_str; /* package name */ + const char *vers_str; /* version */ + char s1, s2; /* status abbreviations */ + int spccnt; /* space count */ + int j; + + stat_str = name_hashtable[status_hashtable[i]->status]; + name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name]; + vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version]; + + if (pattern && fnmatch(pattern, name_str, 0) != 0) + continue; + + /* get abbreviation for status field 1 */ + s1 = stat_str[0] == 'i' ? 'i' : 'r'; + + /* get abbreviation for status field 2 */ + for (j = 0, spccnt = 0; stat_str[j] && spccnt < 2; j++) { + if (stat_str[j] == ' ') spccnt++; + } + s2 = stat_str[j]; + + /* print out the line formatted like Debian dpkg */ + printf("%c%c %-14s %s\n", s1, s2, name_str, vers_str); + } + } +} + +static void remove_package(const unsigned package_num, int noisy) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + const char *package_version = name_hashtable[package_hashtable[package_num]->version]; + const unsigned status_num = search_status_hashtable(package_name); + const int package_name_length = strlen(package_name); + char **remove_files; + char **exclude_files; + char list_name[package_name_length + 25]; + char conffile_name[package_name_length + 30]; + + if (noisy) + printf("Removing %s (%s)...\n", package_name, package_version); + + /* Run prerm script */ + run_package_script_or_die(package_name, "prerm"); + + /* Create a list of files to remove, and a separate list of those to keep */ + sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list"); + remove_files = create_list(list_name); + + sprintf(conffile_name, "/var/lib/dpkg/info/%s.%s", package_name, "conffiles"); + exclude_files = create_list(conffile_name); + + /* Some directories can't be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, exclude_files)) + continue; + free_array(exclude_files); + free_array(remove_files); + + /* Create a list of files in /var/lib/dpkg/info/.* to keep */ + exclude_files = xzalloc(sizeof(exclude_files[0]) * 3); + exclude_files[0] = xstrdup(conffile_name); + exclude_files[1] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm"); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = all_control_list(package_name); + + remove_file_array(remove_files, exclude_files); + free_array(remove_files); + free_array(exclude_files); + + /* rename .conffiles to .list + * The conffiles control file isn't required in Debian packages, so don't + * error out if it's missing. */ + rename(conffile_name, list_name); + + /* Change package status */ + set_status(status_num, "config-files", 3); +} + +static void purge_package(const unsigned package_num) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + const char *package_version = name_hashtable[package_hashtable[package_num]->version]; + const unsigned status_num = search_status_hashtable(package_name); + char **remove_files; + char **exclude_files; + char list_name[strlen(package_name) + 25]; + + printf("Purging %s (%s)...\n", package_name, package_version); + + /* Run prerm script */ + run_package_script_or_die(package_name, "prerm"); + + /* Create a list of files to remove */ + sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list"); + remove_files = create_list(list_name); + + /* Some directories cant be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, NULL)) + continue; + free_array(remove_files); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = all_control_list(package_name); + + /* Delete all of them except the postrm script */ + exclude_files = xzalloc(sizeof(exclude_files[0]) * 2); + exclude_files[0] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm"); + remove_file_array(remove_files, exclude_files); + free_array(exclude_files); + + /* Run and remove postrm script */ + run_package_script_or_die(package_name, "postrm"); + remove_file_array(remove_files, NULL); + + free_array(remove_files); + + /* Change package status */ + set_status(status_num, "not-installed", 3); +} + +static archive_handle_t *init_archive_deb_ar(const char *filename) +{ + archive_handle_t *ar_handle; + + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_handle = init_handle(); + ar_handle->filter = filter_accept_list_reassign; + ar_handle->src_fd = xopen(filename, O_RDONLY); + + return ar_handle; +} + +static void init_archive_deb_control(archive_handle_t *ar_handle) +{ + archive_handle_t *tar_handle; + + /* Setup the tar archive handle */ + tar_handle = init_handle(); + tar_handle->src_fd = ar_handle->src_fd; + + /* We don't care about data.tar.* or debian-binary, just control.tar.* */ +#if ENABLE_FEATURE_SEAMLESS_GZ + llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz"); +#endif +#if ENABLE_FEATURE_SEAMLESS_BZ2 + llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2"); +#endif + + /* Assign the tar handle as a subarchive of the ar handle */ + ar_handle->dpkg__sub_archive = tar_handle; +} + +static void init_archive_deb_data(archive_handle_t *ar_handle) +{ + archive_handle_t *tar_handle; + + /* Setup the tar archive handle */ + tar_handle = init_handle(); + tar_handle->src_fd = ar_handle->src_fd; + + /* We don't care about control.tar.* or debian-binary, just data.tar.* */ +#if ENABLE_FEATURE_SEAMLESS_GZ + llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz"); +#endif +#if ENABLE_FEATURE_SEAMLESS_BZ2 + llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2"); +#endif + + /* Assign the tar handle as a subarchive of the ar handle */ + ar_handle->dpkg__sub_archive = tar_handle; +} + +static void FAST_FUNC data_extract_to_buffer(archive_handle_t *archive_handle) +{ + unsigned size = archive_handle->file_header->size; + + archive_handle->dpkg__buffer = xzalloc(size + 1); + xread(archive_handle->src_fd, archive_handle->dpkg__buffer, size); +} + +static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept) +{ + ar_handle->dpkg__sub_archive->action_data = data_extract_to_buffer; + ar_handle->dpkg__sub_archive->accept = myaccept; + ar_handle->dpkg__sub_archive->filter = filter_accept_list; + + unpack_ar_archive(ar_handle); + close(ar_handle->src_fd); + + return ar_handle->dpkg__sub_archive->dpkg__buffer; +} + +static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll) +{ + FILE *fp; + char *filename, *line; + + filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, control_name); + fp = fopen_for_read(filename); + free(filename); + if (fp != NULL) { + while ((line = xmalloc_fgetline(fp)) != NULL) + llist_add_to(ll, line); + fclose(fp); + } +} + +static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle) +{ + int fd; + char *name_ptr = archive_handle->file_header->name + 1; + + /* Is this file marked as config file? */ + if (!find_list_entry(archive_handle->accept, name_ptr)) + return EXIT_SUCCESS; /* no */ + + fd = open(name_ptr, O_RDONLY); + if (fd >= 0) { + md5_ctx_t md5; + char *md5line, *buf; + int count; + + /* Calculate MD5 of existing file */ + buf = xmalloc(4096); + md5_begin(&md5); + while ((count = safe_read(fd, buf, 4096)) > 0) + md5_hash(&md5, buf, count); + md5_end(&md5, buf); /* using buf as result storage */ + close(fd); + + md5line = xmalloc(16 * 2 + 2 + strlen(name_ptr) + 1); + sprintf(bin2hex(md5line, buf, 16), " %s", name_ptr); + free(buf); + + /* Is it changed after install? */ + if (find_list_entry(archive_handle->accept, md5line) == NULL) { + printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr); + archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name); + } + free(md5line); + } + return EXIT_SUCCESS; +} + +static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle) +{ + char *name_ptr = archive_handle->file_header->name; + + /* Skip all leading "/" */ + while (*name_ptr == '/') + name_ptr++; + /* Skip all leading "./" and "../" */ + while (name_ptr[0] == '.') { + if (name_ptr[1] == '.') + name_ptr++; + if (name_ptr[1] != '/') + break; + name_ptr += 2; + } + + if (name_ptr[0] != '\0') { + archive_handle->file_header->name = xasprintf("%s%s", archive_handle->dpkg__buffer, name_ptr); + data_extract_all(archive_handle); + if (fnmatch("*.dpkg-new", archive_handle->file_header->name, 0) == 0) { + /* remove .dpkg-new suffix */ + archive_handle->file_header->name[strlen(archive_handle->file_header->name) - 9] = '\0'; + } + } +} + +enum { + /* Commands */ + OPT_configure = (1 << 0), + OPT_install = (1 << 1), + OPT_list_installed = (1 << 2), + OPT_purge = (1 << 3), + OPT_remove = (1 << 4), + OPT_unpack = (1 << 5), + OPTMASK_cmd = (1 << 6) - 1, + /* Options */ + OPT_force = (1 << 6), + OPT_force_ignore_depends = (1 << 7), + OPT_force_confnew = (1 << 8), + OPT_force_confold = (1 << 9), +}; + +static void unpack_package(deb_file_t *deb_file) +{ + const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; + const unsigned status_num = search_status_hashtable(package_name); + const unsigned status_package_num = status_hashtable[status_num]->package; + char *info_prefix; + char *list_filename; + archive_handle_t *archive_handle; + FILE *out_stream; + llist_t *accept_list; + llist_t *conffile_list; + int i; + + /* If existing version, remove it first */ + conffile_list = NULL; + if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) { + /* Package is already installed, remove old version first */ + printf("Preparing to replace %s %s (using %s)...\n", package_name, + name_hashtable[package_hashtable[status_package_num]->version], + deb_file->filename); + + /* Read md5sums from old package */ + if (!(option_mask32 & OPT_force_confold)) + append_control_file_to_llist(package_name, "md5sums", &conffile_list); + + remove_package(status_package_num, 0); + } else { + printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename); + } + + /* Extract control.tar.gz to /var/lib/dpkg/info/.filename */ + info_prefix = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, ""); + archive_handle = init_archive_deb_ar(deb_file->filename); + init_archive_deb_control(archive_handle); + + accept_list = NULL; + i = 0; + while (i < ARRAY_SIZE(all_control_files)) { + char *c = xasprintf("./%s", all_control_files[i]); + llist_add_to(&accept_list, c); + i++; + } + archive_handle->dpkg__sub_archive->accept = accept_list; + archive_handle->dpkg__sub_archive->filter = filter_accept_list; + archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix; + archive_handle->dpkg__sub_archive->dpkg__buffer = info_prefix; + archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD; + unpack_ar_archive(archive_handle); + + /* Run the preinst prior to extracting */ + run_package_script_or_die(package_name, "preinst"); + + /* Don't overwrite existing config files */ + if (!(option_mask32 & OPT_force_confnew)) + append_control_file_to_llist(package_name, "conffiles", &conffile_list); + + /* Extract data.tar.gz to the root directory */ + archive_handle = init_archive_deb_ar(deb_file->filename); + init_archive_deb_data(archive_handle); + archive_handle->dpkg__sub_archive->accept = conffile_list; + archive_handle->dpkg__sub_archive->filter = filter_rename_config; + archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix; + archive_handle->dpkg__sub_archive->dpkg__buffer = (char*)"/"; /* huh? */ + archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD; + unpack_ar_archive(archive_handle); + + /* Create the list file */ + list_filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "list"); + out_stream = xfopen_for_write(list_filename); + while (archive_handle->dpkg__sub_archive->passed) { + /* the leading . has been stripped by data_extract_all_prefix already */ + fputs(archive_handle->dpkg__sub_archive->passed->data, out_stream); + fputc('\n', out_stream); + archive_handle->dpkg__sub_archive->passed = archive_handle->dpkg__sub_archive->passed->link; + } + fclose(out_stream); + + /* change status */ + set_status(status_num, "install", 1); + set_status(status_num, "unpacked", 3); + + free(info_prefix); + free(list_filename); +} + +static void configure_package(deb_file_t *deb_file) +{ + const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; + const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version]; + const int status_num = search_status_hashtable(package_name); + + printf("Setting up %s (%s)...\n", package_name, package_version); + + /* Run the postinst script */ + /* TODO: handle failure gracefully */ + run_package_script_or_die(package_name, "postinst"); + + /* Change status to reflect success */ + set_status(status_num, "install", 1); + set_status(status_num, "installed", 3); +} + +int dpkg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dpkg_main(int argc UNUSED_PARAM, char **argv) +{ + deb_file_t **deb_file = NULL; + status_node_t *status_node; + char *str_f; + int opt; + int package_num; + int deb_count = 0; + int state_status; + int status_num; + int i; +#if ENABLE_LONG_OPTS + static const char dpkg_longopts[] ALIGN1 = +// FIXME: we use -C non-compatibly, should be: +// "-C|--audit Check for broken package(s)" + "configure\0" No_argument "C" + "force\0" Required_argument "F" + "install\0" No_argument "i" + "list\0" No_argument "l" + "purge\0" No_argument "P" + "remove\0" No_argument "r" + "unpack\0" No_argument "u" + "force-depends\0" No_argument "\xff" + "force-confnew\0" No_argument "\xfe" + "force-confold\0" No_argument "\xfd" + ; +#endif + + INIT_G(); + + IF_LONG_OPTS(applet_long_options = dpkg_longopts); + opt = getopt32(argv, "CilPruF:", &str_f); + argv += optind; + //if (opt & OPT_configure) ... // -C + if (opt & OPT_force) { // -F (--force in official dpkg) + if (strcmp(str_f, "depends") == 0) + opt |= OPT_force_ignore_depends; + else if (strcmp(str_f, "confnew") == 0) + opt |= OPT_force_confnew; + else if (strcmp(str_f, "confold") == 0) + opt |= OPT_force_confold; + else + bb_show_usage(); + option_mask32 = opt; + } + //if (opt & OPT_install) ... // -i + //if (opt & OPT_list_installed) ... // -l + //if (opt & OPT_purge) ... // -P + //if (opt & OPT_remove) ... // -r + //if (opt & OPT_unpack) ... // -u (--unpack in official dpkg) + if (!(opt & OPTMASK_cmd) /* no cmd */ + || ((opt & OPTMASK_cmd) & ((opt & OPTMASK_cmd)-1)) /* more than one cmd */ + ) { + bb_show_usage(); + } + +/* puts("(Reading database ... xxxxx files and directories installed.)"); */ + index_status_file("/var/lib/dpkg/status"); + + /* if the list action was given print the installed packages and exit */ + if (opt & OPT_list_installed) { + list_packages(argv[0]); /* param can be NULL */ + return EXIT_SUCCESS; + } + + /* Read arguments and store relevant info in structs */ + while (*argv) { + /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */ + deb_file = xrealloc_vector(deb_file, 2, deb_count); + deb_file[deb_count] = xzalloc(sizeof(deb_file[0][0])); + if (opt & (OPT_install | OPT_unpack)) { + /* -i/-u: require filename */ + archive_handle_t *archive_handle; + llist_t *control_list = NULL; + + /* Extract the control file */ + llist_add_to(&control_list, (char*)"./control"); + archive_handle = init_archive_deb_ar(argv[0]); + init_archive_deb_control(archive_handle); + deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list); + if (deb_file[deb_count]->control_file == NULL) { + bb_error_msg_and_die("can't extract control file"); + } + deb_file[deb_count]->filename = xstrdup(argv[0]); + package_num = fill_package_struct(deb_file[deb_count]->control_file); + + if (package_num == -1) { + bb_error_msg("invalid control file in %s", argv[0]); + argv++; + continue; + } + deb_file[deb_count]->package = (unsigned) package_num; + + /* Add the package to the status hashtable */ + if (opt & (OPT_unpack | OPT_install)) { + /* Try and find a currently installed version of this package */ + status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + /* If no previous entry was found initialise a new entry */ + if (status_hashtable[status_num] == NULL + || status_hashtable[status_num]->status == 0 + ) { + status_node = xmalloc(sizeof(status_node_t)); + status_node->package = deb_file[deb_count]->package; + /* reinstreq isnt changed to "ok" until the package control info + * is written to the status file*/ + status_node->status = search_name_hashtable("install reinstreq not-installed"); + status_hashtable[status_num] = status_node; + } else { + set_status(status_num, "install", 1); + set_status(status_num, "reinstreq", 2); + } + } + } else if (opt & (OPT_configure | OPT_purge | OPT_remove)) { + /* -C/-p/-r: require package name */ + deb_file[deb_count]->package = search_package_hashtable( + search_name_hashtable(argv[0]), + search_name_hashtable("ANY"), VER_ANY); + if (package_hashtable[deb_file[deb_count]->package] == NULL) { + bb_error_msg_and_die("package %s is uninstalled or unknown", argv[0]); + } + package_num = deb_file[deb_count]->package; + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + state_status = get_status(status_num, 3); + + /* check package status is "installed" */ + if (opt & OPT_remove) { + if (strcmp(name_hashtable[state_status], "not-installed") == 0 + || strcmp(name_hashtable[state_status], "config-files") == 0 + ) { + bb_error_msg_and_die("%s is already removed", name_hashtable[package_hashtable[package_num]->name]); + } + set_status(status_num, "deinstall", 1); + } else if (opt & OPT_purge) { + /* if package status is "conf-files" then its ok */ + if (strcmp(name_hashtable[state_status], "not-installed") == 0) { + bb_error_msg_and_die("%s is already purged", name_hashtable[package_hashtable[package_num]->name]); + } + set_status(status_num, "purge", 1); + } + } + deb_count++; + argv++; + } + if (!deb_count) + bb_error_msg_and_die("no package files specified"); + deb_file[deb_count] = NULL; + + /* Check that the deb file arguments are installable */ + if (!(opt & OPT_force_ignore_depends)) { + if (!check_deps(deb_file, 0 /*, deb_count*/)) { + bb_error_msg_and_die("dependency check failed"); + } + } + + /* TODO: install or remove packages in the correct dependency order */ + for (i = 0; i < deb_count; i++) { + /* Remove or purge packages */ + if (opt & OPT_remove) { + remove_package(deb_file[i]->package, 1); + } + else if (opt & OPT_purge) { + purge_package(deb_file[i]->package); + } + else if (opt & OPT_unpack) { + unpack_package(deb_file[i]); + } + else if (opt & OPT_install) { + unpack_package(deb_file[i]); + /* package is configured in second pass below */ + } + else if (opt & OPT_configure) { + configure_package(deb_file[i]); + } + } + /* configure installed packages */ + if (opt & OPT_install) { + for (i = 0; i < deb_count; i++) + configure_package(deb_file[i]); + } + + write_status_file(deb_file); + + if (ENABLE_FEATURE_CLEAN_UP) { + for (i = 0; i < deb_count; i++) { + free(deb_file[i]->control_file); + free(deb_file[i]->filename); + free(deb_file[i]); + } + + free(deb_file); + + for (i = 0; i < NAME_HASH_PRIME; i++) { + free(name_hashtable[i]); + } + + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + free_package(package_hashtable[i]); + } + + for (i = 0; i < STATUS_HASH_PRIME; i++) { + free(status_hashtable[i]); + } + + free(status_hashtable); + free(package_hashtable); + free(name_hashtable); + } + + return EXIT_SUCCESS; +} diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c new file mode 100644 index 000000000..5d814d7ef --- /dev/null +++ b/archival/dpkg_deb.c @@ -0,0 +1,121 @@ +/* vi: set sw=4 ts=4: */ +/* + * dpkg-deb packs, unpacks and provides information about Debian archives. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define dpkg_deb_trivial_usage +//usage: "[-cefxX] FILE [argument" +//usage:#define dpkg_deb_full_usage "\n\n" +//usage: "Perform actions on Debian packages (.debs)\n" +//usage: "\n -c List contents of filesystem tree" +//usage: "\n -e Extract control files to [argument] directory" +//usage: "\n -f Display control field name starting with [argument]" +//usage: "\n -x Extract packages filesystem tree to directory" +//usage: "\n -X Verbose extract" +//usage: +//usage:#define dpkg_deb_example_usage +//usage: "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n" + +#include "libbb.h" +#include "archive.h" + +#define DPKG_DEB_OPT_CONTENTS 1 +#define DPKG_DEB_OPT_CONTROL 2 +#define DPKG_DEB_OPT_FIELD 4 +#define DPKG_DEB_OPT_EXTRACT 8 +#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16 + +int dpkg_deb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dpkg_deb_main(int argc, char **argv) +{ + archive_handle_t *ar_archive; + archive_handle_t *tar_archive; + llist_t *control_tar_llist = NULL; + unsigned opt; + const char *extract_dir; + int need_args; + + /* Setup the tar archive handle */ + tar_archive = init_handle(); + + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_archive = init_handle(); + ar_archive->dpkg__sub_archive = tar_archive; + ar_archive->filter = filter_accept_list_reassign; + +#if ENABLE_FEATURE_SEAMLESS_GZ + llist_add_to(&ar_archive->accept, (char*)"data.tar.gz"); + llist_add_to(&control_tar_llist, (char*)"control.tar.gz"); +#endif +#if ENABLE_FEATURE_SEAMLESS_BZ2 + llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2"); + llist_add_to(&control_tar_llist, (char*)"control.tar.bz2"); +#endif +#if ENABLE_FEATURE_SEAMLESS_LZMA + llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma"); + llist_add_to(&control_tar_llist, (char*)"control.tar.lzma"); +#endif + + opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX"; + opt = getopt32(argv, "cefXx"); + argv += optind; + argc -= optind; + + if (opt & DPKG_DEB_OPT_CONTENTS) { + tar_archive->action_header = header_verbose_list; + } + extract_dir = NULL; + need_args = 1; + if (opt & DPKG_DEB_OPT_CONTROL) { + ar_archive->accept = control_tar_llist; + tar_archive->action_data = data_extract_all; + if (1 == argc) { + extract_dir = "./DEBIAN"; + } else { + need_args++; + } + } + if (opt & DPKG_DEB_OPT_FIELD) { + /* Print the entire control file + * it should accept a second argument which specifies a + * specific field to print */ + ar_archive->accept = control_tar_llist; + llist_add_to(&(tar_archive->accept), (char*)"./control"); + tar_archive->filter = filter_accept_list; + tar_archive->action_data = data_extract_to_stdout; + } + if (opt & DPKG_DEB_OPT_EXTRACT) { + tar_archive->action_header = header_list; + } + if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) { + tar_archive->action_data = data_extract_all; + need_args = 2; + } + + if (need_args != argc) { + bb_show_usage(); + } + + tar_archive->src_fd = ar_archive->src_fd = xopen(argv[0], O_RDONLY); + + /* Work out where to extract the files */ + /* 2nd argument is a dir name */ + if (argv[1]) { + extract_dir = argv[1]; + } + if (extract_dir) { + mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */ + xchdir(extract_dir); + } + + /* Do it */ + unpack_ar_archive(ar_archive); + + /* Cleanup */ + if (ENABLE_FEATURE_CLEAN_UP) + close(ar_archive->src_fd); + + return EXIT_SUCCESS; +} diff --git a/archival/gzip.c b/archival/gzip.c new file mode 100644 index 000000000..3af930b7e --- /dev/null +++ b/archival/gzip.c @@ -0,0 +1,2128 @@ +/* vi: set sw=4 ts=4: */ +/* + * Gzip implementation for busybox + * + * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Charles P. Wright + * "this is a stripped down version of gzip I put into busybox, it does + * only standard in to standard out with -9 compression. It also requires + * the zcat module for some important functions." + * + * Adjusted further by Erik Andersen to support + * files as well as stdin/stdout, and to generally behave itself wrt + * command line handling. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* big objects in bss: + * 00000020 b bl_count + * 00000074 b base_length + * 00000078 b base_dist + * 00000078 b static_dtree + * 0000009c b bl_tree + * 000000f4 b dyn_dtree + * 00000100 b length_code + * 00000200 b dist_code + * 0000023d b depth + * 00000400 b flag_buf + * 0000047a b heap + * 00000480 b static_ltree + * 000008f4 b dyn_ltree + */ + +/* TODO: full support for -v for DESKTOP + * "/usr/bin/gzip -v a bogus aa" should say: +a: 85.1% -- replaced with a.gz +gzip: bogus: No such file or directory +aa: 85.1% -- replaced with aa.gz +*/ + +//usage:#define gzip_trivial_usage +//usage: "[-cfd] [FILE]..." +//usage:#define gzip_full_usage "\n\n" +//usage: "Compress FILEs (or stdin)\n" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: +//usage:#define gzip_example_usage +//usage: "$ ls -la /tmp/busybox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/busybox.tar\n" +//usage: "$ gzip /tmp/busybox.tar\n" +//usage: "$ ls -la /tmp/busybox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 554058 Apr 14 17:49 /tmp/busybox.tar.gz\n" + +#include "libbb.h" +#include "archive.h" + + +/* =========================================================================== + */ +//#define DEBUG 1 +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) { if (!(cond)) bb_error_msg(msg); } +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x; } +# define Tracevv(x) {if (verbose > 1) fprintf x; } +# define Tracec(c,x) {if (verbose && (c)) fprintf x; } +# define Tracecv(c,x) {if (verbose > 1 && (c)) fprintf x; } +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +/* =========================================================================== + */ +#if CONFIG_GZIP_FAST == 0 +# define SMALL_MEM +#elif CONFIG_GZIP_FAST == 1 +# define MEDIUM_MEM +#elif CONFIG_GZIP_FAST == 2 +# define BIG_MEM +#else +# error "Invalid CONFIG_GZIP_FAST value" +#endif + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN 0xffff +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +#ifdef MAX_EXT_CHARS +# define MAX_SUFFIX MAX_EXT_CHARS +#else +# define MAX_SUFFIX 30 +#endif + + +/* =========================================================================== + * Compile with MEDIUM_MEM to reduce the memory requirements or + * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the + * entire input file can be held in memory (not possible on 16 bit systems). + * Warning: defining these symbols affects HASH_BITS (see below) and thus + * affects the compression ratio. The compressed output + * is still correct, and might even be smaller in some cases. + */ + +#ifdef SMALL_MEM +# define HASH_BITS 13 /* Number of bits used to hash strings */ +#endif +#ifdef MEDIUM_MEM +# define HASH_BITS 14 +#endif +#ifndef HASH_BITS +# define HASH_BITS 15 + /* For portability to 16 bit machines, do not use values above 15. */ +#endif + +#define HASH_SIZE (unsigned)(1<= 4. + */ + + max_insert_length = max_lazy_match, +/* Insert new strings in the hash table only if the match length + * is not greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + good_match = 32, +/* Use a faster search when the previous match is longer than this */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + + nice_match = 258, /* Stop searching when current match exceeds this */ +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ +}; + + +struct globals { + + lng block_start; + +/* window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + unsigned ins_h; /* hash index of string to be inserted */ + +#define H_SHIFT ((HASH_BITS+MIN_MATCH-1) / MIN_MATCH) +/* Number of bits by which ins_h and del_h must be shifted at each + * input step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * H_SHIFT * MIN_MATCH >= HASH_BITS + */ + + unsigned prev_length; + +/* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + unsigned strstart; /* start of string to insert */ + unsigned match_start; /* start of matching string */ + unsigned lookahead; /* number of valid bytes ahead in window */ + +/* =========================================================================== + */ +#define DECLARE(type, array, size) \ + type * array +#define ALLOC(type, array, size) \ + array = xzalloc((size_t)(((size)+1L)/2) * 2*sizeof(type)) +#define FREE(array) \ + do { free(array); array = NULL; } while (0) + + /* global buffers */ + + /* buffer for literals or lengths */ + /* DECLARE(uch, l_buf, LIT_BUFSIZE); */ + DECLARE(uch, l_buf, INBUFSIZ); + + DECLARE(ush, d_buf, DIST_BUFSIZE); + DECLARE(uch, outbuf, OUTBUFSIZ); + +/* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least WSIZE + * bytes. With this organization, matches are limited to a distance of + * WSIZE-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would + * be less efficient). + */ + DECLARE(uch, window, 2L * WSIZE); + +/* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + /* DECLARE(Pos, prev, WSIZE); */ + DECLARE(ush, prev, 1L << BITS); + +/* Heads of the hash chains or 0. */ + /* DECLARE(Pos, head, 1<> 8; + } else { + put_8bit(w); + put_8bit(w >> 8); + } +} + +static void put_32bit(ulg n) +{ + put_16bit(n); + put_16bit(n >> 16); +} + +/* =========================================================================== + * Run a set of bytes through the crc shift register. If s is a NULL + * pointer, then initialize the crc shift register contents instead. + * Return the current crc in either case. + */ +static void updcrc(uch * s, unsigned n) +{ + G1.crc = crc32_block_endian0(G1.crc, s, n, global_crc32_table /*G1.crc_32_tab*/); +} + + +/* =========================================================================== + * Read a new buffer from the current input file, perform end-of-line + * translation, and update the crc and input file size. + * IN assertion: size >= 2 (for end-of-line translation) + */ +static unsigned file_read(void *buf, unsigned size) +{ + unsigned len; + + Assert(G1.insize == 0, "l_buf not empty"); + + len = safe_read(ifd, buf, size); + if (len == (unsigned)(-1) || len == 0) + return len; + + updcrc(buf, len); + G1.isize += len; + return len; +} + + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +static void send_bits(int value, int length) +{ +#ifdef DEBUG + Tracev((stderr, " l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + G1.bits_sent += length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (G1.bi_valid > (int) BUF_SIZE - length) { + G1.bi_buf |= (value << G1.bi_valid); + put_16bit(G1.bi_buf); + G1.bi_buf = (ush) value >> (BUF_SIZE - G1.bi_valid); + G1.bi_valid += length - BUF_SIZE; + } else { + G1.bi_buf |= value << G1.bi_valid; + G1.bi_valid += length; + } +} + + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +static unsigned bi_reverse(unsigned code, int len) +{ + unsigned res = 0; + + while (1) { + res |= code & 1; + if (--len <= 0) return res; + code >>= 1; + res <<= 1; + } +} + + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +static void bi_windup(void) +{ + if (G1.bi_valid > 8) { + put_16bit(G1.bi_buf); + } else if (G1.bi_valid > 0) { + put_8bit(G1.bi_buf); + } + G1.bi_buf = 0; + G1.bi_valid = 0; +#ifdef DEBUG + G1.bits_sent = (G1.bits_sent + 7) & ~7; +#endif +} + + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +static void copy_block(char *buf, unsigned len, int header) +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + put_16bit(len); + put_16bit(~len); +#ifdef DEBUG + G1.bits_sent += 2 * 16; +#endif + } +#ifdef DEBUG + G1.bits_sent += (ulg) len << 3; +#endif + while (len--) { + put_8bit(*buf++); + } +} + + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +static void fill_window(void) +{ + unsigned n, m; + unsigned more = WINDOW_SIZE - G1.lookahead - G1.strstart; + /* Amount of free space at the end of the window. */ + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned) -1) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (G1.strstart >= WSIZE + MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + Assert(WINDOW_SIZE == 2 * WSIZE, "no sliding with BIG_MEM"); + + memcpy(G1.window, G1.window + WSIZE, WSIZE); + G1.match_start -= WSIZE; + G1.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + G1.block_start -= WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = head[n]; + head[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0); + } + for (n = 0; n < WSIZE; n++) { + m = G1.prev[n]; + G1.prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + /* At this point, more >= 2 */ + if (!G1.eofile) { + n = file_read(G1.window + G1.strstart + G1.lookahead, more); + if (n == 0 || n == (unsigned) -1) { + G1.eofile = 1; + } else { + G1.lookahead += n; + } + } +} + + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ + +/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or + * match.s. The code is functionally equivalent, so you can use the C version + * if desired. + */ +static int longest_match(IPos cur_match) +{ + unsigned chain_length = max_chain_length; /* max hash chain length */ + uch *scan = G1.window + G1.strstart; /* current string */ + uch *match; /* matched string */ + int len; /* length of current match */ + int best_len = G1.prev_length; /* best match length so far */ + IPos limit = G1.strstart > (IPos) MAX_DIST ? G1.strstart - (IPos) MAX_DIST : 0; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + +/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ +#if HASH_BITS < 8 || MAX_MATCH != 258 +# error Code too clever +#endif + uch *strend = G1.window + G1.strstart + MAX_MATCH; + uch scan_end1 = scan[best_len - 1]; + uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (G1.prev_length >= good_match) { + chain_length >>= 2; + } + Assert(G1.strstart <= WINDOW_SIZE - MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(cur_match < G1.strstart, "no future"); + match = G1.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end + || match[best_len - 1] != scan_end1 + || *match != *scan || *++match != scan[1] + ) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && scan < strend); + + len = MAX_MATCH - (int) (strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) { + G1.match_start = cur_match; + best_len = len; + if (len >= nice_match) + break; + scan_end1 = scan[best_len - 1]; + scan_end = scan[best_len]; + } + } while ((cur_match = G1.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +static void check_match(IPos start, IPos match, int length) +{ + /* check that the match is indeed a match */ + if (memcmp(G1.window + match, G1.window + start, length) != 0) { + bb_error_msg(" start %d, match %d, length %d", start, match, length); + bb_error_msg("invalid match"); + } + if (verbose > 1) { + bb_error_msg("\\[%d,%d]", start - match, length); + do { + bb_putchar_stderr(G1.window[start++]); + } while (--length != 0); + } +} +#else +# define check_match(start, match, length) ((void)0) +#endif + + +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* PURPOSE + * Encode various sets of source values using variable-length + * binary code trees. + * + * DISCUSSION + * The PKZIP "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in the ZIP file in a compressed form + * which is itself a Huffman encoding of the lengths of + * all the code strings (in ascending order by source values). + * The actual code strings are reconstructed from the lengths in + * the UNZIP process, as described in the "application note" + * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program. + * + * REFERENCES + * Lynch, Thomas J. + * Data Compression: Techniques and Applications, pp. 53-55. + * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7. + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + * + * INTERFACE + * void ct_init() + * Allocate the match buffer, initialize the various tables [and save + * the location of the internal file attribute (ascii/binary) and + * method (DEFLATE/STORE) -- deleted in bbox] + * + * void ct_tally(int dist, int lc); + * Save the match info and tally the frequency counts. + * + * ulg flush_block(char *buf, ulg stored_len, int eof) + * Determine the best encoding for the current block: dynamic trees, + * static trees or store, and output the encoded block to the zip + * file. Returns the total compressed length for the file so far. + */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +/* extra bits for each length code */ +static const uint8_t extra_lbits[LENGTH_CODES] ALIGN1 = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 0 +}; + +/* extra bits for each distance code */ +static const uint8_t extra_dbits[D_CODES] ALIGN1 = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 13 +}; + +/* extra bits for each bit length code */ +static const uint8_t extra_blbits[BL_CODES] ALIGN1 = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 }; + +/* number of codes at each bit length for an optimal tree */ +static const uint8_t bl_order[BL_CODES] ALIGN1 = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#ifndef LIT_BUFSIZE +# ifdef SMALL_MEM +# define LIT_BUFSIZE 0x2000 +# else +# ifdef MEDIUM_MEM +# define LIT_BUFSIZE 0x4000 +# else +# define LIT_BUFSIZE 0x8000 +# endif +# endif +#endif +#ifndef DIST_BUFSIZE +# define DIST_BUFSIZE LIT_BUFSIZE +#endif +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* =========================================================================== +*/ +/* Data structure describing a single value and its code string. */ +typedef struct ct_data { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +#define HEAP_SIZE (2*L_CODES + 1) +/* maximum heap size */ + +typedef struct tree_desc { + ct_data *dyn_tree; /* the dynamic tree */ + ct_data *static_tree; /* corresponding static tree or NULL */ + const uint8_t *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +struct globals2 { + + ush heap[HEAP_SIZE]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + ct_data dyn_dtree[2 * D_CODES + 1]; /* distance tree */ + + ct_data static_ltree[L_CODES + 2]; + +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ + + ct_data static_dtree[D_CODES]; + +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + + ct_data bl_tree[2 * BL_CODES + 1]; + +/* Huffman tree for the bit lengths */ + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS + 1]; + +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + + uch depth[2 * L_CODES + 1]; + +/* Depth of each subtree used as tie breaker for trees of equal frequency */ + + uch length_code[MAX_MATCH - MIN_MATCH + 1]; + +/* length code for each normalized match length (0 == MIN_MATCH) */ + + uch dist_code[512]; + +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + + int base_length[LENGTH_CODES]; + +/* First normalized length for each code (0 = MIN_MATCH) */ + + int base_dist[D_CODES]; + +/* First normalized distance for each code (0 = distance of 1) */ + + uch flag_buf[LIT_BUFSIZE / 8]; + +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ + + unsigned last_lit; /* running index in l_buf */ + unsigned last_dist; /* running index in d_buf */ + unsigned last_flags; /* running index in flag_buf */ + uch flags; /* current flags not yet saved in flag_buf */ + uch flag_bit; /* current bit used in flags */ + +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + + ulg compressed_len; /* total bit length of compressed file */ +}; + +#define G2ptr ((struct globals2*)(ptr_to_globals)) +#define G2 (*G2ptr) + + +/* =========================================================================== + */ +static void gen_codes(ct_data * tree, int max_code); +static void build_tree(tree_desc * desc); +static void scan_tree(ct_data * tree, int max_code); +static void send_tree(ct_data * tree, int max_code); +static int build_bl_tree(void); +static void send_all_trees(int lcodes, int dcodes, int blcodes); +static void compress_block(ct_data * ltree, ct_data * dtree); + + +#ifndef DEBUG +/* Send a code of the given tree. c and tree must not have side effects */ +# define SEND_CODE(c, tree) send_bits(tree[c].Code, tree[c].Len) +#else +# define SEND_CODE(c, tree) \ +{ \ + if (verbose > 1) bb_error_msg("\ncd %3d ", (c)); \ + send_bits(tree[c].Code, tree[c].Len); \ +} +#endif + +#define D_CODE(dist) \ + ((dist) < 256 ? G2.dist_code[dist] : G2.dist_code[256 + ((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + * The arguments must not have side effects. + */ + + +/* =========================================================================== + * Initialize a new block. + */ +static void init_block(void) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) + G2.dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) + G2.dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) + G2.bl_tree[n].Freq = 0; + + G2.dyn_ltree[END_BLOCK].Freq = 1; + G2.opt_len = G2.static_len = 0; + G2.last_lit = G2.last_dist = G2.last_flags = 0; + G2.flags = 0; + G2.flag_bit = 1; +} + + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ + +/* Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. */ +#define SMALLER(tree, n, m) \ + (tree[n].Freq < tree[m].Freq \ + || (tree[n].Freq == tree[m].Freq && G2.depth[n] <= G2.depth[m])) + +static void pqdownheap(ct_data * tree, int k) +{ + int v = G2.heap[k]; + int j = k << 1; /* left son of k */ + + while (j <= G2.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < G2.heap_len && SMALLER(tree, G2.heap[j + 1], G2.heap[j])) + j++; + + /* Exit if v is smaller than both sons */ + if (SMALLER(tree, v, G2.heap[j])) + break; + + /* Exchange v with the smallest son */ + G2.heap[k] = G2.heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + G2.heap[k] = v; +} + + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +static void gen_bitlen(tree_desc * desc) +{ + ct_data *tree = desc->dyn_tree; + const uint8_t *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) + G2.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[G2.heap[G2.heap_max]].Len = 0; /* root of the heap */ + + for (h = G2.heap_max + 1; h < HEAP_SIZE; h++) { + n = G2.heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n].Len = (ush) bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) + continue; /* not a leaf node */ + + G2.bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n].Freq; + G2.opt_len += (ulg) f *(bits + xbits); + + if (stree) + G2.static_len += (ulg) f * (stree[n].Len + xbits); + } + if (overflow == 0) + return; + + Trace((stderr, "\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (G2.bl_count[bits] == 0) + bits--; + G2.bl_count[bits]--; /* move one leaf down the tree */ + G2.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ + G2.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = G2.bl_count[bits]; + while (n != 0) { + m = G2.heap[--h]; + if (m > max_code) + continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits)); + G2.opt_len += ((int32_t) bits - tree[m].Len) * tree[m].Freq; + tree[m].Len = bits; + } + n--; + } + } +} + + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +static void gen_codes(ct_data * tree, int max_code) +{ + ush next_code[MAX_BITS + 1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + G2.bl_count[bits - 1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(code + G2.bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, + "inconsistent bit counts"); + Tracev((stderr, "\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + + if (len == 0) + continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracec(tree != G2.static_ltree, + (stderr, "\nn %3d %c l %2d c %4x (%x) ", n, + (n > ' ' ? n : ' '), len, tree[n].Code, + next_code[len] - 1)); + } +} + + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ + +/* Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. */ + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + +#define PQREMOVE(tree, top) \ +do { \ + top = G2.heap[SMALLEST]; \ + G2.heap[SMALLEST] = G2.heap[G2.heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} while (0) + +static void build_tree(tree_desc * desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + G2.heap_len = 0; + G2.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + G2.heap[++G2.heap_len] = max_code = n; + G2.depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (G2.heap_len < 2) { + int new = G2.heap[++G2.heap_len] = (max_code < 2 ? ++max_code : 0); + + tree[new].Freq = 1; + G2.depth[new] = 0; + G2.opt_len--; + if (stree) + G2.static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = G2.heap_len / 2; n >= 1; n--) + pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + PQREMOVE(tree, n); /* n = node of least frequency */ + m = G2.heap[SMALLEST]; /* m = node of next least frequency */ + + G2.heap[--G2.heap_max] = n; /* keep the nodes sorted by frequency */ + G2.heap[--G2.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + G2.depth[node] = MAX(G2.depth[n], G2.depth[m]) + 1; + tree[n].Dad = tree[m].Dad = (ush) node; +#ifdef DUMP_BL_TREE + if (tree == G2.bl_tree) { + bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + G2.heap[SMALLEST] = node++; + pqdownheap(tree, SMALLEST); + + } while (G2.heap_len >= 2); + + G2.heap[--G2.heap_max] = G2.heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc *) desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes((ct_data *) tree, max_code); +} + + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +static void scan_tree(ct_data * tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[max_code + 1].Len = 0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].Len; + if (++count < max_count && curlen == nextlen) + continue; + + if (count < min_count) { + G2.bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) + G2.bl_tree[curlen].Freq++; + G2.bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + G2.bl_tree[REPZ_3_10].Freq++; + } else { + G2.bl_tree[REPZ_11_138].Freq++; + } + count = 0; + prevlen = curlen; + + max_count = 7; + min_count = 4; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } + } +} + + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +static void send_tree(ct_data * tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + +/* tree[max_code+1].Len = -1; *//* guard already set */ + if (nextlen == 0) + max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + SEND_CODE(curlen, G2.bl_tree); + } while (--count); + } else if (curlen != 0) { + if (curlen != prevlen) { + SEND_CODE(curlen, G2.bl_tree); + count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + SEND_CODE(REP_3_6, G2.bl_tree); + send_bits(count - 3, 2); + } else if (count <= 10) { + SEND_CODE(REPZ_3_10, G2.bl_tree); + send_bits(count - 3, 3); + } else { + SEND_CODE(REPZ_11_138, G2.bl_tree); + send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } +} + + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +static int build_bl_tree(void) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(G2.dyn_ltree, G2.l_desc.max_code); + scan_tree(G2.dyn_dtree, G2.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(&G2.bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (G2.bl_tree[bl_order[max_blindex]].Len != 0) + break; + } + /* Update opt_len to include the bit length tree and counts */ + G2.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", G2.opt_len, G2.static_len)); + + return max_blindex; +} + + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +static void send_all_trees(int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(lcodes <= L_CODES && dcodes <= D_CODES + && blcodes <= BL_CODES, "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(G2.bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", G1.bits_sent)); + + send_tree((ct_data *) G2.dyn_ltree, lcodes - 1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", G1.bits_sent)); + + send_tree((ct_data *) G2.dyn_dtree, dcodes - 1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", G1.bits_sent)); +} + + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +static int ct_tally(int dist, int lc) +{ + G1.l_buf[G2.last_lit++] = lc; + if (dist == 0) { + /* lc is the unmatched char */ + G2.dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush) dist < (ush) MAX_DIST + && (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH) + && (ush) D_CODE(dist) < (ush) D_CODES, "ct_tally: bad match" + ); + + G2.dyn_ltree[G2.length_code[lc] + LITERALS + 1].Freq++; + G2.dyn_dtree[D_CODE(dist)].Freq++; + + G1.d_buf[G2.last_dist++] = dist; + G2.flags |= G2.flag_bit; + } + G2.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((G2.last_lit & 7) == 0) { + G2.flag_buf[G2.last_flags++] = G2.flags; + G2.flags = 0; + G2.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if ((G2.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = G2.last_lit * 8L; + ulg in_length = (ulg) G1.strstart - G1.block_start; + int dcode; + + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += G2.dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr, + "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + G2.last_lit, G2.last_dist, in_length, out_length, + 100L - out_length * 100L / in_length)); + if (G2.last_dist < G2.last_lit / 2 && out_length < in_length / 2) + return 1; + } + return (G2.last_lit == LIT_BUFSIZE - 1 || G2.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +static void compress_block(ct_data * ltree, ct_data * dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (G2.last_lit != 0) do { + if ((lx & 7) == 0) + flag = G2.flag_buf[fx++]; + lc = G1.l_buf[lx++]; + if ((flag & 1) == 0) { + SEND_CODE(lc, ltree); /* send a literal byte */ + Tracecv(lc > ' ', (stderr, " '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = G2.length_code[lc]; + SEND_CODE(code + LITERALS + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= G2.base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist = G1.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = D_CODE(dist); + Assert(code < D_CODES, "bad d_code"); + + SEND_CODE(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= G2.base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < G2.last_lit); + + SEND_CODE(END_BLOCK, ltree); +} + + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +static ulg flush_block(char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + G2.flag_buf[G2.last_flags] = G2.flags; /* Save the flags for the last 8 items */ + + /* Construct the literal and distance trees */ + build_tree(&G2.l_desc); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", G2.opt_len, G2.static_len)); + + build_tree(&G2.d_desc); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", G2.opt_len, G2.static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (G2.opt_len + 3 + 7) >> 3; + static_lenb = (G2.static_len + 3 + 7) >> 3; + + Trace((stderr, + "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, G2.opt_len, static_lenb, G2.static_len, stored_len, + G2.last_lit, G2.last_dist)); + + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ + if (stored_len <= opt_lenb && eof && G2.compressed_len == 0L && seekable()) { + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == NULL) + bb_error_msg("block vanished"); + + copy_block(buf, (unsigned) stored_len, 0); /* without header */ + G2.compressed_len = stored_len << 3; + + } else if (stored_len + 4 <= opt_lenb && buf != NULL) { + /* 4: two words for the lengths */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK << 1) + eof, 3); /* send block type */ + G2.compressed_len = (G2.compressed_len + 3 + 7) & ~7L; + G2.compressed_len += (stored_len + 4) << 3; + + copy_block(buf, (unsigned) stored_len, 1); /* with header */ + + } else if (static_lenb == opt_lenb) { + send_bits((STATIC_TREES << 1) + eof, 3); + compress_block((ct_data *) G2.static_ltree, (ct_data *) G2.static_dtree); + G2.compressed_len += 3 + G2.static_len; + } else { + send_bits((DYN_TREES << 1) + eof, 3); + send_all_trees(G2.l_desc.max_code + 1, G2.d_desc.max_code + 1, + max_blindex + 1); + compress_block((ct_data *) G2.dyn_ltree, (ct_data *) G2.dyn_dtree); + G2.compressed_len += 3 + G2.opt_len; + } + Assert(G2.compressed_len == G1.bits_sent, "bad compressed size"); + init_block(); + + if (eof) { + bi_windup(); + G2.compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr, "\ncomprlen %lu(%lu) ", G2.compressed_len >> 3, + G2.compressed_len - 7 * eof)); + + return G2.compressed_len >> 3; +} + + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h, c) (h = (((h)<= 0L \ + ? (char*)&G1.window[(unsigned)G1.block_start] \ + : (char*)NULL, \ + (ulg)G1.strstart - G1.block_start, \ + (eof) \ + ) + +/* Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). */ +#define INSERT_STRING(s, match_head) \ +do { \ + UPDATE_HASH(G1.ins_h, G1.window[(s) + MIN_MATCH-1]); \ + G1.prev[(s) & WMASK] = match_head = head[G1.ins_h]; \ + head[G1.ins_h] = (s); \ +} while (0) + +static ulg deflate(void) +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + unsigned match_length = MIN_MATCH - 1; /* length of best match */ + + /* Process the input block. */ + while (G1.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(G1.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + G1.prev_length = match_length; + prev_match = G1.match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != 0 && G1.prev_length < max_lazy_match + && G1.strstart - hash_head <= MAX_DIST + ) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match(hash_head); + /* longest_match() sets match_start */ + if (match_length > G1.lookahead) + match_length = G1.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && G1.strstart - G1.match_start > TOO_FAR) { + /* If prev_match is also MIN_MATCH, G1.match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (G1.prev_length >= MIN_MATCH && match_length <= G1.prev_length) { + check_match(G1.strstart - 1, prev_match, G1.prev_length); + flush = ct_tally(G1.strstart - 1 - prev_match, G1.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + G1.lookahead -= G1.prev_length - 1; + G1.prev_length -= 2; + do { + G1.strstart++; + INSERT_STRING(G1.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--G1.prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + G1.strstart++; + if (flush) { + FLUSH_BLOCK(0); + G1.block_start = G1.strstart; + } + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr, "%c", G1.window[G1.strstart - 1])); + if (ct_tally(0, G1.window[G1.strstart - 1])) { + FLUSH_BLOCK(0); + G1.block_start = G1.strstart; + } + G1.strstart++; + G1.lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + G1.strstart++; + G1.lookahead--; + } + Assert(G1.strstart <= G1.isize && lookahead <= G1.isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile) + fill_window(); + } + if (match_available) + ct_tally(0, G1.window[G1.strstart - 1]); + + return FLUSH_BLOCK(1); /* eof */ +} + + +/* =========================================================================== + * Initialize the bit string routines. + */ +static void bi_init(void) +{ + G1.bi_buf = 0; + G1.bi_valid = 0; +#ifdef DEBUG + G1.bits_sent = 0L; +#endif +} + + +/* =========================================================================== + * Initialize the "longest match" routines for a new file + */ +static void lm_init(ush * flagsp) +{ + unsigned j; + + /* Initialize the hash table. */ + memset(head, 0, HASH_SIZE * sizeof(*head)); + /* prev will be initialized on the fly */ + + /* speed options for the general purpose bit flag */ + *flagsp |= 2; /* FAST 4, SLOW 2 */ + /* ??? reduce max_chain_length for binary files */ + + G1.strstart = 0; + G1.block_start = 0L; + + G1.lookahead = file_read(G1.window, + sizeof(int) <= 2 ? (unsigned) WSIZE : 2 * WSIZE); + + if (G1.lookahead == 0 || G1.lookahead == (unsigned) -1) { + G1.eofile = 1; + G1.lookahead = 0; + return; + } + G1.eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile) + fill_window(); + + G1.ins_h = 0; + for (j = 0; j < MIN_MATCH - 1; j++) + UPDATE_HASH(G1.ins_h, G1.window[j]); + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +} + + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + * One callsite in zip() + */ +static void ct_init(void) +{ + int n; /* iterates over tree elements */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + G2.compressed_len = 0L; + +#ifdef NOT_NEEDED + if (G2.static_dtree[0].Len != 0) + return; /* ct_init already called */ +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES - 1; code++) { + G2.base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + G2.length_code[length++] = code; + } + } + Assert(length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + G2.length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + G2.base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + G2.dist_code[dist++] = code; + } + } + Assert(dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES; code++) { + G2.base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + G2.dist_code[256 + dist++] = code; + } + } + Assert(dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + /* already zeroed - it's in bss + for (n = 0; n <= MAX_BITS; n++) + G2.bl_count[n] = 0; */ + + n = 0; + while (n <= 143) { + G2.static_ltree[n++].Len = 8; + G2.bl_count[8]++; + } + while (n <= 255) { + G2.static_ltree[n++].Len = 9; + G2.bl_count[9]++; + } + while (n <= 279) { + G2.static_ltree[n++].Len = 7; + G2.bl_count[7]++; + } + while (n <= 287) { + G2.static_ltree[n++].Len = 8; + G2.bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *) G2.static_ltree, L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + G2.static_dtree[n].Len = 5; + G2.static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + + +/* =========================================================================== + * Deflate in to out. + * IN assertions: the input and output buffers are cleared. + */ + +static void zip(ulg time_stamp) +{ + ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ + + G1.outcnt = 0; + + /* Write the header to the gzip file. See algorithm.doc for the format */ + /* magic header for gzip files: 1F 8B */ + /* compression method: 8 (DEFLATED) */ + /* general flags: 0 */ + put_32bit(0x00088b1f); + put_32bit(time_stamp); + + /* Write deflated file to zip file */ + G1.crc = ~0; + + bi_init(); + ct_init(); + lm_init(&deflate_flags); + + put_8bit(deflate_flags); /* extra flags */ + put_8bit(3); /* OS identifier = 3 (Unix) */ + + deflate(); + + /* Write the crc and uncompressed size */ + put_32bit(~G1.crc); + put_32bit(G1.isize); + + flush_outbuf(); +} + + +/* ======================================================================== */ +static +IF_DESKTOP(long long) int FAST_FUNC pack_gzip(unpack_info_t *info UNUSED_PARAM) +{ + struct stat s; + + /* Clear input and output buffers */ + G1.outcnt = 0; +#ifdef DEBUG + G1.insize = 0; +#endif + G1.isize = 0; + + /* Reinit G2.xxx */ + memset(&G2, 0, sizeof(G2)); + G2.l_desc.dyn_tree = G2.dyn_ltree; + G2.l_desc.static_tree = G2.static_ltree; + G2.l_desc.extra_bits = extra_lbits; + G2.l_desc.extra_base = LITERALS + 1; + G2.l_desc.elems = L_CODES; + G2.l_desc.max_length = MAX_BITS; + //G2.l_desc.max_code = 0; + G2.d_desc.dyn_tree = G2.dyn_dtree; + G2.d_desc.static_tree = G2.static_dtree; + G2.d_desc.extra_bits = extra_dbits; + //G2.d_desc.extra_base = 0; + G2.d_desc.elems = D_CODES; + G2.d_desc.max_length = MAX_BITS; + //G2.d_desc.max_code = 0; + G2.bl_desc.dyn_tree = G2.bl_tree; + //G2.bl_desc.static_tree = NULL; + G2.bl_desc.extra_bits = extra_blbits, + //G2.bl_desc.extra_base = 0; + G2.bl_desc.elems = BL_CODES; + G2.bl_desc.max_length = MAX_BL_BITS; + //G2.bl_desc.max_code = 0; + + s.st_ctime = 0; + fstat(STDIN_FILENO, &s); + zip(s.st_ctime); + return 0; +} + +#if ENABLE_FEATURE_GZIP_LONG_OPTIONS +static const char gzip_longopts[] ALIGN1 = + "stdout\0" No_argument "c" + "to-stdout\0" No_argument "c" + "force\0" No_argument "f" + "verbose\0" No_argument "v" +#if ENABLE_GUNZIP + "decompress\0" No_argument "d" + "uncompress\0" No_argument "d" + "test\0" No_argument "t" +#endif + "quiet\0" No_argument "q" + "fast\0" No_argument "1" + "best\0" No_argument "9" + ; +#endif + +/* + * Linux kernel build uses gzip -d -n. We accept and ignore -n. + * Man page says: + * -n --no-name + * gzip: do not save the original file name and time stamp. + * (The original name is always saved if the name had to be truncated.) + * gunzip: do not restore the original file name/time even if present + * (remove only the gzip suffix from the compressed file name). + * This option is the default when decompressing. + * -N --name + * gzip: always save the original file name and time stamp (this is the default) + * gunzip: restore the original file name and time stamp if present. + */ + +int gzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +#if ENABLE_GUNZIP +int gzip_main(int argc, char **argv) +#else +int gzip_main(int argc UNUSED_PARAM, char **argv) +#endif +{ + unsigned opt; + +#if ENABLE_FEATURE_GZIP_LONG_OPTIONS + applet_long_options = gzip_longopts; +#endif + /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ + opt = getopt32(argv, "cfv" IF_GUNZIP("dt") "q123456789n"); +#if ENABLE_GUNZIP /* gunzip_main may not be visible... */ + if (opt & 0x18) // -d and/or -t + return gunzip_main(argc, argv); +#endif + option_mask32 &= 0x7; /* ignore -q, -0..9 */ + //if (opt & 0x1) // -c + //if (opt & 0x2) // -f + //if (opt & 0x4) // -v + argv += optind; + + SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2)) + + sizeof(struct globals)); + + /* Allocate all global buffers (for DYN_ALLOC option) */ + ALLOC(uch, G1.l_buf, INBUFSIZ); + ALLOC(uch, G1.outbuf, OUTBUFSIZ); + ALLOC(ush, G1.d_buf, DIST_BUFSIZE); + ALLOC(uch, G1.window, 2L * WSIZE); + ALLOC(ush, G1.prev, 1L << BITS); + + /* Initialize the CRC32 table */ + global_crc32_table = crc32_filltable(NULL, 0); + + return bbunpack(argv, pack_gzip, append_ext, "gz"); +} diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src new file mode 100644 index 000000000..b0bc4e5aa --- /dev/null +++ b/archival/libarchive/Kbuild.src @@ -0,0 +1,64 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 by Erik Andersen +# +# Licensed under GPLv2 or later, see file LICENSE in this source tree. + +lib-y:= + +COMMON_FILES:= \ +\ + data_skip.o \ + data_extract_all.o \ + data_extract_to_stdout.o \ +\ + filter_accept_all.o \ + filter_accept_list.o \ + filter_accept_reject_list.o \ +\ + header_skip.o \ + header_list.o \ + header_verbose_list.o \ +\ + seek_by_read.o \ + seek_by_jump.o \ +\ + data_align.o \ + find_list_entry.o \ + init_handle.o + +DPKG_FILES:= \ + get_header_ar.o \ + unpack_ar_archive.o \ + get_header_tar.o \ + filter_accept_list_reassign.o + +INSERT + +lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o +lib-$(CONFIG_BUNZIP2) += decompress_bunzip2.o +lib-$(CONFIG_UNLZMA) += decompress_unlzma.o +lib-$(CONFIG_UNXZ) += decompress_unxz.o +lib-$(CONFIG_CPIO) += get_header_cpio.o +lib-$(CONFIG_DPKG) += $(DPKG_FILES) +lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES) +lib-$(CONFIG_GUNZIP) += decompress_unzip.o +lib-$(CONFIG_RPM2CPIO) += decompress_unzip.o get_header_cpio.o +lib-$(CONFIG_RPM) += open_transformer.o decompress_unzip.o get_header_cpio.o +lib-$(CONFIG_TAR) += get_header_tar.o +lib-$(CONFIG_UNCOMPRESS) += decompress_uncompress.o +lib-$(CONFIG_UNZIP) += decompress_unzip.o +lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o +lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o +lib-$(CONFIG_FEATURE_SEAMLESS_Z) += open_transformer.o decompress_uncompress.o +lib-$(CONFIG_FEATURE_SEAMLESS_GZ) += open_transformer.o decompress_unzip.o get_header_tar_gz.o +lib-$(CONFIG_FEATURE_SEAMLESS_BZ2) += open_transformer.o decompress_bunzip2.o get_header_tar_bz2.o +lib-$(CONFIG_FEATURE_SEAMLESS_LZMA) += open_transformer.o decompress_unlzma.o get_header_tar_lzma.o +lib-$(CONFIG_FEATURE_SEAMLESS_XZ) += open_transformer.o decompress_unxz.o +lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += decompress_bunzip2.o +lib-$(CONFIG_FEATURE_COMPRESS_BBCONFIG) += decompress_bunzip2.o +lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o + +ifneq ($(lib-y),) +lib-y += $(COMMON_FILES) +endif diff --git a/archival/libarchive/bz/LICENSE b/archival/libarchive/bz/LICENSE new file mode 100644 index 000000000..da4346520 --- /dev/null +++ b/archival/libarchive/bz/LICENSE @@ -0,0 +1,44 @@ +bzip2 applet in busybox is based on lightly-modified source +of bzip2 version 1.0.4. bzip2 source is distributed +under the following conditions (copied verbatim from LICENSE file) +=========================================================== + + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2006 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + +Julian Seward, Cambridge, UK. +jseward@bzip.org +bzip2/libbzip2 version 1.0.4 of 20 December 2006 diff --git a/archival/libarchive/bz/README b/archival/libarchive/bz/README new file mode 100644 index 000000000..fffd47b8a --- /dev/null +++ b/archival/libarchive/bz/README @@ -0,0 +1,90 @@ +This file is an abridged version of README from bzip2 1.0.4 +Build instructions (which are not relevant to busyboxed bzip2) +are removed. +=========================================================== + + +This is the README for bzip2/libzip2. +This version is fully compatible with the previous public releases. + +------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in this file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ + +Please read and be aware of the following: + + +WARNING: + + This program and library (attempts to) compress data by + performing several non-trivial transformations on it. + Unless you are 100% familiar with *all* the algorithms + contained herein, and with the consequences of modifying them, + you should NOT meddle with the compression or decompression + machinery. Incorrect changes can and very likely *will* + lead to disastrous loss of data. + + +DISCLAIMER: + + I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE + USE OF THIS PROGRAM/LIBRARY, HOWSOEVER CAUSED. + + Every compression of a file implies an assumption that the + compressed file can be decompressed to reproduce the original. + Great efforts in design, coding and testing have been made to + ensure that this program works correctly. However, the complexity + of the algorithms, and, in particular, the presence of various + special cases in the code which occur with very low but non-zero + probability make it impossible to rule out the possibility of bugs + remaining in the program. DO NOT COMPRESS ANY DATA WITH THIS + PROGRAM UNLESS YOU ARE PREPARED TO ACCEPT THE POSSIBILITY, HOWEVER + SMALL, THAT THE DATA WILL NOT BE RECOVERABLE. + + That is not to say this program is inherently unreliable. + Indeed, I very much hope the opposite is true. bzip2/libbzip2 + has been carefully constructed and extensively tested. + + +PATENTS: + + To the best of my knowledge, bzip2/libbzip2 does not use any + patented algorithms. However, I do not have the resources + to carry out a patent search. Therefore I cannot give any + guarantee of the above statement. + + +I hope you find bzip2 useful. Feel free to contact me at + jseward@bzip.org +if you have any suggestions or queries. Many people mailed me with +comments, suggestions and patches after the releases of bzip-0.15, +bzip-0.21, and bzip2 versions 0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1, +1.0.2 and 1.0.3, and the changes in bzip2 are largely a result of this +feedback. I thank you for your comments. + +bzip2's "home" is http://www.bzip.org/ + +Julian Seward +jseward@bzip.org +Cambridge, UK. + +18 July 1996 (version 0.15) +25 August 1996 (version 0.21) + 7 August 1997 (bzip2, version 0.1) +29 August 1997 (bzip2, version 0.1pl2) +23 August 1998 (bzip2, version 0.9.0) + 8 June 1999 (bzip2, version 0.9.5) + 4 Sept 1999 (bzip2, version 0.9.5d) + 5 May 2000 (bzip2, version 1.0pre8) +30 December 2001 (bzip2, version 1.0.2pre1) +15 February 2005 (bzip2, version 1.0.3) +20 December 2006 (bzip2, version 1.0.4) diff --git a/archival/libarchive/bz/blocksort.c b/archival/libarchive/bz/blocksort.c new file mode 100644 index 000000000..e600cb7a7 --- /dev/null +++ b/archival/libarchive/bz/blocksort.c @@ -0,0 +1,1072 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* #include "bzlib_private.h" */ + +#define mswap(zz1, zz2) \ +{ \ + int32_t zztmp = zz1; \ + zz1 = zz2; \ + zz2 = zztmp; \ +} + +static +/* No measurable speed gain with inlining */ +/* ALWAYS_INLINE */ +void mvswap(uint32_t* ptr, int32_t zzp1, int32_t zzp2, int32_t zzn) +{ + while (zzn > 0) { + mswap(ptr[zzp1], ptr[zzp2]); + zzp1++; + zzp2++; + zzn--; + } +} + +static +ALWAYS_INLINE +int32_t mmin(int32_t a, int32_t b) +{ + return (a < b) ? a : b; +} + + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +inline +void fallbackSimpleSort(uint32_t* fmap, + uint32_t* eclass, + int32_t lo, + int32_t hi) +{ + int32_t i, j, tmp; + uint32_t ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for (i = hi-4; i >= lo; i--) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for (j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for (i = hi-1; i >= lo; i--) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for (j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fpush(lz,hz) { \ + stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; \ +} + +#define fpop(lz,hz) { \ + sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ +} + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + +static +void fallbackQSort3(uint32_t* fmap, + uint32_t* eclass, + int32_t loSt, + int32_t hiSt) +{ + int32_t unLo, unHi, ltLo, gtHi, n, m; + int32_t sp, lo, hi; + uint32_t med, r, r3; + int32_t stackLo[FALLBACK_QSORT_STACK_SIZE]; + int32_t stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush(loSt, hiSt); + + while (sp > 0) { + AssertH(sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004); + + fpop(lo, hi); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort(fmap, eclass, lo, hi); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + * avoid bad cases. Median of 9 seems to help but + * looks rather expensive. This too seems to work but + * is cheaper. Guidance for the magic constants + * 7621 and 32768 is taken from Sedgewick's algorithms + * book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) + med = eclass[fmap[lo]]; + else if (r3 == 1) + med = eclass[fmap[(lo+hi)>>1]]; + else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (int32_t)eclass[fmap[unLo]] - (int32_t)med; + if (n == 0) { + mswap(fmap[unLo], fmap[ltLo]); + ltLo++; + unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (int32_t)eclass[fmap[unHi]] - (int32_t)med; + if (n == 0) { + mswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD(unHi == unLo-1, "fallbackQSort3(2)"); + + if (gtHi < ltLo) continue; + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(fmap, lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(fmap, unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush(lo, n); + fpush(m, hi); + } else { + fpush(m, hi); + fpush(lo, n); + } + } +} + +#undef fpush +#undef fpop +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + * nblock > 0 + * eclass exists for [0 .. nblock-1] + * ((uint8_t*)eclass) [0 .. nblock-1] holds block + * ptr exists for [0 .. nblock-1] + * + * Post: + * ((uint8_t*)eclass) [0 .. nblock-1] holds block + * All other areas of eclass destroyed + * fmap [0 .. nblock-1] holds sorted order + * bhtab[0 .. 2+(nblock/32)] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort(uint32_t* fmap, + uint32_t* eclass, + uint32_t* bhtab, + int32_t nblock) +{ + int32_t ftab[257]; + int32_t ftabCopy[256]; + int32_t H, i, j, k, l, r, cc, cc1; + int32_t nNotDone; + int32_t nBhtab; + uint8_t* eclass8 = (uint8_t*)eclass; + + /* + * Initial 1-char radix sort to generate + * initial fmap and initial BH bits. + */ + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + + j = ftab[0]; /* bbox: optimized */ + for (i = 1; i < 257; i++) { + j += ftab[i]; + ftab[i] = j; + } + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + ((uint32_t)nblock / 32); /* bbox: unsigned div is easier */ + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /* + * Inductively refine the buckets. Kind-of an + * "exponential radix sort" (!), inspired by the + * Manber-Myers suffix array construction algorithm. + */ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) + j = i; + k = fmap[i] - H; + if (k < 0) + k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) + k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) + break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) + k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) + break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3(fmap, eclass, l, r); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { + SET_BH(i); + cc = cc1; + }; + } + } + } + + H *= 2; + if (H > nblock || nNotDone == 0) + break; + } + + /* + * Reconstruct the original block in + * eclass8 [0 .. nblock-1], since the + * previous phase destroyed it. + */ + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) + j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (uint8_t)j; + } + AssertH(j < 256, 1005); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +NOINLINE +int mainGtU( + uint32_t i1, + uint32_t i2, + uint8_t* block, + uint16_t* quadrant, + uint32_t nblock, + int32_t* budget) +{ + int32_t k; + uint8_t c1, c2; + uint16_t s1, s2; + +/* Loop unrolling here is actually very useful + * (generated code is much simpler), + * code size increase is only 270 bytes (i386) + * but speeds up compression 10% overall + */ + +#if CONFIG_BZIP2_FAST >= 1 + +#define TIMES_8(code) \ + code; code; code; code; \ + code; code; code; code; +#define TIMES_12(code) \ + code; code; code; code; \ + code; code; code; code; \ + code; code; code; code; + +#else + +#define TIMES_8(code) \ +{ \ + int nn = 8; \ + do { \ + code; \ + } while (--nn); \ +} +#define TIMES_12(code) \ +{ \ + int nn = 12; \ + do { \ + code; \ + } while (--nn); \ +} + +#endif + + AssertD(i1 != i2, "mainGtU"); + TIMES_12( + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + ) + + k = nblock + 8; + + do { + TIMES_8( + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + ) + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + (*budget)--; + k -= 8; + } while (k >= 0); + + return False; +} +#undef TIMES_8 +#undef TIMES_12 + +/*---------------------------------------------*/ +/* + * Knuth's increments seem to work better + * than Incerpi-Sedgewick here. Possibly + * because the number of elems to sort is + * usually small, typically <= 20. + */ +static +const int32_t incs[14] = { + 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 +}; + +static +void mainSimpleSort(uint32_t* ptr, + uint8_t* block, + uint16_t* quadrant, + int32_t nblock, + int32_t lo, + int32_t hi, + int32_t d, + int32_t* budget) +{ + int32_t i, j, h, bigN, hp; + uint32_t v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (1) { + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while (mainGtU(ptr[j-h]+d, v+d, block, quadrant, nblock, budget)) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + +/* 1.5% overall speedup, +290 bytes */ +#if CONFIG_BZIP2_FAST >= 3 + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while (mainGtU(ptr[j-h]+d, v+d, block, quadrant, nblock, budget)) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while (mainGtU(ptr[j-h]+d, v+d, block, quadrant, nblock, budget)) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; +#endif + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/* + * The following is an implementation of + * an elegant 3-way quicksort for strings, + * described in a paper "Fast Algorithms for + * Sorting and Searching Strings", by Robert + * Sedgewick and Jon L. Bentley. + */ + +static +ALWAYS_INLINE +uint8_t mmed3(uint8_t a, uint8_t b, uint8_t c) +{ + uint8_t t; + if (a > b) { + t = a; + a = b; + b = t; + }; + /* here b >= a */ + if (b > c) { + b = c; + if (a > b) + b = a; + } + return b; +} + +#define mpush(lz,hz,dz) \ +{ \ + stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; \ +} + +#define mpop(lz,hz,dz) \ +{ \ + sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; \ +} + +#define mnextsize(az) (nextHi[az] - nextLo[az]) + +#define mnextswap(az,bz) \ +{ \ + int32_t tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; \ +} + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static NOINLINE +void mainQSort3(uint32_t* ptr, + uint8_t* block, + uint16_t* quadrant, + int32_t nblock, + int32_t loSt, + int32_t hiSt, + int32_t dSt, + int32_t* budget) +{ + int32_t unLo, unHi, ltLo, gtHi, n, m, med; + int32_t sp, lo, hi, d; + + int32_t stackLo[MAIN_QSORT_STACK_SIZE]; + int32_t stackHi[MAIN_QSORT_STACK_SIZE]; + int32_t stackD [MAIN_QSORT_STACK_SIZE]; + + int32_t nextLo[3]; + int32_t nextHi[3]; + int32_t nextD [3]; + + sp = 0; + mpush(loSt, hiSt, dSt); + + while (sp > 0) { + AssertH(sp < MAIN_QSORT_STACK_SIZE - 2, 1001); + + mpop(lo, hi, d); + if (hi - lo < MAIN_QSORT_SMALL_THRESH + || d > MAIN_QSORT_DEPTH_THRESH + ) { + mainSimpleSort(ptr, block, quadrant, nblock, lo, hi, d, budget); + if (*budget < 0) + return; + continue; + } + med = (int32_t) mmed3(block[ptr[lo ] + d], + block[ptr[hi ] + d], + block[ptr[(lo+hi) >> 1] + d]); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) + break; + n = ((int32_t)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; + unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) + break; + n = ((int32_t)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; + unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) + break; + mswap(ptr[unLo], ptr[unHi]); + unLo++; + unHi--; + } + + AssertD(unHi == unLo-1, "mainQSort3(2)"); + + if (gtHi < ltLo) { + mpush(lo, hi, d + 1); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(ptr, lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(ptr, unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0, 1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1, 2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0, 1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)"); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)"); + + mpush(nextLo[0], nextHi[0], nextD[0]); + mpush(nextLo[1], nextHi[1], nextD[1]); + mpush(nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mpush +#undef mpop +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + * nblock > N_OVERSHOOT + * block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + * ((uint8_t*)block32) [0 .. nblock-1] holds block + * ptr exists for [0 .. nblock-1] + * + * Post: + * ((uint8_t*)block32) [0 .. nblock-1] holds block + * All other areas of block32 destroyed + * ftab[0 .. 65536] destroyed + * ptr [0 .. nblock-1] holds sorted order + * if (*budget < 0), sorting was abandoned + */ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static NOINLINE +void mainSort(EState* state, + uint32_t* ptr, + uint8_t* block, + uint16_t* quadrant, + uint32_t* ftab, + int32_t nblock, + int32_t* budget) +{ + int32_t i, j, k, ss, sb; + uint8_t c1; + int32_t numQSorted; + uint16_t s; + Bool bigDone[256]; + /* bbox: moved to EState to save stack + int32_t runningOrder[256]; + int32_t copyStart[256]; + int32_t copyEnd [256]; + */ +#define runningOrder (state->mainSort__runningOrder) +#define copyStart (state->mainSort__copyStart) +#define copyEnd (state->mainSort__copyEnd) + + /*-- set up the 2-byte frequency table --*/ + /* was: for (i = 65536; i >= 0; i--) ftab[i] = 0; */ + memset(ftab, 0, 65537 * sizeof(ftab[0])); + + j = block[0] << 8; + i = nblock - 1; +/* 3%, +300 bytes */ +#if CONFIG_BZIP2_FAST >= 2 + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | (((uint16_t)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | (((uint16_t)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | (((uint16_t)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | (((uint16_t)block[i-3]) << 8); + ftab[j]++; + } +#endif + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | (((uint16_t)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + /*-- Complete the initial radix sort --*/ + j = ftab[0]; /* bbox: optimized */ + for (i = 1; i <= 65536; i++) { + j += ftab[i]; + ftab[i] = j; + } + + s = block[0] << 8; + i = nblock - 1; +#if CONFIG_BZIP2_FAST >= 2 + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i-3; + } +#endif + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i; + } + + /* + * Now ftab contains the first loc of every small bucket. + * Calculate the running order, from smallest to largest + * big bucket. + */ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + int32_t vv; + /* bbox: was: int32_t h = 1; */ + /* do h = 3 * h + 1; while (h <= 256); */ + uint32_t h = 364; + + do { + /*h = h / 3;*/ + h = (h * 171) >> 9; /* bbox: fast h/3 */ + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while (BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv)) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) + goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /* + * The main sorting loop. + */ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /* + * Process big buckets, starting with the least full. + * Basically this is a 3-step process in which we call + * mainQSort3 to sort the small buckets [ss, j], but + * also make a big effort to avoid the calls if we can. + */ + ss = runningOrder[i]; + + /* + * Step 1: + * Complete the big bucket [ss] by quicksorting + * any unsorted small buckets [ss, j], for j != ss. + * Hopefully previous pointer-scanning phases have already + * completed many of the small buckets [ss, j], so + * we don't have to sort them at all. + */ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if (!(ftab[sb] & SETMASK)) { + int32_t lo = ftab[sb] & CLEARMASK; + int32_t hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + mainQSort3( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + if (*budget < 0) return; + numQSorted += (hi - lo + 1); + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH(!bigDone[ss], 1006); + + /* + * Step 2: + * Now scan this big bucket [ss] so as to synthesise the + * sorted order for small buckets [t, ss] for all t, + * including, magically, the bucket [ss,ss] too. + * This will avoid doing Real Work in subsequent Step 1's. + */ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j] - 1; + if (k < 0) + k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[copyStart[c1]++] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; + if (k < 0) + k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[copyEnd[c1]--] = k; + } + } + + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + * Necessity for this case is demonstrated by compressing + * a sequence of approximately 48.5 million of character + * 251; 1.0.0/1.0.1 will then die here. */ + AssertH((copyStart[ss]-1 == copyEnd[ss]) \ + || (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), 1007); + + for (j = 0; j <= 255; j++) + ftab[(j << 8) + ss] |= SETMASK; + + /* + * Step 3: + * The [ss] big bucket is now done. Record this fact, + * and update the quadrant descriptors. Remember to + * update quadrants in the overshoot area too, if + * necessary. The "if (i < 255)" test merely skips + * this updating for the last bucket processed, since + * updating for the last bucket is pointless. + * + * The quadrant array provides a way to incrementally + * cache sort orderings, as they appear, so as to + * make subsequent comparisons in fullGtU() complete + * faster. For repetitive blocks this makes a big + * difference (but not big enough to be able to avoid + * the fallback sorting mechanism, exponential radix sort). + * + * The precise meaning is: at all times: + * + * for 0 <= i < nblock and 0 <= j <= nblock + * + * if block[i] != block[j], + * + * then the relative values of quadrant[i] and + * quadrant[j] are meaningless. + * + * else { + * if quadrant[i] < quadrant[j] + * then the string starting at i lexicographically + * precedes the string starting at j + * + * else if quadrant[i] > quadrant[j] + * then the string starting at j lexicographically + * precedes the string starting at i + * + * else + * the relative ordering of the strings starting + * at i and j has not yet been determined. + * } + */ + bigDone[ss] = True; + + if (i < 255) { + int32_t bbStart = ftab[ss << 8] & CLEARMASK; + int32_t bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + int32_t shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + int32_t a2update = ptr[bbStart + j]; + uint16_t qVal = (uint16_t)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH(((bbSize-1) >> shifts) <= 65535, 1002); + } + } +#undef runningOrder +#undef copyStart +#undef copyEnd +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + * nblock > 0 + * arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + * ((uint8_t*)arr2)[0 .. nblock-1] holds block + * arr1 exists for [0 .. nblock-1] + * + * Post: + * ((uint8_t*)arr2) [0 .. nblock-1] holds block + * All other areas of block destroyed + * ftab[0 .. 65536] destroyed + * arr1[0 .. nblock-1] holds sorted order + */ +static NOINLINE +void BZ2_blockSort(EState* s) +{ + /* In original bzip2 1.0.4, it's a parameter, but 30 + * (which was the default) should work ok. */ + enum { wfact = 30 }; + + uint32_t* ptr = s->ptr; + uint8_t* block = s->block; + uint32_t* ftab = s->ftab; + int32_t nblock = s->nblock; + uint16_t* quadrant; + int32_t budget; + int32_t i; + + if (nblock < 10000) { + fallbackSort(s->arr1, s->arr2, ftab, nblock); + } else { + /* Calculate the location for quadrant, remembering to get + * the alignment right. Assumes that &(block[0]) is at least + * 2-byte aligned -- this should be ok since block is really + * the first section of arr2. + */ + i = nblock + BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (uint16_t*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + * transition point at very roughly the same place as + * with v0.1 and v0.9.0. + * Not that it particularly matters any more, since the + * resulting compressed stream is now the same regardless + * of whether or not we use the main sort or fallback sort. + */ + budget = nblock * ((wfact-1) / 3); + + mainSort(s, ptr, block, quadrant, ftab, nblock, &budget); + if (budget < 0) { + fallbackSort(s->arr1, s->arr2, ftab, nblock); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) { + s->origPtr = i; + break; + }; + + AssertH(s->origPtr != -1, 1003); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/archival/libarchive/bz/bzlib.c b/archival/libarchive/bz/bzlib.c new file mode 100644 index 000000000..5f7db747a --- /dev/null +++ b/archival/libarchive/bz/bzlib.c @@ -0,0 +1,429 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* CHANGES + * 0.9.0 -- original version. + * 0.9.0a/b -- no changes in this file. + * 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + * fixed bzWrite/bzRead to ignore zero-length requests. + * fixed bzread to correctly handle read requests after EOF. + * wrong parameter order in call to bzDecompressInit in + * bzBuffToBuffDecompress. Fixed. + */ + +/* #include "bzlib_private.h" */ + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +#if BZ_LIGHT_DEBUG +static +void bz_assert_fail(int errcode) +{ + /* if (errcode == 1007) bb_error_msg_and_die("probably bad RAM"); */ + bb_error_msg_and_die("internal error %d", errcode); +} +#endif + +/*---------------------------------------------------*/ +static +void prepare_new_block(EState* s) +{ + int i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC(s->blockCRC); + /* inlined memset would be nice to have here */ + for (i = 0; i < 256; i++) + s->inUse[i] = 0; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +ALWAYS_INLINE +void init_RL(EState* s) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +int isempty_RL(EState* s) +{ + return (s->state_in_ch >= 256 || s->state_in_len <= 0); +} + + +/*---------------------------------------------------*/ +static +void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k) +{ + int32_t n; + EState* s; + + s = xzalloc(sizeof(EState)); + s->strm = strm; + + n = 100000 * blockSize100k; + s->arr1 = xmalloc(n * sizeof(uint32_t)); + s->mtfv = (uint16_t*)s->arr1; + s->ptr = (uint32_t*)s->arr1; + s->arr2 = xmalloc((n + BZ_N_OVERSHOOT) * sizeof(uint32_t)); + s->block = (uint8_t*)s->arr2; + s->ftab = xmalloc(65537 * sizeof(uint32_t)); + + s->crc32table = crc32_filltable(NULL, 1); + + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->blockSize100k = blockSize100k; + s->nblockMAX = n - 19; + + strm->state = s; + /*strm->total_in = 0;*/ + strm->total_out = 0; + init_RL(s); + prepare_new_block(s); +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block(EState* s) +{ + int32_t i; + uint8_t ch = (uint8_t)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC(s, s->blockCRC, ch); + } + s->inUse[s->state_in_ch] = 1; + switch (s->state_in_len) { + case 3: + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + /* fall through */ + case 2: + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + /* fall through */ + case 1: + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len - 4] = 1; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)(s->state_in_len - 4); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL(EState* s) +{ + if (s->state_in_ch < 256) add_pair_to_block(s); + init_RL(s); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs, zchh0) \ +{ \ + uint32_t zchh = (uint32_t)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && zs->state_in_len == 1) { \ + uint8_t ch = (uint8_t)(zs->state_in_ch); \ + BZ_UPDATE_CRC(zs, zs->blockCRC, ch); \ + zs->inUse[zs->state_in_ch] = 1; \ + zs->block[zs->nblock] = (uint8_t)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block(zs); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +void /*Bool*/ copy_input_until_stop(EState* s) +{ + /*Bool progress_in = False;*/ + +#ifdef SAME_CODE_AS_BELOW + if (s->mode == BZ_M_RUNNING) { + /*-- fast track the common case --*/ + while (1) { + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*progress_in = True;*/ + ADD_CHAR_TO_BLOCK(s, (uint32_t)(*(uint8_t*)(s->strm->next_in))); + s->strm->next_in++; + s->strm->avail_in--; + /*s->strm->total_in++;*/ + } + } else +#endif + { + /*-- general, uncommon case --*/ + while (1) { + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + //# /*-- flush/finish end? --*/ + //# if (s->avail_in_expect == 0) break; + /*progress_in = True;*/ + ADD_CHAR_TO_BLOCK(s, *(uint8_t*)(s->strm->next_in)); + s->strm->next_in++; + s->strm->avail_in--; + /*s->strm->total_in++;*/ + //# s->avail_in_expect--; + } + } + /*return progress_in;*/ +} + + +/*---------------------------------------------------*/ +static +void /*Bool*/ copy_output_until_stop(EState* s) +{ + /*Bool progress_out = False;*/ + + while (1) { + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + /*progress_out = True;*/ + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out++; + } + /*return progress_out;*/ +} + + +/*---------------------------------------------------*/ +static +void /*Bool*/ handle_compress(bz_stream *strm) +{ + /*Bool progress_in = False;*/ + /*Bool progress_out = False;*/ + EState* s = strm->state; + + while (1) { + if (s->state == BZ_S_OUTPUT) { + /*progress_out |=*/ copy_output_until_stop(s); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING + //# && s->avail_in_expect == 0 + && s->strm->avail_in == 0 + && isempty_RL(s)) + break; + prepare_new_block(s); + s->state = BZ_S_INPUT; +#ifdef FLUSH_IS_UNUSED + if (s->mode == BZ_M_FLUSHING + && s->avail_in_expect == 0 + && isempty_RL(s)) + break; +#endif + } + + if (s->state == BZ_S_INPUT) { + /*progress_in |=*/ copy_input_until_stop(s); + //#if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + if (s->mode != BZ_M_RUNNING && s->strm->avail_in == 0) { + flush_RL(s); + BZ2_compressBlock(s, (s->mode == BZ_M_FINISHING)); + s->state = BZ_S_OUTPUT; + } else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock(s, 0); + s->state = BZ_S_OUTPUT; + } else + if (s->strm->avail_in == 0) { + break; + } + } + } + + /*return progress_in || progress_out;*/ +} + + +/*---------------------------------------------------*/ +static +int BZ2_bzCompress(bz_stream *strm, int action) +{ + /*Bool progress;*/ + EState* s; + + s = strm->state; + + switch (s->mode) { + case BZ_M_RUNNING: + if (action == BZ_RUN) { + /*progress =*/ handle_compress(strm); + /*return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;*/ + return BZ_RUN_OK; + } +#ifdef FLUSH_IS_UNUSED + else + if (action == BZ_FLUSH) { + //#s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto case_BZ_M_FLUSHING; + } +#endif + else + /*if (action == BZ_FINISH)*/ { + //#s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto case_BZ_M_FINISHING; + } + +#ifdef FLUSH_IS_UNUSED + case_BZ_M_FLUSHING: + case BZ_M_FLUSHING: + /*if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR;*/ + /*progress =*/ handle_compress(strm); + if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ) + return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; +#endif + + case_BZ_M_FINISHING: + /*case BZ_M_FINISHING:*/ + default: + /*if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR;*/ + /*progress =*/ handle_compress(strm); + /*if (!progress) return BZ_SEQUENCE_ERROR;*/ + //#if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ) + //# return BZ_FINISH_OK; + if (s->strm->avail_in > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ) + return BZ_FINISH_OK; + /*s->mode = BZ_M_IDLE;*/ + return BZ_STREAM_END; + } + /* return BZ_OK; --not reached--*/ +} + + +/*---------------------------------------------------*/ +static +void BZ2_bzCompressEnd(bz_stream *strm) +{ + EState* s; + + s = strm->state; + free(s->arr1); + free(s->arr2); + free(s->ftab); + free(s->crc32table); + free(s); +} + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +#ifdef EXAMPLE_CODE_FOR_MEM_TO_MEM_COMPRESSION +static +int BZ2_bzBuffToBuffCompress(char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL + || source == NULL + || blockSize100k < 1 || blockSize100k > 9 + ) { + return BZ_PARAM_ERROR; + } + + BZ2_bzCompressInit(&strm, blockSize100k); + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress(&strm, BZ_FINISH); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd(&strm); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd(&strm); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd(&strm); + return ret; +} +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/archival/libarchive/bz/bzlib.h b/archival/libarchive/bz/bzlib.h new file mode 100644 index 000000000..1bb811c4a --- /dev/null +++ b/archival/libarchive/bz/bzlib.h @@ -0,0 +1,65 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef struct bz_stream { + void *state; + char *next_in; + char *next_out; + unsigned avail_in; + unsigned avail_out; + /*unsigned long long total_in;*/ + unsigned long long total_out; +} bz_stream; + +/*-- Core (low-level) library functions --*/ + +static void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k); +static int BZ2_bzCompress(bz_stream *strm, int action); +#if ENABLE_FEATURE_CLEAN_UP +static void BZ2_bzCompressEnd(bz_stream *strm); +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/archival/libarchive/bz/bzlib_private.h b/archival/libarchive/bz/bzlib_private.h new file mode 100644 index 000000000..43e674bec --- /dev/null +++ b/archival/libarchive/bz/bzlib_private.h @@ -0,0 +1,219 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* #include "bzlib.h" */ + +/*-- General stuff. --*/ + +typedef unsigned char Bool; + +#define True ((Bool)1) +#define False ((Bool)0) + +#if BZ_LIGHT_DEBUG +static void bz_assert_fail(int errcode) NORETURN; +#define AssertH(cond, errcode) \ +do { \ + if (!(cond)) \ + bz_assert_fail(errcode); \ +} while (0) +#else +#define AssertH(cond, msg) do { } while (0) +#endif + +#if BZ_DEBUG +#define AssertD(cond, msg) \ +do { \ + if (!(cond)) \ + bb_error_msg_and_die("(debug build): internal error %s", msg); \ +} while (0) +#else +#define AssertD(cond, msg) do { } while (0) +#endif + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +#define BZ_HDR_BZh0 0x425a6830 + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + +/*-- Stuff for doing CRCs. --*/ + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(s, crcVar, cha) \ +{ \ + crcVar = (crcVar << 8) ^ s->crc32table[(crcVar >> 24) ^ ((uint8_t)cha)]; \ +} + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef struct EState { + /* pointer back to the struct bz_stream */ + bz_stream *strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + int32_t mode; + int32_t state; + + /* remembers avail_in when flush/finish requested */ +/* bbox: not needed, strm->avail_in always has the same value */ +/* commented out with '//#' throughout the code */ + /* uint32_t avail_in_expect; */ + + /* for doing the block sorting */ + int32_t origPtr; + uint32_t *arr1; + uint32_t *arr2; + uint32_t *ftab; + + /* aliases for arr1 and arr2 */ + uint32_t *ptr; + uint8_t *block; + uint16_t *mtfv; + uint8_t *zbits; + + /* guess what */ + uint32_t *crc32table; + + /* run-length-encoding of the input */ + uint32_t state_in_ch; + int32_t state_in_len; + + /* input and output limits and current posns */ + int32_t nblock; + int32_t nblockMAX; + int32_t numZ; + int32_t state_out_pos; + + /* the buffer for bit stream creation */ + uint32_t bsBuff; + int32_t bsLive; + + /* block and combined CRCs */ + uint32_t blockCRC; + uint32_t combinedCRC; + + /* misc administratium */ + int32_t blockNo; + int32_t blockSize100k; + + /* stuff for coding the MTF values */ + int32_t nMTF; + + /* map of bytes used in block */ + int32_t nInUse; + Bool inUse[256] ALIGNED(sizeof(long)); + uint8_t unseqToSeq[256]; + + /* stuff for coding the MTF values */ + int32_t mtfFreq [BZ_MAX_ALPHA_SIZE]; + uint8_t selector [BZ_MAX_SELECTORS]; + uint8_t selectorMtf[BZ_MAX_SELECTORS]; + + uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + /* stack-saving measures: these can be local, but they are too big */ + int32_t sendMTFValues__code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + int32_t sendMTFValues__rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +#if CONFIG_BZIP2_FAST >= 5 + /* second dimension: only 3 needed; 4 makes index calculations faster */ + uint32_t sendMTFValues__len_pack[BZ_MAX_ALPHA_SIZE][4]; +#endif + int32_t BZ2_hbMakeCodeLengths__heap [BZ_MAX_ALPHA_SIZE + 2]; + int32_t BZ2_hbMakeCodeLengths__weight[BZ_MAX_ALPHA_SIZE * 2]; + int32_t BZ2_hbMakeCodeLengths__parent[BZ_MAX_ALPHA_SIZE * 2]; + + int32_t mainSort__runningOrder[256]; + int32_t mainSort__copyStart[256]; + int32_t mainSort__copyEnd[256]; +} EState; + + +/*-- compression. --*/ + +static void +BZ2_blockSort(EState*); + +static void +BZ2_compressBlock(EState*, int); + +static void +BZ2_bsInitWrite(EState*); + +static void +BZ2_hbAssignCodes(int32_t*, uint8_t*, int32_t, int32_t, int32_t); + +static void +BZ2_hbMakeCodeLengths(EState*, uint8_t*, int32_t*, int32_t, int32_t); + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/archival/libarchive/bz/compress.c b/archival/libarchive/bz/compress.c new file mode 100644 index 000000000..e9f1afdaf --- /dev/null +++ b/archival/libarchive/bz/compress.c @@ -0,0 +1,680 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* CHANGES + * 0.9.0 -- original version. + * 0.9.0a/b -- no changes in this file. + * 0.9.0c -- changed setting of nGroups in sendMTFValues() + * so as to do a bit better on small files +*/ + +/* #include "bzlib_private.h" */ + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void BZ2_bsInitWrite(EState* s) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static NOINLINE +void bsFinishWrite(EState* s) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (uint8_t)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +static +/* Helps only on level 5, on other levels hurts. ? */ +#if CONFIG_BZIP2_FAST >= 5 +ALWAYS_INLINE +#endif +void bsW(EState* s, int32_t n, uint32_t v) +{ + while (s->bsLive >= 8) { + s->zbits[s->numZ] = (uint8_t)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutU32(EState* s, unsigned u) +{ + bsW(s, 8, (u >> 24) & 0xff); + bsW(s, 8, (u >> 16) & 0xff); + bsW(s, 8, (u >> 8) & 0xff); + bsW(s, 8, u & 0xff); +} + + +/*---------------------------------------------------*/ +static +void bsPutU16(EState* s, unsigned u) +{ + bsW(s, 8, (u >> 8) & 0xff); + bsW(s, 8, u & 0xff); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e(EState* s) +{ + int i; + s->nInUse = 0; + for (i = 0; i < 256; i++) { + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } + } +} + + +/*---------------------------------------------------*/ +static NOINLINE +void generateMTFValues(EState* s) +{ + uint8_t yy[256]; + int32_t i, j; + int32_t zPend; + int32_t wr; + int32_t EOB; + + /* + * After sorting (eg, here), + * s->arr1[0 .. s->nblock-1] holds sorted order, + * and + * ((uint8_t*)s->arr2)[0 .. s->nblock-1] + * holds the original block data. + * + * The first thing to do is generate the MTF values, + * and put them in ((uint16_t*)s->arr1)[0 .. s->nblock-1]. + * + * Because there are strictly fewer or equal MTF values + * than block values, ptr values in this area are overwritten + * with MTF values only when they are no longer needed. + * + * The final compressed bitstream is generated into the + * area starting at &((uint8_t*)s->arr2)[s->nblock] + * + * These storage aliases are set up in bzCompressInit(), + * except for the last one, which is arranged in + * compressBlock(). + */ + uint32_t* ptr = s->ptr; + uint8_t* block = s->block; + uint16_t* mtfv = s->mtfv; + + makeMaps_e(s); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) + s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) + yy[i] = (uint8_t) i; + + for (i = 0; i < s->nblock; i++) { + uint8_t ll_i; + AssertD(wr <= i, "generateMTFValues(1)"); + j = ptr[i] - 1; + if (j < 0) + j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD(ll_i < s->nInUse, "generateMTFValues(2a)"); + + if (yy[0] == ll_i) { + zPend++; + } else { + if (zPend > 0) { + zPend--; + while (1) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (uint32_t)(zPend - 2) / 2; + /* bbox: unsigned div is easier */ + }; + zPend = 0; + } + { + register uint8_t rtmp; + register uint8_t* ryy_j; + register uint8_t rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while (rll_i != rtmp) { + register uint8_t rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; + wr++; + s->mtfFreq[j+1]++; + } + } + } + + if (zPend > 0) { + zPend--; + while (1) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; + wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; + wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) + break; + zPend = (uint32_t)(zPend - 2) / 2; + /* bbox: unsigned div is easier */ + }; + zPend = 0; + } + + mtfv[wr] = EOB; + wr++; + s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static NOINLINE +void sendMTFValues(EState* s) +{ + int32_t v, t, i, j, gs, ge, totc, bt, bc, iter; + int32_t nSelectors, alphaSize, minLen, maxLen, selCtr; + int32_t nGroups; + + /* + * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + * is a global since the decoder also needs it. + * + * int32_t code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + * int32_t rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + * are also globals only used in this proc. + * Made global to keep stack frame size small. + */ +#define code sendMTFValues__code +#define rfreq sendMTFValues__rfreq +#define len_pack sendMTFValues__len_pack + + uint16_t cost[BZ_N_GROUPS]; + int32_t fave[BZ_N_GROUPS]; + + uint16_t* mtfv = s->mtfv; + + alphaSize = s->nInUse + 2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH(s->nMTF > 0, 3001); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + int32_t nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs - 1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups - nPart) % 2 == 1) /* bbox: can this be replaced by x & 1? */ + ) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; + else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge + 1; + remF -= aFreq; + } + } + + /* + * Iterate up to BZ_N_ITERS times to improve the tables. + */ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + for (t = 0; t < nGroups; t++) + fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + +#if CONFIG_BZIP2_FAST >= 5 + /* + * Set up an auxiliary length table which is used to fast-track + * the common case (nGroups == 6). + */ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } +#endif + nSelectors = 0; + totc = 0; + gs = 0; + while (1) { + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) + break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) + ge = s->nMTF-1; + + /* + * Calculate the cost of this group as coded + * by each of the coding tables. + */ + for (t = 0; t < nGroups; t++) + cost[t] = 0; +#if CONFIG_BZIP2_FAST >= 5 + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register uint32_t cost01, cost23, cost45; + register uint16_t icv; + cost01 = cost23 = cost45 = 0; +#define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); +#undef BZ_ITER + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else +#endif + { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + uint16_t icv = mtfv[i]; + for (t = 0; t < nGroups; t++) + cost[t] += s->len[t][icv]; + } + } + /* + * Find the coding table which is best for this group, + * and record its identity in the selector table. + */ + /*bc = 999999999;*/ + /*bt = -1;*/ + bc = cost[0]; + bt = 0; + for (t = 1 /*0*/; t < nGroups; t++) { + if (cost[t] < bc) { + bc = cost[t]; + bt = t; + } + } + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /* + * Increment the symbol frequencies for the selected table. + */ +/* 1% faster compress. +800 bytes */ +#if CONFIG_BZIP2_FAST >= 4 + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ +#define BZ_ITUR(nn) s->rfreq[bt][mtfv[gs + (nn)]]++ + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); +#undef BZ_ITUR + gs = ge + 1; + } else +#endif + { + /*--- slow version which correctly handles all situations ---*/ + while (gs <= ge) { + s->rfreq[bt][mtfv[gs]]++; + gs++; + } + /* already is: gs = ge + 1; */ + } + } + + /* + * Recompute the tables based on the accumulated frequencies. + */ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + * comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths(s, &(s->len[t][0]), &(s->rfreq[t][0]), alphaSize, 17 /*20*/); + } + + AssertH(nGroups < 8, 3002); + AssertH(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZ_G_SIZE)), 3003); + + /*--- Compute MTF values for the selectors. ---*/ + { + uint8_t pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + + for (i = 0; i < nGroups; i++) + pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH(!(maxLen > 17 /*20*/), 3004); + AssertH(!(minLen < 1), 3005); + BZ2_hbAssignCodes(&(s->code[t][0]), &(s->len[t][0]), minLen, maxLen, alphaSize); + } + + /*--- Transmit the mapping table. ---*/ + { + /* bbox: optimized a bit more than in bzip2 */ + int inUse16 = 0; + for (i = 0; i < 16; i++) { + if (sizeof(long) <= 4) { + inUse16 = inUse16*2 + + ((*(uint32_t*)&(s->inUse[i * 16 + 0]) + | *(uint32_t*)&(s->inUse[i * 16 + 4]) + | *(uint32_t*)&(s->inUse[i * 16 + 8]) + | *(uint32_t*)&(s->inUse[i * 16 + 12])) != 0); + } else { /* Our CPU can do better */ + inUse16 = inUse16*2 + + ((*(uint64_t*)&(s->inUse[i * 16 + 0]) + | *(uint64_t*)&(s->inUse[i * 16 + 8])) != 0); + } + } + + bsW(s, 16, inUse16); + + inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */ + for (i = 0; i < 16; i++) { + if (inUse16 < 0) { + unsigned v16 = 0; + for (j = 0; j < 16; j++) + v16 = v16*2 + s->inUse[i * 16 + j]; + bsW(s, 16, v16); + } + inUse16 <<= 1; + } + } + + /*--- Now the selectors. ---*/ + bsW(s, 3, nGroups); + bsW(s, 15, nSelectors); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) + bsW(s, 1, 1); + bsW(s, 1, 0); + } + + /*--- Now the coding tables. ---*/ + for (t = 0; t < nGroups; t++) { + int32_t curr = s->len[t][0]; + bsW(s, 5, curr); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s, 2, 2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s, 2, 3); curr--; /* 11 */ }; + bsW(s, 1, 0); + } + } + + /*--- And finally, the block data proper ---*/ + selCtr = 0; + gs = 0; + while (1) { + if (gs >= s->nMTF) + break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) + ge = s->nMTF-1; + AssertH(s->selector[selCtr] < nGroups, 3006); + +/* Costs 1300 bytes and is _slower_ (on Intel Core 2) */ +#if 0 + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + uint16_t mtfv_i; + uint8_t* s_len_sel_selCtr = &(s->len[s->selector[selCtr]][0]); + int32_t* s_code_sel_selCtr = &(s->code[s->selector[selCtr]][0]); +#define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW(s, s_len_sel_selCtr[mtfv_i], s_code_sel_selCtr[mtfv_i]) + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); +#undef BZ_ITAH + gs = ge+1; + } else +#endif + { + /*--- slow version which correctly handles all situations ---*/ + /* code is bit bigger, but moves multiply out of the loop */ + uint8_t* s_len_sel_selCtr = &(s->len [s->selector[selCtr]][0]); + int32_t* s_code_sel_selCtr = &(s->code[s->selector[selCtr]][0]); + while (gs <= ge) { + bsW(s, + s_len_sel_selCtr[mtfv[gs]], + s_code_sel_selCtr[mtfv[gs]] + ); + gs++; + } + /* already is: gs = ge+1; */ + } + selCtr++; + } + AssertH(selCtr == nSelectors, 3007); +#undef code +#undef rfreq +#undef len_pack +} + + +/*---------------------------------------------------*/ +static +void BZ2_compressBlock(EState* s, int is_last_block) +{ + if (s->nblock > 0) { + BZ_FINALISE_CRC(s->blockCRC); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) + s->numZ = 0; + + BZ2_blockSort(s); + } + + s->zbits = &((uint8_t*)s->arr2)[s->nblock]; + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite(s); + /*bsPutU8(s, BZ_HDR_B);*/ + /*bsPutU8(s, BZ_HDR_Z);*/ + /*bsPutU8(s, BZ_HDR_h);*/ + /*bsPutU8(s, BZ_HDR_0 + s->blockSize100k);*/ + bsPutU32(s, BZ_HDR_BZh0 + s->blockSize100k); + } + + if (s->nblock > 0) { + /*bsPutU8(s, 0x31);*/ + /*bsPutU8(s, 0x41);*/ + /*bsPutU8(s, 0x59);*/ + /*bsPutU8(s, 0x26);*/ + bsPutU32(s, 0x31415926); + /*bsPutU8(s, 0x53);*/ + /*bsPutU8(s, 0x59);*/ + bsPutU16(s, 0x5359); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutU32(s, s->blockCRC); + + /* + * Now a single bit indicating (non-)randomisation. + * As of version 0.9.5, we use a better sorting algorithm + * which makes randomisation unnecessary. So always set + * the randomised bit to 'no'. Of course, the decoder + * still needs to be able to handle randomised blocks + * so as to maintain backwards compatibility with + * older versions of bzip2. + */ + bsW(s, 1, 0); + + bsW(s, 24, s->origPtr); + generateMTFValues(s); + sendMTFValues(s); + } + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + /*bsPutU8(s, 0x17);*/ + /*bsPutU8(s, 0x72);*/ + /*bsPutU8(s, 0x45);*/ + /*bsPutU8(s, 0x38);*/ + bsPutU32(s, 0x17724538); + /*bsPutU8(s, 0x50);*/ + /*bsPutU8(s, 0x90);*/ + bsPutU16(s, 0x5090); + bsPutU32(s, s->combinedCRC); + bsFinishWrite(s); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/archival/libarchive/bz/huffman.c b/archival/libarchive/bz/huffman.c new file mode 100644 index 000000000..bbec11adb --- /dev/null +++ b/archival/libarchive/bz/huffman.c @@ -0,0 +1,229 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* #include "bzlib_private.h" */ + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + int32_t zz, tmp; \ + zz = z; \ + tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + + +/* 90 bytes, 0.3% of overall compress speed */ +#if CONFIG_BZIP2_FAST >= 1 + +/* macro works better than inline (gcc 4.2.1) */ +#define DOWNHEAP1(heap, weight, Heap) \ +{ \ + int32_t zz, yy, tmp; \ + zz = 1; \ + tmp = heap[zz]; \ + while (1) { \ + yy = zz << 1; \ + if (yy > nHeap) \ + break; \ + if (yy < nHeap \ + && weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) \ + break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + +#else + +static +void DOWNHEAP1(int32_t *heap, int32_t *weight, int32_t nHeap) +{ + int32_t zz, yy, tmp; + zz = 1; + tmp = heap[zz]; + while (1) { + yy = zz << 1; + if (yy > nHeap) + break; + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) + yy++; + if (weight[tmp] < weight[heap[yy]]) + break; + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; +} + +#endif + +/*---------------------------------------------------*/ +static +void BZ2_hbMakeCodeLengths(EState *s, + uint8_t *len, + int32_t *freq, + int32_t alphaSize, + int32_t maxLen) +{ + /* + * Nodes and heap entries run from 1. Entry 0 + * for both the heap and nodes is a sentinel. + */ + int32_t nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + /* bbox: moved to EState to save stack + int32_t heap [BZ_MAX_ALPHA_SIZE + 2]; + int32_t weight[BZ_MAX_ALPHA_SIZE * 2]; + int32_t parent[BZ_MAX_ALPHA_SIZE * 2]; + */ +#define heap (s->BZ2_hbMakeCodeLengths__heap) +#define weight (s->BZ2_hbMakeCodeLengths__weight) +#define parent (s->BZ2_hbMakeCodeLengths__parent) + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (1) { + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH(nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP1(heap, weight, nHeap); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP1(heap, weight, nHeap); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH(nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { + k = parent[k]; + j++; + } + len[i-1] = j; + if (j > maxLen) + tooLong = True; + } + + if (!tooLong) + break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + /* bbox: yes, it is a signed division. + * don't replace with shift! */ + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +#undef heap +#undef weight +#undef parent +} + + +/*---------------------------------------------------*/ +static +void BZ2_hbAssignCodes(int32_t *code, + uint8_t *length, + int32_t minLen, + int32_t maxLen, + int32_t alphaSize) +{ + int32_t n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) { + if (length[i] == n) { + code[i] = vec; + vec++; + }; + } + vec <<= 1; + } +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/archival/libarchive/data_align.c b/archival/libarchive/data_align.c new file mode 100644 index 000000000..2e56fa8ff --- /dev/null +++ b/archival/libarchive/data_align.c @@ -0,0 +1,15 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC data_align(archive_handle_t *archive_handle, unsigned boundary) +{ + unsigned skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary; + + archive_handle->seek(archive_handle->src_fd, skip_amount); + archive_handle->offset += skip_amount; +} diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c new file mode 100644 index 000000000..1b25c8bd6 --- /dev/null +++ b/archival/libarchive/data_extract_all.c @@ -0,0 +1,200 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + int dst_fd; + int res; + +#if ENABLE_FEATURE_TAR_SELINUX + char *sctx = archive_handle->tar__next_file_sctx; + if (!sctx) + sctx = archive_handle->tar__global_sctx; + if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ + setfscreatecon(sctx); + free(archive_handle->tar__next_file_sctx); + archive_handle->tar__next_file_sctx = NULL; + } +#endif + + if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { + char *slash = strrchr(file_header->name, '/'); + if (slash) { + *slash = '\0'; + bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); + *slash = '/'; + } + } + + if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) { + /* Remove the entry if it exists */ + if (!S_ISDIR(file_header->mode)) { + /* Is it hardlink? + * We encode hard links as regular files of size 0 with a symlink */ + if (S_ISREG(file_header->mode) + && file_header->link_target + && file_header->size == 0 + ) { + /* Ugly special case: + * tar cf t.tar hardlink1 hardlink2 hardlink1 + * results in this tarball structure: + * hardlink1 + * hardlink2 -> hardlink1 + * hardlink1 -> hardlink1 <== !!! + */ + if (strcmp(file_header->link_target, file_header->name) == 0) + goto ret; + } + /* Proceed with deleting */ + if (unlink(file_header->name) == -1 + && errno != ENOENT + ) { + bb_perror_msg_and_die("can't remove old file %s", + file_header->name); + } + } + } + else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) { + /* Remove the existing entry if its older than the extracted entry */ + struct stat existing_sb; + if (lstat(file_header->name, &existing_sb) == -1) { + if (errno != ENOENT) { + bb_perror_msg_and_die("can't stat old file"); + } + } + else if (existing_sb.st_mtime >= file_header->mtime) { + if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) + && !S_ISDIR(file_header->mode) + ) { + bb_error_msg("%s not created: newer or " + "same age file exists", file_header->name); + } + data_skip(archive_handle); + goto ret; + } + else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { + bb_perror_msg_and_die("can't remove old file %s", + file_header->name); + } + } + + /* Handle hard links separately + * We encode hard links as regular files of size 0 with a symlink */ + if (S_ISREG(file_header->mode) + && file_header->link_target + && file_header->size == 0 + ) { + /* hard link */ + res = link(file_header->link_target, file_header->name); + if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { + bb_perror_msg("can't create %slink " + "from %s to %s", "hard", + file_header->name, + file_header->link_target); + } + /* Hardlinks have no separate mode/ownership, skip chown/chmod */ + goto ret; + } + + /* Create the filesystem entry */ + switch (file_header->mode & S_IFMT) { + case S_IFREG: { + /* Regular file */ + int flags = O_WRONLY | O_CREAT | O_EXCL; + if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) + flags = O_WRONLY | O_CREAT | O_TRUNC; + dst_fd = xopen3(file_header->name, + flags, + file_header->mode + ); + bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); + close(dst_fd); + break; + } + case S_IFDIR: + res = mkdir(file_header->name, file_header->mode); + if ((res == -1) + && (errno != EISDIR) /* btw, Linux doesn't return this */ + && (errno != EEXIST) + && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) + ) { + bb_perror_msg("can't make dir %s", file_header->name); + } + break; + case S_IFLNK: + /* Symlink */ +//TODO: what if file_header->link_target == NULL (say, corrupted tarball?) + res = symlink(file_header->link_target, file_header->name); + if ((res == -1) + && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) + ) { + bb_perror_msg("can't create %slink " + "from %s to %s", "sym", + file_header->name, + file_header->link_target); + } + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + res = mknod(file_header->name, file_header->mode, file_header->device); + if ((res == -1) + && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) + ) { + bb_perror_msg("can't create node %s", file_header->name); + } + break; + default: + bb_error_msg_and_die("unrecognized file type"); + } + + if (!S_ISLNK(file_header->mode)) { + if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) { + uid_t uid = file_header->uid; + gid_t gid = file_header->gid; +#if ENABLE_FEATURE_TAR_UNAME_GNAME + if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) { + if (file_header->tar__uname) { +//TODO: cache last name/id pair? + struct passwd *pwd = getpwnam(file_header->tar__uname); + if (pwd) uid = pwd->pw_uid; + } + if (file_header->tar__gname) { + struct group *grp = getgrnam(file_header->tar__gname); + if (grp) gid = grp->gr_gid; + } + } +#endif + /* GNU tar 1.15.1 uses chown, not lchown */ + chown(file_header->name, uid, gid); + } + /* uclibc has no lchmod, glibc is even stranger - + * it has lchmod which seems to do nothing! + * so we use chmod... */ + if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) { + chmod(file_header->name, file_header->mode); + } + if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { + struct timeval t[2]; + + t[1].tv_sec = t[0].tv_sec = file_header->mtime; + t[1].tv_usec = t[0].tv_usec = 0; + utimes(file_header->name, t); + } + } + + ret: ; +#if ENABLE_FEATURE_TAR_SELINUX + if (sctx) { + /* reset the context after creating an entry */ + setfscreatecon(NULL); + } +#endif +} diff --git a/archival/libarchive/data_extract_to_command.c b/archival/libarchive/data_extract_to_command.c new file mode 100644 index 000000000..0e977049d --- /dev/null +++ b/archival/libarchive/data_extract_to_command.c @@ -0,0 +1,138 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +enum { + //TAR_FILETYPE, + TAR_MODE, + TAR_FILENAME, + TAR_REALNAME, +#if ENABLE_FEATURE_TAR_UNAME_GNAME + TAR_UNAME, + TAR_GNAME, +#endif + TAR_SIZE, + TAR_UID, + TAR_GID, + TAR_MAX, +}; + +static const char *const tar_var[] = { + // "FILETYPE", + "MODE", + "FILENAME", + "REALNAME", +#if ENABLE_FEATURE_TAR_UNAME_GNAME + "UNAME", + "GNAME", +#endif + "SIZE", + "UID", + "GID", +}; + +static void xputenv(char *str) +{ + if (putenv(str)) + bb_error_msg_and_die(bb_msg_memory_exhausted); +} + +static void str2env(char *env[], int idx, const char *str) +{ + env[idx] = xasprintf("TAR_%s=%s", tar_var[idx], str); + xputenv(env[idx]); +} + +static void dec2env(char *env[], int idx, unsigned long long val) +{ + env[idx] = xasprintf("TAR_%s=%llu", tar_var[idx], val); + xputenv(env[idx]); +} + +static void oct2env(char *env[], int idx, unsigned long val) +{ + env[idx] = xasprintf("TAR_%s=%lo", tar_var[idx], val); + xputenv(env[idx]); +} + +void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + +#if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */ + char *sctx = archive_handle->tar__next_file_sctx; + if (!sctx) + sctx = archive_handle->tar__global_sctx; + if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ + setfscreatecon(sctx); + free(archive_handle->tar__next_file_sctx); + archive_handle->tar__next_file_sctx = NULL; + } +#endif + + if ((file_header->mode & S_IFMT) == S_IFREG) { + pid_t pid; + int p[2], status; + char *tar_env[TAR_MAX]; + + memset(tar_env, 0, sizeof(tar_env)); + + xpipe(p); + pid = BB_MMU ? xfork() : xvfork(); + if (pid == 0) { + /* Child */ + /* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */ + oct2env(tar_env, TAR_MODE, file_header->mode); + str2env(tar_env, TAR_FILENAME, file_header->name); + str2env(tar_env, TAR_REALNAME, file_header->name); +#if ENABLE_FEATURE_TAR_UNAME_GNAME + str2env(tar_env, TAR_UNAME, file_header->tar__uname); + str2env(tar_env, TAR_GNAME, file_header->tar__gname); +#endif + dec2env(tar_env, TAR_SIZE, file_header->size); + dec2env(tar_env, TAR_UID, file_header->uid); + dec2env(tar_env, TAR_GID, file_header->gid); + close(p[1]); + xdup2(p[0], STDIN_FILENO); + signal(SIGPIPE, SIG_DFL); + execl(archive_handle->tar__to_command_shell, + archive_handle->tar__to_command_shell, + "-c", + archive_handle->tar__to_command, + NULL); + bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell); + } + close(p[0]); + /* Our caller is expected to do signal(SIGPIPE, SIG_IGN) + * so that we don't die if child don't read all the input: */ + bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size); + close(p[1]); + + if (safe_waitpid(pid, &status, 0) == -1) + bb_perror_msg_and_die("waitpid"); + if (WIFEXITED(status) && WEXITSTATUS(status)) + bb_error_msg_and_die("'%s' returned status %d", + archive_handle->tar__to_command, WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + bb_error_msg_and_die("'%s' terminated on signal %d", + archive_handle->tar__to_command, WTERMSIG(status)); + + if (!BB_MMU) { + int i; + for (i = 0; i < TAR_MAX; i++) { + if (tar_env[i]) + bb_unsetenv_and_free(tar_env[i]); + } + } + } + +#if 0 /* ENABLE_FEATURE_TAR_SELINUX */ + if (sctx) + /* reset the context after creating an entry */ + setfscreatecon(NULL); +#endif +} diff --git a/archival/libarchive/data_extract_to_stdout.c b/archival/libarchive/data_extract_to_stdout.c new file mode 100644 index 000000000..91f3f3539 --- /dev/null +++ b/archival/libarchive/data_extract_to_stdout.c @@ -0,0 +1,14 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC data_extract_to_stdout(archive_handle_t *archive_handle) +{ + bb_copyfd_exact_size(archive_handle->src_fd, + STDOUT_FILENO, + archive_handle->file_header->size); +} diff --git a/archival/libarchive/data_skip.c b/archival/libarchive/data_skip.c new file mode 100644 index 000000000..a055424e2 --- /dev/null +++ b/archival/libarchive/data_skip.c @@ -0,0 +1,12 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC data_skip(archive_handle_t *archive_handle) +{ + archive_handle->seek(archive_handle->src_fd, archive_handle->file_header->size); +} diff --git a/archival/libarchive/decompress_bunzip2.c b/archival/libarchive/decompress_bunzip2.c new file mode 100644 index 000000000..4e46e6849 --- /dev/null +++ b/archival/libarchive/decompress_bunzip2.c @@ -0,0 +1,822 @@ +/* vi: set sw=4 ts=4: */ +/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net). + + Based on bzip2 decompression code by Julian R Seward (jseward@acm.org), + which also acknowledges contributions by Mike Burrows, David Wheeler, + Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten, + Robert Sedgewick, and Jon L. Bentley. + + Licensed under GPLv2 or later, see file LICENSE in this source tree. +*/ + +/* + Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org). + + More efficient reading of Huffman codes, a streamlined read_bunzip() + function, and various other tweaks. In (limited) tests, approximately + 20% faster than bzcat on x86 and about 10% faster on arm. + + Note that about 2/3 of the time is spent in read_bunzip() reversing + the Burrows-Wheeler transformation. Much of that time is delay + resulting from cache misses. + + (2010 update by vda: profiled "bzcat <84mbyte.bz2 >/dev/null" + on x86-64 CPU with L2 > 1M: get_next_block is hotter than read_bunzip: + %time seconds calls function + 71.01 12.69 444 get_next_block + 28.65 5.12 93065 read_bunzip + 00.22 0.04 7736490 get_bits + 00.11 0.02 47 dealloc_bunzip + 00.00 0.00 93018 full_write + ...) + + + I would ask that anyone benefiting from this work, especially those + using it in commercial products, consider making a donation to my local + non-profit hospice organization (www.hospiceacadiana.com) in the name of + the woman I loved, Toni W. Hagan, who passed away Feb. 12, 2003. + + Manuel + */ + +#include "libbb.h" +#include "archive.h" + +/* Constants for Huffman coding */ +#define MAX_GROUPS 6 +#define GROUP_SIZE 50 /* 64 would have been more efficient */ +#define MAX_HUFCODE_BITS 20 /* Longest Huffman code allowed */ +#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */ +#define SYMBOL_RUNA 0 +#define SYMBOL_RUNB 1 + +/* Status return values */ +#define RETVAL_OK 0 +#define RETVAL_LAST_BLOCK (-1) +#define RETVAL_NOT_BZIP_DATA (-2) +#define RETVAL_UNEXPECTED_INPUT_EOF (-3) +#define RETVAL_SHORT_WRITE (-4) +#define RETVAL_DATA_ERROR (-5) +#define RETVAL_OUT_OF_MEMORY (-6) +#define RETVAL_OBSOLETE_INPUT (-7) + +/* Other housekeeping constants */ +#define IOBUF_SIZE 4096 + +/* This is what we know about each Huffman coding group */ +struct group_data { + /* We have an extra slot at the end of limit[] for a sentinel value. */ + int limit[MAX_HUFCODE_BITS+1], base[MAX_HUFCODE_BITS], permute[MAX_SYMBOLS]; + int minLen, maxLen; +}; + +/* Structure holding all the housekeeping data, including IO buffers and + * memory that persists between calls to bunzip + * Found the most used member: + * cat this_file.c | sed -e 's/"/ /g' -e "s/'/ /g" | xargs -n1 \ + * | grep 'bd->' | sed 's/^.*bd->/bd->/' | sort | $PAGER + * and moved it (inbufBitCount) to offset 0. + */ +struct bunzip_data { + /* I/O tracking data (file handles, buffers, positions, etc.) */ + unsigned inbufBitCount, inbufBits; + int in_fd, out_fd, inbufCount, inbufPos /*, outbufPos*/; + uint8_t *inbuf /*,*outbuf*/; + + /* State for interrupting output loop */ + int writeCopies, writePos, writeRunCountdown, writeCount; + int writeCurrent; /* actually a uint8_t */ + + /* The CRC values stored in the block header and calculated from the data */ + uint32_t headerCRC, totalCRC, writeCRC; + + /* Intermediate buffer and its size (in bytes) */ + uint32_t *dbuf; + unsigned dbufSize; + + /* For I/O error handling */ + jmp_buf jmpbuf; + + /* Big things go last (register-relative addressing can be larger for big offsets) */ + uint32_t crc32Table[256]; + uint8_t selectors[32768]; /* nSelectors=15 bits */ + struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */ +}; +/* typedef struct bunzip_data bunzip_data; -- done in .h file */ + + +/* Return the next nnn bits of input. All reads from the compressed input + are done through this function. All reads are big endian */ +static unsigned get_bits(bunzip_data *bd, int bits_wanted) +{ + unsigned bits = 0; + /* Cache bd->inbufBitCount in a CPU register (hopefully): */ + int bit_count = bd->inbufBitCount; + + /* If we need to get more data from the byte buffer, do so. (Loop getting + one byte at a time to enforce endianness and avoid unaligned access.) */ + while (bit_count < bits_wanted) { + + /* If we need to read more data from file into byte buffer, do so */ + if (bd->inbufPos == bd->inbufCount) { + /* if "no input fd" case: in_fd == -1, read fails, we jump */ + bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE); + if (bd->inbufCount <= 0) + longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); + bd->inbufPos = 0; + } + + /* Avoid 32-bit overflow (dump bit buffer to top of output) */ + if (bit_count >= 24) { + bits = bd->inbufBits & ((1 << bit_count) - 1); + bits_wanted -= bit_count; + bits <<= bits_wanted; + bit_count = 0; + } + + /* Grab next 8 bits of input from buffer. */ + bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++]; + bit_count += 8; + } + + /* Calculate result */ + bit_count -= bits_wanted; + bd->inbufBitCount = bit_count; + bits |= (bd->inbufBits >> bit_count) & ((1 << bits_wanted) - 1); + + return bits; +} + +/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */ +static int get_next_block(bunzip_data *bd) +{ + struct group_data *hufGroup; + int dbufCount, dbufSize, groupCount, *base, *limit, selector, + i, j, t, runPos, symCount, symTotal, nSelectors, byteCount[256]; + int runCnt = runCnt; /* for compiler */ + uint8_t uc, symToByte[256], mtfSymbol[256], *selectors; + uint32_t *dbuf; + unsigned origPtr; + + dbuf = bd->dbuf; + dbufSize = bd->dbufSize; + selectors = bd->selectors; + +/* In bbox, we are ok with aborting through setjmp which is set up in start_bunzip */ +#if 0 + /* Reset longjmp I/O error handling */ + i = setjmp(bd->jmpbuf); + if (i) return i; +#endif + + /* Read in header signature and CRC, then validate signature. + (last block signature means CRC is for whole file, return now) */ + i = get_bits(bd, 24); + j = get_bits(bd, 24); + bd->headerCRC = get_bits(bd, 32); + if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK; + if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA; + + /* We can add support for blockRandomised if anybody complains. There was + some code for this in busybox 1.0.0-pre3, but nobody ever noticed that + it didn't actually work. */ + if (get_bits(bd, 1)) return RETVAL_OBSOLETE_INPUT; + origPtr = get_bits(bd, 24); + if ((int)origPtr > dbufSize) return RETVAL_DATA_ERROR; + + /* mapping table: if some byte values are never used (encoding things + like ascii text), the compression code removes the gaps to have fewer + symbols to deal with, and writes a sparse bitfield indicating which + values were present. We make a translation table to convert the symbols + back to the corresponding bytes. */ + symTotal = 0; + i = 0; + t = get_bits(bd, 16); + do { + if (t & (1 << 15)) { + unsigned inner_map = get_bits(bd, 16); + do { + if (inner_map & (1 << 15)) + symToByte[symTotal++] = i; + inner_map <<= 1; + i++; + } while (i & 15); + i -= 16; + } + t <<= 1; + i += 16; + } while (i < 256); + + /* How many different Huffman coding groups does this block use? */ + groupCount = get_bits(bd, 3); + if (groupCount < 2 || groupCount > MAX_GROUPS) + return RETVAL_DATA_ERROR; + + /* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding + group. Read in the group selector list, which is stored as MTF encoded + bit runs. (MTF=Move To Front, as each value is used it's moved to the + start of the list.) */ + for (i = 0; i < groupCount; i++) + mtfSymbol[i] = i; + nSelectors = get_bits(bd, 15); + if (!nSelectors) + return RETVAL_DATA_ERROR; + for (i = 0; i < nSelectors; i++) { + uint8_t tmp_byte; + /* Get next value */ + int n = 0; + while (get_bits(bd, 1)) { + if (n >= groupCount) return RETVAL_DATA_ERROR; + n++; + } + /* Decode MTF to get the next selector */ + tmp_byte = mtfSymbol[n]; + while (--n >= 0) + mtfSymbol[n + 1] = mtfSymbol[n]; + mtfSymbol[0] = selectors[i] = tmp_byte; + } + + /* Read the Huffman coding tables for each group, which code for symTotal + literal symbols, plus two run symbols (RUNA, RUNB) */ + symCount = symTotal + 2; + for (j = 0; j < groupCount; j++) { + uint8_t length[MAX_SYMBOLS]; + /* 8 bits is ALMOST enough for temp[], see below */ + unsigned temp[MAX_HUFCODE_BITS+1]; + int minLen, maxLen, pp, len_m1; + + /* Read Huffman code lengths for each symbol. They're stored in + a way similar to mtf; record a starting value for the first symbol, + and an offset from the previous value for every symbol after that. + (Subtracting 1 before the loop and then adding it back at the end is + an optimization that makes the test inside the loop simpler: symbol + length 0 becomes negative, so an unsigned inequality catches it.) */ + len_m1 = get_bits(bd, 5) - 1; + for (i = 0; i < symCount; i++) { + for (;;) { + int two_bits; + if ((unsigned)len_m1 > (MAX_HUFCODE_BITS-1)) + return RETVAL_DATA_ERROR; + + /* If first bit is 0, stop. Else second bit indicates whether + to increment or decrement the value. Optimization: grab 2 + bits and unget the second if the first was 0. */ + two_bits = get_bits(bd, 2); + if (two_bits < 2) { + bd->inbufBitCount++; + break; + } + + /* Add one if second bit 1, else subtract 1. Avoids if/else */ + len_m1 += (((two_bits+1) & 2) - 1); + } + + /* Correct for the initial -1, to get the final symbol length */ + length[i] = len_m1 + 1; + } + + /* Find largest and smallest lengths in this group */ + minLen = maxLen = length[0]; + for (i = 1; i < symCount; i++) { + if (length[i] > maxLen) maxLen = length[i]; + else if (length[i] < minLen) minLen = length[i]; + } + + /* Calculate permute[], base[], and limit[] tables from length[]. + * + * permute[] is the lookup table for converting Huffman coded symbols + * into decoded symbols. base[] is the amount to subtract from the + * value of a Huffman symbol of a given length when using permute[]. + * + * limit[] indicates the largest numerical value a symbol with a given + * number of bits can have. This is how the Huffman codes can vary in + * length: each code with a value>limit[length] needs another bit. + */ + hufGroup = bd->groups + j; + hufGroup->minLen = minLen; + hufGroup->maxLen = maxLen; + + /* Note that minLen can't be smaller than 1, so we adjust the base + and limit array pointers so we're not always wasting the first + entry. We do this again when using them (during symbol decoding). */ + base = hufGroup->base - 1; + limit = hufGroup->limit - 1; + + /* Calculate permute[]. Concurently, initialize temp[] and limit[]. */ + pp = 0; + for (i = minLen; i <= maxLen; i++) { + int k; + temp[i] = limit[i] = 0; + for (k = 0; k < symCount; k++) + if (length[k] == i) + hufGroup->permute[pp++] = k; + } + + /* Count symbols coded for at each bit length */ + /* NB: in pathological cases, temp[8] can end ip being 256. + * That's why uint8_t is too small for temp[]. */ + for (i = 0; i < symCount; i++) temp[length[i]]++; + + /* Calculate limit[] (the largest symbol-coding value at each bit + * length, which is (previous limit<<1)+symbols at this level), and + * base[] (number of symbols to ignore at each bit length, which is + * limit minus the cumulative count of symbols coded for already). */ + pp = t = 0; + for (i = minLen; i < maxLen;) { + unsigned temp_i = temp[i]; + + pp += temp_i; + + /* We read the largest possible symbol size and then unget bits + after determining how many we need, and those extra bits could + be set to anything. (They're noise from future symbols.) At + each level we're really only interested in the first few bits, + so here we set all the trailing to-be-ignored bits to 1 so they + don't affect the value>limit[length] comparison. */ + limit[i] = (pp << (maxLen - i)) - 1; + pp <<= 1; + t += temp_i; + base[++i] = pp - t; + } + limit[maxLen] = pp + temp[maxLen] - 1; + limit[maxLen+1] = INT_MAX; /* Sentinel value for reading next sym. */ + base[minLen] = 0; + } + + /* We've finished reading and digesting the block header. Now read this + block's Huffman coded symbols from the file and undo the Huffman coding + and run length encoding, saving the result into dbuf[dbufCount++] = uc */ + + /* Initialize symbol occurrence counters and symbol Move To Front table */ + /*memset(byteCount, 0, sizeof(byteCount)); - smaller, but slower */ + for (i = 0; i < 256; i++) { + byteCount[i] = 0; + mtfSymbol[i] = (uint8_t)i; + } + + /* Loop through compressed symbols. */ + + runPos = dbufCount = selector = 0; + for (;;) { + int nextSym; + + /* Fetch next Huffman coding group from list. */ + symCount = GROUP_SIZE - 1; + if (selector >= nSelectors) return RETVAL_DATA_ERROR; + hufGroup = bd->groups + selectors[selector++]; + base = hufGroup->base - 1; + limit = hufGroup->limit - 1; + + continue_this_group: + /* Read next Huffman-coded symbol. */ + + /* Note: It is far cheaper to read maxLen bits and back up than it is + to read minLen bits and then add additional bit at a time, testing + as we go. Because there is a trailing last block (with file CRC), + there is no danger of the overread causing an unexpected EOF for a + valid compressed file. + */ + if (1) { + /* As a further optimization, we do the read inline + (falling back to a call to get_bits if the buffer runs dry). + */ + int new_cnt; + while ((new_cnt = bd->inbufBitCount - hufGroup->maxLen) < 0) { + /* bd->inbufBitCount < hufGroup->maxLen */ + if (bd->inbufPos == bd->inbufCount) { + nextSym = get_bits(bd, hufGroup->maxLen); + goto got_huff_bits; + } + bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount += 8; + }; + bd->inbufBitCount = new_cnt; /* "bd->inbufBitCount -= hufGroup->maxLen;" */ + nextSym = (bd->inbufBits >> new_cnt) & ((1 << hufGroup->maxLen) - 1); + got_huff_bits: ; + } else { /* unoptimized equivalent */ + nextSym = get_bits(bd, hufGroup->maxLen); + } + /* Figure how many bits are in next symbol and unget extras */ + i = hufGroup->minLen; + while (nextSym > limit[i]) ++i; + j = hufGroup->maxLen - i; + if (j < 0) + return RETVAL_DATA_ERROR; + bd->inbufBitCount += j; + + /* Huffman decode value to get nextSym (with bounds checking) */ + nextSym = (nextSym >> j) - base[i]; + if ((unsigned)nextSym >= MAX_SYMBOLS) + return RETVAL_DATA_ERROR; + nextSym = hufGroup->permute[nextSym]; + + /* We have now decoded the symbol, which indicates either a new literal + byte, or a repeated run of the most recent literal byte. First, + check if nextSym indicates a repeated run, and if so loop collecting + how many times to repeat the last literal. */ + if ((unsigned)nextSym <= SYMBOL_RUNB) { /* RUNA or RUNB */ + + /* If this is the start of a new run, zero out counter */ + if (runPos == 0) { + runPos = 1; + runCnt = 0; + } + + /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at + each bit position, add 1 or 2 instead. For example, + 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2. + You can make any bit pattern that way using 1 less symbol than + the basic or 0/1 method (except all bits 0, which would use no + symbols, but a run of length 0 doesn't mean anything in this + context). Thus space is saved. */ + runCnt += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */ + if (runPos < dbufSize) runPos <<= 1; + goto end_of_huffman_loop; + } + + /* When we hit the first non-run symbol after a run, we now know + how many times to repeat the last literal, so append that many + copies to our buffer of decoded symbols (dbuf) now. (The last + literal used is the one at the head of the mtfSymbol array.) */ + if (runPos != 0) { + uint8_t tmp_byte; + if (dbufCount + runCnt >= dbufSize) return RETVAL_DATA_ERROR; + tmp_byte = symToByte[mtfSymbol[0]]; + byteCount[tmp_byte] += runCnt; + while (--runCnt >= 0) dbuf[dbufCount++] = (uint32_t)tmp_byte; + runPos = 0; + } + + /* Is this the terminating symbol? */ + if (nextSym > symTotal) break; + + /* At this point, nextSym indicates a new literal character. Subtract + one to get the position in the MTF array at which this literal is + currently to be found. (Note that the result can't be -1 or 0, + because 0 and 1 are RUNA and RUNB. But another instance of the + first symbol in the mtf array, position 0, would have been handled + as part of a run above. Therefore 1 unused mtf position minus + 2 non-literal nextSym values equals -1.) */ + if (dbufCount >= dbufSize) return RETVAL_DATA_ERROR; + i = nextSym - 1; + uc = mtfSymbol[i]; + + /* Adjust the MTF array. Since we typically expect to move only a + * small number of symbols, and are bound by 256 in any case, using + * memmove here would typically be bigger and slower due to function + * call overhead and other assorted setup costs. */ + do { + mtfSymbol[i] = mtfSymbol[i-1]; + } while (--i); + mtfSymbol[0] = uc; + uc = symToByte[uc]; + + /* We have our literal byte. Save it into dbuf. */ + byteCount[uc]++; + dbuf[dbufCount++] = (uint32_t)uc; + + /* Skip group initialization if we're not done with this group. Done + * this way to avoid compiler warning. */ + end_of_huffman_loop: + if (--symCount >= 0) goto continue_this_group; + } + + /* At this point, we've read all the Huffman-coded symbols (and repeated + runs) for this block from the input stream, and decoded them into the + intermediate buffer. There are dbufCount many decoded bytes in dbuf[]. + Now undo the Burrows-Wheeler transform on dbuf. + See http://dogma.net/markn/articles/bwt/bwt.htm + */ + + /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */ + j = 0; + for (i = 0; i < 256; i++) { + int tmp_count = j + byteCount[i]; + byteCount[i] = j; + j = tmp_count; + } + + /* Figure out what order dbuf would be in if we sorted it. */ + for (i = 0; i < dbufCount; i++) { + uint8_t tmp_byte = (uint8_t)dbuf[i]; + int tmp_count = byteCount[tmp_byte]; + dbuf[tmp_count] |= (i << 8); + byteCount[tmp_byte] = tmp_count + 1; + } + + /* Decode first byte by hand to initialize "previous" byte. Note that it + doesn't get output, and if the first three characters are identical + it doesn't qualify as a run (hence writeRunCountdown=5). */ + if (dbufCount) { + uint32_t tmp; + if ((int)origPtr >= dbufCount) return RETVAL_DATA_ERROR; + tmp = dbuf[origPtr]; + bd->writeCurrent = (uint8_t)tmp; + bd->writePos = (tmp >> 8); + bd->writeRunCountdown = 5; + } + bd->writeCount = dbufCount; + + return RETVAL_OK; +} + +/* Undo Burrows-Wheeler transform on intermediate buffer to produce output. + If start_bunzip was initialized with out_fd=-1, then up to len bytes of + data are written to outbuf. Return value is number of bytes written or + error (all errors are negative numbers). If out_fd!=-1, outbuf and len + are ignored, data is written to out_fd and return is RETVAL_OK or error. + + NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes + in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0. + (Why? This allows to get rid of one local variable) +*/ +int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len) +{ + const uint32_t *dbuf; + int pos, current, previous; + uint32_t CRC; + + /* If we already have error/end indicator, return it */ + if (bd->writeCount < 0) + return bd->writeCount; + + dbuf = bd->dbuf; + + /* Register-cached state (hopefully): */ + pos = bd->writePos; + current = bd->writeCurrent; + CRC = bd->writeCRC; /* small loss on x86-32 (not enough regs), win on x86-64 */ + + /* We will always have pending decoded data to write into the output + buffer unless this is the very first call (in which case we haven't + Huffman-decoded a block into the intermediate buffer yet). */ + if (bd->writeCopies) { + + dec_writeCopies: + /* Inside the loop, writeCopies means extra copies (beyond 1) */ + --bd->writeCopies; + + /* Loop outputting bytes */ + for (;;) { + + /* If the output buffer is full, save cached state and return */ + if (--len < 0) { + /* Unlikely branch. + * Use of "goto" instead of keeping code here + * helps compiler to realize this. */ + goto outbuf_full; + } + + /* Write next byte into output buffer, updating CRC */ + *outbuf++ = current; + CRC = (CRC << 8) ^ bd->crc32Table[(CRC >> 24) ^ current]; + + /* Loop now if we're outputting multiple copies of this byte */ + if (bd->writeCopies) { + /* Unlikely branch */ + /*--bd->writeCopies;*/ + /*continue;*/ + /* Same, but (ab)using other existing --writeCopies operation + * (and this if() compiles into just test+branch pair): */ + goto dec_writeCopies; + } + decode_next_byte: + if (--bd->writeCount < 0) + break; /* input block is fully consumed, need next one */ + + /* Follow sequence vector to undo Burrows-Wheeler transform */ + previous = current; + pos = dbuf[pos]; + current = (uint8_t)pos; + pos >>= 8; + + /* After 3 consecutive copies of the same byte, the 4th + * is a repeat count. We count down from 4 instead + * of counting up because testing for non-zero is faster */ + if (--bd->writeRunCountdown != 0) { + if (current != previous) + bd->writeRunCountdown = 4; + } else { + /* Unlikely branch */ + /* We have a repeated run, this byte indicates the count */ + bd->writeCopies = current; + current = previous; + bd->writeRunCountdown = 5; + + /* Sometimes there are just 3 bytes (run length 0) */ + if (!bd->writeCopies) goto decode_next_byte; + + /* Subtract the 1 copy we'd output anyway to get extras */ + --bd->writeCopies; + } + } /* for(;;) */ + + /* Decompression of this input block completed successfully */ + bd->writeCRC = CRC = ~CRC; + bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ CRC; + + /* If this block had a CRC error, force file level CRC error */ + if (CRC != bd->headerCRC) { + bd->totalCRC = bd->headerCRC + 1; + return RETVAL_LAST_BLOCK; + } + } + + /* Refill the intermediate buffer by Huffman-decoding next block of input */ + { + int r = get_next_block(bd); + if (r) { /* error/end */ + bd->writeCount = r; + return (r != RETVAL_LAST_BLOCK) ? r : len; + } + } + + CRC = ~0; + pos = bd->writePos; + current = bd->writeCurrent; + goto decode_next_byte; + + outbuf_full: + /* Output buffer is full, save cached state and return */ + bd->writePos = pos; + bd->writeCurrent = current; + bd->writeCRC = CRC; + + bd->writeCopies++; + + return 0; +} + +/* Allocate the structure, read file header. If in_fd==-1, inbuf must contain + a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are + ignored, and data is read from file handle into temporary buffer. */ + +/* Because bunzip2 is used for help text unpacking, and because bb_show_usage() + should work for NOFORK applets too, we must be extremely careful to not leak + any allocations! */ +int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd, + const void *inbuf, int len) +{ + bunzip_data *bd; + unsigned i; + enum { + BZh0 = ('B' << 24) + ('Z' << 16) + ('h' << 8) + '0', + h0 = ('h' << 8) + '0', + }; + + /* Figure out how much data to allocate */ + i = sizeof(bunzip_data); + if (in_fd != -1) i += IOBUF_SIZE; + + /* Allocate bunzip_data. Most fields initialize to zero. */ + bd = *bdp = xzalloc(i); + + /* Setup input buffer */ + bd->in_fd = in_fd; + if (-1 == in_fd) { + /* in this case, bd->inbuf is read-only */ + bd->inbuf = (void*)inbuf; /* cast away const-ness */ + } else { + bd->inbuf = (uint8_t*)(bd + 1); + memcpy(bd->inbuf, inbuf, len); + } + bd->inbufCount = len; + + /* Init the CRC32 table (big endian) */ + crc32_filltable(bd->crc32Table, 1); + + /* Setup for I/O error handling via longjmp */ + i = setjmp(bd->jmpbuf); + if (i) return i; + + /* Ensure that file starts with "BZh['1'-'9']." */ + /* Update: now caller verifies 1st two bytes, makes .gz/.bz2 + * integration easier */ + /* was: */ + /* i = get_bits(bd, 32); */ + /* if ((unsigned)(i - BZh0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; */ + i = get_bits(bd, 16); + if ((unsigned)(i - h0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; + + /* Fourth byte (ascii '1'-'9') indicates block size in units of 100k of + uncompressed data. Allocate intermediate buffer for block. */ + /* bd->dbufSize = 100000 * (i - BZh0); */ + bd->dbufSize = 100000 * (i - h0); + + /* Cannot use xmalloc - may leak bd in NOFORK case! */ + bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(bd->dbuf[0])); + if (!bd->dbuf) { + free(bd); + xfunc_die(); + } + return RETVAL_OK; +} + +void FAST_FUNC dealloc_bunzip(bunzip_data *bd) +{ + free(bd->dbuf); + free(bd); +} + + +/* Decompress src_fd to dst_fd. Stops at end of bzip data, not end of file. */ +IF_DESKTOP(long long) int FAST_FUNC +unpack_bz2_stream(int src_fd, int dst_fd) +{ + IF_DESKTOP(long long total_written = 0;) + bunzip_data *bd; + char *outbuf; + int i; + unsigned len; + + outbuf = xmalloc(IOBUF_SIZE); + len = 0; + while (1) { /* "Process one BZ... stream" loop */ + + i = start_bunzip(&bd, src_fd, outbuf + 2, len); + + if (i == 0) { + while (1) { /* "Produce some output bytes" loop */ + i = read_bunzip(bd, outbuf, IOBUF_SIZE); + if (i < 0) /* error? */ + break; + i = IOBUF_SIZE - i; /* number of bytes produced */ + if (i == 0) /* EOF? */ + break; + if (i != full_write(dst_fd, outbuf, i)) { + bb_error_msg("short write"); + i = RETVAL_SHORT_WRITE; + goto release_mem; + } + IF_DESKTOP(total_written += i;) + } + } + + if (i != RETVAL_LAST_BLOCK) { + bb_error_msg("bunzip error %d", i); + break; + } + if (bd->headerCRC != bd->totalCRC) { + bb_error_msg("CRC error"); + break; + } + + /* Successfully unpacked one BZ stream */ + i = RETVAL_OK; + + /* Do we have "BZ..." after last processed byte? + * pbzip2 (parallelized bzip2) produces such files. + */ + len = bd->inbufCount - bd->inbufPos; + memcpy(outbuf, &bd->inbuf[bd->inbufPos], len); + if (len < 2) { + if (safe_read(src_fd, outbuf + len, 2 - len) != 2 - len) + break; + len = 2; + } + if (*(uint16_t*)outbuf != BZIP2_MAGIC) /* "BZ"? */ + break; + dealloc_bunzip(bd); + len -= 2; + } + + release_mem: + dealloc_bunzip(bd); + free(outbuf); + + return i ? i : IF_DESKTOP(total_written) + 0; +} + +IF_DESKTOP(long long) int FAST_FUNC +unpack_bz2_stream_prime(int src_fd, int dst_fd) +{ + uint16_t magic2; + xread(src_fd, &magic2, 2); + if (magic2 != BZIP2_MAGIC) { + bb_error_msg_and_die("invalid magic"); + } + return unpack_bz2_stream(src_fd, dst_fd); +} + +#ifdef TESTING + +static char *const bunzip_errors[] = { + NULL, "Bad file checksum", "Not bzip data", + "Unexpected input EOF", "Unexpected output EOF", "Data error", + "Out of memory", "Obsolete (pre 0.9.5) bzip format not supported" +}; + +/* Dumb little test thing, decompress stdin to stdout */ +int main(int argc, char **argv) +{ + int i; + char c; + + int i = unpack_bz2_stream_prime(0, 1); + if (i < 0) + fprintf(stderr, "%s\n", bunzip_errors[-i]); + else if (read(STDIN_FILENO, &c, 1)) + fprintf(stderr, "Trailing garbage ignored\n"); + return -i; +} +#endif diff --git a/archival/libarchive/decompress_uncompress.c b/archival/libarchive/decompress_uncompress.c new file mode 100644 index 000000000..d1061a2bb --- /dev/null +++ b/archival/libarchive/decompress_uncompress.c @@ -0,0 +1,310 @@ +/* vi: set sw=4 ts=4: */ +/* uncompress for busybox -- (c) 2002 Robert Griebl + * + * based on the original compress42.c source + * (see disclaimer below) + */ + +/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992. + * + * Authors: + * Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * Dave Mack (csu@alembic.acs.com) + * Peter Jannesen, Network Communication Systems + * (peter@ncs.nl) + * + * marc@suse.de : a small security fix for a buffer overflow + * + * [... History snipped ...] + * + */ + +#include "libbb.h" +#include "archive.h" + + +/* Default input buffer size */ +#define IBUFSIZ 2048 + +/* Default output buffer size */ +#define OBUFSIZ 2048 + +/* Defines for third byte of header */ +#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */ + /* Masks 0x20 and 0x40 are free. */ + /* I think 0x20 should mean that there is */ + /* a fourth header byte (for expansion). */ +#define BLOCK_MODE 0x80 /* Block compression if table is full and */ + /* compression rate is dropping flush tables */ + /* the next two codes should not be changed lightly, as they must not */ + /* lie within the contiguous general code space. */ +#define FIRST 257 /* first free entry */ +#define CLEAR 256 /* table clear output code */ + +#define INIT_BITS 9 /* initial number of bits/code */ + + +/* machine variants which require cc -Dmachine: pdp11, z8000, DOS */ +#define HBITS 17 /* 50% occupancy */ +#define HSIZE (1< BITS) { + bb_error_msg("compressed with %d bits, can only handle " + BITS_STR" bits", maxbits); + goto err; + } + + n_bits = INIT_BITS; + maxcode = MAXCODE(INIT_BITS) - 1; + bitmask = (1 << INIT_BITS) - 1; + oldcode = -1; + finchar = 0; + outpos = 0; + posbits = 0 << 3; + + free_ent = ((block_mode) ? FIRST : 256); + + /* As above, initialize the first 256 entries in the table. */ + /*clear_tab_prefixof(); - done by xzalloc */ + + for (code = 255; code >= 0; --code) { + tab_suffixof(code) = (unsigned char) code; + } + + do { + resetbuf: + { + int i; + int e; + int o; + + o = posbits >> 3; + e = insize - o; + + for (i = 0; i < e; ++i) + inbuf[i] = inbuf[i + o]; + + insize = e; + posbits = 0; + } + + if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) { + rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ); + if (rsize < 0) + bb_error_msg_and_die(bb_msg_read_error); + insize += rsize; + } + + inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 : + (insize << 3) - (n_bits - 1)); + + while (inbits > posbits) { + if (free_ent > maxcode) { + posbits = + ((posbits - 1) + + ((n_bits << 3) - + (posbits - 1 + (n_bits << 3)) % (n_bits << 3))); + ++n_bits; + if (n_bits == maxbits) { + maxcode = maxmaxcode; + } else { + maxcode = MAXCODE(n_bits) - 1; + } + bitmask = (1 << n_bits) - 1; + goto resetbuf; + } + { + unsigned char *p = &inbuf[posbits >> 3]; + + code = ((((long) (p[0])) | ((long) (p[1]) << 8) | + ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask; + } + posbits += n_bits; + + + if (oldcode == -1) { + if (code >= 256) + bb_error_msg_and_die("corrupted data"); /* %ld", code); */ + oldcode = code; + finchar = (int) oldcode; + outbuf[outpos++] = (unsigned char) finchar; + continue; + } + + if (code == CLEAR && block_mode) { + clear_tab_prefixof(); + free_ent = FIRST - 1; + posbits = + ((posbits - 1) + + ((n_bits << 3) - + (posbits - 1 + (n_bits << 3)) % (n_bits << 3))); + n_bits = INIT_BITS; + maxcode = MAXCODE(INIT_BITS) - 1; + bitmask = (1 << INIT_BITS) - 1; + goto resetbuf; + } + + incode = code; + stackp = de_stack; + + /* Special case for KwKwK string. */ + if (code >= free_ent) { + if (code > free_ent) { + unsigned char *p; + + posbits -= n_bits; + p = &inbuf[posbits >> 3]; + + bb_error_msg + ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)", + insize, posbits, p[-1], p[0], p[1], p[2], p[3], + (posbits & 07)); + bb_error_msg("corrupted data"); + goto err; + } + + *--stackp = (unsigned char) finchar; + code = oldcode; + } + + /* Generate output characters in reverse order */ + while ((long) code >= (long) 256) { + if (stackp <= &htabof(0)) + bb_error_msg_and_die("corrupted data"); + *--stackp = tab_suffixof(code); + code = tab_prefixof(code); + } + + finchar = tab_suffixof(code); + *--stackp = (unsigned char) finchar; + + /* And put them out in forward order */ + { + int i; + + i = de_stack - stackp; + if (outpos + i >= OBUFSIZ) { + do { + if (i > OBUFSIZ - outpos) { + i = OBUFSIZ - outpos; + } + + if (i > 0) { + memcpy(outbuf + outpos, stackp, i); + outpos += i; + } + + if (outpos >= OBUFSIZ) { + xwrite(fd_out, outbuf, outpos); + IF_DESKTOP(total_written += outpos;) + outpos = 0; + } + stackp += i; + i = de_stack - stackp; + } while (i > 0); + } else { + memcpy(outbuf + outpos, stackp, i); + outpos += i; + } + } + + /* Generate the new entry. */ + code = free_ent; + if (code < maxmaxcode) { + tab_prefixof(code) = (unsigned short) oldcode; + tab_suffixof(code) = (unsigned char) finchar; + free_ent = code + 1; + } + + /* Remember previous code. */ + oldcode = incode; + } + + } while (rsize > 0); + + if (outpos > 0) { + xwrite(fd_out, outbuf, outpos); + IF_DESKTOP(total_written += outpos;) + } + + retval = IF_DESKTOP(total_written) + 0; + err: + free(inbuf); + free(outbuf); + free(htab); + free(codetab); + return retval; +} diff --git a/archival/libarchive/decompress_unlzma.c b/archival/libarchive/decompress_unlzma.c new file mode 100644 index 000000000..a04714341 --- /dev/null +++ b/archival/libarchive/decompress_unlzma.c @@ -0,0 +1,465 @@ +/* vi: set sw=4 ts=4: */ +/* + * Small lzma deflate implementation. + * Copyright (C) 2006 Aurelien Jacobs + * + * Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/) + * Copyright (C) 1999-2005 Igor Pavlov + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "archive.h" + +#if ENABLE_FEATURE_LZMA_FAST +# define speed_inline ALWAYS_INLINE +# define size_inline +#else +# define speed_inline +# define size_inline ALWAYS_INLINE +#endif + + +typedef struct { + int fd; + uint8_t *ptr; + +/* Was keeping rc on stack in unlzma and separately allocating buffer, + * but with "buffer 'attached to' allocated rc" code is smaller: */ + /* uint8_t *buffer; */ +#define RC_BUFFER ((uint8_t*)(rc+1)) + + uint8_t *buffer_end; + +/* Had provisions for variable buffer, but we don't need it here */ + /* int buffer_size; */ +#define RC_BUFFER_SIZE 0x10000 + + uint32_t code; + uint32_t range; + uint32_t bound; +} rc_t; + +#define RC_TOP_BITS 24 +#define RC_MOVE_BITS 5 +#define RC_MODEL_TOTAL_BITS 11 + + +/* Called twice: once at startup (LZMA_FAST only) and once in rc_normalize() */ +static size_inline void rc_read(rc_t *rc) +{ + int buffer_size = safe_read(rc->fd, RC_BUFFER, RC_BUFFER_SIZE); +//TODO: return -1 instead +//This will make unlzma delete broken unpacked file on unpack errors + if (buffer_size <= 0) + bb_error_msg_and_die("unexpected EOF"); + rc->ptr = RC_BUFFER; + rc->buffer_end = RC_BUFFER + buffer_size; +} + +/* Called twice, but one callsite is in speed_inline'd rc_is_bit_1() */ +static void rc_do_normalize(rc_t *rc) +{ + if (rc->ptr >= rc->buffer_end) + rc_read(rc); + rc->range <<= 8; + rc->code = (rc->code << 8) | *rc->ptr++; +} + +/* Called once */ +static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */ +{ + int i; + rc_t *rc; + + rc = xzalloc(sizeof(*rc) + RC_BUFFER_SIZE); + + rc->fd = fd; + /* rc->ptr = rc->buffer_end; */ + + for (i = 0; i < 5; i++) { +#if ENABLE_FEATURE_LZMA_FAST + if (rc->ptr >= rc->buffer_end) + rc_read(rc); + rc->code = (rc->code << 8) | *rc->ptr++; +#else + rc_do_normalize(rc); +#endif + } + rc->range = 0xFFFFFFFF; + return rc; +} + +/* Called once */ +static ALWAYS_INLINE void rc_free(rc_t *rc) +{ + free(rc); +} + +static ALWAYS_INLINE void rc_normalize(rc_t *rc) +{ + if (rc->range < (1 << RC_TOP_BITS)) { + rc_do_normalize(rc); + } +} + +/* rc_is_bit_1 is called 9 times */ +static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p) +{ + rc_normalize(rc); + rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS); + if (rc->code < rc->bound) { + rc->range = rc->bound; + *p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS; + return 0; + } + rc->range -= rc->bound; + rc->code -= rc->bound; + *p -= *p >> RC_MOVE_BITS; + return 1; +} + +/* Called 4 times in unlzma loop */ +static speed_inline int rc_get_bit(rc_t *rc, uint16_t *p, int *symbol) +{ + int ret = rc_is_bit_1(rc, p); + *symbol = *symbol * 2 + ret; + return ret; +} + +/* Called once */ +static ALWAYS_INLINE int rc_direct_bit(rc_t *rc) +{ + rc_normalize(rc); + rc->range >>= 1; + if (rc->code >= rc->range) { + rc->code -= rc->range; + return 1; + } + return 0; +} + +/* Called twice */ +static speed_inline void +rc_bit_tree_decode(rc_t *rc, uint16_t *p, int num_levels, int *symbol) +{ + int i = num_levels; + + *symbol = 1; + while (i--) + rc_get_bit(rc, p + *symbol, symbol); + *symbol -= 1 << num_levels; +} + + +typedef struct { + uint8_t pos; + uint32_t dict_size; + uint64_t dst_size; +} PACKED lzma_header_t; + + +/* #defines will force compiler to compute/optimize each one with each usage. + * Have heart and use enum instead. */ +enum { + LZMA_BASE_SIZE = 1846, + LZMA_LIT_SIZE = 768, + + LZMA_NUM_POS_BITS_MAX = 4, + + LZMA_LEN_NUM_LOW_BITS = 3, + LZMA_LEN_NUM_MID_BITS = 3, + LZMA_LEN_NUM_HIGH_BITS = 8, + + LZMA_LEN_CHOICE = 0, + LZMA_LEN_CHOICE_2 = (LZMA_LEN_CHOICE + 1), + LZMA_LEN_LOW = (LZMA_LEN_CHOICE_2 + 1), + LZMA_LEN_MID = (LZMA_LEN_LOW \ + + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS))), + LZMA_LEN_HIGH = (LZMA_LEN_MID \ + + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS))), + LZMA_NUM_LEN_PROBS = (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS)), + + LZMA_NUM_STATES = 12, + LZMA_NUM_LIT_STATES = 7, + + LZMA_START_POS_MODEL_INDEX = 4, + LZMA_END_POS_MODEL_INDEX = 14, + LZMA_NUM_FULL_DISTANCES = (1 << (LZMA_END_POS_MODEL_INDEX >> 1)), + + LZMA_NUM_POS_SLOT_BITS = 6, + LZMA_NUM_LEN_TO_POS_STATES = 4, + + LZMA_NUM_ALIGN_BITS = 4, + + LZMA_MATCH_MIN_LEN = 2, + + LZMA_IS_MATCH = 0, + LZMA_IS_REP = (LZMA_IS_MATCH + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)), + LZMA_IS_REP_G0 = (LZMA_IS_REP + LZMA_NUM_STATES), + LZMA_IS_REP_G1 = (LZMA_IS_REP_G0 + LZMA_NUM_STATES), + LZMA_IS_REP_G2 = (LZMA_IS_REP_G1 + LZMA_NUM_STATES), + LZMA_IS_REP_0_LONG = (LZMA_IS_REP_G2 + LZMA_NUM_STATES), + LZMA_POS_SLOT = (LZMA_IS_REP_0_LONG \ + + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)), + LZMA_SPEC_POS = (LZMA_POS_SLOT \ + + (LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS)), + LZMA_ALIGN = (LZMA_SPEC_POS \ + + LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX), + LZMA_LEN_CODER = (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS)), + LZMA_REP_LEN_CODER = (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS), + LZMA_LITERAL = (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS), +}; + + +IF_DESKTOP(long long) int FAST_FUNC +unpack_lzma_stream(int src_fd, int dst_fd) +{ + IF_DESKTOP(long long total_written = 0;) + lzma_header_t header; + int lc, pb, lp; + uint32_t pos_state_mask; + uint32_t literal_pos_mask; + uint16_t *p; + int num_bits; + int num_probs; + rc_t *rc; + int i; + uint8_t *buffer; + uint8_t previous_byte = 0; + size_t buffer_pos = 0, global_pos = 0; + int len = 0; + int state = 0; + uint32_t rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + + if (full_read(src_fd, &header, sizeof(header)) != sizeof(header) + || header.pos >= (9 * 5 * 5) + ) { + bb_error_msg("bad lzma header"); + return -1; + } + + i = header.pos / 9; + lc = header.pos % 9; + pb = i / 5; + lp = i % 5; + pos_state_mask = (1 << pb) - 1; + literal_pos_mask = (1 << lp) - 1; + + header.dict_size = SWAP_LE32(header.dict_size); + header.dst_size = SWAP_LE64(header.dst_size); + + if (header.dict_size == 0) + header.dict_size++; + + buffer = xmalloc(MIN(header.dst_size, header.dict_size)); + + num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)); + p = xmalloc(num_probs * sizeof(*p)); + num_probs += LZMA_LITERAL - LZMA_BASE_SIZE; + for (i = 0; i < num_probs; i++) + p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1; + + rc = rc_init(src_fd); /*, RC_BUFFER_SIZE); */ + + while (global_pos + buffer_pos < header.dst_size) { + int pos_state = (buffer_pos + global_pos) & pos_state_mask; + uint16_t *prob = p + LZMA_IS_MATCH + (state << LZMA_NUM_POS_BITS_MAX) + pos_state; + + if (!rc_is_bit_1(rc, prob)) { + static const char next_state[LZMA_NUM_STATES] = + { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 }; + int mi = 1; + + prob = (p + LZMA_LITERAL + + (LZMA_LIT_SIZE * ((((buffer_pos + global_pos) & literal_pos_mask) << lc) + + (previous_byte >> (8 - lc)) + ) + ) + ); + + if (state >= LZMA_NUM_LIT_STATES) { + int match_byte; + uint32_t pos = buffer_pos - rep0; + + while (pos >= header.dict_size) + pos += header.dict_size; + match_byte = buffer[pos]; + do { + int bit; + + match_byte <<= 1; + bit = match_byte & 0x100; + bit ^= (rc_get_bit(rc, prob + 0x100 + bit + mi, &mi) << 8); /* 0x100 or 0 */ + if (bit) + break; + } while (mi < 0x100); + } + while (mi < 0x100) { + rc_get_bit(rc, prob + mi, &mi); + } + + state = next_state[state]; + + previous_byte = (uint8_t) mi; +#if ENABLE_FEATURE_LZMA_FAST + one_byte1: + buffer[buffer_pos++] = previous_byte; + if (buffer_pos == header.dict_size) { + buffer_pos = 0; + global_pos += header.dict_size; + if (full_write(dst_fd, buffer, header.dict_size) != (ssize_t)header.dict_size) + goto bad; + IF_DESKTOP(total_written += header.dict_size;) + } +#else + len = 1; + goto one_byte2; +#endif + } else { + int offset; + uint16_t *prob2; +#define prob_len prob2 + + prob2 = p + LZMA_IS_REP + state; + if (!rc_is_bit_1(rc, prob2)) { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < LZMA_NUM_LIT_STATES ? 0 : 3; + prob2 = p + LZMA_LEN_CODER; + } else { + prob2 += LZMA_IS_REP_G0 - LZMA_IS_REP; + if (!rc_is_bit_1(rc, prob2)) { + prob2 = (p + LZMA_IS_REP_0_LONG + + (state << LZMA_NUM_POS_BITS_MAX) + + pos_state + ); + if (!rc_is_bit_1(rc, prob2)) { +#if ENABLE_FEATURE_LZMA_FAST + uint32_t pos = buffer_pos - rep0; + state = state < LZMA_NUM_LIT_STATES ? 9 : 11; + while (pos >= header.dict_size) + pos += header.dict_size; + previous_byte = buffer[pos]; + goto one_byte1; +#else + state = state < LZMA_NUM_LIT_STATES ? 9 : 11; + len = 1; + goto string; +#endif + } + } else { + uint32_t distance; + + prob2 += LZMA_IS_REP_G1 - LZMA_IS_REP_G0; + distance = rep1; + if (rc_is_bit_1(rc, prob2)) { + prob2 += LZMA_IS_REP_G2 - LZMA_IS_REP_G1; + distance = rep2; + if (rc_is_bit_1(rc, prob2)) { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < LZMA_NUM_LIT_STATES ? 8 : 11; + prob2 = p + LZMA_REP_LEN_CODER; + } + + prob_len = prob2 + LZMA_LEN_CHOICE; + num_bits = LZMA_LEN_NUM_LOW_BITS; + if (!rc_is_bit_1(rc, prob_len)) { + prob_len += LZMA_LEN_LOW - LZMA_LEN_CHOICE + + (pos_state << LZMA_LEN_NUM_LOW_BITS); + offset = 0; + } else { + prob_len += LZMA_LEN_CHOICE_2 - LZMA_LEN_CHOICE; + if (!rc_is_bit_1(rc, prob_len)) { + prob_len += LZMA_LEN_MID - LZMA_LEN_CHOICE_2 + + (pos_state << LZMA_LEN_NUM_MID_BITS); + offset = 1 << LZMA_LEN_NUM_LOW_BITS; + num_bits += LZMA_LEN_NUM_MID_BITS - LZMA_LEN_NUM_LOW_BITS; + } else { + prob_len += LZMA_LEN_HIGH - LZMA_LEN_CHOICE_2; + offset = ((1 << LZMA_LEN_NUM_LOW_BITS) + + (1 << LZMA_LEN_NUM_MID_BITS)); + num_bits += LZMA_LEN_NUM_HIGH_BITS - LZMA_LEN_NUM_LOW_BITS; + } + } + rc_bit_tree_decode(rc, prob_len, num_bits, &len); + len += offset; + + if (state < 4) { + int pos_slot; + uint16_t *prob3; + + state += LZMA_NUM_LIT_STATES; + prob3 = p + LZMA_POS_SLOT + + ((len < LZMA_NUM_LEN_TO_POS_STATES ? len : + LZMA_NUM_LEN_TO_POS_STATES - 1) + << LZMA_NUM_POS_SLOT_BITS); + rc_bit_tree_decode(rc, prob3, + LZMA_NUM_POS_SLOT_BITS, &pos_slot); + rep0 = pos_slot; + if (pos_slot >= LZMA_START_POS_MODEL_INDEX) { + int i2, mi2, num_bits2 = (pos_slot >> 1) - 1; + rep0 = 2 | (pos_slot & 1); + if (pos_slot < LZMA_END_POS_MODEL_INDEX) { + rep0 <<= num_bits2; + prob3 = p + LZMA_SPEC_POS + rep0 - pos_slot - 1; + } else { + for (; num_bits2 != LZMA_NUM_ALIGN_BITS; num_bits2--) + rep0 = (rep0 << 1) | rc_direct_bit(rc); + rep0 <<= LZMA_NUM_ALIGN_BITS; + prob3 = p + LZMA_ALIGN; + } + i2 = 1; + mi2 = 1; + while (num_bits2--) { + if (rc_get_bit(rc, prob3 + mi2, &mi2)) + rep0 |= i2; + i2 <<= 1; + } + } + if (++rep0 == 0) + break; + } + + len += LZMA_MATCH_MIN_LEN; + IF_NOT_FEATURE_LZMA_FAST(string:) + do { + uint32_t pos = buffer_pos - rep0; + while (pos >= header.dict_size) + pos += header.dict_size; + previous_byte = buffer[pos]; + IF_NOT_FEATURE_LZMA_FAST(one_byte2:) + buffer[buffer_pos++] = previous_byte; + if (buffer_pos == header.dict_size) { + buffer_pos = 0; + global_pos += header.dict_size; + if (full_write(dst_fd, buffer, header.dict_size) != (ssize_t)header.dict_size) + goto bad; + IF_DESKTOP(total_written += header.dict_size;) + } + len--; + } while (len != 0 && buffer_pos < header.dst_size); + } + } + + { + IF_NOT_DESKTOP(int total_written = 0; /* success */) + IF_DESKTOP(total_written += buffer_pos;) + if (full_write(dst_fd, buffer, buffer_pos) != (ssize_t)buffer_pos) { + bad: + total_written = -1; /* failure */ + } + rc_free(rc); + free(p); + free(buffer); + return total_written; + } +} diff --git a/archival/libarchive/decompress_unxz.c b/archival/libarchive/decompress_unxz.c new file mode 100644 index 000000000..e90dfb06f --- /dev/null +++ b/archival/libarchive/decompress_unxz.c @@ -0,0 +1,98 @@ +/* + * This file uses XZ Embedded library code which is written + * by Lasse Collin + * and Igor Pavlov + * + * See README file in unxz/ directory for more information. + * + * This file is: + * Copyright (C) 2010 Denys Vlasenko + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "archive.h" + +#define XZ_FUNC FAST_FUNC +#define XZ_EXTERN static + +#define XZ_DEC_DYNALLOC + +/* Skip check (rather than fail) of unsupported hash functions */ +#define XZ_DEC_ANY_CHECK 1 + +/* We use our own crc32 function */ +#define XZ_INTERNAL_CRC32 0 +static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) +{ + return ~crc32_block_endian0(~crc, buf, size, global_crc32_table); +} + +/* We use arch-optimized unaligned accessors */ +#define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); }) +#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); }) +#define put_unaligned_le32(val, buf) move_to_unaligned16(buf, SWAP_LE32(val)) +#define put_unaligned_be32(val, buf) move_to_unaligned16(buf, SWAP_BE32(val)) + +#include "unxz/xz_dec_bcj.c" +#include "unxz/xz_dec_lzma2.c" +#include "unxz/xz_dec_stream.c" + +IF_DESKTOP(long long) int FAST_FUNC +unpack_xz_stream(int src_fd, int dst_fd) +{ + struct xz_buf iobuf; + struct xz_dec *state; + unsigned char *membuf; + IF_DESKTOP(long long) int total = 0; + + if (!global_crc32_table) + global_crc32_table = crc32_filltable(NULL, /*endian:*/ 0); + + memset(&iobuf, 0, sizeof(iobuf)); + /* Preload XZ file signature */ + membuf = (void*) strcpy(xmalloc(2 * BUFSIZ), HEADER_MAGIC); + iobuf.in = membuf; + iobuf.in_size = HEADER_MAGIC_SIZE; + iobuf.out = membuf + BUFSIZ; + iobuf.out_size = BUFSIZ; + + /* Limit memory usage to about 64 MiB. */ + state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024); + + while (1) { + enum xz_ret r; + + if (iobuf.in_pos == iobuf.in_size) { + int rd = safe_read(src_fd, membuf, BUFSIZ); + if (rd < 0) { + bb_error_msg(bb_msg_read_error); + total = -1; + break; + } + iobuf.in_size = rd; + iobuf.in_pos = 0; + } +// bb_error_msg(">in pos:%d size:%d out pos:%d size:%d", +// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size); + r = xz_dec_run(state, &iobuf); +// bb_error_msg(" + * based on gzip sources + * + * Adjusted further by Erik Andersen to support + * files as well as stdin/stdout, and to generally behave itself wrt + * command line handling. + * + * General cleanup to better adhere to the style guide and make use of standard + * busybox functions by Glenn McGrath + * + * read_gz interface + associated hacking by Laurence Anderson + * + * Fixed huft_build() so decoding end-of-block code does not grab more bits + * than necessary (this is required by unzip applet), added inflate_cleanup() + * to free leaked bytebuffer memory (used in unzip.c), and some minor style + * guide cleanups by Ed Clark + * + * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the file algorithm.doc for the compression algorithms and file formats. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include +#include "libbb.h" +#include "archive.h" + +typedef struct huft_t { + unsigned char e; /* number of extra bits or operation */ + unsigned char b; /* number of bits in this code or subcode */ + union { + unsigned short n; /* literal, length base, or distance base */ + struct huft_t *t; /* pointer to next level of table */ + } v; +} huft_t; + +enum { + /* gunzip_window size--must be a power of two, and + * at least 32K for zip's deflate method */ + GUNZIP_WSIZE = 0x8000, + /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ + BMAX = 16, /* maximum bit length of any code (16 for explode) */ + N_MAX = 288, /* maximum number of codes in any set */ +}; + + +/* This is somewhat complex-looking arrangement, but it allows + * to place decompressor state either in bss or in + * malloc'ed space simply by changing #defines below. + * Sizes on i386: + * text data bss dec hex + * 5256 0 108 5364 14f4 - bss + * 4915 0 0 4915 1333 - malloc + */ +#define STATE_IN_BSS 0 +#define STATE_IN_MALLOC 1 + + +typedef struct state_t { + off_t gunzip_bytes_out; /* number of output bytes */ + uint32_t gunzip_crc; + + int gunzip_src_fd; + unsigned gunzip_outbuf_count; /* bytes in output buffer */ + + unsigned char *gunzip_window; + + uint32_t *gunzip_crc_table; + + /* bitbuffer */ + unsigned gunzip_bb; /* bit buffer */ + unsigned char gunzip_bk; /* bits in bit buffer */ + + /* input (compressed) data */ + unsigned char *bytebuffer; /* buffer itself */ + off_t to_read; /* compressed bytes to read (unzip only, -1 for gunzip) */ +// unsigned bytebuffer_max; /* buffer size */ + unsigned bytebuffer_offset; /* buffer position */ + unsigned bytebuffer_size; /* how much data is there (size <= max) */ + + /* private data of inflate_codes() */ + unsigned inflate_codes_ml; /* masks for bl and bd bits */ + unsigned inflate_codes_md; /* masks for bl and bd bits */ + unsigned inflate_codes_bb; /* bit buffer */ + unsigned inflate_codes_k; /* number of bits in bit buffer */ + unsigned inflate_codes_w; /* current gunzip_window position */ + huft_t *inflate_codes_tl; + huft_t *inflate_codes_td; + unsigned inflate_codes_bl; + unsigned inflate_codes_bd; + unsigned inflate_codes_nn; /* length and index for copy */ + unsigned inflate_codes_dd; + + smallint resume_copy; + + /* private data of inflate_get_next_window() */ + smallint method; /* method == -1 for stored, -2 for codes */ + smallint need_another_block; + smallint end_reached; + + /* private data of inflate_stored() */ + unsigned inflate_stored_n; + unsigned inflate_stored_b; + unsigned inflate_stored_k; + unsigned inflate_stored_w; + + const char *error_msg; + jmp_buf error_jmp; +} state_t; +#define gunzip_bytes_out (S()gunzip_bytes_out ) +#define gunzip_crc (S()gunzip_crc ) +#define gunzip_src_fd (S()gunzip_src_fd ) +#define gunzip_outbuf_count (S()gunzip_outbuf_count) +#define gunzip_window (S()gunzip_window ) +#define gunzip_crc_table (S()gunzip_crc_table ) +#define gunzip_bb (S()gunzip_bb ) +#define gunzip_bk (S()gunzip_bk ) +#define to_read (S()to_read ) +// #define bytebuffer_max (S()bytebuffer_max ) +// Both gunzip and unzip can use constant buffer size now (16k): +#define bytebuffer_max 0x4000 +#define bytebuffer (S()bytebuffer ) +#define bytebuffer_offset (S()bytebuffer_offset ) +#define bytebuffer_size (S()bytebuffer_size ) +#define inflate_codes_ml (S()inflate_codes_ml ) +#define inflate_codes_md (S()inflate_codes_md ) +#define inflate_codes_bb (S()inflate_codes_bb ) +#define inflate_codes_k (S()inflate_codes_k ) +#define inflate_codes_w (S()inflate_codes_w ) +#define inflate_codes_tl (S()inflate_codes_tl ) +#define inflate_codes_td (S()inflate_codes_td ) +#define inflate_codes_bl (S()inflate_codes_bl ) +#define inflate_codes_bd (S()inflate_codes_bd ) +#define inflate_codes_nn (S()inflate_codes_nn ) +#define inflate_codes_dd (S()inflate_codes_dd ) +#define resume_copy (S()resume_copy ) +#define method (S()method ) +#define need_another_block (S()need_another_block ) +#define end_reached (S()end_reached ) +#define inflate_stored_n (S()inflate_stored_n ) +#define inflate_stored_b (S()inflate_stored_b ) +#define inflate_stored_k (S()inflate_stored_k ) +#define inflate_stored_w (S()inflate_stored_w ) +#define error_msg (S()error_msg ) +#define error_jmp (S()error_jmp ) + +/* This is a generic part */ +#if STATE_IN_BSS /* Use global data segment */ +#define DECLARE_STATE /*nothing*/ +#define ALLOC_STATE /*nothing*/ +#define DEALLOC_STATE ((void)0) +#define S() state. +#define PASS_STATE /*nothing*/ +#define PASS_STATE_ONLY /*nothing*/ +#define STATE_PARAM /*nothing*/ +#define STATE_PARAM_ONLY void +static state_t state; +#endif + +#if STATE_IN_MALLOC /* Use malloc space */ +#define DECLARE_STATE state_t *state +#define ALLOC_STATE (state = xzalloc(sizeof(*state))) +#define DEALLOC_STATE free(state) +#define S() state-> +#define PASS_STATE state, +#define PASS_STATE_ONLY state +#define STATE_PARAM state_t *state, +#define STATE_PARAM_ONLY state_t *state +#endif + + +static const uint16_t mask_bits[] ALIGN2 = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* Copy lengths for literal codes 257..285 */ +static const uint16_t cplens[] ALIGN2 = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +/* note: see note #13 above about the 258 in this list. */ +/* Extra bits for literal codes 257..285 */ +static const uint8_t cplext[] ALIGN1 = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0, 99, 99 +}; /* 99 == invalid */ + +/* Copy offsets for distance codes 0..29 */ +static const uint16_t cpdist[] ALIGN2 = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* Extra bits for distance codes */ +static const uint8_t cpdext[] ALIGN1 = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +/* Tables for deflate from PKZIP's appnote.txt. */ +/* Order of the bit length code lengths */ +static const uint8_t border[] ALIGN1 = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + + +/* + * Free the malloc'ed tables built by huft_build(), which makes a linked + * list of the tables it made, with the links in a dummy first entry of + * each table. + * t: table to free + */ +static void huft_free(huft_t *p) +{ + huft_t *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + while (p) { + q = (--p)->v.t; + free(p); + p = q; + } +} + +static void huft_free_all(STATE_PARAM_ONLY) +{ + huft_free(inflate_codes_tl); + huft_free(inflate_codes_td); + inflate_codes_tl = NULL; + inflate_codes_td = NULL; +} + +static void abort_unzip(STATE_PARAM_ONLY) NORETURN; +static void abort_unzip(STATE_PARAM_ONLY) +{ + huft_free_all(PASS_STATE_ONLY); + longjmp(error_jmp, 1); +} + +static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current, const unsigned required) +{ + while (*current < required) { + if (bytebuffer_offset >= bytebuffer_size) { + unsigned sz = bytebuffer_max - 4; + if (to_read >= 0 && to_read < sz) /* unzip only */ + sz = to_read; + /* Leave the first 4 bytes empty so we can always unwind the bitbuffer + * to the front of the bytebuffer */ + bytebuffer_size = safe_read(gunzip_src_fd, &bytebuffer[4], sz); + if ((int)bytebuffer_size < 1) { + error_msg = "unexpected end of file"; + abort_unzip(PASS_STATE_ONLY); + } + if (to_read >= 0) /* unzip only */ + to_read -= bytebuffer_size; + bytebuffer_size += 4; + bytebuffer_offset = 4; + } + bitbuffer |= ((unsigned) bytebuffer[bytebuffer_offset]) << *current; + bytebuffer_offset++; + *current += 8; + } + return bitbuffer; +} + + +/* Given a list of code lengths and a maximum table size, make a set of + * tables to decode that set of codes. Return zero on success, one if + * the given code set is incomplete (the tables are still built in this + * case), two if the input is invalid (all zero length codes or an + * oversubscribed set of lengths) - in this case stores NULL in *t. + * + * b: code lengths in bits (all assumed <= BMAX) + * n: number of codes (assumed <= N_MAX) + * s: number of simple-valued codes (0..s-1) + * d: list of base values for non-simple codes + * e: list of extra bits for non-simple codes + * t: result: starting table + * m: maximum lookup bits, returns actual + */ +static int huft_build(const unsigned *b, const unsigned n, + const unsigned s, const unsigned short *d, + const unsigned char *e, huft_t **t, unsigned *m) +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX + 1]; /* bit length count table */ + unsigned eob_len; /* length of end-of-block code (value 256) */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int htl; /* table level */ + unsigned i; /* counter, current code */ + unsigned j; /* counter */ + int k; /* number of bits in current code */ + unsigned *p; /* pointer into c[], b[], or v[] */ + huft_t *q; /* points to current table */ + huft_t r; /* table entry for structure assignment */ + huft_t *u[BMAX]; /* table stack */ + unsigned v[N_MAX]; /* values in order of bit length */ + int ws[BMAX + 1]; /* bits decoded stack */ + int w; /* bits decoded */ + unsigned x[BMAX + 1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + + /* Length of EOB code, if any */ + eob_len = n > 256 ? b[256] : BMAX; + + *t = NULL; + + /* Generate counts for each bit length */ + memset(c, 0, sizeof(c)); + p = (unsigned *) b; /* cast allows us to reuse p for pointing to b */ + i = n; + do { + c[*p]++; /* assume all entries <= BMAX */ + p++; /* can't combine with above line (Solaris bug) */ + } while (--i); + if (c[0] == n) { /* null input - all zero length codes */ + *m = 0; + return 2; + } + + /* Find minimum and maximum length, bound *m by those */ + for (j = 1; (c[j] == 0) && (j <= BMAX); j++) + continue; + k = j; /* minimum code length */ + for (i = BMAX; (c[i] == 0) && i; i--) + continue; + g = i; /* maximum code length */ + *m = (*m < j) ? j : ((*m > i) ? i : *m); + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) { + y -= c[j]; + if (y < 0) + return 2; /* bad input: more codes than bits */ + } + y -= c[i]; + if (y < 0) + return 2; + c[i] += y; + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; + xp = x + 2; + while (--i) { /* note that i == g from above */ + j += *p++; + *xp++ = j; + } + + /* Make a table of values in order of bit lengths */ + p = (unsigned *) b; + i = 0; + do { + j = *p++; + if (j != 0) { + v[x[j]++] = i; + } + } while (++i < n); + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + htl = -1; /* no tables yet--level -1 */ + w = ws[0] = 0; /* bits decoded */ + u[0] = NULL; /* just to keep compilers happy */ + q = NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) { + a = c[k]; + while (a--) { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > ws[htl + 1]) { + w = ws[++htl]; + + /* compute minimum size table less than or equal to *m bits */ + z = g - w; + z = z > *m ? *m : z; /* upper limit on table size */ + j = k - w; + f = 1 << j; + if (f > a + 1) { /* try a k-w bit table */ + /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) { /* try smaller tables up to z bits */ + f <<= 1; + if (f <= *++xp) { + break; /* enough codes to use up j bits */ + } + f -= *xp; /* else deduct codes from patterns */ + } + } + j = (w + j > eob_len && w < eob_len) ? eob_len - w : j; /* make EOB code end at table */ + z = 1 << j; /* table entries for j-bit table */ + ws[htl+1] = w + j; /* set bits decoded in stack */ + + /* allocate and link in new table */ + q = xzalloc((z + 1) * sizeof(huft_t)); + *t = q + 1; /* link to list for huft_free() */ + t = &(q->v.t); + u[htl] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (htl) { + x[htl] = i; /* save pattern for backing up */ + r.b = (unsigned char) (w - ws[htl - 1]); /* bits to dump before this table */ + r.e = (unsigned char) (16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = (i & ((1 << w) - 1)) >> ws[htl - 1]; + u[htl - 1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (unsigned char) (k - w); + if (p >= v + n) { + r.e = 99; /* out of values--invalid code */ + } else if (*p < s) { + r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB code */ + r.v.n = (unsigned short) (*p++); /* simple code is just the value */ + } else { + r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) { + q[j] = r; + } + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) { + i ^= j; + } + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[htl]) { + w = ws[--htl]; + } + } + } + + /* return actual size of base table */ + *m = ws[1]; + + /* Return 1 if we were given an incomplete table */ + return y != 0 && g != 1; +} + + +/* + * inflate (decompress) the codes in a deflated (compressed) block. + * Return an error code or zero if it all goes ok. + * + * tl, td: literal/length and distance decoder tables + * bl, bd: number of bits decoded by tl[] and td[] + */ +/* called once from inflate_block */ + +/* map formerly local static variables to globals */ +#define ml inflate_codes_ml +#define md inflate_codes_md +#define bb inflate_codes_bb +#define k inflate_codes_k +#define w inflate_codes_w +#define tl inflate_codes_tl +#define td inflate_codes_td +#define bl inflate_codes_bl +#define bd inflate_codes_bd +#define nn inflate_codes_nn +#define dd inflate_codes_dd +static void inflate_codes_setup(STATE_PARAM unsigned my_bl, unsigned my_bd) +{ + bl = my_bl; + bd = my_bd; + /* make local copies of globals */ + bb = gunzip_bb; /* initialize bit buffer */ + k = gunzip_bk; + w = gunzip_outbuf_count; /* initialize gunzip_window position */ + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; +} +/* called once from inflate_get_next_window */ +static NOINLINE int inflate_codes(STATE_PARAM_ONLY) +{ + unsigned e; /* table entry flag/number of extra bits */ + huft_t *t; /* pointer to table entry */ + + if (resume_copy) + goto do_copy; + + while (1) { /* do until end of block */ + bb = fill_bitbuffer(PASS_STATE bb, &k, bl); + t = tl + ((unsigned) bb & ml); + e = t->e; + if (e > 16) + do { + if (e == 99) + abort_unzip(PASS_STATE_ONLY);; + bb >>= t->b; + k -= t->b; + e -= 16; + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + t = t->v.t + ((unsigned) bb & mask_bits[e]); + e = t->e; + } while (e > 16); + bb >>= t->b; + k -= t->b; + if (e == 16) { /* then it's a literal */ + gunzip_window[w++] = (unsigned char) t->v.n; + if (w == GUNZIP_WSIZE) { + gunzip_outbuf_count = w; + //flush_gunzip_window(); + w = 0; + return 1; // We have a block to read + } + } else { /* it's an EOB or a length */ + /* exit if end of block */ + if (e == 15) { + break; + } + + /* get length of block to copy */ + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + nn = t->v.n + ((unsigned) bb & mask_bits[e]); + bb >>= e; + k -= e; + + /* decode distance of block to copy */ + bb = fill_bitbuffer(PASS_STATE bb, &k, bd); + t = td + ((unsigned) bb & md); + e = t->e; + if (e > 16) + do { + if (e == 99) + abort_unzip(PASS_STATE_ONLY); + bb >>= t->b; + k -= t->b; + e -= 16; + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + t = t->v.t + ((unsigned) bb & mask_bits[e]); + e = t->e; + } while (e > 16); + bb >>= t->b; + k -= t->b; + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + dd = w - t->v.n - ((unsigned) bb & mask_bits[e]); + bb >>= e; + k -= e; + + /* do the copy */ + do_copy: + do { + /* Was: nn -= (e = (e = GUNZIP_WSIZE - ((dd &= GUNZIP_WSIZE - 1) > w ? dd : w)) > nn ? nn : e); */ + /* Who wrote THAT?? rewritten as: */ + unsigned delta; + + dd &= GUNZIP_WSIZE - 1; + e = GUNZIP_WSIZE - (dd > w ? dd : w); + delta = w > dd ? w - dd : dd - w; + if (e > nn) e = nn; + nn -= e; + + /* copy to new buffer to prevent possible overwrite */ + if (delta >= e) { + memcpy(gunzip_window + w, gunzip_window + dd, e); + w += e; + dd += e; + } else { + /* do it slow to avoid memcpy() overlap */ + /* !NOMEMCPY */ + do { + gunzip_window[w++] = gunzip_window[dd++]; + } while (--e); + } + if (w == GUNZIP_WSIZE) { + gunzip_outbuf_count = w; + resume_copy = (nn != 0); + //flush_gunzip_window(); + w = 0; + return 1; + } + } while (nn); + resume_copy = 0; + } + } + + /* restore the globals from the locals */ + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = bb; /* restore global bit buffer */ + gunzip_bk = k; + + /* normally just after call to inflate_codes, but save code by putting it here */ + /* free the decoding tables (tl and td), return */ + huft_free_all(PASS_STATE_ONLY); + + /* done */ + return 0; +} +#undef ml +#undef md +#undef bb +#undef k +#undef w +#undef tl +#undef td +#undef bl +#undef bd +#undef nn +#undef dd + + +/* called once from inflate_block */ +static void inflate_stored_setup(STATE_PARAM int my_n, int my_b, int my_k) +{ + inflate_stored_n = my_n; + inflate_stored_b = my_b; + inflate_stored_k = my_k; + /* initialize gunzip_window position */ + inflate_stored_w = gunzip_outbuf_count; +} +/* called once from inflate_get_next_window */ +static int inflate_stored(STATE_PARAM_ONLY) +{ + /* read and output the compressed data */ + while (inflate_stored_n--) { + inflate_stored_b = fill_bitbuffer(PASS_STATE inflate_stored_b, &inflate_stored_k, 8); + gunzip_window[inflate_stored_w++] = (unsigned char) inflate_stored_b; + if (inflate_stored_w == GUNZIP_WSIZE) { + gunzip_outbuf_count = inflate_stored_w; + //flush_gunzip_window(); + inflate_stored_w = 0; + inflate_stored_b >>= 8; + inflate_stored_k -= 8; + return 1; /* We have a block */ + } + inflate_stored_b >>= 8; + inflate_stored_k -= 8; + } + + /* restore the globals from the locals */ + gunzip_outbuf_count = inflate_stored_w; /* restore global gunzip_window pointer */ + gunzip_bb = inflate_stored_b; /* restore global bit buffer */ + gunzip_bk = inflate_stored_k; + return 0; /* Finished */ +} + + +/* + * decompress an inflated block + * e: last block flag + * + * GLOBAL VARIABLES: bb, kk, + */ +/* Return values: -1 = inflate_stored, -2 = inflate_codes */ +/* One callsite in inflate_get_next_window */ +static int inflate_block(STATE_PARAM smallint *e) +{ + unsigned ll[286 + 30]; /* literal/length and distance code lengths */ + unsigned t; /* block type */ + unsigned b; /* bit buffer */ + unsigned k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + + b = gunzip_bb; + k = gunzip_bk; + + /* read in last block bit */ + b = fill_bitbuffer(PASS_STATE b, &k, 1); + *e = b & 1; + b >>= 1; + k -= 1; + + /* read in block type */ + b = fill_bitbuffer(PASS_STATE b, &k, 2); + t = (unsigned) b & 3; + b >>= 2; + k -= 2; + + /* restore the global bit buffer */ + gunzip_bb = b; + gunzip_bk = k; + + /* Do we see block type 1 often? Yes! + * TODO: fix performance problem (see below) */ + //bb_error_msg("blktype %d", t); + + /* inflate that block type */ + switch (t) { + case 0: /* Inflate stored */ + { + unsigned n; /* number of bytes in block */ + unsigned b_stored; /* bit buffer */ + unsigned k_stored; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b_stored = gunzip_bb; /* initialize bit buffer */ + k_stored = gunzip_bk; + + /* go to byte boundary */ + n = k_stored & 7; + b_stored >>= n; + k_stored -= n; + + /* get the length and its complement */ + b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16); + n = ((unsigned) b_stored & 0xffff); + b_stored >>= 16; + k_stored -= 16; + + b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16); + if (n != (unsigned) ((~b_stored) & 0xffff)) { + abort_unzip(PASS_STATE_ONLY); /* error in compressed data */ + } + b_stored >>= 16; + k_stored -= 16; + + inflate_stored_setup(PASS_STATE n, b_stored, k_stored); + + return -1; + } + case 1: + /* Inflate fixed + * decompress an inflated type 1 (fixed Huffman codes) block. We should + * either replace this with a custom decoder, or at least precompute the + * Huffman tables. TODO */ + { + int i; /* temporary variable */ + unsigned bl; /* lookup bits for tl */ + unsigned bd; /* lookup bits for td */ + /* gcc 4.2.1 is too dumb to reuse stackspace. Moved up... */ + //unsigned ll[288]; /* length list for huft_build */ + + /* set up literal table */ + for (i = 0; i < 144; i++) + ll[i] = 8; + for (; i < 256; i++) + ll[i] = 9; + for (; i < 280; i++) + ll[i] = 7; + for (; i < 288; i++) /* make a complete, but wrong code set */ + ll[i] = 8; + bl = 7; + huft_build(ll, 288, 257, cplens, cplext, &inflate_codes_tl, &bl); + /* huft_build() never return nonzero - we use known data */ + + /* set up distance table */ + for (i = 0; i < 30; i++) /* make an incomplete code set */ + ll[i] = 5; + bd = 5; + huft_build(ll, 30, 0, cpdist, cpdext, &inflate_codes_td, &bd); + + /* set up data for inflate_codes() */ + inflate_codes_setup(PASS_STATE bl, bd); + + /* huft_free code moved into inflate_codes */ + + return -2; + } + case 2: /* Inflate dynamic */ + { + enum { dbits = 6 }; /* bits in base distance lookup table */ + enum { lbits = 9 }; /* bits in base literal/length lookup table */ + + huft_t *td; /* distance code table */ + unsigned i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + unsigned bl; /* lookup bits for tl */ + unsigned bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ + + //unsigned ll[286 + 30];/* literal/length and distance code lengths */ + unsigned b_dynamic; /* bit buffer */ + unsigned k_dynamic; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b_dynamic = gunzip_bb; + k_dynamic = gunzip_bk; + + /* read in table lengths */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5); + nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + + b_dynamic >>= 5; + k_dynamic -= 5; + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5); + nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + + b_dynamic >>= 5; + k_dynamic -= 5; + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 4); + nb = 4 + ((unsigned) b_dynamic & 0xf); /* number of bit length codes */ + + b_dynamic >>= 4; + k_dynamic -= 4; + if (nl > 286 || nd > 30) + abort_unzip(PASS_STATE_ONLY); /* bad lengths */ + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) { + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3); + ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic >>= 3; + k_dynamic -= 3; + } + for (; j < 19; j++) + ll[border[j]] = 0; + + /* build decoding table for trees - single level, 7 bit lookup */ + bl = 7; + i = huft_build(ll, 19, 19, NULL, NULL, &inflate_codes_tl, &bl); + if (i != 0) { + abort_unzip(PASS_STATE_ONLY); //return i; /* incomplete code set */ + } + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned) i < n) { + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, (unsigned)bl); + td = inflate_codes_tl + ((unsigned) b_dynamic & m); + j = td->b; + b_dynamic >>= j; + k_dynamic -= j; + j = td->v.n; + if (j < 16) { /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + } else if (j == 16) { /* repeat last length 3 to 6 times */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 2); + j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic >>= 2; + k_dynamic -= 2; + if ((unsigned) i + j > n) { + abort_unzip(PASS_STATE_ONLY); //return 1; + } + while (j--) { + ll[i++] = l; + } + } else if (j == 17) { /* 3 to 10 zero length codes */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3); + j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic >>= 3; + k_dynamic -= 3; + if ((unsigned) i + j > n) { + abort_unzip(PASS_STATE_ONLY); //return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } else { /* j == 18: 11 to 138 zero length codes */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 7); + j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic >>= 7; + k_dynamic -= 7; + if ((unsigned) i + j > n) { + abort_unzip(PASS_STATE_ONLY); //return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } + } + + /* free decoding table for trees */ + huft_free(inflate_codes_tl); + + /* restore the global bit buffer */ + gunzip_bb = b_dynamic; + gunzip_bk = k_dynamic; + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + + i = huft_build(ll, nl, 257, cplens, cplext, &inflate_codes_tl, &bl); + if (i != 0) + abort_unzip(PASS_STATE_ONLY); + bd = dbits; + i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &inflate_codes_td, &bd); + if (i != 0) + abort_unzip(PASS_STATE_ONLY); + + /* set up data for inflate_codes() */ + inflate_codes_setup(PASS_STATE bl, bd); + + /* huft_free code moved into inflate_codes */ + + return -2; + } + default: + abort_unzip(PASS_STATE_ONLY); + } +} + +/* Two callsites, both in inflate_get_next_window */ +static void calculate_gunzip_crc(STATE_PARAM_ONLY) +{ + gunzip_crc = crc32_block_endian0(gunzip_crc, gunzip_window, gunzip_outbuf_count, gunzip_crc_table); + gunzip_bytes_out += gunzip_outbuf_count; +} + +/* One callsite in inflate_unzip_internal */ +static int inflate_get_next_window(STATE_PARAM_ONLY) +{ + gunzip_outbuf_count = 0; + + while (1) { + int ret; + + if (need_another_block) { + if (end_reached) { + calculate_gunzip_crc(PASS_STATE_ONLY); + end_reached = 0; + /* NB: need_another_block is still set */ + return 0; /* Last block */ + } + method = inflate_block(PASS_STATE &end_reached); + need_another_block = 0; + } + + switch (method) { + case -1: + ret = inflate_stored(PASS_STATE_ONLY); + break; + case -2: + ret = inflate_codes(PASS_STATE_ONLY); + break; + default: /* cannot happen */ + abort_unzip(PASS_STATE_ONLY); + } + + if (ret == 1) { + calculate_gunzip_crc(PASS_STATE_ONLY); + return 1; /* more data left */ + } + need_another_block = 1; /* end of that block */ + } + /* Doesnt get here */ +} + + +/* Called from unpack_gz_stream() and inflate_unzip() */ +static IF_DESKTOP(long long) int +inflate_unzip_internal(STATE_PARAM int in, int out) +{ + IF_DESKTOP(long long) int n = 0; + ssize_t nwrote; + + /* Allocate all global buffers (for DYN_ALLOC option) */ + gunzip_window = xmalloc(GUNZIP_WSIZE); + gunzip_outbuf_count = 0; + gunzip_bytes_out = 0; + gunzip_src_fd = in; + + /* (re) initialize state */ + method = -1; + need_another_block = 1; + resume_copy = 0; + gunzip_bk = 0; + gunzip_bb = 0; + + /* Create the crc table */ + gunzip_crc_table = crc32_filltable(NULL, 0); + gunzip_crc = ~0; + + error_msg = "corrupted data"; + if (setjmp(error_jmp)) { + /* Error from deep inside zip machinery */ + n = -1; + goto ret; + } + + while (1) { + int r = inflate_get_next_window(PASS_STATE_ONLY); + nwrote = full_write(out, gunzip_window, gunzip_outbuf_count); + if (nwrote != (ssize_t)gunzip_outbuf_count) { + bb_perror_msg("write"); + n = -1; + goto ret; + } + IF_DESKTOP(n += nwrote;) + if (r == 0) break; + } + + /* Store unused bytes in a global buffer so calling applets can access it */ + if (gunzip_bk >= 8) { + /* Undo too much lookahead. The next read will be byte aligned + * so we can discard unused bits in the last meaningful byte. */ + bytebuffer_offset--; + bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff; + gunzip_bb >>= 8; + gunzip_bk -= 8; + } + ret: + /* Cleanup */ + free(gunzip_window); + free(gunzip_crc_table); + return n; +} + + +/* External entry points */ + +/* For unzip */ + +IF_DESKTOP(long long) int FAST_FUNC +inflate_unzip(inflate_unzip_result *res, off_t compr_size, int in, int out) +{ + IF_DESKTOP(long long) int n; + DECLARE_STATE; + + ALLOC_STATE; + + to_read = compr_size; +// bytebuffer_max = 0x8000; + bytebuffer_offset = 4; + bytebuffer = xmalloc(bytebuffer_max); + n = inflate_unzip_internal(PASS_STATE in, out); + free(bytebuffer); + + res->crc = gunzip_crc; + res->bytes_out = gunzip_bytes_out; + DEALLOC_STATE; + return n; +} + + +/* For gunzip */ + +/* helpers first */ + +/* Top up the input buffer with at least n bytes. */ +static int top_up(STATE_PARAM unsigned n) +{ + int count = bytebuffer_size - bytebuffer_offset; + + if (count < (int)n) { + memmove(bytebuffer, &bytebuffer[bytebuffer_offset], count); + bytebuffer_offset = 0; + bytebuffer_size = full_read(gunzip_src_fd, &bytebuffer[count], bytebuffer_max - count); + if ((int)bytebuffer_size < 0) { + bb_error_msg(bb_msg_read_error); + return 0; + } + bytebuffer_size += count; + if (bytebuffer_size < n) + return 0; + } + return 1; +} + +static uint16_t buffer_read_le_u16(STATE_PARAM_ONLY) +{ + uint16_t res; +#if BB_LITTLE_ENDIAN + move_from_unaligned16(res, &bytebuffer[bytebuffer_offset]); +#else + res = bytebuffer[bytebuffer_offset]; + res |= bytebuffer[bytebuffer_offset + 1] << 8; +#endif + bytebuffer_offset += 2; + return res; +} + +static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY) +{ + uint32_t res; +#if BB_LITTLE_ENDIAN + move_from_unaligned32(res, &bytebuffer[bytebuffer_offset]); +#else + res = bytebuffer[bytebuffer_offset]; + res |= bytebuffer[bytebuffer_offset + 1] << 8; + res |= bytebuffer[bytebuffer_offset + 2] << 16; + res |= bytebuffer[bytebuffer_offset + 3] << 24; +#endif + bytebuffer_offset += 4; + return res; +} + +static int check_header_gzip(STATE_PARAM unpack_info_t *info) +{ + union { + unsigned char raw[8]; + struct { + uint8_t gz_method; + uint8_t flags; + uint32_t mtime; + uint8_t xtra_flags_UNUSED; + uint8_t os_flags_UNUSED; + } PACKED formatted; + } header; + struct BUG_header { + char BUG_header[sizeof(header) == 8 ? 1 : -1]; + }; + + /* + * Rewind bytebuffer. We use the beginning because the header has 8 + * bytes, leaving enough for unwinding afterwards. + */ + bytebuffer_size -= bytebuffer_offset; + memmove(bytebuffer, &bytebuffer[bytebuffer_offset], bytebuffer_size); + bytebuffer_offset = 0; + + if (!top_up(PASS_STATE 8)) + return 0; + memcpy(header.raw, &bytebuffer[bytebuffer_offset], 8); + bytebuffer_offset += 8; + + /* Check the compression method */ + if (header.formatted.gz_method != 8) { + return 0; + } + + if (header.formatted.flags & 0x04) { + /* bit 2 set: extra field present */ + unsigned extra_short; + + if (!top_up(PASS_STATE 2)) + return 0; + extra_short = buffer_read_le_u16(PASS_STATE_ONLY); + if (!top_up(PASS_STATE extra_short)) + return 0; + /* Ignore extra field */ + bytebuffer_offset += extra_short; + } + + /* Discard original name and file comment if any */ + /* bit 3 set: original file name present */ + /* bit 4 set: file comment present */ + if (header.formatted.flags & 0x18) { + while (1) { + do { + if (!top_up(PASS_STATE 1)) + return 0; + } while (bytebuffer[bytebuffer_offset++] != 0); + if ((header.formatted.flags & 0x18) != 0x18) + break; + header.formatted.flags &= ~0x18; + } + } + + if (info) + info->mtime = SWAP_LE32(header.formatted.mtime); + + /* Read the header checksum */ + if (header.formatted.flags & 0x02) { + if (!top_up(PASS_STATE 2)) + return 0; + bytebuffer_offset += 2; + } + return 1; +} + +IF_DESKTOP(long long) int FAST_FUNC +unpack_gz_stream_with_info(int in, int out, unpack_info_t *info) +{ + uint32_t v32; + IF_DESKTOP(long long) int n; + DECLARE_STATE; + + n = 0; + + ALLOC_STATE; + to_read = -1; +// bytebuffer_max = 0x8000; + bytebuffer = xmalloc(bytebuffer_max); + gunzip_src_fd = in; + + again: + if (!check_header_gzip(PASS_STATE info)) { + bb_error_msg("corrupted data"); + n = -1; + goto ret; + } + n += inflate_unzip_internal(PASS_STATE in, out); + if (n < 0) + goto ret; + + if (!top_up(PASS_STATE 8)) { + bb_error_msg("corrupted data"); + n = -1; + goto ret; + } + + /* Validate decompression - crc */ + v32 = buffer_read_le_u32(PASS_STATE_ONLY); + if ((~gunzip_crc) != v32) { + bb_error_msg("crc error"); + n = -1; + goto ret; + } + + /* Validate decompression - size */ + v32 = buffer_read_le_u32(PASS_STATE_ONLY); + if ((uint32_t)gunzip_bytes_out != v32) { + bb_error_msg("incorrect length"); + n = -1; + } + + if (!top_up(PASS_STATE 2)) + goto ret; /* EOF */ + + if (bytebuffer[bytebuffer_offset] == 0x1f + && bytebuffer[bytebuffer_offset + 1] == 0x8b + ) { + bytebuffer_offset += 2; + goto again; + } + /* GNU gzip says: */ + /*bb_error_msg("decompression OK, trailing garbage ignored");*/ + + ret: + free(bytebuffer); + DEALLOC_STATE; + return n; +} + +IF_DESKTOP(long long) int FAST_FUNC +unpack_gz_stream(int in, int out) +{ + return unpack_gz_stream_with_info(in, out, NULL); +} diff --git a/archival/libarchive/filter_accept_all.c b/archival/libarchive/filter_accept_all.c new file mode 100644 index 000000000..e69deb679 --- /dev/null +++ b/archival/libarchive/filter_accept_all.c @@ -0,0 +1,17 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +/* Accept any non-null name, its not really a filter at all */ +char FAST_FUNC filter_accept_all(archive_handle_t *archive_handle) +{ + if (archive_handle->file_header->name) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} diff --git a/archival/libarchive/filter_accept_list.c b/archival/libarchive/filter_accept_list.c new file mode 100644 index 000000000..a7640af79 --- /dev/null +++ b/archival/libarchive/filter_accept_list.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +/* + * Accept names that are in the accept list, ignoring reject list. + */ +char FAST_FUNC filter_accept_list(archive_handle_t *archive_handle) +{ + if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} diff --git a/archival/libarchive/filter_accept_list_reassign.c b/archival/libarchive/filter_accept_list_reassign.c new file mode 100644 index 000000000..d80f71668 --- /dev/null +++ b/archival/libarchive/filter_accept_list_reassign.c @@ -0,0 +1,51 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +/* Built and used only if ENABLE_DPKG || ENABLE_DPKG_DEB */ + +/* + * Reassign the subarchive metadata parser based on the filename extension + * e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz + * or if its a .tar.bz2 make archive_handle->sub_archive handle that + */ +char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle) +{ + /* Check the file entry is in the accept list */ + if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) { + const char *name_ptr; + + /* Find extension */ + name_ptr = strrchr(archive_handle->file_header->name, '.'); + if (!name_ptr) + return EXIT_FAILURE; + name_ptr++; + + /* Modify the subarchive handler based on the extension */ + if (ENABLE_FEATURE_SEAMLESS_GZ + && strcmp(name_ptr, "gz") == 0 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_gz; + return EXIT_SUCCESS; + } + if (ENABLE_FEATURE_SEAMLESS_BZ2 + && strcmp(name_ptr, "bz2") == 0 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_bz2; + return EXIT_SUCCESS; + } + if (ENABLE_FEATURE_SEAMLESS_LZMA + && strcmp(name_ptr, "lzma") == 0 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_lzma; + return EXIT_SUCCESS; + } + } + return EXIT_FAILURE; +} diff --git a/archival/libarchive/filter_accept_reject_list.c b/archival/libarchive/filter_accept_reject_list.c new file mode 100644 index 000000000..3e86cca65 --- /dev/null +++ b/archival/libarchive/filter_accept_reject_list.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +/* + * Accept names that are in the accept list and not in the reject list + */ +char FAST_FUNC filter_accept_reject_list(archive_handle_t *archive_handle) +{ + const char *key; + const llist_t *reject_entry; + const llist_t *accept_entry; + + key = archive_handle->file_header->name; + + /* If the key is in a reject list fail */ + reject_entry = find_list_entry2(archive_handle->reject, key); + if (reject_entry) { + return EXIT_FAILURE; + } + accept_entry = find_list_entry2(archive_handle->accept, key); + + /* Fail if an accept list was specified and the key wasnt in there */ + if ((accept_entry == NULL) && archive_handle->accept) { + return EXIT_FAILURE; + } + + /* Accepted */ + return EXIT_SUCCESS; +} diff --git a/archival/libarchive/find_list_entry.c b/archival/libarchive/find_list_entry.c new file mode 100644 index 000000000..5efd1af2e --- /dev/null +++ b/archival/libarchive/find_list_entry.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include +#include "libbb.h" +#include "archive.h" + +/* Find a string in a shell pattern list */ +const llist_t* FAST_FUNC find_list_entry(const llist_t *list, const char *filename) +{ + while (list) { + if (fnmatch(list->data, filename, 0) == 0) { + return list; + } + list = list->link; + } + return NULL; +} + +/* Same, but compares only path components present in pattern + * (extra trailing path components in filename are assumed to match) + */ +const llist_t* FAST_FUNC find_list_entry2(const llist_t *list, const char *filename) +{ + char buf[PATH_MAX]; + int pattern_slash_cnt; + const char *c; + char *d; + + while (list) { + c = list->data; + pattern_slash_cnt = 0; + while (*c) + if (*c++ == '/') pattern_slash_cnt++; + c = filename; + d = buf; + /* paranoia is better than buffer overflows */ + while (*c && d != buf + sizeof(buf)-1) { + if (*c == '/' && --pattern_slash_cnt < 0) + break; + *d++ = *c++; + } + *d = '\0'; + if (fnmatch(list->data, buf, 0) == 0) { + return list; + } + list = list->link; + } + return NULL; +} diff --git a/archival/libarchive/get_header_ar.c b/archival/libarchive/get_header_ar.c new file mode 100644 index 000000000..df603b111 --- /dev/null +++ b/archival/libarchive/get_header_ar.c @@ -0,0 +1,133 @@ +/* vi: set sw=4 ts=4: */ +/* Copyright 2001 Glenn McGrath. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" +#include "ar.h" + +static unsigned read_num(const char *str, int base) +{ + /* This code works because + * on misformatted numbers bb_strtou returns all-ones */ + int err = bb_strtou(str, NULL, base); + if (err == -1) + bb_error_msg_and_die("invalid ar header"); + return err; +} + +char FAST_FUNC get_header_ar(archive_handle_t *archive_handle) +{ + file_header_t *typed = archive_handle->file_header; + unsigned size; + union { + char raw[60]; + struct ar_header formatted; + } ar; +#if ENABLE_FEATURE_AR_LONG_FILENAMES + static char *ar_long_names; + static unsigned ar_long_name_size; +#endif + + /* dont use xread as we want to handle the error ourself */ + if (read(archive_handle->src_fd, ar.raw, 60) != 60) { + /* End Of File */ + return EXIT_FAILURE; + } + + /* ar header starts on an even byte (2 byte aligned) + * '\n' is used for padding + */ + if (ar.raw[0] == '\n') { + /* fix up the header, we started reading 1 byte too early */ + memmove(ar.raw, &ar.raw[1], 59); + ar.raw[59] = xread_char(archive_handle->src_fd); + archive_handle->offset++; + } + archive_handle->offset += 60; + + if (ar.formatted.magic[0] != '`' || ar.formatted.magic[1] != '\n') + bb_error_msg_and_die("invalid ar header"); + + /* FIXME: more thorough routine would be in order here + * (we have something like that in tar) + * but for now we are lax. */ + ar.formatted.magic[0] = '\0'; /* else 4G-2 file will have size="4294967294`\n..." */ + typed->size = size = read_num(ar.formatted.size, 10); + + /* special filenames have '/' as the first character */ + if (ar.formatted.name[0] == '/') { + if (ar.formatted.name[1] == ' ') { + /* This is the index of symbols in the file for compilers */ + data_skip(archive_handle); + archive_handle->offset += size; + return get_header_ar(archive_handle); /* Return next header */ + } +#if ENABLE_FEATURE_AR_LONG_FILENAMES + if (ar.formatted.name[1] == '/') { + /* If the second char is a '/' then this entries data section + * stores long filename for multiple entries, they are stored + * in static variable long_names for use in future entries + */ + ar_long_name_size = size; + free(ar_long_names); + ar_long_names = xmalloc(size); + xread(archive_handle->src_fd, ar_long_names, size); + archive_handle->offset += size; + /* Return next header */ + return get_header_ar(archive_handle); + } +#else + bb_error_msg_and_die("long filenames not supported"); +#endif + } + /* Only size is always present, the rest may be missing in + * long filename pseudo file. Thus we decode the rest + * after dealing with long filename pseudo file. + */ + typed->mode = read_num(ar.formatted.mode, 8); + typed->mtime = read_num(ar.formatted.date, 10); + typed->uid = read_num(ar.formatted.uid, 10); + typed->gid = read_num(ar.formatted.gid, 10); + +#if ENABLE_FEATURE_AR_LONG_FILENAMES + if (ar.formatted.name[0] == '/') { + unsigned long_offset; + + /* The number after the '/' indicates the offset in the ar data section + * (saved in ar_long_names) that conatains the real filename */ + long_offset = read_num(&ar.formatted.name[1], 10); + if (long_offset >= ar_long_name_size) { + bb_error_msg_and_die("can't resolve long filename"); + } + typed->name = xstrdup(ar_long_names + long_offset); + } else +#endif + { + /* short filenames */ + typed->name = xstrndup(ar.formatted.name, 16); + } + + typed->name[strcspn(typed->name, " /")] = '\0'; + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_header(typed); +#if ENABLE_DPKG || ENABLE_DPKG_DEB + if (archive_handle->dpkg__sub_archive) { + while (archive_handle->dpkg__action_data_subarchive(archive_handle->dpkg__sub_archive) == EXIT_SUCCESS) + continue; + } else +#endif + archive_handle->action_data(archive_handle); + } else { + data_skip(archive_handle); + } + + archive_handle->offset += typed->size; + /* Set the file pointer to the correct spot, we may have been reading a compressed file */ + lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET); + + return EXIT_SUCCESS; +} diff --git a/archival/libarchive/get_header_cpio.c b/archival/libarchive/get_header_cpio.c new file mode 100644 index 000000000..3d99b492a --- /dev/null +++ b/archival/libarchive/get_header_cpio.c @@ -0,0 +1,186 @@ +/* vi: set sw=4 ts=4: */ +/* Copyright 2002 Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +typedef struct hardlinks_t { + struct hardlinks_t *next; + int inode; /* TODO: must match maj/min too! */ + int mode ; + int mtime; /* These three are useful only in corner case */ + int uid ; /* of hardlinks with zero size body */ + int gid ; + char name[1]; +} hardlinks_t; + +char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + char cpio_header[110]; + int namesize; + int major, minor, nlink, mode, inode; + unsigned size, uid, gid, mtime; + + /* There can be padding before archive header */ + data_align(archive_handle, 4); + + size = full_read(archive_handle->src_fd, cpio_header, 110); + if (size == 0) { + goto create_hardlinks; + } + if (size != 110) { + bb_error_msg_and_die("short read"); + } + archive_handle->offset += 110; + + if (strncmp(&cpio_header[0], "07070", 5) != 0 + || (cpio_header[5] != '1' && cpio_header[5] != '2') + ) { + bb_error_msg_and_die("unsupported cpio format, use newc or crc"); + } + + if (sscanf(cpio_header + 6, + "%8x" "%8x" "%8x" "%8x" + "%8x" "%8x" "%8x" /*maj,min:*/ "%*16c" + /*rmaj,rmin:*/"%8x" "%8x" "%8x" /*chksum: "%*8c"*/, + &inode, &mode, &uid, &gid, + &nlink, &mtime, &size, + &major, &minor, &namesize) != 10) + bb_error_msg_and_die("damaged cpio file"); + file_header->mode = mode; + file_header->uid = uid; + file_header->gid = gid; + file_header->mtime = mtime; + file_header->size = size; + + namesize &= 0x1fff; /* paranoia: limit names to 8k chars */ + file_header->name = xzalloc(namesize + 1); + /* Read in filename */ + xread(archive_handle->src_fd, file_header->name, namesize); + if (file_header->name[0] == '/') { + /* Testcase: echo /etc/hosts | cpio -pvd /tmp + * Without this code, it tries to unpack /etc/hosts + * into "/etc/hosts", not "etc/hosts". + */ + char *p = file_header->name; + do p++; while (*p == '/'); + overlapping_strcpy(file_header->name, p); + } + archive_handle->offset += namesize; + + /* Update offset amount and skip padding before file contents */ + data_align(archive_handle, 4); + + if (strcmp(file_header->name, "TRAILER!!!") == 0) { + /* Always round up. ">> 9" divides by 512 */ + archive_handle->cpio__blocks = (uoff_t)(archive_handle->offset + 511) >> 9; + goto create_hardlinks; + } + + file_header->link_target = NULL; + if (S_ISLNK(file_header->mode)) { + file_header->size &= 0x1fff; /* paranoia: limit names to 8k chars */ + file_header->link_target = xzalloc(file_header->size + 1); + xread(archive_handle->src_fd, file_header->link_target, file_header->size); + archive_handle->offset += file_header->size; + file_header->size = 0; /* Stop possible seeks in future */ + } + +// TODO: data_extract_all can't deal with hardlinks to non-files... +// when fixed, change S_ISREG to !S_ISDIR here + + if (nlink > 1 && S_ISREG(file_header->mode)) { + hardlinks_t *new = xmalloc(sizeof(*new) + namesize); + new->inode = inode; + new->mode = mode ; + new->mtime = mtime; + new->uid = uid ; + new->gid = gid ; + strcpy(new->name, file_header->name); + /* Put file on a linked list for later */ + if (size == 0) { + new->next = archive_handle->cpio__hardlinks_to_create; + archive_handle->cpio__hardlinks_to_create = new; + return EXIT_SUCCESS; /* Skip this one */ + /* TODO: this breaks cpio -t (it does not show hardlinks) */ + } + new->next = archive_handle->cpio__created_hardlinks; + archive_handle->cpio__created_hardlinks = new; + } + file_header->device = makedev(major, minor); + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_data(archive_handle); +//TODO: run "echo /etc/hosts | cpio -pv /tmp" twice. On 2nd run: +//cpio: etc/hosts not created: newer or same age file exists +//etc/hosts <-- should NOT show it +//2 blocks <-- should say "0 blocks" + archive_handle->action_header(file_header); + } else { + data_skip(archive_handle); + } + + archive_handle->offset += file_header->size; + + free(file_header->link_target); + free(file_header->name); + file_header->link_target = NULL; + file_header->name = NULL; + + return EXIT_SUCCESS; + + create_hardlinks: + free(file_header->link_target); + free(file_header->name); + + while (archive_handle->cpio__hardlinks_to_create) { + hardlinks_t *cur; + hardlinks_t *make_me = archive_handle->cpio__hardlinks_to_create; + + archive_handle->cpio__hardlinks_to_create = make_me->next; + + memset(file_header, 0, sizeof(*file_header)); + file_header->mtime = make_me->mtime; + file_header->name = make_me->name; + file_header->mode = make_me->mode; + file_header->uid = make_me->uid; + file_header->gid = make_me->gid; + /*file_header->size = 0;*/ + /*file_header->link_target = NULL;*/ + + /* Try to find a file we are hardlinked to */ + cur = archive_handle->cpio__created_hardlinks; + while (cur) { + /* TODO: must match maj/min too! */ + if (cur->inode == make_me->inode) { + file_header->link_target = cur->name; + /* link_target != NULL, size = 0: "I am a hardlink" */ + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) + archive_handle->action_data(archive_handle); + free(make_me); + goto next_link; + } + cur = cur->next; + } + /* Oops... no file with such inode was created... do it now + * (happens when hardlinked files are empty (zero length)) */ + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) + archive_handle->action_data(archive_handle); + /* Move to the list of created hardlinked files */ + make_me->next = archive_handle->cpio__created_hardlinks; + archive_handle->cpio__created_hardlinks = make_me; + next_link: ; + } + + while (archive_handle->cpio__created_hardlinks) { + hardlinks_t *p = archive_handle->cpio__created_hardlinks; + archive_handle->cpio__created_hardlinks = p->next; + free(p); + } + + return EXIT_FAILURE; /* "No more files to process" */ +} diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c new file mode 100644 index 000000000..79caff55a --- /dev/null +++ b/archival/libarchive/get_header_tar.c @@ -0,0 +1,497 @@ +/* vi: set sw=4 ts=4: */ +/* Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * FIXME: + * In privileged mode if uname and gname map to a uid and gid then use the + * mapped value instead of the uid/gid values in tar header + * + * References: + * GNU tar and star man pages, + * Opengroup's ustar interchange format, + * http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html + */ + +#include "libbb.h" +#include "archive.h" + +typedef uint32_t aliased_uint32_t FIX_ALIASING; +typedef off_t aliased_off_t FIX_ALIASING; + + +const char* FAST_FUNC strip_unsafe_prefix(const char *str) +{ + const char *cp = str; + while (1) { + char *cp2; + if (*cp == '/') { + cp++; + continue; + } + if (strncmp(cp, "/../"+1, 3) == 0) { + cp += 3; + continue; + } + cp2 = strstr(cp, "/../"); + if (!cp2) + break; + cp = cp2 + 4; + } + if (cp != str) { + static smallint warned = 0; + if (!warned) { + warned = 1; + bb_error_msg("removing leading '%.*s' from member names", + (int)(cp - str), str); + } + } + return cp; +} + +/* NB: _DESTROYS_ str[len] character! */ +static unsigned long long getOctal(char *str, int len) +{ + unsigned long long v; + char *end; + /* NB: leading spaces are allowed. Using strtoull to handle that. + * The downside is that we accept e.g. "-123" too :( + */ + str[len] = '\0'; + v = strtoull(str, &end, 8); + /* std: "Each numeric field is terminated by one or more + * or NUL characters". We must support ' '! */ + if (*end != '\0' && *end != ' ') { + int8_t first = str[0]; + if (!(first & 0x80)) + bb_error_msg_and_die("corrupted octal value in tar header"); + /* + * GNU tar uses "base-256 encoding" for very large numbers. + * Encoding is binary, with highest bit always set as a marker + * and sign in next-highest bit: + * 80 00 .. 00 - zero + * bf ff .. ff - largest positive number + * ff ff .. ff - minus 1 + * c0 00 .. 00 - smallest negative number + * + * Example of tar file with 8914993153 (0x213600001) byte file. + * Field starts at offset 7c: + * 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....| + * 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336| + * + * NB: tarballs with NEGATIVE unix times encoded that way were seen! + */ + v = first; + /* Sign-extend using 6th bit: */ + v <<= sizeof(unsigned long long)*8 - 7; + v = (long long)v >> (sizeof(unsigned long long)*8 - 7); + while (--len != 0) + v = (v << 8) + (unsigned char) *str++; + } + return v; +} +#define GET_OCTAL(a) getOctal((a), sizeof(a)) + +#if ENABLE_FEATURE_TAR_SELINUX +/* Scan a PAX header for SELinux contexts, via "RHT.security.selinux" keyword. + * This is what Red Hat's patched version of tar uses. + */ +# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux" +static char *get_selinux_sctx_from_pax_hdr(archive_handle_t *archive_handle, unsigned sz) +{ + char *buf, *p; + char *result; + + p = buf = xmalloc(sz + 1); + /* prevent bb_strtou from running off the buffer */ + buf[sz] = '\0'; + xread(archive_handle->src_fd, buf, sz); + archive_handle->offset += sz; + + result = NULL; + while (sz != 0) { + char *end, *value; + unsigned len; + + /* Every record has this format: "LEN NAME=VALUE\n" */ + len = bb_strtou(p, &end, 10); + /* expect errno to be EINVAL, because the character + * following the digits should be a space + */ + p += len; + sz -= len; + if ((int)sz < 0 + || len == 0 + || errno != EINVAL + || *end != ' ' + ) { + bb_error_msg("malformed extended header, skipped"); + // More verbose version: + //bb_error_msg("malformed extended header at %"OFF_FMT"d, skipped", + // archive_handle->offset - (sz + len)); + break; + } + /* overwrite the terminating newline with NUL + * (we do not bother to check that it *was* a newline) + */ + p[-1] = '\0'; + /* Is it selinux security context? */ + value = end + 1; + if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) { + value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1; + result = xstrdup(value); + break; + } + } + + free(buf); + return result; +} +#endif + +char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + struct tar_header_t tar; + char *cp; + int i, sum_u, sum; +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + int sum_s; +#endif + int parse_names; + + /* Our "private data" */ +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS +# define p_longname (archive_handle->tar__longname) +# define p_linkname (archive_handle->tar__linkname) +#else +# define p_longname 0 +# define p_linkname 0 +#endif + +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX + again: +#endif + /* Align header */ + data_align(archive_handle, 512); + + again_after_align: + +#if ENABLE_DESKTOP || ENABLE_FEATURE_TAR_AUTODETECT + /* to prevent misdetection of bz2 sig */ + *(aliased_uint32_t*)&tar = 0; + i = full_read(archive_handle->src_fd, &tar, 512); + /* If GNU tar sees EOF in above read, it says: + * "tar: A lone zero block at N", where N = kilobyte + * where EOF was met (not EOF block, actual EOF!), + * and exits with EXIT_SUCCESS. + * We will mimic exit(EXIT_SUCCESS), although we will not mimic + * the message and we don't check whether we indeed + * saw zero block directly before this. */ + if (i == 0) { + xfunc_error_retval = 0; + short_read: + bb_error_msg_and_die("short read"); + } + if (i != 512) { + IF_FEATURE_TAR_AUTODETECT(goto autodetect;) + goto short_read; + } + +#else + i = 512; + xread(archive_handle->src_fd, &tar, i); +#endif + archive_handle->offset += i; + + /* If there is no filename its an empty header */ + if (tar.name[0] == 0 && tar.prefix[0] == 0) { + if (archive_handle->tar__end) { + /* Second consecutive empty header - end of archive. + * Read until the end to empty the pipe from gz or bz2 + */ + while (full_read(archive_handle->src_fd, &tar, 512) == 512) + continue; + return EXIT_FAILURE; + } + archive_handle->tar__end = 1; + return EXIT_SUCCESS; + } + archive_handle->tar__end = 0; + + /* Check header has valid magic, "ustar" is for the proper tar, + * five NULs are for the old tar format */ + if (strncmp(tar.magic, "ustar", 5) != 0 + && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY + || memcmp(tar.magic, "\0\0\0\0", 5) != 0) + ) { +#if ENABLE_FEATURE_TAR_AUTODETECT + char FAST_FUNC (*get_header_ptr)(archive_handle_t *); + uint16_t magic2; + + autodetect: + magic2 = *(bb__aliased_uint16_t*)tar.name; + /* tar gz/bz autodetect: check for gz/bz2 magic. + * If we see the magic, and it is the very first block, + * we can switch to get_header_tar_gz/bz2/lzma(). + * Needs seekable fd. I wish recv(MSG_PEEK) works + * on any fd... */ +# if ENABLE_FEATURE_SEAMLESS_GZ + if (magic2 == GZIP_MAGIC) { + get_header_ptr = get_header_tar_gz; + } else +# endif +# if ENABLE_FEATURE_SEAMLESS_BZ2 + if (magic2 == BZIP2_MAGIC + && tar.name[2] == 'h' && isdigit(tar.name[3]) + ) { /* bzip2 */ + get_header_ptr = get_header_tar_bz2; + } else +# endif +# if ENABLE_FEATURE_SEAMLESS_XZ + //TODO: if (magic2 == XZ_MAGIC1)... + //else +# endif + goto err; + /* Two different causes for lseek() != 0: + * unseekable fd (would like to support that too, but...), + * or not first block (false positive, it's not .gz/.bz2!) */ + if (lseek(archive_handle->src_fd, -i, SEEK_CUR) != 0) + goto err; + while (get_header_ptr(archive_handle) == EXIT_SUCCESS) + continue; + return EXIT_FAILURE; + err: +#endif /* FEATURE_TAR_AUTODETECT */ + bb_error_msg_and_die("invalid tar magic"); + } + + /* Do checksum on headers. + * POSIX says that checksum is done on unsigned bytes, but + * Sun and HP-UX gets it wrong... more details in + * GNU tar source. */ +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + sum_s = ' ' * sizeof(tar.chksum); +#endif + sum_u = ' ' * sizeof(tar.chksum); + for (i = 0; i < 148; i++) { + sum_u += ((unsigned char*)&tar)[i]; +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + sum_s += ((signed char*)&tar)[i]; +#endif + } + for (i = 156; i < 512; i++) { + sum_u += ((unsigned char*)&tar)[i]; +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + sum_s += ((signed char*)&tar)[i]; +#endif + } + /* This field does not need special treatment (getOctal) */ + { + char *endp; /* gcc likes temp var for &endp */ + sum = strtoul(tar.chksum, &endp, 8); + if ((*endp != '\0' && *endp != ' ') + || (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) + ) { + bb_error_msg_and_die("invalid tar header checksum"); + } + } + /* don't use xstrtoul, tar.chksum may have leading spaces */ + sum = strtoul(tar.chksum, NULL, 8); + if (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) { + bb_error_msg_and_die("invalid tar header checksum"); + } + + /* 0 is reserved for high perf file, treat as normal file */ + if (!tar.typeflag) tar.typeflag = '0'; + parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7'); + + /* getOctal trashes subsequent field, therefore we call it + * on fields in reverse order */ + if (tar.devmajor[0]) { + char t = tar.prefix[0]; + /* we trash prefix[0] here, but we DO need it later! */ + unsigned minor = GET_OCTAL(tar.devminor); + unsigned major = GET_OCTAL(tar.devmajor); + file_header->device = makedev(major, minor); + tar.prefix[0] = t; + } + file_header->link_target = NULL; + if (!p_linkname && parse_names && tar.linkname[0]) { + file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname)); + /* FIXME: what if we have non-link object with link_target? */ + /* Will link_target be free()ed? */ + } +#if ENABLE_FEATURE_TAR_UNAME_GNAME + file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL; + file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; +#endif + file_header->mtime = GET_OCTAL(tar.mtime); + file_header->size = GET_OCTAL(tar.size); + file_header->gid = GET_OCTAL(tar.gid); + file_header->uid = GET_OCTAL(tar.uid); + /* Set bits 0-11 of the files mode */ + file_header->mode = 07777 & GET_OCTAL(tar.mode); + + file_header->name = NULL; + if (!p_longname && parse_names) { + /* we trash mode[0] here, it's ok */ + //tar.name[sizeof(tar.name)] = '\0'; - gcc 4.3.0 would complain + tar.mode[0] = '\0'; + if (tar.prefix[0]) { + /* and padding[0] */ + //tar.prefix[sizeof(tar.prefix)] = '\0'; - gcc 4.3.0 would complain + tar.padding[0] = '\0'; + file_header->name = concat_path_file(tar.prefix, tar.name); + } else + file_header->name = xstrdup(tar.name); + } + + /* Set bits 12-15 of the files mode */ + /* (typeflag was not trashed because chksum does not use getOctal) */ + switch (tar.typeflag) { + case '1': /* hardlink */ + /* we mark hardlinks as regular files with zero size and a link name */ + file_header->mode |= S_IFREG; + /* on size of link fields from star(4) + * ... For tar archives written by pre POSIX.1-1988 + * implementations, the size field usually contains the size of + * the file and needs to be ignored as no data may follow this + * header type. For POSIX.1- 1988 compliant archives, the size + * field needs to be 0. For POSIX.1-2001 compliant archives, + * the size field may be non zero, indicating that file data is + * included in the archive. + * i.e; always assume this is zero for safety. + */ + goto size0; + case '7': + /* case 0: */ + case '0': +#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY + if (last_char_is(file_header->name, '/')) { + goto set_dir; + } +#endif + file_header->mode |= S_IFREG; + break; + case '2': + file_header->mode |= S_IFLNK; + /* have seen tarballs with size field containing + * the size of the link target's name */ + size0: + file_header->size = 0; + break; + case '3': + file_header->mode |= S_IFCHR; + goto size0; /* paranoia */ + case '4': + file_header->mode |= S_IFBLK; + goto size0; + case '5': + IF_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:) + file_header->mode |= S_IFDIR; + goto size0; + case '6': + file_header->mode |= S_IFIFO; + goto size0; +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + case 'L': + /* free: paranoia: tar with several consecutive longnames */ + free(p_longname); + /* For paranoia reasons we allocate extra NUL char */ + p_longname = xzalloc(file_header->size + 1); + /* We read ASCIZ string, including NUL */ + xread(archive_handle->src_fd, p_longname, file_header->size); + archive_handle->offset += file_header->size; + /* return get_header_tar(archive_handle); */ + /* gcc 4.1.1 didn't optimize it into jump */ + /* so we will do it ourself, this also saves stack */ + goto again; + case 'K': + free(p_linkname); + p_linkname = xzalloc(file_header->size + 1); + xread(archive_handle->src_fd, p_linkname, file_header->size); + archive_handle->offset += file_header->size; + /* return get_header_tar(archive_handle); */ + goto again; + case 'D': /* GNU dump dir */ + case 'M': /* Continuation of multi volume archive */ + case 'N': /* Old GNU for names > 100 characters */ + case 'S': /* Sparse file */ + case 'V': /* Volume header */ +#endif +#if !ENABLE_FEATURE_TAR_SELINUX + case 'g': /* pax global header */ + case 'x': /* pax extended header */ +#else + skip_ext_hdr: +#endif + { + off_t sz; + bb_error_msg("warning: skipping header '%c'", tar.typeflag); + sz = (file_header->size + 511) & ~(off_t)511; + archive_handle->offset += sz; + sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ + while (sz--) + xread(archive_handle->src_fd, &tar, 512); + /* return get_header_tar(archive_handle); */ + goto again_after_align; + } +#if ENABLE_FEATURE_TAR_SELINUX + case 'g': /* pax global header */ + case 'x': { /* pax extended header */ + char **pp; + if ((uoff_t)file_header->size > 0xfffff) /* paranoia */ + goto skip_ext_hdr; + pp = (tar.typeflag == 'g') ? &archive_handle->tar__global_sctx : &archive_handle->tar__next_file_sctx; + free(*pp); + *pp = get_selinux_sctx_from_pax_hdr(archive_handle, file_header->size); + goto again; + } +#endif + default: + bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); + } + +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + if (p_longname) { + file_header->name = p_longname; + p_longname = NULL; + } + if (p_linkname) { + file_header->link_target = p_linkname; + p_linkname = NULL; + } +#endif + + /* Everything up to and including last ".." component is stripped */ + overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name)); + + /* Strip trailing '/' in directories */ + /* Must be done after mode is set as '/' is used to check if it's a directory */ + cp = last_char_is(file_header->name, '/'); + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_header(/*archive_handle->*/ file_header); + /* Note that we kill the '/' only after action_header() */ + /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ + if (cp) + *cp = '\0'; + archive_handle->action_data(archive_handle); + if (archive_handle->accept || archive_handle->reject) + llist_add_to(&archive_handle->passed, file_header->name); + else /* Caller isn't interested in list of unpacked files */ + free(file_header->name); + } else { + data_skip(archive_handle); + free(file_header->name); + } + archive_handle->offset += file_header->size; + + free(file_header->link_target); + /* Do not free(file_header->name)! + * It might be inserted in archive_handle->passed - see above */ +#if ENABLE_FEATURE_TAR_UNAME_GNAME + free(file_header->tar__uname); + free(file_header->tar__gname); +#endif + return EXIT_SUCCESS; +} diff --git a/archival/libarchive/get_header_tar_bz2.c b/archival/libarchive/get_header_tar_bz2.c new file mode 100644 index 000000000..60d32069f --- /dev/null +++ b/archival/libarchive/get_header_tar_bz2.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +char FAST_FUNC get_header_tar_bz2(archive_handle_t *archive_handle) +{ + /* Can't lseek over pipes */ + archive_handle->seek = seek_by_read; + + open_transformer(archive_handle->src_fd, unpack_bz2_stream_prime, "bunzip2"); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS) + continue; + + /* Can only do one file at a time */ + return EXIT_FAILURE; +} diff --git a/archival/libarchive/get_header_tar_gz.c b/archival/libarchive/get_header_tar_gz.c new file mode 100644 index 000000000..889fed0d9 --- /dev/null +++ b/archival/libarchive/get_header_tar_gz.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle) +{ +#if BB_MMU + uint16_t magic; +#endif + + /* Can't lseek over pipes */ + archive_handle->seek = seek_by_read; + + /* Check gzip magic only if open_transformer will invoke unpack_gz_stream (MMU case). + * Otherwise, it will invoke an external helper "gunzip -cf" (NOMMU case) which will + * need the header. */ +#if BB_MMU + xread(archive_handle->src_fd, &magic, 2); + /* Can skip this check, but error message will be less clear */ + if (magic != GZIP_MAGIC) { + bb_error_msg_and_die("invalid gzip magic"); + } +#endif + + open_transformer(archive_handle->src_fd, unpack_gz_stream, "gunzip"); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS) + continue; + + /* Can only do one file at a time */ + return EXIT_FAILURE; +} diff --git a/archival/libarchive/get_header_tar_lzma.c b/archival/libarchive/get_header_tar_lzma.c new file mode 100644 index 000000000..da08e0c72 --- /dev/null +++ b/archival/libarchive/get_header_tar_lzma.c @@ -0,0 +1,24 @@ +/* vi: set sw=4 ts=4: */ +/* + * Small lzma deflate implementation. + * Copyright (C) 2006 Aurelien Jacobs + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +char FAST_FUNC get_header_tar_lzma(archive_handle_t *archive_handle) +{ + /* Can't lseek over pipes */ + archive_handle->seek = seek_by_read; + + open_transformer(archive_handle->src_fd, unpack_lzma_stream, "unlzma"); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS) + continue; + + /* Can only do one file at a time */ + return EXIT_FAILURE; +} diff --git a/archival/libarchive/header_list.c b/archival/libarchive/header_list.c new file mode 100644 index 000000000..c4fc75f38 --- /dev/null +++ b/archival/libarchive/header_list.c @@ -0,0 +1,12 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC header_list(const file_header_t *file_header) +{ +//TODO: cpio -vp DIR should output "DIR/NAME", not just "NAME" */ + puts(file_header->name); +} diff --git a/archival/libarchive/header_skip.c b/archival/libarchive/header_skip.c new file mode 100644 index 000000000..2bfc5253c --- /dev/null +++ b/archival/libarchive/header_skip.c @@ -0,0 +1,10 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC header_skip(const file_header_t *file_header UNUSED_PARAM) +{ +} diff --git a/archival/libarchive/header_verbose_list.c b/archival/libarchive/header_verbose_list.c new file mode 100644 index 000000000..bc4e4154b --- /dev/null +++ b/archival/libarchive/header_verbose_list.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC header_verbose_list(const file_header_t *file_header) +{ + struct tm tm_time; + struct tm *ptm = &tm_time; //localtime(&file_header->mtime); + +#if ENABLE_FEATURE_TAR_UNAME_GNAME + char uid[sizeof(int)*3 + 2]; + /*char gid[sizeof(int)*3 + 2];*/ + char *user; + char *group; + + localtime_r(&file_header->mtime, ptm); + + user = file_header->tar__uname; + if (user == NULL) { + sprintf(uid, "%u", (unsigned)file_header->uid); + user = uid; + } + group = file_header->tar__gname; + if (group == NULL) { + /*sprintf(gid, "%u", (unsigned)file_header->gid);*/ + group = utoa(file_header->gid); + } + printf("%s %s/%s %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s", + bb_mode_string(file_header->mode), + user, + group, + file_header->size, + 1900 + ptm->tm_year, + 1 + ptm->tm_mon, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + file_header->name); + +#else /* !FEATURE_TAR_UNAME_GNAME */ + + localtime_r(&file_header->mtime, ptm); + + printf("%s %u/%u %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s", + bb_mode_string(file_header->mode), + (unsigned)file_header->uid, + (unsigned)file_header->gid, + file_header->size, + 1900 + ptm->tm_year, + 1 + ptm->tm_mon, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + file_header->name); + +#endif /* FEATURE_TAR_UNAME_GNAME */ + + /* NB: GNU tar shows "->" for symlinks and "link to" for hardlinks */ + if (file_header->link_target) { + printf(" -> %s", file_header->link_target); + } + bb_putchar('\n'); +} diff --git a/archival/libarchive/init_handle.c b/archival/libarchive/init_handle.c new file mode 100644 index 000000000..6644ea13b --- /dev/null +++ b/archival/libarchive/init_handle.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +archive_handle_t* FAST_FUNC init_handle(void) +{ + archive_handle_t *archive_handle; + + /* Initialize default values */ + archive_handle = xzalloc(sizeof(archive_handle_t)); + archive_handle->file_header = xzalloc(sizeof(file_header_t)); + archive_handle->action_header = header_skip; + archive_handle->action_data = data_skip; + archive_handle->filter = filter_accept_all; + archive_handle->seek = seek_by_jump; + + return archive_handle; +} diff --git a/archival/libarchive/liblzo.h b/archival/libarchive/liblzo.h new file mode 100644 index 000000000..843997cb9 --- /dev/null +++ b/archival/libarchive/liblzo.h @@ -0,0 +1,93 @@ +/* + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "liblzo_interface.h" + +/* lzo-2.03/src/config1x.h */ +#define M2_MIN_LEN 3 +#define M2_MAX_LEN 8 +#define M3_MAX_LEN 33 +#define M4_MAX_LEN 9 +#define M1_MAX_OFFSET 0x0400 +#define M2_MAX_OFFSET 0x0800 +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff +#define M1_MARKER 0 +#define M3_MARKER 32 +#define M4_MARKER 16 + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) +#define MIN_LOOKAHEAD (M2_MAX_LEN + 1) + +#define LZO_EOF_CODE + +/* lzo-2.03/src/lzo_dict.h */ +#define GINDEX(m_pos,m_off,dict,dindex,in) m_pos = dict[dindex] +#define DX2(p,s1,s2) \ + (((((unsigned)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]) +//#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0]) +//#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0]) +#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0]) + +#define D_SIZE (1U << D_BITS) +#define D_MASK ((1U << D_BITS) - 1) +#define D_HIGH ((D_MASK >> 1) + 1) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + ( \ + m_pos = ip - (unsigned)(ip - m_pos), \ + ((uintptr_t)m_pos < (uintptr_t)in \ + || (m_off = (unsigned)(ip - m_pos)) <= 0 \ + || m_off > max_offset) \ + ) + +#define DENTRY(p,in) (p) +#define UPDATE_I(dict,drun,index,p,in) dict[index] = DENTRY(p,in) + +#define DMS(v,s) ((unsigned) (((v) & (D_MASK >> (s))) << (s))) +#define DM(v) ((unsigned) ((v) & D_MASK)) +#define DMUL(a,b) ((unsigned) ((a) * (b))) + +/* lzo-2.03/src/lzo_ptr.h */ +#define pd(a,b) ((unsigned)((a)-(b))) + +# define TEST_IP (ip < ip_end) +# define NEED_IP(x) \ + if ((unsigned)(ip_end - ip) < (unsigned)(x)) goto input_overrun + +# undef TEST_OP /* don't need both of the tests here */ +# define TEST_OP 1 +# define NEED_OP(x) \ + if ((unsigned)(op_end - op) < (unsigned)(x)) goto output_overrun + +#define HAVE_ANY_OP 1 + +//#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +# define TEST_LB(m_pos) if (m_pos < out || m_pos >= op) goto lookbehind_overrun +//# define TEST_LBO(m_pos,o) if (m_pos < out || m_pos >= op - (o)) goto lookbehind_overrun +//#else +//# define TEST_LB(m_pos) ((void) 0) +//# define TEST_LBO(m_pos,o) ((void) 0) +//#endif diff --git a/archival/libarchive/lzo1x_1.c b/archival/libarchive/lzo1x_1.c new file mode 100644 index 000000000..a88839846 --- /dev/null +++ b/archival/libarchive/lzo1x_1.c @@ -0,0 +1,35 @@ +/* LZO1X-1 compression + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libbb.h" +#include "liblzo.h" + +#define D_BITS 14 +#define D_INDEX1(d,p) d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) + +#define DO_COMPRESS lzo1x_1_compress + +#include "lzo1x_c.c" diff --git a/archival/libarchive/lzo1x_1o.c b/archival/libarchive/lzo1x_1o.c new file mode 100644 index 000000000..3c61253e0 --- /dev/null +++ b/archival/libarchive/lzo1x_1o.c @@ -0,0 +1,35 @@ +/* LZO1X-1(15) compression + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libbb.h" +#include "liblzo.h" + +#define D_BITS 15 +#define D_INDEX1(d,p) d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) + +#define DO_COMPRESS lzo1x_1_15_compress + +#include "lzo1x_c.c" diff --git a/archival/libarchive/lzo1x_9x.c b/archival/libarchive/lzo1x_9x.c new file mode 100644 index 000000000..483205155 --- /dev/null +++ b/archival/libarchive/lzo1x_9x.c @@ -0,0 +1,921 @@ +/* lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ +*/ +#include "libbb.h" + +/* The following is probably only safe on Intel-compatible processors ... */ +#define LZO_UNALIGNED_OK_2 +#define LZO_UNALIGNED_OK_4 + +#include "liblzo.h" + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define LZO_MAX3(a,b,c) ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c)) + +/*********************************************************************** +// +************************************************************************/ +#define SWD_N M4_MAX_OFFSET /* size of ring buffer */ +#define SWD_F 2048 /* upper limit for match length */ + +#define SWD_BEST_OFF (LZO_MAX3(M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN) + 1) + +typedef struct { + int init; + + unsigned look; /* bytes in lookahead buffer */ + + unsigned m_len; + unsigned m_off; + + const uint8_t *bp; + const uint8_t *ip; + const uint8_t *in; + const uint8_t *in_end; + uint8_t *out; + + unsigned r1_lit; + +} lzo1x_999_t; + +#define getbyte(c) ((c).ip < (c).in_end ? *((c).ip)++ : (-1)) + +/* lzo_swd.c -- sliding window dictionary */ + +/*********************************************************************** +// +************************************************************************/ +#define SWD_UINT_MAX USHRT_MAX + +#ifndef SWD_HSIZE +# define SWD_HSIZE 16384 +#endif +#ifndef SWD_MAX_CHAIN +# define SWD_MAX_CHAIN 2048 +#endif + +#define HEAD3(b, p) \ + ( ((0x9f5f * ((((b[p]<<5)^b[p+1])<<5) ^ b[p+2])) >> 5) & (SWD_HSIZE-1) ) + +#if defined(LZO_UNALIGNED_OK_2) +# define HEAD2(b,p) (* (uint16_t *) &(b[p])) +#else +# define HEAD2(b,p) (b[p] ^ ((unsigned)b[p+1]<<8)) +#endif +#define NIL2 SWD_UINT_MAX + +typedef struct lzo_swd { + /* public - "built-in" */ + + /* public - configuration */ + unsigned max_chain; + int use_best_off; + + /* public - output */ + unsigned m_len; + unsigned m_off; + unsigned look; + int b_char; +#if defined(SWD_BEST_OFF) + unsigned best_off[SWD_BEST_OFF]; +#endif + + /* semi public */ + lzo1x_999_t *c; + unsigned m_pos; +#if defined(SWD_BEST_OFF) + unsigned best_pos[SWD_BEST_OFF]; +#endif + + /* private */ + unsigned ip; /* input pointer (lookahead) */ + unsigned bp; /* buffer pointer */ + unsigned rp; /* remove pointer */ + + unsigned node_count; + unsigned first_rp; + + uint8_t b[SWD_N + SWD_F]; + uint8_t b_wrap[SWD_F]; /* must follow b */ + uint16_t head3[SWD_HSIZE]; + uint16_t succ3[SWD_N + SWD_F]; + uint16_t best3[SWD_N + SWD_F]; + uint16_t llen3[SWD_HSIZE]; +#ifdef HEAD2 + uint16_t head2[65536L]; +#endif +} lzo_swd_t, *lzo_swd_p; + +#define SIZEOF_LZO_SWD_T (sizeof(lzo_swd_t)) + + +/* Access macro for head3. + * head3[key] may be uninitialized, but then its value will never be used. + */ +#define s_get_head3(s,key) s->head3[key] + + +/*********************************************************************** +// +************************************************************************/ +#define B_SIZE (SWD_N + SWD_F) + +static int swd_init(lzo_swd_p s) +{ + /* defaults */ + s->node_count = SWD_N; + + memset(s->llen3, 0, sizeof(s->llen3[0]) * (unsigned)SWD_HSIZE); +#ifdef HEAD2 + memset(s->head2, 0xff, sizeof(s->head2[0]) * 65536L); + assert(s->head2[0] == NIL2); +#endif + + s->ip = 0; + s->bp = s->ip; + s->first_rp = s->ip; + + assert(s->ip + SWD_F <= B_SIZE); + s->look = (unsigned) (s->c->in_end - s->c->ip); + if (s->look > 0) { + if (s->look > SWD_F) + s->look = SWD_F; + memcpy(&s->b[s->ip], s->c->ip, s->look); + s->c->ip += s->look; + s->ip += s->look; + } + if (s->ip == B_SIZE) + s->ip = 0; + + s->rp = s->first_rp; + if (s->rp >= s->node_count) + s->rp -= s->node_count; + else + s->rp += B_SIZE - s->node_count; + + return LZO_E_OK; +} + +#define swd_pos2off(s,pos) \ + (s->bp > (pos) ? s->bp - (pos) : B_SIZE - ((pos) - s->bp)) + + +/*********************************************************************** +// +************************************************************************/ +static void swd_getbyte(lzo_swd_p s) +{ + int c; + + if ((c = getbyte(*(s->c))) < 0) { + if (s->look > 0) + --s->look; + } else { + s->b[s->ip] = c; + if (s->ip < SWD_F) + s->b_wrap[s->ip] = c; + } + if (++s->ip == B_SIZE) + s->ip = 0; + if (++s->bp == B_SIZE) + s->bp = 0; + if (++s->rp == B_SIZE) + s->rp = 0; +} + + +/*********************************************************************** +// remove node from lists +************************************************************************/ +static void swd_remove_node(lzo_swd_p s, unsigned node) +{ + if (s->node_count == 0) { + unsigned key; + + key = HEAD3(s->b,node); + assert(s->llen3[key] > 0); + --s->llen3[key]; + +#ifdef HEAD2 + key = HEAD2(s->b,node); + assert(s->head2[key] != NIL2); + if ((unsigned) s->head2[key] == node) + s->head2[key] = NIL2; +#endif + } else + --s->node_count; +} + + +/*********************************************************************** +// +************************************************************************/ +static void swd_accept(lzo_swd_p s, unsigned n) +{ + assert(n <= s->look); + + while (n--) { + unsigned key; + + swd_remove_node(s,s->rp); + + /* add bp into HEAD3 */ + key = HEAD3(s->b, s->bp); + s->succ3[s->bp] = s_get_head3(s, key); + s->head3[key] = s->bp; + s->best3[s->bp] = SWD_F + 1; + s->llen3[key]++; + assert(s->llen3[key] <= SWD_N); + +#ifdef HEAD2 + /* add bp into HEAD2 */ + key = HEAD2(s->b, s->bp); + s->head2[key] = s->bp; +#endif + + swd_getbyte(s); + } +} + + +/*********************************************************************** +// +************************************************************************/ +static void swd_search(lzo_swd_p s, unsigned node, unsigned cnt) +{ + const uint8_t *p1; + const uint8_t *p2; + const uint8_t *px; + unsigned m_len = s->m_len; + const uint8_t *b = s->b; + const uint8_t *bp = s->b + s->bp; + const uint8_t *bx = s->b + s->bp + s->look; + unsigned char scan_end1; + + assert(s->m_len > 0); + + scan_end1 = bp[m_len - 1]; + for ( ; cnt-- > 0; node = s->succ3[node]) { + p1 = bp; + p2 = b + node; + px = bx; + + assert(m_len < s->look); + + if (p2[m_len - 1] == scan_end1 + && p2[m_len] == p1[m_len] + && p2[0] == p1[0] + && p2[1] == p1[1] + ) { + unsigned i; + assert(lzo_memcmp(bp, &b[node], 3) == 0); + + p1 += 2; p2 += 2; + do {} while (++p1 < px && *p1 == *++p2); + i = p1-bp; + + assert(lzo_memcmp(bp, &b[node], i) == 0); + +#if defined(SWD_BEST_OFF) + if (i < SWD_BEST_OFF) { + if (s->best_pos[i] == 0) + s->best_pos[i] = node + 1; + } +#endif + if (i > m_len) { + s->m_len = m_len = i; + s->m_pos = node; + if (m_len == s->look) + return; + if (m_len >= SWD_F) + return; + if (m_len > (unsigned) s->best3[node]) + return; + scan_end1 = bp[m_len - 1]; + } + } + } +} + + +/*********************************************************************** +// +************************************************************************/ +#ifdef HEAD2 + +static int swd_search2(lzo_swd_p s) +{ + unsigned key; + + assert(s->look >= 2); + assert(s->m_len > 0); + + key = s->head2[HEAD2(s->b, s->bp)]; + if (key == NIL2) + return 0; + assert(lzo_memcmp(&s->b[s->bp], &s->b[key], 2) == 0); +#if defined(SWD_BEST_OFF) + if (s->best_pos[2] == 0) + s->best_pos[2] = key + 1; +#endif + + if (s->m_len < 2) { + s->m_len = 2; + s->m_pos = key; + } + return 1; +} + +#endif + + +/*********************************************************************** +// +************************************************************************/ +static void swd_findbest(lzo_swd_p s) +{ + unsigned key; + unsigned cnt, node; + unsigned len; + + assert(s->m_len > 0); + + /* get current head, add bp into HEAD3 */ + key = HEAD3(s->b,s->bp); + node = s->succ3[s->bp] = s_get_head3(s, key); + cnt = s->llen3[key]++; + assert(s->llen3[key] <= SWD_N + SWD_F); + if (cnt > s->max_chain) + cnt = s->max_chain; + s->head3[key] = s->bp; + + s->b_char = s->b[s->bp]; + len = s->m_len; + if (s->m_len >= s->look) { + if (s->look == 0) + s->b_char = -1; + s->m_off = 0; + s->best3[s->bp] = SWD_F + 1; + } else { +#ifdef HEAD2 + if (swd_search2(s)) +#endif + if (s->look >= 3) + swd_search(s, node, cnt); + if (s->m_len > len) + s->m_off = swd_pos2off(s,s->m_pos); + s->best3[s->bp] = s->m_len; + +#if defined(SWD_BEST_OFF) + if (s->use_best_off) { + int i; + for (i = 2; i < SWD_BEST_OFF; i++) { + if (s->best_pos[i] > 0) + s->best_off[i] = swd_pos2off(s, s->best_pos[i]-1); + else + s->best_off[i] = 0; + } + } +#endif + } + + swd_remove_node(s,s->rp); + +#ifdef HEAD2 + /* add bp into HEAD2 */ + key = HEAD2(s->b, s->bp); + s->head2[key] = s->bp; +#endif +} + +#undef HEAD3 +#undef HEAD2 +#undef s_get_head3 + + +/*********************************************************************** +// +************************************************************************/ +static int init_match(lzo1x_999_t *c, lzo_swd_p s, uint32_t use_best_off) +{ + int r; + + assert(!c->init); + c->init = 1; + + s->c = c; + + r = swd_init(s); + if (r != 0) + return r; + + s->use_best_off = use_best_off; + return r; +} + + +/*********************************************************************** +// +************************************************************************/ +static int find_match(lzo1x_999_t *c, lzo_swd_p s, + unsigned this_len, unsigned skip) +{ + assert(c->init); + + if (skip > 0) { + assert(this_len >= skip); + swd_accept(s, this_len - skip); + } else { + assert(this_len <= 1); + } + + s->m_len = 1; + s->m_len = 1; +#ifdef SWD_BEST_OFF + if (s->use_best_off) + memset(s->best_pos, 0, sizeof(s->best_pos)); +#endif + swd_findbest(s); + c->m_len = s->m_len; + c->m_off = s->m_off; + + swd_getbyte(s); + + if (s->b_char < 0) { + c->look = 0; + c->m_len = 0; + } else { + c->look = s->look + 1; + } + c->bp = c->ip - c->look; + + return LZO_E_OK; +} + +/* this is a public functions, but there is no prototype in a header file */ +static int lzo1x_999_compress_internal(const uint8_t *in , unsigned in_len, + uint8_t *out, unsigned *out_len, + void *wrkmem, + unsigned good_length, + unsigned max_lazy, + unsigned max_chain, + uint32_t use_best_off); + + +/*********************************************************************** +// +************************************************************************/ +static uint8_t *code_match(lzo1x_999_t *c, + uint8_t *op, unsigned m_len, unsigned m_off) +{ + assert(op > c->out); + if (m_len == 2) { + assert(m_off <= M1_MAX_OFFSET); + assert(c->r1_lit > 0); + assert(c->r1_lit < 4); + m_off -= 1; + *op++ = M1_MARKER | ((m_off & 3) << 2); + *op++ = m_off >> 2; + } else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) { + assert(m_len >= 3); + m_off -= 1; + *op++ = ((m_len - 1) << 5) | ((m_off & 7) << 2); + *op++ = m_off >> 3; + assert(op[-2] >= M2_MARKER); + } else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && c->r1_lit >= 4) { + assert(m_len == 3); + assert(m_off > M2_MAX_OFFSET); + m_off -= 1 + M2_MAX_OFFSET; + *op++ = M1_MARKER | ((m_off & 3) << 2); + *op++ = m_off >> 2; + } else if (m_off <= M3_MAX_OFFSET) { + assert(m_len >= 3); + m_off -= 1; + if (m_len <= M3_MAX_LEN) + *op++ = M3_MARKER | (m_len - 2); + else { + m_len -= M3_MAX_LEN; + *op++ = M3_MARKER | 0; + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = m_len; + } + *op++ = m_off << 2; + *op++ = m_off >> 6; + } else { + unsigned k; + + assert(m_len >= 3); + assert(m_off > 0x4000); + assert(m_off <= 0xbfff); + m_off -= 0x4000; + k = (m_off & 0x4000) >> 11; + if (m_len <= M4_MAX_LEN) + *op++ = M4_MARKER | k | (m_len - 2); + else { + m_len -= M4_MAX_LEN; + *op++ = M4_MARKER | k | 0; + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = m_len; + } + *op++ = m_off << 2; + *op++ = m_off >> 6; + } + + return op; +} + + +static uint8_t *STORE_RUN(lzo1x_999_t *c, uint8_t *op, + const uint8_t *ii, unsigned t) +{ + if (op == c->out && t <= 238) { + *op++ = 17 + t; + } else if (t <= 3) { + op[-2] |= t; + } else if (t <= 18) { + *op++ = t - 3; + } else { + unsigned tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = tt; + } + do *op++ = *ii++; while (--t > 0); + + return op; +} + + +static uint8_t *code_run(lzo1x_999_t *c, uint8_t *op, const uint8_t *ii, + unsigned lit) +{ + if (lit > 0) { + assert(m_len >= 2); + op = STORE_RUN(c, op, ii, lit); + } else { + assert(m_len >= 3); + } + c->r1_lit = lit; + + return op; +} + + +/*********************************************************************** +// +************************************************************************/ +static int len_of_coded_match(unsigned m_len, unsigned m_off, unsigned lit) +{ + int n = 4; + + if (m_len < 2) + return -1; + if (m_len == 2) + return (m_off <= M1_MAX_OFFSET && lit > 0 && lit < 4) ? 2 : -1; + if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) + return 2; + if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4) + return 2; + if (m_off <= M3_MAX_OFFSET) { + if (m_len <= M3_MAX_LEN) + return 3; + m_len -= M3_MAX_LEN; + } else if (m_off <= M4_MAX_OFFSET) { + if (m_len <= M4_MAX_LEN) + return 3; + m_len -= M4_MAX_LEN; + } else + return -1; + while (m_len > 255) { + m_len -= 255; + n++; + } + return n; +} + + +static int min_gain(unsigned ahead, unsigned lit1, + unsigned lit2, int l1, int l2, int l3) +{ + int lazy_match_min_gain = 0; + + assert (ahead >= 1); + lazy_match_min_gain += ahead; + + if (lit1 <= 3) + lazy_match_min_gain += (lit2 <= 3) ? 0 : 2; + else if (lit1 <= 18) + lazy_match_min_gain += (lit2 <= 18) ? 0 : 1; + + lazy_match_min_gain += (l2 - l1) * 2; + if (l3 > 0) + lazy_match_min_gain -= (ahead - l3) * 2; + + if (lazy_match_min_gain < 0) + lazy_match_min_gain = 0; + + return lazy_match_min_gain; +} + + +/*********************************************************************** +// +************************************************************************/ +#if defined(SWD_BEST_OFF) + +static void better_match(const lzo_swd_p swd, + unsigned *m_len, unsigned *m_off) +{ + if (*m_len <= M2_MIN_LEN) + return; + + if (*m_off <= M2_MAX_OFFSET) + return; + + /* M3/M4 -> M2 */ + if (*m_off > M2_MAX_OFFSET + && *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1 + && swd->best_off[*m_len-1] && swd->best_off[*m_len-1] <= M2_MAX_OFFSET + ) { + *m_len = *m_len - 1; + *m_off = swd->best_off[*m_len]; + return; + } + + /* M4 -> M2 */ + if (*m_off > M3_MAX_OFFSET + && *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2 + && swd->best_off[*m_len-2] && swd->best_off[*m_len-2] <= M2_MAX_OFFSET + ) { + *m_len = *m_len - 2; + *m_off = swd->best_off[*m_len]; + return; + } + /* M4 -> M3 */ + if (*m_off > M3_MAX_OFFSET + && *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1 + && swd->best_off[*m_len-1] && swd->best_off[*m_len-1] <= M3_MAX_OFFSET + ) { + *m_len = *m_len - 1; + *m_off = swd->best_off[*m_len]; + } +} + +#endif + + +/*********************************************************************** +// +************************************************************************/ +static int lzo1x_999_compress_internal(const uint8_t *in, unsigned in_len, + uint8_t *out, unsigned *out_len, + void *wrkmem, + unsigned good_length, + unsigned max_lazy, + unsigned max_chain, + uint32_t use_best_off) +{ + uint8_t *op; + const uint8_t *ii; + unsigned lit; + unsigned m_len, m_off; + lzo1x_999_t cc; + lzo1x_999_t *const c = &cc; + const lzo_swd_p swd = (lzo_swd_p) wrkmem; + int r; + + c->init = 0; + c->ip = c->in = in; + c->in_end = in + in_len; + c->out = out; + + op = out; + ii = c->ip; /* point to start of literal run */ + lit = 0; + c->r1_lit = 0; + + r = init_match(c, swd, use_best_off); + if (r != 0) + return r; + swd->max_chain = max_chain; + + r = find_match(c, swd, 0, 0); + if (r != 0) + return r; + + while (c->look > 0) { + unsigned ahead; + unsigned max_ahead; + int l1, l2, l3; + + m_len = c->m_len; + m_off = c->m_off; + + assert(c->bp == c->ip - c->look); + assert(c->bp >= in); + if (lit == 0) + ii = c->bp; + assert(ii + lit == c->bp); + assert(swd->b_char == *(c->bp)); + + if (m_len < 2 + || (m_len == 2 && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4)) + /* Do not accept this match for compressed-data compatibility + * with LZO v1.01 and before + * [ might be a problem for decompress() and optimize() ] + */ + || (m_len == 2 && op == out) + || (op == out && lit == 0) + ) { + /* a literal */ + m_len = 0; + } + else if (m_len == M2_MIN_LEN) { + /* compression ratio improves if we code a literal in some cases */ + if (m_off > MX_MAX_OFFSET && lit >= 4) + m_len = 0; + } + + if (m_len == 0) { + /* a literal */ + lit++; + swd->max_chain = max_chain; + r = find_match(c, swd, 1, 0); + assert(r == 0); + continue; + } + + /* a match */ +#if defined(SWD_BEST_OFF) + if (swd->use_best_off) + better_match(swd, &m_len, &m_off); +#endif + + /* shall we try a lazy match ? */ + ahead = 0; + if (m_len >= max_lazy) { + /* no */ + l1 = 0; + max_ahead = 0; + } else { + /* yes, try a lazy match */ + l1 = len_of_coded_match(m_len, m_off, lit); + assert(l1 > 0); + max_ahead = LZO_MIN(2, (unsigned)l1 - 1); + } + + + while (ahead < max_ahead && c->look > m_len) { + int lazy_match_min_gain; + + if (m_len >= good_length) + swd->max_chain = max_chain >> 2; + else + swd->max_chain = max_chain; + r = find_match(c, swd, 1, 0); + ahead++; + + assert(r == 0); + assert(c->look > 0); + assert(ii + lit + ahead == c->bp); + + if (c->m_len < m_len) + continue; + if (c->m_len == m_len && c->m_off >= m_off) + continue; +#if defined(SWD_BEST_OFF) + if (swd->use_best_off) + better_match(swd, &c->m_len, &c->m_off); +#endif + l2 = len_of_coded_match(c->m_len, c->m_off, lit+ahead); + if (l2 < 0) + continue; + + /* compressed-data compatibility [see above] */ + l3 = (op == out) ? -1 : len_of_coded_match(ahead, m_off, lit); + + lazy_match_min_gain = min_gain(ahead, lit, lit+ahead, l1, l2, l3); + if (c->m_len >= m_len + lazy_match_min_gain) { + if (l3 > 0) { + /* code previous run */ + op = code_run(c, op, ii, lit); + lit = 0; + /* code shortened match */ + op = code_match(c, op, ahead, m_off); + } else { + lit += ahead; + assert(ii + lit == c->bp); + } + goto lazy_match_done; + } + } + + assert(ii + lit + ahead == c->bp); + + /* 1 - code run */ + op = code_run(c, op, ii, lit); + lit = 0; + + /* 2 - code match */ + op = code_match(c, op, m_len, m_off); + swd->max_chain = max_chain; + r = find_match(c, swd, m_len, 1+ahead); + assert(r == 0); + + lazy_match_done: ; + } + + /* store final run */ + if (lit > 0) + op = STORE_RUN(c, op, ii, lit); + +#if defined(LZO_EOF_CODE) + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; +#endif + + *out_len = op - out; + + return LZO_E_OK; +} + + +/*********************************************************************** +// +************************************************************************/ +int lzo1x_999_compress_level(const uint8_t *in, unsigned in_len, + uint8_t *out, unsigned *out_len, + void *wrkmem, + int compression_level) +{ + static const struct { + uint16_t good_length; + uint16_t max_lazy; + uint16_t max_chain; + uint16_t use_best_off; + } c[3] = { + { 8, 32, 256, 0 }, + { 32, 128, 2048, 1 }, + { SWD_F, SWD_F, 4096, 1 } /* max. compression */ + }; + + if (compression_level < 7 || compression_level > 9) + return LZO_E_ERROR; + + compression_level -= 7; + return lzo1x_999_compress_internal(in, in_len, out, out_len, wrkmem, + c[compression_level].good_length, + c[compression_level].max_lazy, + c[compression_level].max_chain, + c[compression_level].use_best_off); +} diff --git a/archival/libarchive/lzo1x_c.c b/archival/libarchive/lzo1x_c.c new file mode 100644 index 000000000..cc86f74b1 --- /dev/null +++ b/archival/libarchive/lzo1x_c.c @@ -0,0 +1,296 @@ +/* implementation of the LZO1[XY]-1 compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/*********************************************************************** +// compress a block of data. +************************************************************************/ +static NOINLINE unsigned +do_compress(const uint8_t* in, unsigned in_len, + uint8_t* out, unsigned* out_len, + void* wrkmem) +{ + register const uint8_t* ip; + uint8_t* op; + const uint8_t* const in_end = in + in_len; + const uint8_t* const ip_end = in + in_len - M2_MAX_LEN - 5; + const uint8_t* ii; + const void* *const dict = (const void**) wrkmem; + + op = out; + ip = in; + ii = ip; + + ip += 4; + for (;;) { + register const uint8_t* m_pos; + unsigned m_off; + unsigned m_len; + unsigned dindex; + + D_INDEX1(dindex,ip); + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; +#if 1 + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + D_INDEX2(dindex,ip); +#endif + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + goto literal; + + try_match: +#if 1 && defined(LZO_UNALIGNED_OK_2) + if (* (const lzo_ushortp) m_pos != * (const lzo_ushortp) ip) +#else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) +#endif + { + } else { + if (m_pos[2] == ip[2]) { +#if 0 + if (m_off <= M2_MAX_OFFSET) + goto match; + if (lit <= 3) + goto match; + if (lit == 3) { /* better compression, but slower */ + assert(op - 2 > out); op[-2] |= (uint8_t)(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + if (m_pos[3] == ip[3]) +#endif + goto match; + } + else { + /* still need a better way for finding M1 matches */ +#if 0 + /* a M1 match */ +#if 0 + if (m_off <= M1_MAX_OFFSET && lit > 0 && lit <= 3) +#else + if (m_off <= M1_MAX_OFFSET && lit == 3) +#endif + { + register unsigned t; + + t = lit; + assert(op - 2 > out); op[-2] |= (uint8_t)(t); + do *op++ = *ii++; while (--t > 0); + assert(ii == ip); + m_off -= 1; + *op++ = (uint8_t)(M1_MARKER | ((m_off & 3) << 2)); + *op++ = (uint8_t)(m_off >> 2); + ip += 2; + goto match_done; + } +#endif + } + } + + /* a literal */ + literal: + UPDATE_I(dict, 0, dindex, ip, in); + ++ip; + if (ip >= ip_end) + break; + continue; + + /* a match */ +match: + UPDATE_I(dict, 0, dindex, ip, in); + /* store current literal run */ + if (pd(ip, ii) > 0) { + register unsigned t = pd(ip, ii); + + if (t <= 3) { + assert(op - 2 > out); + op[-2] |= (uint8_t)(t); + } + else if (t <= 18) + *op++ = (uint8_t)(t - 3); + else { + register unsigned tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = (uint8_t)(tt); + } + do *op++ = *ii++; while (--t > 0); + } + + /* code the match */ + assert(ii == ip); + ip += 3; + if (m_pos[3] != *ip++ || m_pos[4] != *ip++ || m_pos[5] != *ip++ + || m_pos[6] != *ip++ || m_pos[7] != *ip++ || m_pos[8] != *ip++ +#ifdef LZO1Y + || m_pos[ 9] != *ip++ || m_pos[10] != *ip++ || m_pos[11] != *ip++ + || m_pos[12] != *ip++ || m_pos[13] != *ip++ || m_pos[14] != *ip++ +#endif + ) { + --ip; + m_len = pd(ip, ii); + assert(m_len >= 3); + assert(m_len <= M2_MAX_LEN); + + if (m_off <= M2_MAX_OFFSET) { + m_off -= 1; +#if defined(LZO1X) + *op++ = (uint8_t)(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = (uint8_t)(m_off >> 3); +#elif defined(LZO1Y) + *op++ = (uint8_t)(((m_len + 1) << 4) | ((m_off & 3) << 2)); + *op++ = (uint8_t)(m_off >> 2); +#endif + } + else if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + *op++ = (uint8_t)(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } else { +#if defined(LZO1X) + m_off -= 0x4000; + assert(m_off > 0); + assert(m_off <= 0x7fff); + *op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; +#elif defined(LZO1Y) + goto m4_match; +#endif + } + } + else { + { + const uint8_t* end = in_end; + const uint8_t* m = m_pos + M2_MAX_LEN + 1; + while (ip < end && *m == *ip) + m++, ip++; + m_len = pd(ip, ii); + } + assert(m_len > M2_MAX_LEN); + + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= 33) + *op++ = (uint8_t)(M3_MARKER | (m_len - 2)); + else { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } else { +#if defined(LZO1Y) + m4_match: +#endif + m_off -= 0x4000; + assert(m_off > 0); + assert(m_off <= 0x7fff); + if (m_len <= M4_MAX_LEN) + *op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2)); + else { + m_len -= M4_MAX_LEN; + *op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11)); + m3_m4_len: + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = (uint8_t)(m_len); + } + } + m3_m4_offset: + *op++ = (uint8_t)((m_off & 63) << 2); + *op++ = (uint8_t)(m_off >> 6); + } +#if 0 + match_done: +#endif + ii = ip; + if (ip >= ip_end) + break; + } + + *out_len = pd(op, out); + return pd(in_end, ii); +} + +/*********************************************************************** +// public entry point +************************************************************************/ +int DO_COMPRESS(const uint8_t* in, unsigned in_len, + uint8_t* out, unsigned* out_len, + void* wrkmem) +{ + uint8_t* op = out; + unsigned t; + + if (in_len <= M2_MAX_LEN + 5) + t = in_len; + else { + t = do_compress(in,in_len,op,out_len,wrkmem); + op += *out_len; + } + + if (t > 0) { + const uint8_t* ii = in + in_len - t; + + if (op == out && t <= 238) + *op++ = (uint8_t)(17 + t); + else if (t <= 3) + op[-2] |= (uint8_t)(t); + else if (t <= 18) + *op++ = (uint8_t)(t - 3); + else { + unsigned tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = (uint8_t)(tt); + } + do *op++ = *ii++; while (--t > 0); + } + + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + + *out_len = pd(op, out); + return 0; /*LZO_E_OK*/ +} diff --git a/archival/libarchive/lzo1x_d.c b/archival/libarchive/lzo1x_d.c new file mode 100644 index 000000000..348a85510 --- /dev/null +++ b/archival/libarchive/lzo1x_d.c @@ -0,0 +1,420 @@ +/* implementation of the LZO1X decompression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libbb.h" +#include "liblzo.h" + +/*********************************************************************** +// decompress a block of data. +************************************************************************/ +/* safe decompression with overrun testing */ +int lzo1x_decompress_safe(const uint8_t* in, unsigned in_len, + uint8_t* out, unsigned* out_len, + void* wrkmem UNUSED_PARAM) +{ + register uint8_t* op; + register const uint8_t* ip; + register unsigned t; +#if defined(COPY_DICT) + unsigned m_off; + const uint8_t* dict_end; +#else + register const uint8_t* m_pos = NULL; /* possibly not needed */ +#endif + const uint8_t* const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + uint8_t* const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + unsigned last_m_off = 0; +#endif + +// LZO_UNUSED(wrkmem); + +#if defined(COPY_DICT) + if (dict) { + if (dict_len > M4_MAX_OFFSET) { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } else { + dict_len = 0; + dict_end = NULL; + } +#endif /* COPY_DICT */ + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert(t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + if (t == 0) { + NEED_IP(1); + while (*ip == 0) { + t += 255; + ip++; + NEED_IP(1); + } + t += 15 + *ip++; + } + /* copy literals */ + assert(t > 0); + NEED_OP(t+3); + NEED_IP(t+4); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK_4) + if (PTR_ALIGNED2_4(op, ip)) +# endif + { + COPY4(op, ip); + op += 4; + ip += 4; + if (--t > 0) { + if (t >= 4) { + do { + COPY4(op, ip); + op += 4; + ip += 4; + t -= 4; + } while (t >= 4); + if (t > 0) + do *op++ = *ip++; while (--t > 0); + } else { + do *op++ = *ip++; while (--t > 0); + } + } + } +# if !defined(LZO_UNALIGNED_OK_4) + else +# endif +#endif +#if !defined(LZO_UNALIGNED_OK_4) + { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + + first_literal_run: + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t,m_off) +#else /* !COPY_DICT */ +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(3); + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos; +#endif /* COPY_DICT */ + goto match_done; + + /* handle matches */ + do { + match: + if (t >= 64) { /* a M2 match */ +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else /* !COPY_DICT */ +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + unsigned off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) { + assert(last_m_off > 0); + m_pos -= last_m_off; + } else { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif /* COPY_DICT */ + } + else if (t >= 32) { /* a M3 match */ + t &= 31; + if (t == 0) { + NEED_IP(1); + while (*ip == 0) { + t += 255; + ip++; + NEED_IP(1); + } + t += 31 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else /* !COPY_DICT */ +#if defined(LZO1Z) + { + unsigned off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif /* COPY_DICT */ + ip += 2; + } + else if (t >= 16) { /* a M4 match */ +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else /* !COPY_DICT */ + m_pos = op; + m_pos -= (t & 8) << 11; +#endif /* COPY_DICT */ + t &= 7; + if (t == 0) { + NEED_IP(1); + while (*ip == 0) { + t += 255; + ip++; + NEED_IP(1); + } + t += 7 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else /* !COPY_DICT */ +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN) + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = pd((const uint8_t*)op, m_pos); +#endif +#endif /* COPY_DICT */ + } + else { /* a M1 match */ +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t,m_off) +#else /* !COPY_DICT */ +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(2); + *op++ = *m_pos++; + *op++ = *m_pos; +#endif /* COPY_DICT */ + goto match_done; + } + + /* copy match */ +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t,m_off) + +#else /* !COPY_DICT */ + + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK_4) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) { + assert((op - m_pos) >= 4); /* both pointers are aligned */ +# else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { +# endif + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) + do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { + copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif /* COPY_DICT */ + + match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + + /* copy literals */ + match_next: + assert(t > 0); + assert(t < 4); + NEED_OP(t); + NEED_IP(t+1); +#if 0 + do *op++ = *ip++; while (--t > 0); +#else + *op++ = *ip++; + if (t > 1) { + *op++ = *ip++; + if (t > 2) + *op++ = *ip++; + } +#endif + t = *ip++; + } while (TEST_IP && TEST_OP); + } + +//#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP) + /* no EOF code was found */ + *out_len = pd(op, out); + return LZO_E_EOF_NOT_FOUND; +//#endif + + eof_found: + assert(t == 1); + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +//#if defined(HAVE_NEED_IP) + input_overrun: + *out_len = pd(op, out); + return LZO_E_INPUT_OVERRUN; +//#endif + +//#if defined(HAVE_NEED_OP) + output_overrun: + *out_len = pd(op, out); + return LZO_E_OUTPUT_OVERRUN; +//#endif + +//#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) + lookbehind_overrun: + *out_len = pd(op, out); + return LZO_E_LOOKBEHIND_OVERRUN; +//#endif +} diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c new file mode 100644 index 000000000..26ae565f5 --- /dev/null +++ b/archival/libarchive/open_transformer.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +/* transformer(), more than meets the eye */ +/* + * On MMU machine, the transform_prog is removed by macro magic + * in include/archive.h. On NOMMU, transformer is removed. + */ +void FAST_FUNC open_transformer(int fd, + IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd), + const char *transform_prog) +{ + struct fd_pair fd_pipe; + int pid; + + xpiped_pair(fd_pipe); + pid = BB_MMU ? xfork() : xvfork(); + if (pid == 0) { + /* Child */ + close(fd_pipe.rd); /* we don't want to read from the parent */ + // FIXME: error check? +#if BB_MMU + transformer(fd, fd_pipe.wr); + if (ENABLE_FEATURE_CLEAN_UP) { + close(fd_pipe.wr); /* send EOF */ + close(fd); + } + /* must be _exit! bug was actually seen here */ + _exit(EXIT_SUCCESS); +#else + { + char *argv[4]; + xmove_fd(fd, 0); + xmove_fd(fd_pipe.wr, 1); + argv[0] = (char*)transform_prog; + argv[1] = (char*)"-cf"; + argv[2] = (char*)"-"; + argv[3] = NULL; + BB_EXECVP(transform_prog, argv); + bb_perror_msg_and_die("can't execute '%s'", transform_prog); + } +#endif + /* notreached */ + } + + /* parent process */ + close(fd_pipe.wr); /* don't want to write to the child */ + xmove_fd(fd_pipe.rd, fd); +} diff --git a/archival/libarchive/seek_by_jump.c b/archival/libarchive/seek_by_jump.c new file mode 100644 index 000000000..7c2c52ae1 --- /dev/null +++ b/archival/libarchive/seek_by_jump.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +void FAST_FUNC seek_by_jump(int fd, off_t amount) +{ + if (amount + && lseek(fd, amount, SEEK_CUR) == (off_t) -1 + ) { + if (errno == ESPIPE) + seek_by_read(fd, amount); + else + bb_perror_msg_and_die("seek failure"); + } +} diff --git a/archival/libarchive/seek_by_read.c b/archival/libarchive/seek_by_read.c new file mode 100644 index 000000000..ad931a8de --- /dev/null +++ b/archival/libarchive/seek_by_read.c @@ -0,0 +1,16 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" + +/* If we are reading through a pipe, or from stdin then we can't lseek, + * we must read and discard the data to skip over it. + */ +void FAST_FUNC seek_by_read(int fd, off_t amount) +{ + if (amount) + bb_copyfd_exact_size(fd, -1, amount); +} diff --git a/archival/libarchive/unpack_ar_archive.c b/archival/libarchive/unpack_ar_archive.c new file mode 100644 index 000000000..18dbfd54d --- /dev/null +++ b/archival/libarchive/unpack_ar_archive.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "archive.h" +#include "ar.h" + +void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive) +{ + char magic[7]; + + xread(ar_archive->src_fd, magic, AR_MAGIC_LEN); + if (strncmp(magic, AR_MAGIC, AR_MAGIC_LEN) != 0) { + bb_error_msg_and_die("invalid ar magic"); + } + ar_archive->offset += AR_MAGIC_LEN; + + while (get_header_ar(ar_archive) == EXIT_SUCCESS) + continue; +} diff --git a/archival/libarchive/unxz/README b/archival/libarchive/unxz/README new file mode 100644 index 000000000..c5972f6b8 --- /dev/null +++ b/archival/libarchive/unxz/README @@ -0,0 +1,135 @@ + +XZ Embedded +=========== + + XZ Embedded is a relatively small, limited implementation of the .xz + file format. Currently only decoding is implemented. + + XZ Embedded was written for use in the Linux kernel, but the code can + be easily used in other environments too, including regular userspace + applications. + + This README contains information that is useful only when the copy + of XZ Embedded isn't part of the Linux kernel tree. You should also + read linux/Documentation/xz.txt even if you aren't using XZ Embedded + as part of Linux; information in that file is not repeated in this + README. + +Compiling the Linux kernel module + + The xz_dec module depends on crc32 module, so make sure that you have + it enabled (CONFIG_CRC32). + + Building the xz_dec and xz_dec_test modules without support for BCJ + filters: + + cd linux/lib/xz + make -C /path/to/kernel/source \ + KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \ + CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m + + Building the xz_dec and xz_dec_test modules with support for BCJ + filters: + + cd linux/lib/xz + make -C /path/to/kernel/source \ + KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \ + CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \ + CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \ + CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \ + CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y + + If you want only one or a few of the BCJ filters, omit the appropriate + variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support + code shared between all BCJ filters. + + Most people don't need the xz_dec_test module. You can skip building + it by omitting CONFIG_XZ_DEC_TEST=m from the make command line. + +Compiler requirements + + XZ Embedded should compile as either GNU-C89 (used in the Linux + kernel) or with any C99 compiler. Getting the code to compile with + non-GNU C89 compiler or a C++ compiler should be quite easy as + long as there is a data type for unsigned 64-bit integer (or the + code is modified not to support large files, which needs some more + care than just using 32-bit integer instead of 64-bit). + + If you use GCC, try to use a recent version. For example, on x86, + xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when + compiled with GCC 4.3.3. + +Embedding into userspace applications + + To embed the XZ decoder, copy the following files into a single + directory in your source code tree: + + linux/include/linux/xz.h + linux/lib/xz/xz_crc32.c + linux/lib/xz/xz_dec_lzma2.c + linux/lib/xz/xz_dec_stream.c + linux/lib/xz/xz_lzma2.h + linux/lib/xz/xz_private.h + linux/lib/xz/xz_stream.h + userspace/xz_config.h + + Alternatively, xz.h may be placed into a different directory but then + that directory must be in the compiler include path when compiling + the .c files. + + Your code should use only the functions declared in xz.h. The rest of + the .h files are meant only for internal use in XZ Embedded. + + You may want to modify xz_config.h to be more suitable for your build + environment. Probably you should at least skim through it even if the + default file works as is. + +BCJ filter support + + If you want support for one or more BCJ filters, you need to copy also + linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate + #defines in xz_config.h or in compiler flags. You don't need these + #defines in the code that just uses XZ Embedded via xz.h, but having + them always #defined doesn't hurt either. + + #define Instruction set BCJ filter endianness + XZ_DEC_X86 x86 or x86-64 Little endian only + XZ_DEC_POWERPC PowerPC Big endian only + XZ_DEC_IA64 Itanium (IA-64) Big or little endian + XZ_DEC_ARM ARM Little endian only + XZ_DEC_ARMTHUMB ARM-Thumb Little endian only + XZ_DEC_SPARC SPARC Big or little endian + + While some architectures are (partially) bi-endian, the endianness + setting doesn't change the endianness of the instructions on all + architectures. That's why Itanium and SPARC filters work for both big + and little endian executables (Itanium has little endian instructions + and SPARC has big endian instructions). + + There currently is no filter for little endian PowerPC or big endian + ARM or ARM-Thumb. Implementing filters for them can be considered if + there is a need for such filters in real-world applications. + +Notes about shared libraries + + If you are including XZ Embedded into a shared library, you very + probably should rename the xz_* functions to prevent symbol + conflicts in case your library is linked against some other library + or application that also has XZ Embedded in it (which may even be + a different version of XZ Embedded). TODO: Provide an easy way + to do this. + + Please don't create a shared library of XZ Embedded itself unless + it is fine to rebuild everything depending on that shared library + everytime you upgrade to a newer version of XZ Embedded. There are + no API or ABI stability guarantees between different versions of + XZ Embedded. + +Specifying the calling convention + + XZ_FUNC macro was included to support declaring functions with __init + in Linux. Outside Linux, it can be used to specify the calling + convention on systems that support multiple calling conventions. + For example, on Windows, you may make all functions use the stdcall + calling convention by defining XZ_FUNC=__stdcall when building and + using the functions from XZ Embedded. diff --git a/archival/libarchive/unxz/xz.h b/archival/libarchive/unxz/xz.h new file mode 100644 index 000000000..c6c071c4a --- /dev/null +++ b/archival/libarchive/unxz/xz.h @@ -0,0 +1,271 @@ +/* + * XZ decompressor + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_H +#define XZ_H + +#ifdef __KERNEL__ +# include +# include +#else +# include +# include +#endif + +/* In Linux, this is used to make extern functions static when needed. */ +#ifndef XZ_EXTERN +# define XZ_EXTERN extern +#endif + +/* In Linux, this is used to mark the functions with __init when needed. */ +#ifndef XZ_FUNC +# define XZ_FUNC +#endif + +/** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ +enum xz_mode { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC +}; + +/** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * NOTE: This return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't support. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compressed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer + * is too small, or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ +enum xz_ret { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR +}; + +/** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and only + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ +struct xz_buf { + const uint8_t *in; + size_t in_pos; + size_t in_size; + + uint8_t *out; + size_t out_pos; + size_t out_size; +}; + +/** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ +struct xz_dec; + +/** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sense. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded using + * a big dictionary are not a problem in single-call mode. It is enough that + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is too + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be + * decoded to avoid allocating excessive amount of memory for the dictionary. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ +XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init( + enum xz_mode mode, uint32_t dict_max); + +/** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * NOTE: If an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified, and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filter + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect to + * get that amount valid data from the beginning of the stream. You must use + * the multi-call decoder if you don't want to uncompress the whole stream. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b); + +/** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ +XZ_EXTERN void XZ_FUNC xz_dec_reset(struct xz_dec *s); + +/** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ +XZ_EXTERN void XZ_FUNC xz_dec_end(struct xz_dec *s); + +/* + * Standalone build (userspace build or in-kernel build for boot time use) + * needs a CRC32 implementation. For normal in-kernel use, kernel's own + * CRC32 module is used instead, and users of this module don't need to + * care about the functions below. + */ +#ifndef XZ_INTERNAL_CRC32 +# ifdef __KERNEL__ +# define XZ_INTERNAL_CRC32 0 +# else +# define XZ_INTERNAL_CRC32 1 +# endif +#endif + +#if XZ_INTERNAL_CRC32 +/* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ +XZ_EXTERN void XZ_FUNC xz_crc32_init(void); + +/* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint32_t XZ_FUNC xz_crc32( + const uint8_t *buf, size_t size, uint32_t crc); +#endif +#endif diff --git a/archival/libarchive/unxz/xz_config.h b/archival/libarchive/unxz/xz_config.h new file mode 100644 index 000000000..187e1cbed --- /dev/null +++ b/archival/libarchive/unxz/xz_config.h @@ -0,0 +1,123 @@ +/* + * Private includes and definitions for userspace use of XZ Embedded + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_CONFIG_H +#define XZ_CONFIG_H + +/* Uncomment as needed to enable BCJ filter decoders. */ +/* #define XZ_DEC_X86 */ +/* #define XZ_DEC_POWERPC */ +/* #define XZ_DEC_IA64 */ +/* #define XZ_DEC_ARM */ +/* #define XZ_DEC_ARMTHUMB */ +/* #define XZ_DEC_SPARC */ + +#include +#include +#include + +#include "xz.h" + +#define kmalloc(size, flags) malloc(size) +#define kfree(ptr) free(ptr) +#define vmalloc(size) malloc(size) +#define vfree(ptr) free(ptr) + +#define memeq(a, b, size) (memcmp(a, b, size) == 0) +#define memzero(buf, size) memset(buf, 0, size) + +#undef min +#undef min_t +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define min_t(type, x, y) min(x, y) + +/* + * Some functions have been marked with __always_inline to keep the + * performance reasonable even when the compiler is optimizing for + * small code size. You may be able to save a few bytes by #defining + * __always_inline to plain inline, but don't complain if the code + * becomes slow. + * + * NOTE: System headers on GNU/Linux may #define this macro already, + * so if you want to change it, you need to #undef it first. + */ +#ifndef __always_inline +# ifdef __GNUC__ +# define __always_inline \ + inline __attribute__((__always_inline__)) +# else +# define __always_inline inline +# endif +#endif + +/* + * Some functions are marked to never be inlined to reduce stack usage. + * If you don't care about stack usage, you may want to modify this so + * that noinline_for_stack is #defined to be empty even when using GCC. + * Doing so may save a few bytes in binary size. + */ +#ifndef noinline_for_stack +# ifdef __GNUC__ +# define noinline_for_stack __attribute__((__noinline__)) +# else +# define noinline_for_stack +# endif +#endif + +/* Inline functions to access unaligned unsigned 32-bit integers */ +#ifndef get_unaligned_le32 +static inline uint32_t XZ_FUNC get_unaligned_le32(const uint8_t *buf) +{ + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +} +#endif + +#ifndef get_unaligned_be32 +static inline uint32_t XZ_FUNC get_unaligned_be32(const uint8_t *buf) +{ + return (uint32_t)(buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +} +#endif + +#ifndef put_unaligned_le32 +static inline void XZ_FUNC put_unaligned_le32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)val; + buf[1] = (uint8_t)(val >> 8); + buf[2] = (uint8_t)(val >> 16); + buf[3] = (uint8_t)(val >> 24); +} +#endif + +#ifndef put_unaligned_be32 +static inline void XZ_FUNC put_unaligned_be32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)(val >> 24); + buf[1] = (uint8_t)(val >> 16); + buf[2] = (uint8_t)(val >> 8); + buf[3] = (uint8_t)val; +} +#endif + +/* + * Use get_unaligned_le32() also for aligned access for simplicity. On + * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr)) + * could save a few bytes in code size. + */ +#ifndef get_le32 +# define get_le32 get_unaligned_le32 +#endif + +#endif diff --git a/archival/libarchive/unxz/xz_dec_bcj.c b/archival/libarchive/unxz/xz_dec_bcj.c new file mode 100644 index 000000000..09162b51f --- /dev/null +++ b/archival/libarchive/unxz/xz_dec_bcj.c @@ -0,0 +1,564 @@ +/* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" + +/* + * The rest of the file is inside this ifdef. It makes things a little more + * convenient when building without support for any BCJ filters. + */ +#ifdef XZ_DEC_BCJ + +struct xz_dec_bcj { + /* Type of the BCJ filter being used */ + enum { + BCJ_X86 = 4, /* x86 or x86-64 */ + BCJ_POWERPC = 5, /* Big endian only */ + BCJ_IA64 = 6, /* Big or little endian */ + BCJ_ARM = 7, /* Little endian only */ + BCJ_ARMTHUMB = 8, /* Little endian only */ + BCJ_SPARC = 9 /* Big or little endian */ + } type; + + /* + * Return value of the next filter in the chain. We need to preserve + * this information across calls, because we must not call the next + * filter anymore once it has returned XZ_STREAM_END. + */ + enum xz_ret ret; + + /* True if we are operating in single-call mode. */ + bool single_call; + + /* + * Absolute position relative to the beginning of the uncompressed + * data (in a single .xz Block). We care only about the lowest 32 + * bits so this doesn't need to be uint64_t even with big files. + */ + uint32_t pos; + + /* x86 filter state */ + uint32_t x86_prev_mask; + + /* Temporary space to hold the variables from struct xz_buf */ + uint8_t *out; + size_t out_pos; + size_t out_size; + + struct { + /* Amount of already filtered data in the beginning of buf */ + size_t filtered; + + /* Total amount of data currently stored in buf */ + size_t size; + + /* + * Buffer to hold a mix of filtered and unfiltered data. This + * needs to be big enough to hold Alignment + 2 * Look-ahead: + * + * Type Alignment Look-ahead + * x86 1 4 + * PowerPC 4 0 + * IA-64 16 0 + * ARM 4 0 + * ARM-Thumb 2 2 + * SPARC 4 0 + */ + uint8_t buf[16]; + } temp; +}; + +#ifdef XZ_DEC_X86 +/* + * This is macro used to test the most significant byte of a memory address + * in an x86 instruction. + */ +#define bcj_x86_test_msbyte(b) ((b) == 0x00 || (b) == 0xFF) + +static noinline_for_stack size_t XZ_FUNC bcj_x86( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + static const bool mask_to_allowed_status[8] + = { true, true, true, false, true, false, false, false }; + + static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 }; + + size_t i; + size_t prev_pos = (size_t)-1; + uint32_t prev_mask = s->x86_prev_mask; + uint32_t src; + uint32_t dest; + uint32_t j; + uint8_t b; + + if (size <= 4) + return 0; + + size -= 4; + for (i = 0; i < size; ++i) { + if ((buf[i] & 0xFE) != 0xE8) + continue; + + prev_pos = i - prev_pos; + if (prev_pos > 3) { + prev_mask = 0; + } else { + prev_mask = (prev_mask << (prev_pos - 1)) & 7; + if (prev_mask != 0) { + b = buf[i + 4 - mask_to_bit_num[prev_mask]]; + if (!mask_to_allowed_status[prev_mask] + || bcj_x86_test_msbyte(b)) { + prev_pos = i; + prev_mask = (prev_mask << 1) | 1; + continue; + } + } + } + + prev_pos = i; + + if (bcj_x86_test_msbyte(buf[i + 4])) { + src = get_unaligned_le32(buf + i + 1); + while (true) { + dest = src - (s->pos + (uint32_t)i + 5); + if (prev_mask == 0) + break; + + j = mask_to_bit_num[prev_mask] * 8; + b = (uint8_t)(dest >> (24 - j)); + if (!bcj_x86_test_msbyte(b)) + break; + + src = dest ^ (((uint32_t)1 << (32 - j)) - 1); + } + + dest &= 0x01FFFFFF; + dest |= (uint32_t)0 - (dest & 0x01000000); + put_unaligned_le32(dest, buf + i + 1); + i += 4; + } else { + prev_mask = (prev_mask << 1) | 1; + } + } + + prev_pos = i - prev_pos; + s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1); + return i; +} +#endif + +#ifdef XZ_DEC_POWERPC +static noinline_for_stack size_t XZ_FUNC bcj_powerpc( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_be32(buf + i); + if ((instr & 0xFC000003) == 0x48000001) { + instr &= 0x03FFFFFC; + instr -= s->pos + (uint32_t)i; + instr &= 0x03FFFFFC; + instr |= 0x48000001; + put_unaligned_be32(instr, buf + i); + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_IA64 +static noinline_for_stack size_t XZ_FUNC bcj_ia64( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + static const uint8_t branch_table[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 + }; + + /* + * The local variables take a little bit stack space, but it's less + * than what LZMA2 decoder takes, so it doesn't make sense to reduce + * stack usage here without doing that for the LZMA2 decoder too. + */ + + /* Loop counters */ + size_t i; + size_t j; + + /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */ + uint32_t slot; + + /* Bitwise offset of the instruction indicated by slot */ + uint32_t bit_pos; + + /* bit_pos split into byte and bit parts */ + uint32_t byte_pos; + uint32_t bit_res; + + /* Address part of an instruction */ + uint32_t addr; + + /* Mask used to detect which instructions to convert */ + uint32_t mask; + + /* 41-bit instruction stored somewhere in the lowest 48 bits */ + uint64_t instr; + + /* Instruction normalized with bit_res for easier manipulation */ + uint64_t norm; + + for (i = 0; i + 16 <= size; i += 16) { + mask = branch_table[buf[i] & 0x1F]; + for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) { + if (((mask >> slot) & 1) == 0) + continue; + + byte_pos = bit_pos >> 3; + bit_res = bit_pos & 7; + instr = 0; + for (j = 0; j < 6; ++j) + instr |= (uint64_t)(buf[i + j + byte_pos]) + << (8 * j); + + norm = instr >> bit_res; + + if (((norm >> 37) & 0x0F) == 0x05 + && ((norm >> 9) & 0x07) == 0) { + addr = (norm >> 13) & 0x0FFFFF; + addr |= ((uint32_t)(norm >> 36) & 1) << 20; + addr <<= 4; + addr -= s->pos + (uint32_t)i; + addr >>= 4; + + norm &= ~((uint64_t)0x8FFFFF << 13); + norm |= (uint64_t)(addr & 0x0FFFFF) << 13; + norm |= (uint64_t)(addr & 0x100000) + << (36 - 20); + + instr &= (1 << bit_res) - 1; + instr |= norm << bit_res; + + for (j = 0; j < 6; j++) + buf[i + j + byte_pos] + = (uint8_t)(instr >> (8 * j)); + } + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_ARM +static noinline_for_stack size_t XZ_FUNC bcj_arm( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + if (buf[i + 3] == 0xEB) { + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= s->pos + (uint32_t)i + 8; + addr >>= 2; + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_ARMTHUMB +static noinline_for_stack size_t XZ_FUNC bcj_armthumb( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 2) { + if ((buf[i + 1] & 0xF8) == 0xF0 + && (buf[i + 3] & 0xF8) == 0xF8) { + addr = (((uint32_t)buf[i + 1] & 0x07) << 19) + | ((uint32_t)buf[i] << 11) + | (((uint32_t)buf[i + 3] & 0x07) << 8) + | (uint32_t)buf[i + 2]; + addr <<= 1; + addr -= s->pos + (uint32_t)i + 4; + addr >>= 1; + buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07)); + buf[i] = (uint8_t)(addr >> 11); + buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07)); + buf[i + 2] = (uint8_t)addr; + i += 2; + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_SPARC +static noinline_for_stack size_t XZ_FUNC bcj_sparc( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_be32(buf + i); + if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) { + instr <<= 2; + instr -= s->pos + (uint32_t)i; + instr >>= 2; + instr = ((uint32_t)0x40000000 - (instr & 0x400000)) + | 0x40000000 | (instr & 0x3FFFFF); + put_unaligned_be32(instr, buf + i); + } + } + + return i; +} +#endif + +/* + * Apply the selected BCJ filter. Update *pos and s->pos to match the amount + * of data that got filtered. + * + * NOTE: This is implemented as a switch statement to avoid using function + * pointers, which could be problematic in the kernel boot code, which must + * avoid pointers to static data (at least on x86). + */ +static void XZ_FUNC bcj_apply(struct xz_dec_bcj *s, + uint8_t *buf, size_t *pos, size_t size) +{ + size_t filtered; + + buf += *pos; + size -= *pos; + + switch (s->type) { +#ifdef XZ_DEC_X86 + case BCJ_X86: + filtered = bcj_x86(s, buf, size); + break; +#endif +#ifdef XZ_DEC_POWERPC + case BCJ_POWERPC: + filtered = bcj_powerpc(s, buf, size); + break; +#endif +#ifdef XZ_DEC_IA64 + case BCJ_IA64: + filtered = bcj_ia64(s, buf, size); + break; +#endif +#ifdef XZ_DEC_ARM + case BCJ_ARM: + filtered = bcj_arm(s, buf, size); + break; +#endif +#ifdef XZ_DEC_ARMTHUMB + case BCJ_ARMTHUMB: + filtered = bcj_armthumb(s, buf, size); + break; +#endif +#ifdef XZ_DEC_SPARC + case BCJ_SPARC: + filtered = bcj_sparc(s, buf, size); + break; +#endif + default: + /* Never reached but silence compiler warnings. */ + filtered = 0; + break; + } + + *pos += filtered; + s->pos += filtered; +} + +/* + * Flush pending filtered data from temp to the output buffer. + * Move the remaining mixture of possibly filtered and unfiltered + * data to the beginning of temp. + */ +static void XZ_FUNC bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b) +{ + size_t copy_size; + + copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos); + memcpy(b->out + b->out_pos, s->temp.buf, copy_size); + b->out_pos += copy_size; + + s->temp.filtered -= copy_size; + s->temp.size -= copy_size; + memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size); +} + +/* + * The BCJ filter functions are primitive in sense that they process the + * data in chunks of 1-16 bytes. To hide this issue, this function does + * some buffering. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, struct xz_buf *b) +{ + size_t out_start; + + /* + * Flush pending already filtered data to the output buffer. Return + * immediatelly if we couldn't flush everything, or if the next + * filter in the chain had already returned XZ_STREAM_END. + */ + if (s->temp.filtered > 0) { + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + } + + /* + * If we have more output space than what is currently pending in + * temp, copy the unfiltered data from temp to the output buffer + * and try to fill the output buffer by decoding more data from the + * next filter in the chain. Apply the BCJ filter on the new data + * in the output buffer. If everything cannot be filtered, copy it + * to temp and rewind the output buffer position accordingly. + */ + if (s->temp.size < b->out_size - b->out_pos) { + out_start = b->out_pos; + memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size); + b->out_pos += s->temp.size; + + s->ret = xz_dec_lzma2_run(lzma2, b); + if (s->ret != XZ_STREAM_END + && (s->ret != XZ_OK || s->single_call)) + return s->ret; + + bcj_apply(s, b->out, &out_start, b->out_pos); + + /* + * As an exception, if the next filter returned XZ_STREAM_END, + * we can do that too, since the last few bytes that remain + * unfiltered are meant to remain unfiltered. + */ + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + + s->temp.size = b->out_pos - out_start; + b->out_pos -= s->temp.size; + memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size); + } + + /* + * If we have unfiltered data in temp, try to fill by decoding more + * data from the next filter. Apply the BCJ filter on temp. Then we + * hopefully can fill the actual output buffer by copying filtered + * data from temp. A mix of filtered and unfiltered data may be left + * in temp; it will be taken care on the next call to this function. + */ + if (s->temp.size > 0) { + /* Make b->out{,_pos,_size} temporarily point to s->temp. */ + s->out = b->out; + s->out_pos = b->out_pos; + s->out_size = b->out_size; + b->out = s->temp.buf; + b->out_pos = s->temp.size; + b->out_size = sizeof(s->temp.buf); + + s->ret = xz_dec_lzma2_run(lzma2, b); + + s->temp.size = b->out_pos; + b->out = s->out; + b->out_pos = s->out_pos; + b->out_size = s->out_size; + + if (s->ret != XZ_OK && s->ret != XZ_STREAM_END) + return s->ret; + + bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size); + + /* + * If the next filter returned XZ_STREAM_END, we mark that + * everything is filtered, since the last unfiltered bytes + * of the stream are meant to be left as is. + */ + if (s->ret == XZ_STREAM_END) + s->temp.filtered = s->temp.size; + + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + } + + return s->ret; +} + +XZ_EXTERN struct xz_dec_bcj * XZ_FUNC xz_dec_bcj_create(bool single_call) +{ + struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s != NULL) + s->single_call = single_call; + + return s; +} + +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_reset( + struct xz_dec_bcj *s, uint8_t id) +{ + switch (id) { +#ifdef XZ_DEC_X86 + case BCJ_X86: +#endif +#ifdef XZ_DEC_POWERPC + case BCJ_POWERPC: +#endif +#ifdef XZ_DEC_IA64 + case BCJ_IA64: +#endif +#ifdef XZ_DEC_ARM + case BCJ_ARM: +#endif +#ifdef XZ_DEC_ARMTHUMB + case BCJ_ARMTHUMB: +#endif +#ifdef XZ_DEC_SPARC + case BCJ_SPARC: +#endif + break; + + default: + /* Unsupported Filter ID */ + return XZ_OPTIONS_ERROR; + } + + s->type = id; + s->ret = XZ_OK; + s->pos = 0; + s->x86_prev_mask = 0; + s->temp.filtered = 0; + s->temp.size = 0; + + return XZ_OK; +} + +#endif diff --git a/archival/libarchive/unxz/xz_dec_lzma2.c b/archival/libarchive/unxz/xz_dec_lzma2.c new file mode 100644 index 000000000..da71cb4d4 --- /dev/null +++ b/archival/libarchive/unxz/xz_dec_lzma2.c @@ -0,0 +1,1175 @@ +/* + * LZMA2 decoder + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_lzma2.h" + +/* + * Range decoder initialization eats the first five bytes of each LZMA chunk. + */ +#define RC_INIT_BYTES 5 + +/* + * Minimum number of usable input buffer to safely decode one LZMA symbol. + * The worst case is that we decode 22 bits using probabilities and 26 + * direct bits. This may decode at maximum of 20 bytes of input. However, + * lzma_main() does an extra normalization before returning, thus we + * need to put 21 here. + */ +#define LZMA_IN_REQUIRED 21 + +/* + * Dictionary (history buffer) + * + * These are always true: + * start <= pos <= full <= end + * pos <= limit <= end + * + * In multi-call mode, also these are true: + * end == size + * size <= size_max + * allocated <= size + * + * Most of these variables are size_t to support single-call mode, + * in which the dictionary variables address the actual output + * buffer directly. + */ +struct dictionary { + /* Beginning of the history buffer */ + uint8_t *buf; + + /* Old position in buf (before decoding more data) */ + size_t start; + + /* Position in buf */ + size_t pos; + + /* + * How full dictionary is. This is used to detect corrupt input that + * would read beyond the beginning of the uncompressed stream. + */ + size_t full; + + /* Write limit; we don't write to buf[limit] or later bytes. */ + size_t limit; + + /* + * End of the dictionary buffer. In multi-call mode, this is + * the same as the dictionary size. In single-call mode, this + * indicates the size of the output buffer. + */ + size_t end; + + /* + * Size of the dictionary as specified in Block Header. This is used + * together with "full" to detect corrupt input that would make us + * read beyond the beginning of the uncompressed stream. + */ + uint32_t size; + + /* + * Maximum allowed dictionary size in multi-call mode. + * This is ignored in single-call mode. + */ + uint32_t size_max; + + /* + * Amount of memory currently allocated for the dictionary. + * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, + * size_max is always the same as the allocated size.) + */ + uint32_t allocated; + + /* Operation mode */ + enum xz_mode mode; +}; + +/* Range decoder */ +struct rc_dec { + uint32_t range; + uint32_t code; + + /* + * Number of initializing bytes remaining to be read + * by rc_read_init(). + */ + uint32_t init_bytes_left; + + /* + * Buffer from which we read our input. It can be either + * temp.buf or the caller-provided input buffer. + */ + const uint8_t *in; + size_t in_pos; + size_t in_limit; +}; + +/* Probabilities for a length decoder. */ +struct lzma_len_dec { + /* Probability of match length being at least 10 */ + uint16_t choice; + + /* Probability of match length being at least 18 */ + uint16_t choice2; + + /* Probabilities for match lengths 2-9 */ + uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; + + /* Probabilities for match lengths 10-17 */ + uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; + + /* Probabilities for match lengths 18-273 */ + uint16_t high[LEN_HIGH_SYMBOLS]; +}; + +struct lzma_dec { + /* Distances of latest four matches */ + uint32_t rep0; + uint32_t rep1; + uint32_t rep2; + uint32_t rep3; + + /* Types of the most recently seen LZMA symbols */ + enum lzma_state state; + + /* + * Length of a match. This is updated so that dict_repeat can + * be called again to finish repeating the whole match. + */ + uint32_t len; + + /* + * LZMA properties or related bit masks (number of literal + * context bits, a mask dervied from the number of literal + * position bits, and a mask dervied from the number + * position bits) + */ + uint32_t lc; + uint32_t literal_pos_mask; /* (1 << lp) - 1 */ + uint32_t pos_mask; /* (1 << pb) - 1 */ + + /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ + uint16_t is_match[STATES][POS_STATES_MAX]; + + /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */ + uint16_t is_rep[STATES]; + + /* + * If 0, distance of a repeated match is rep0. + * Otherwise check is_rep1. + */ + uint16_t is_rep0[STATES]; + + /* + * If 0, distance of a repeated match is rep1. + * Otherwise check is_rep2. + */ + uint16_t is_rep1[STATES]; + + /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */ + uint16_t is_rep2[STATES]; + + /* + * If 1, the repeated match has length of one byte. Otherwise + * the length is decoded from rep_len_decoder. + */ + uint16_t is_rep0_long[STATES][POS_STATES_MAX]; + + /* + * Probability tree for the highest two bits of the match + * distance. There is a separate probability tree for match + * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. + */ + uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; + + /* + * Probility trees for additional bits for match distance + * when the distance is in the range [4, 127]. + */ + uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; + + /* + * Probability tree for the lowest four bits of a match + * distance that is equal to or greater than 128. + */ + uint16_t dist_align[ALIGN_SIZE]; + + /* Length of a normal match */ + struct lzma_len_dec match_len_dec; + + /* Length of a repeated match */ + struct lzma_len_dec rep_len_dec; + + /* Probabilities of literals */ + uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; +}; + +struct lzma2_dec { + /* Position in xz_dec_lzma2_run(). */ + enum lzma2_seq { + SEQ_CONTROL, + SEQ_UNCOMPRESSED_1, + SEQ_UNCOMPRESSED_2, + SEQ_COMPRESSED_0, + SEQ_COMPRESSED_1, + SEQ_PROPERTIES, + SEQ_LZMA_PREPARE, + SEQ_LZMA_RUN, + SEQ_COPY + } sequence; + + /* Next position after decoding the compressed size of the chunk. */ + enum lzma2_seq next_sequence; + + /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ + uint32_t uncompressed; + + /* + * Compressed size of LZMA chunk or compressed/uncompressed + * size of uncompressed chunk (64 KiB at maximum) + */ + uint32_t compressed; + + /* + * True if dictionary reset is needed. This is false before + * the first chunk (LZMA or uncompressed). + */ + bool need_dict_reset; + + /* + * True if new LZMA properties are needed. This is false + * before the first LZMA chunk. + */ + bool need_props; +}; + +struct xz_dec_lzma2 { + /* + * The order below is important on x86 to reduce code size and + * it shouldn't hurt on other platforms. Everything up to and + * including lzma.pos_mask are in the first 128 bytes on x86-32, + * which allows using smaller instructions to access those + * variables. On x86-64, fewer variables fit into the first 128 + * bytes, but this is still the best order without sacrificing + * the readability by splitting the structures. + */ + struct rc_dec rc; + struct dictionary dict; + struct lzma2_dec lzma2; + struct lzma_dec lzma; + + /* + * Temporary buffer which holds small number of input bytes between + * decoder calls. See lzma2_lzma() for details. + */ + struct { + uint32_t size; + uint8_t buf[3 * LZMA_IN_REQUIRED]; + } temp; +}; + +/************** + * Dictionary * + **************/ + +/* + * Reset the dictionary state. When in single-call mode, set up the beginning + * of the dictionary to point to the actual output buffer. + */ +static void XZ_FUNC dict_reset(struct dictionary *dict, struct xz_buf *b) +{ + if (DEC_IS_SINGLE(dict->mode)) { + dict->buf = b->out + b->out_pos; + dict->end = b->out_size - b->out_pos; + } + + dict->start = 0; + dict->pos = 0; + dict->limit = 0; + dict->full = 0; +} + +/* Set dictionary write limit */ +static void XZ_FUNC dict_limit(struct dictionary *dict, size_t out_max) +{ + if (dict->end - dict->pos <= out_max) + dict->limit = dict->end; + else + dict->limit = dict->pos + out_max; +} + +/* Return true if at least one byte can be written into the dictionary. */ +static __always_inline bool XZ_FUNC dict_has_space(const struct dictionary *dict) +{ + return dict->pos < dict->limit; +} + +/* + * Get a byte from the dictionary at the given distance. The distance is + * assumed to valid, or as a special case, zero when the dictionary is + * still empty. This special case is needed for single-call decoding to + * avoid writing a '\0' to the end of the destination buffer. + */ +static __always_inline uint32_t XZ_FUNC dict_get( + const struct dictionary *dict, uint32_t dist) +{ + size_t offset = dict->pos - dist - 1; + + if (dist >= dict->pos) + offset += dict->end; + + return dict->full > 0 ? dict->buf[offset] : 0; +} + +/* + * Put one byte into the dictionary. It is assumed that there is space for it. + */ +static inline void XZ_FUNC dict_put(struct dictionary *dict, uint8_t byte) +{ + dict->buf[dict->pos++] = byte; + + if (dict->full < dict->pos) + dict->full = dict->pos; +} + +/* + * Repeat given number of bytes from the given distance. If the distance is + * invalid, false is returned. On success, true is returned and *len is + * updated to indicate how many bytes were left to be repeated. + */ +static bool XZ_FUNC dict_repeat( + struct dictionary *dict, uint32_t *len, uint32_t dist) +{ + size_t back; + uint32_t left; + + if (dist >= dict->full || dist >= dict->size) + return false; + + left = min_t(size_t, dict->limit - dict->pos, *len); + *len -= left; + + back = dict->pos - dist - 1; + if (dist >= dict->pos) + back += dict->end; + + do { + dict->buf[dict->pos++] = dict->buf[back++]; + if (back == dict->end) + back = 0; + } while (--left > 0); + + if (dict->full < dict->pos) + dict->full = dict->pos; + + return true; +} + +/* Copy uncompressed data as is from input to dictionary and output buffers. */ +static void XZ_FUNC dict_uncompressed( + struct dictionary *dict, struct xz_buf *b, uint32_t *left) +{ + size_t copy_size; + + while (*left > 0 && b->in_pos < b->in_size + && b->out_pos < b->out_size) { + copy_size = min(b->in_size - b->in_pos, + b->out_size - b->out_pos); + if (copy_size > dict->end - dict->pos) + copy_size = dict->end - dict->pos; + if (copy_size > *left) + copy_size = *left; + + *left -= copy_size; + + memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size); + dict->pos += copy_size; + + if (dict->full < dict->pos) + dict->full = dict->pos; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, b->in + b->in_pos, + copy_size); + } + + dict->start = dict->pos; + + b->out_pos += copy_size; + b->in_pos += copy_size; + + } +} + +/* + * Flush pending data from dictionary to b->out. It is assumed that there is + * enough space in b->out. This is guaranteed because caller uses dict_limit() + * before decoding data into the dictionary. + */ +static uint32_t XZ_FUNC dict_flush(struct dictionary *dict, struct xz_buf *b) +{ + size_t copy_size = dict->pos - dict->start; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, dict->buf + dict->start, + copy_size); + } + + dict->start = dict->pos; + b->out_pos += copy_size; + return copy_size; +} + +/***************** + * Range decoder * + *****************/ + +/* Reset the range decoder. */ +static void XZ_FUNC rc_reset(struct rc_dec *rc) +{ + rc->range = (uint32_t)-1; + rc->code = 0; + rc->init_bytes_left = RC_INIT_BYTES; +} + +/* + * Read the first five initial bytes into rc->code if they haven't been + * read already. (Yes, the first byte gets completely ignored.) + */ +static bool XZ_FUNC rc_read_init(struct rc_dec *rc, struct xz_buf *b) +{ + while (rc->init_bytes_left > 0) { + if (b->in_pos == b->in_size) + return false; + + rc->code = (rc->code << 8) + b->in[b->in_pos++]; + --rc->init_bytes_left; + } + + return true; +} + +/* Return true if there may not be enough input for the next decoding loop. */ +static inline bool XZ_FUNC rc_limit_exceeded(const struct rc_dec *rc) +{ + return rc->in_pos > rc->in_limit; +} + +/* + * Return true if it is possible (from point of view of range decoder) that + * we have reached the end of the LZMA chunk. + */ +static inline bool XZ_FUNC rc_is_finished(const struct rc_dec *rc) +{ + return rc->code == 0; +} + +/* Read the next input byte if needed. */ +static __always_inline void XZ_FUNC rc_normalize(struct rc_dec *rc) +{ + if (rc->range < RC_TOP_VALUE) { + rc->range <<= RC_SHIFT_BITS; + rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++]; + } +} + +/* + * Decode one bit. In some versions, this function has been splitted in three + * functions so that the compiler is supposed to be able to more easily avoid + * an extra branch. In this particular version of the LZMA decoder, this + * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3 + * on x86). Using a non-splitted version results in nicer looking code too. + * + * NOTE: This must return an int. Do not make it return a bool or the speed + * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care, + * and it generates 10-20 % faster code than GCC 3.x from this file anyway.) + */ +static __always_inline int XZ_FUNC rc_bit(struct rc_dec *rc, uint16_t *prob) +{ + uint32_t bound; + int bit; + + rc_normalize(rc); + bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; + if (rc->code < bound) { + rc->range = bound; + *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; + bit = 0; + } else { + rc->range -= bound; + rc->code -= bound; + *prob -= *prob >> RC_MOVE_BITS; + bit = 1; + } + + return bit; +} + +/* Decode a bittree starting from the most significant bit. */ +static __always_inline uint32_t XZ_FUNC rc_bittree( + struct rc_dec *rc, uint16_t *probs, uint32_t limit) +{ + uint32_t symbol = 1; + + do { + if (rc_bit(rc, &probs[symbol])) + symbol = (symbol << 1) + 1; + else + symbol <<= 1; + } while (symbol < limit); + + return symbol; +} + +/* Decode a bittree starting from the least significant bit. */ +static __always_inline void XZ_FUNC rc_bittree_reverse(struct rc_dec *rc, + uint16_t *probs, uint32_t *dest, uint32_t limit) +{ + uint32_t symbol = 1; + uint32_t i = 0; + + do { + if (rc_bit(rc, &probs[symbol])) { + symbol = (symbol << 1) + 1; + *dest += 1 << i; + } else { + symbol <<= 1; + } + } while (++i < limit); +} + +/* Decode direct bits (fixed fifty-fifty probability) */ +static inline void XZ_FUNC rc_direct( + struct rc_dec *rc, uint32_t *dest, uint32_t limit) +{ + uint32_t mask; + + do { + rc_normalize(rc); + rc->range >>= 1; + rc->code -= rc->range; + mask = (uint32_t)0 - (rc->code >> 31); + rc->code += rc->range & mask; + *dest = (*dest << 1) + (mask + 1); + } while (--limit > 0); +} + +/******** + * LZMA * + ********/ + +/* Get pointer to literal coder probability array. */ +static uint16_t * XZ_FUNC lzma_literal_probs(struct xz_dec_lzma2 *s) +{ + uint32_t prev_byte = dict_get(&s->dict, 0); + uint32_t low = prev_byte >> (8 - s->lzma.lc); + uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc; + return s->lzma.literal[low + high]; +} + +/* Decode a literal (one 8-bit byte) */ +static void XZ_FUNC lzma_literal(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + uint32_t symbol; + uint32_t match_byte; + uint32_t match_bit; + uint32_t offset; + uint32_t i; + + probs = lzma_literal_probs(s); + + if (lzma_state_is_literal(s->lzma.state)) { + symbol = rc_bittree(&s->rc, probs, 0x100); + } else { + symbol = 1; + match_byte = dict_get(&s->dict, s->lzma.rep0) << 1; + offset = 0x100; + + do { + match_bit = match_byte & offset; + match_byte <<= 1; + i = offset + match_bit + symbol; + + if (rc_bit(&s->rc, &probs[i])) { + symbol = (symbol << 1) + 1; + offset &= match_bit; + } else { + symbol <<= 1; + offset &= ~match_bit; + } + } while (symbol < 0x100); + } + + dict_put(&s->dict, (uint8_t)symbol); + lzma_state_literal(&s->lzma.state); +} + +/* Decode the length of the match into s->lzma.len. */ +static void XZ_FUNC lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, + uint32_t pos_state) +{ + uint16_t *probs; + uint32_t limit; + + if (!rc_bit(&s->rc, &l->choice)) { + probs = l->low[pos_state]; + limit = LEN_LOW_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN; + } else { + if (!rc_bit(&s->rc, &l->choice2)) { + probs = l->mid[pos_state]; + limit = LEN_MID_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS; + } else { + probs = l->high; + limit = LEN_HIGH_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS + + LEN_MID_SYMBOLS; + } + } + + s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit; +} + +/* Decode a match. The distance will be stored in s->lzma.rep0. */ +static void XZ_FUNC lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint16_t *probs; + uint32_t dist_slot; + uint32_t limit; + + lzma_state_match(&s->lzma.state); + + s->lzma.rep3 = s->lzma.rep2; + s->lzma.rep2 = s->lzma.rep1; + s->lzma.rep1 = s->lzma.rep0; + + lzma_len(s, &s->lzma.match_len_dec, pos_state); + + probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; + dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; + + if (dist_slot < DIST_MODEL_START) { + s->lzma.rep0 = dist_slot; + } else { + limit = (dist_slot >> 1) - 1; + s->lzma.rep0 = 2 + (dist_slot & 1); + + if (dist_slot < DIST_MODEL_END) { + s->lzma.rep0 <<= limit; + probs = s->lzma.dist_special + s->lzma.rep0 + - dist_slot - 1; + rc_bittree_reverse(&s->rc, probs, + &s->lzma.rep0, limit); + } else { + rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS); + s->lzma.rep0 <<= ALIGN_BITS; + rc_bittree_reverse(&s->rc, s->lzma.dist_align, + &s->lzma.rep0, ALIGN_BITS); + } + } +} + +/* + * Decode a repeated match. The distance is one of the four most recently + * seen matches. The distance will be stored in s->lzma.rep0. + */ +static void XZ_FUNC lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint32_t tmp; + + if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) { + if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[ + s->lzma.state][pos_state])) { + lzma_state_short_rep(&s->lzma.state); + s->lzma.len = 1; + return; + } + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) { + tmp = s->lzma.rep1; + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) { + tmp = s->lzma.rep2; + } else { + tmp = s->lzma.rep3; + s->lzma.rep3 = s->lzma.rep2; + } + + s->lzma.rep2 = s->lzma.rep1; + } + + s->lzma.rep1 = s->lzma.rep0; + s->lzma.rep0 = tmp; + } + + lzma_state_long_rep(&s->lzma.state); + lzma_len(s, &s->lzma.rep_len_dec, pos_state); +} + +/* LZMA decoder core */ +static bool XZ_FUNC lzma_main(struct xz_dec_lzma2 *s) +{ + uint32_t pos_state; + + /* + * If the dictionary was reached during the previous call, try to + * finish the possibly pending repeat in the dictionary. + */ + if (dict_has_space(&s->dict) && s->lzma.len > 0) + dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); + + /* + * Decode more LZMA symbols. One iteration may consume up to + * LZMA_IN_REQUIRED - 1 bytes. + */ + while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) { + pos_state = s->dict.pos & s->lzma.pos_mask; + + if (!rc_bit(&s->rc, &s->lzma.is_match[ + s->lzma.state][pos_state])) { + lzma_literal(s); + } else { + if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) + lzma_rep_match(s, pos_state); + else + lzma_match(s, pos_state); + + if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0)) + return false; + } + } + + /* + * Having the range decoder always normalized when we are outside + * this function makes it easier to correctly handle end of the chunk. + */ + rc_normalize(&s->rc); + + return true; +} + +/* + * Reset the LZMA decoder and range decoder state. Dictionary is nore reset + * here, because LZMA state may be reset without resetting the dictionary. + */ +static void XZ_FUNC lzma_reset(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + size_t i; + + s->lzma.state = STATE_LIT_LIT; + s->lzma.rep0 = 0; + s->lzma.rep1 = 0; + s->lzma.rep2 = 0; + s->lzma.rep3 = 0; + + /* + * All probabilities are initialized to the same value. This hack + * makes the code smaller by avoiding a separate loop for each + * probability array. + * + * This could be optimized so that only that part of literal + * probabilities that are actually required. In the common case + * we would write 12 KiB less. + */ + probs = s->lzma.is_match[0]; + for (i = 0; i < PROBS_TOTAL; ++i) + probs[i] = RC_BIT_MODEL_TOTAL / 2; + + rc_reset(&s->rc); +} + +/* + * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks + * from the decoded lp and pb values. On success, the LZMA decoder state is + * reset and true is returned. + */ +static bool XZ_FUNC lzma_props(struct xz_dec_lzma2 *s, uint8_t props) +{ + if (props > (4 * 5 + 4) * 9 + 8) + return false; + + s->lzma.pos_mask = 0; + while (props >= 9 * 5) { + props -= 9 * 5; + ++s->lzma.pos_mask; + } + + s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1; + + s->lzma.literal_pos_mask = 0; + while (props >= 9) { + props -= 9; + ++s->lzma.literal_pos_mask; + } + + s->lzma.lc = props; + + if (s->lzma.lc + s->lzma.literal_pos_mask > 4) + return false; + + s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1; + + lzma_reset(s); + + return true; +} + +/********* + * LZMA2 * + *********/ + +/* + * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't + * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This + * wrapper function takes care of making the LZMA decoder's assumption safe. + * + * As long as there is plenty of input left to be decoded in the current LZMA + * chunk, we decode directly from the caller-supplied input buffer until + * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into + * s->temp.buf, which (hopefully) gets filled on the next call to this + * function. We decode a few bytes from the temporary buffer so that we can + * continue decoding from the caller-supplied input buffer again. + */ +static bool XZ_FUNC lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + size_t in_avail; + uint32_t tmp; + + in_avail = b->in_size - b->in_pos; + if (s->temp.size > 0 || s->lzma2.compressed == 0) { + tmp = 2 * LZMA_IN_REQUIRED - s->temp.size; + if (tmp > s->lzma2.compressed - s->temp.size) + tmp = s->lzma2.compressed - s->temp.size; + if (tmp > in_avail) + tmp = in_avail; + + memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); + + if (s->temp.size + tmp == s->lzma2.compressed) { + memzero(s->temp.buf + s->temp.size + tmp, + sizeof(s->temp.buf) + - s->temp.size - tmp); + s->rc.in_limit = s->temp.size + tmp; + } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) { + s->temp.size += tmp; + b->in_pos += tmp; + return true; + } else { + s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED; + } + + s->rc.in = s->temp.buf; + s->rc.in_pos = 0; + + if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) + return false; + + s->lzma2.compressed -= s->rc.in_pos; + + if (s->rc.in_pos < s->temp.size) { + s->temp.size -= s->rc.in_pos; + memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, + s->temp.size); + return true; + } + + b->in_pos += s->rc.in_pos - s->temp.size; + s->temp.size = 0; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail >= LZMA_IN_REQUIRED) { + s->rc.in = b->in; + s->rc.in_pos = b->in_pos; + + if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED) + s->rc.in_limit = b->in_pos + s->lzma2.compressed; + else + s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED; + + if (!lzma_main(s)) + return false; + + in_avail = s->rc.in_pos - b->in_pos; + if (in_avail > s->lzma2.compressed) + return false; + + s->lzma2.compressed -= in_avail; + b->in_pos = s->rc.in_pos; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail < LZMA_IN_REQUIRED) { + if (in_avail > s->lzma2.compressed) + in_avail = s->lzma2.compressed; + + memcpy(s->temp.buf, b->in + b->in_pos, in_avail); + s->temp.size = in_avail; + b->in_pos += in_avail; + } + + return true; +} + +/* + * Take care of the LZMA2 control layer, and forward the job of actual LZMA + * decoding or copying of uncompressed chunks to other functions. + */ +XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run( + struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + uint32_t tmp; + + while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) { + switch (s->lzma2.sequence) { + case SEQ_CONTROL: + /* + * LZMA2 control byte + * + * Exact values: + * 0x00 End marker + * 0x01 Dictionary reset followed by + * an uncompressed chunk + * 0x02 Uncompressed chunk (no dictionary reset) + * + * Highest three bits (s->control & 0xE0): + * 0xE0 Dictionary reset, new properties and state + * reset, followed by LZMA compressed chunk + * 0xC0 New properties and state reset, followed + * by LZMA compressed chunk (no dictionary + * reset) + * 0xA0 State reset using old properties, + * followed by LZMA compressed chunk (no + * dictionary reset) + * 0x80 LZMA chunk (no dictionary or state reset) + * + * For LZMA compressed chunks, the lowest five bits + * (s->control & 1F) are the highest bits of the + * uncompressed size (bits 16-20). + * + * A new LZMA2 stream must begin with a dictionary + * reset. The first LZMA chunk must set new + * properties and reset the LZMA state. + * + * Values that don't match anything described above + * are invalid and we return XZ_DATA_ERROR. + */ + tmp = b->in[b->in_pos++]; + + if (tmp >= 0xE0 || tmp == 0x01) { + s->lzma2.need_props = true; + s->lzma2.need_dict_reset = false; + dict_reset(&s->dict, b); + } else if (s->lzma2.need_dict_reset) { + return XZ_DATA_ERROR; + } + + if (tmp >= 0x80) { + s->lzma2.uncompressed = (tmp & 0x1F) << 16; + s->lzma2.sequence = SEQ_UNCOMPRESSED_1; + + if (tmp >= 0xC0) { + /* + * When there are new properties, + * state reset is done at + * SEQ_PROPERTIES. + */ + s->lzma2.need_props = false; + s->lzma2.next_sequence + = SEQ_PROPERTIES; + + } else if (s->lzma2.need_props) { + return XZ_DATA_ERROR; + + } else { + s->lzma2.next_sequence + = SEQ_LZMA_PREPARE; + if (tmp >= 0xA0) + lzma_reset(s); + } + } else { + if (tmp == 0x00) + return XZ_STREAM_END; + + if (tmp > 0x02) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_COMPRESSED_0; + s->lzma2.next_sequence = SEQ_COPY; + } + + break; + + case SEQ_UNCOMPRESSED_1: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_UNCOMPRESSED_2; + break; + + case SEQ_UNCOMPRESSED_2: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = SEQ_COMPRESSED_0; + break; + + case SEQ_COMPRESSED_0: + s->lzma2.compressed + = (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_COMPRESSED_1; + break; + + case SEQ_COMPRESSED_1: + s->lzma2.compressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = s->lzma2.next_sequence; + break; + + case SEQ_PROPERTIES: + if (!lzma_props(s, b->in[b->in_pos++])) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_LZMA_PREPARE; + + case SEQ_LZMA_PREPARE: + if (s->lzma2.compressed < RC_INIT_BYTES) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -= RC_INIT_BYTES; + s->lzma2.sequence = SEQ_LZMA_RUN; + + case SEQ_LZMA_RUN: + /* + * Set dictionary limit to indicate how much we want + * to be encoded at maximum. Decode new data into the + * dictionary. Flush the new data from dictionary to + * b->out. Check if we finished decoding this chunk. + * In case the dictionary got full but we didn't fill + * the output buffer yet, we may run this loop + * multiple times without changing s->lzma2.sequence. + */ + dict_limit(&s->dict, min_t(size_t, + b->out_size - b->out_pos, + s->lzma2.uncompressed)); + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -= dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed == 0) { + if (s->lzma2.compressed > 0 || s->lzma.len > 0 + || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + + rc_reset(&s->rc); + s->lzma2.sequence = SEQ_CONTROL; + + } else if (b->out_pos == b->out_size + || (b->in_pos == b->in_size + && s->temp.size + < s->lzma2.compressed)) { + return XZ_OK; + } + + break; + + case SEQ_COPY: + dict_uncompressed(&s->dict, b, &s->lzma2.compressed); + if (s->lzma2.compressed > 0) + return XZ_OK; + + s->lzma2.sequence = SEQ_CONTROL; + break; + } + } + + return XZ_OK; +} + +XZ_EXTERN struct xz_dec_lzma2 * XZ_FUNC xz_dec_lzma2_create( + enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->dict.mode = mode; + s->dict.size_max = dict_max; + + if (DEC_IS_PREALLOC(mode)) { + s->dict.buf = vmalloc(dict_max); + if (s->dict.buf == NULL) { + kfree(s); + return NULL; + } + } else if (DEC_IS_DYNALLOC(mode)) { + s->dict.buf = NULL; + s->dict.allocated = 0; + } + + return s; +} + +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_reset( + struct xz_dec_lzma2 *s, uint8_t props) +{ + /* This limits dictionary size to 3 GiB to keep parsing simpler. */ + if (props > 39) + return XZ_OPTIONS_ERROR; + + s->dict.size = 2 + (props & 1); + s->dict.size <<= (props >> 1) + 11; + + if (DEC_IS_MULTI(s->dict.mode)) { + if (s->dict.size > s->dict.size_max) + return XZ_MEMLIMIT_ERROR; + + s->dict.end = s->dict.size; + + if (DEC_IS_DYNALLOC(s->dict.mode)) { + if (s->dict.allocated < s->dict.size) { + vfree(s->dict.buf); + s->dict.buf = vmalloc(s->dict.size); + if (s->dict.buf == NULL) { + s->dict.allocated = 0; + return XZ_MEM_ERROR; + } + } + } + } + + s->lzma.len = 0; + + s->lzma2.sequence = SEQ_CONTROL; + s->lzma2.need_dict_reset = true; + + s->temp.size = 0; + + return XZ_OK; +} + +XZ_EXTERN void XZ_FUNC xz_dec_lzma2_end(struct xz_dec_lzma2 *s) +{ + if (DEC_IS_MULTI(s->dict.mode)) + vfree(s->dict.buf); + + kfree(s); +} diff --git a/archival/libarchive/unxz/xz_dec_stream.c b/archival/libarchive/unxz/xz_dec_stream.c new file mode 100644 index 000000000..bdcbf1ba3 --- /dev/null +++ b/archival/libarchive/unxz/xz_dec_stream.c @@ -0,0 +1,822 @@ +/* + * .xz Stream decoder + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_stream.h" + +/* Hash used to validate the Index field */ +struct xz_dec_hash { + vli_type unpadded; + vli_type uncompressed; + uint32_t crc32; +}; + +struct xz_dec { + /* Position in dec_main() */ + enum { + SEQ_STREAM_HEADER, + SEQ_BLOCK_START, + SEQ_BLOCK_HEADER, + SEQ_BLOCK_UNCOMPRESS, + SEQ_BLOCK_PADDING, + SEQ_BLOCK_CHECK, + SEQ_INDEX, + SEQ_INDEX_PADDING, + SEQ_INDEX_CRC32, + SEQ_STREAM_FOOTER + } sequence; + + /* Position in variable-length integers and Check fields */ + uint32_t pos; + + /* Variable-length integer decoded by dec_vli() */ + vli_type vli; + + /* Saved in_pos and out_pos */ + size_t in_start; + size_t out_start; + + /* CRC32 value in Block or Index */ + uint32_t crc32; + + /* Type of the integrity check calculated from uncompressed data */ + enum xz_check check_type; + + /* Operation mode */ + enum xz_mode mode; + + /* + * True if the next call to xz_dec_run() is allowed to return + * XZ_BUF_ERROR. + */ + bool allow_buf_error; + + /* Information stored in Block Header */ + struct { + /* + * Value stored in the Compressed Size field, or + * VLI_UNKNOWN if Compressed Size is not present. + */ + vli_type compressed; + + /* + * Value stored in the Uncompressed Size field, or + * VLI_UNKNOWN if Uncompressed Size is not present. + */ + vli_type uncompressed; + + /* Size of the Block Header field */ + uint32_t size; + } block_header; + + /* Information collected when decoding Blocks */ + struct { + /* Observed compressed size of the current Block */ + vli_type compressed; + + /* Observed uncompressed size of the current Block */ + vli_type uncompressed; + + /* Number of Blocks decoded so far */ + vli_type count; + + /* + * Hash calculated from the Block sizes. This is used to + * validate the Index field. + */ + struct xz_dec_hash hash; + } block; + + /* Variables needed when verifying the Index field */ + struct { + /* Position in dec_index() */ + enum { + SEQ_INDEX_COUNT, + SEQ_INDEX_UNPADDED, + SEQ_INDEX_UNCOMPRESSED + } sequence; + + /* Size of the Index in bytes */ + vli_type size; + + /* Number of Records (matches block.count in valid files) */ + vli_type count; + + /* + * Hash calculated from the Records (matches block.hash in + * valid files). + */ + struct xz_dec_hash hash; + } index; + + /* + * Temporary buffer needed to hold Stream Header, Block Header, + * and Stream Footer. The Block Header is the biggest (1 KiB) + * so we reserve space according to that. buf[] has to be aligned + * to a multiple of four bytes; the size_t variables before it + * should guarantee this. + */ + struct { + size_t pos; + size_t size; + uint8_t buf[1024]; + } temp; + + struct xz_dec_lzma2 *lzma2; + +#ifdef XZ_DEC_BCJ + struct xz_dec_bcj *bcj; + bool bcj_active; +#endif +}; + +#ifdef XZ_DEC_ANY_CHECK +/* Sizes of the Check field with different Check IDs */ +static const uint8_t check_sizes[16] = { + 0, + 4, 4, 4, + 8, 8, 8, + 16, 16, 16, + 32, 32, 32, + 64, 64, 64 +}; +#endif + +/* + * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller + * must have set s->temp.pos to indicate how much data we are supposed + * to copy into s->temp.buf. Return true once s->temp.pos has reached + * s->temp.size. + */ +static bool XZ_FUNC fill_temp(struct xz_dec *s, struct xz_buf *b) +{ + size_t copy_size = min_t(size_t, + b->in_size - b->in_pos, s->temp.size - s->temp.pos); + + memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); + b->in_pos += copy_size; + s->temp.pos += copy_size; + + if (s->temp.pos == s->temp.size) { + s->temp.pos = 0; + return true; + } + + return false; +} + +/* Decode a variable-length integer (little-endian base-128 encoding) */ +static enum xz_ret XZ_FUNC dec_vli(struct xz_dec *s, + const uint8_t *in, size_t *in_pos, size_t in_size) +{ + uint8_t byte; + + if (s->pos == 0) + s->vli = 0; + + while (*in_pos < in_size) { + byte = in[*in_pos]; + ++*in_pos; + + s->vli |= (vli_type)(byte & 0x7F) << s->pos; + + if ((byte & 0x80) == 0) { + /* Don't allow non-minimal encodings. */ + if (byte == 0 && s->pos != 0) + return XZ_DATA_ERROR; + + s->pos = 0; + return XZ_STREAM_END; + } + + s->pos += 7; + if (s->pos == 7 * VLI_BYTES_MAX) + return XZ_DATA_ERROR; + } + + return XZ_OK; +} + +/* + * Decode the Compressed Data field from a Block. Update and validate + * the observed compressed and uncompressed sizes of the Block so that + * they don't exceed the values possibly stored in the Block Header + * (validation assumes that no integer overflow occurs, since vli_type + * is normally uint64_t). Update the CRC32 if presence of the CRC32 + * field was indicated in Stream Header. + * + * Once the decoding is finished, validate that the observed sizes match + * the sizes possibly stored in the Block Header. Update the hash and + * Block count, which are later used to validate the Index field. + */ +static enum xz_ret XZ_FUNC dec_block(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + s->in_start = b->in_pos; + s->out_start = b->out_pos; + +#ifdef XZ_DEC_BCJ + if (s->bcj_active) + ret = xz_dec_bcj_run(s->bcj, s->lzma2, b); + else +#endif + ret = xz_dec_lzma2_run(s->lzma2, b); + + s->block.compressed += b->in_pos - s->in_start; + s->block.uncompressed += b->out_pos - s->out_start; + + /* + * There is no need to separately check for VLI_UNKNOWN, since + * the observed sizes are always smaller than VLI_UNKNOWN. + */ + if (s->block.compressed > s->block_header.compressed + || s->block.uncompressed + > s->block_header.uncompressed) + return XZ_DATA_ERROR; + + if (s->check_type == XZ_CHECK_CRC32) + s->crc32 = xz_crc32(b->out + s->out_start, + b->out_pos - s->out_start, s->crc32); + + if (ret == XZ_STREAM_END) { + if (s->block_header.compressed != VLI_UNKNOWN + && s->block_header.compressed + != s->block.compressed) + return XZ_DATA_ERROR; + + if (s->block_header.uncompressed != VLI_UNKNOWN + && s->block_header.uncompressed + != s->block.uncompressed) + return XZ_DATA_ERROR; + + s->block.hash.unpadded += s->block_header.size + + s->block.compressed; + +#ifdef XZ_DEC_ANY_CHECK + s->block.hash.unpadded += check_sizes[s->check_type]; +#else + if (s->check_type == XZ_CHECK_CRC32) + s->block.hash.unpadded += 4; +#endif + + s->block.hash.uncompressed += s->block.uncompressed; + s->block.hash.crc32 = xz_crc32( + (const uint8_t *)&s->block.hash, + sizeof(s->block.hash), s->block.hash.crc32); + + ++s->block.count; + } + + return ret; +} + +/* Update the Index size and the CRC32 value. */ +static void XZ_FUNC index_update(struct xz_dec *s, const struct xz_buf *b) +{ + size_t in_used = b->in_pos - s->in_start; + s->index.size += in_used; + s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32); +} + +/* + * Decode the Number of Records, Unpadded Size, and Uncompressed Size + * fields from the Index field. That is, Index Padding and CRC32 are not + * decoded by this function. + * + * This can return XZ_OK (more input needed), XZ_STREAM_END (everything + * successfully decoded), or XZ_DATA_ERROR (input is corrupt). + */ +static enum xz_ret XZ_FUNC dec_index(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + do { + ret = dec_vli(s, b->in, &b->in_pos, b->in_size); + if (ret != XZ_STREAM_END) { + index_update(s, b); + return ret; + } + + switch (s->index.sequence) { + case SEQ_INDEX_COUNT: + s->index.count = s->vli; + + /* + * Validate that the Number of Records field + * indicates the same number of Records as + * there were Blocks in the Stream. + */ + if (s->index.count != s->block.count) + return XZ_DATA_ERROR; + + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + + case SEQ_INDEX_UNPADDED: + s->index.hash.unpadded += s->vli; + s->index.sequence = SEQ_INDEX_UNCOMPRESSED; + break; + + case SEQ_INDEX_UNCOMPRESSED: + s->index.hash.uncompressed += s->vli; + s->index.hash.crc32 = xz_crc32( + (const uint8_t *)&s->index.hash, + sizeof(s->index.hash), + s->index.hash.crc32); + --s->index.count; + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + } + } while (s->index.count > 0); + + return XZ_STREAM_END; +} + +/* + * Validate that the next four input bytes match the value of s->crc32. + * s->pos must be zero when starting to validate the first byte. + */ +static enum xz_ret XZ_FUNC crc32_validate(struct xz_dec *s, struct xz_buf *b) +{ + do { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++]) + return XZ_DATA_ERROR; + + s->pos += 8; + + } while (s->pos < 32); + + s->crc32 = 0; + s->pos = 0; + + return XZ_STREAM_END; +} + +#ifdef XZ_DEC_ANY_CHECK +/* + * Skip over the Check field when the Check ID is not supported. + * Returns true once the whole Check field has been skipped over. + */ +static bool XZ_FUNC check_skip(struct xz_dec *s, struct xz_buf *b) +{ + while (s->pos < check_sizes[s->check_type]) { + if (b->in_pos == b->in_size) + return false; + + ++b->in_pos; + ++s->pos; + } + + s->pos = 0; + + return true; +} +#endif + +/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */ +static enum xz_ret XZ_FUNC dec_stream_header(struct xz_dec *s) +{ + if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) + return XZ_FORMAT_ERROR; + + if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) + != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) + return XZ_DATA_ERROR; + + if (s->temp.buf[HEADER_MAGIC_SIZE] != 0) + return XZ_OPTIONS_ERROR; + + /* + * Of integrity checks, we support only none (Check ID = 0) and + * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined, + * we will accept other check types too, but then the check won't + * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given. + */ + s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1]; + +#ifdef XZ_DEC_ANY_CHECK + if (s->check_type > XZ_CHECK_MAX) + return XZ_OPTIONS_ERROR; + + if (s->check_type > XZ_CHECK_CRC32) + return XZ_UNSUPPORTED_CHECK; +#else + if (s->check_type > XZ_CHECK_CRC32) + return XZ_OPTIONS_ERROR; +#endif + + return XZ_OK; +} + +/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */ +static enum xz_ret XZ_FUNC dec_stream_footer(struct xz_dec *s) +{ + if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) + return XZ_DATA_ERROR; + + if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf)) + return XZ_DATA_ERROR; + + /* + * Validate Backward Size. Note that we never added the size of the + * Index CRC32 field to s->index.size, thus we use s->index.size / 4 + * instead of s->index.size / 4 - 1. + */ + if ((s->index.size >> 2) != get_le32(s->temp.buf + 4)) + return XZ_DATA_ERROR; + + if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type) + return XZ_DATA_ERROR; + + /* + * Use XZ_STREAM_END instead of XZ_OK to be more convenient + * for the caller. + */ + return XZ_STREAM_END; +} + +/* Decode the Block Header and initialize the filter chain. */ +static enum xz_ret XZ_FUNC dec_block_header(struct xz_dec *s) +{ + enum xz_ret ret; + + /* + * Validate the CRC32. We know that the temp buffer is at least + * eight bytes so this is safe. + */ + s->temp.size -= 4; + if (xz_crc32(s->temp.buf, s->temp.size, 0) + != get_le32(s->temp.buf + s->temp.size)) + return XZ_DATA_ERROR; + + s->temp.pos = 2; + + /* + * Catch unsupported Block Flags. We support only one or two filters + * in the chain, so we catch that with the same test. + */ +#ifdef XZ_DEC_BCJ + if (s->temp.buf[1] & 0x3E) +#else + if (s->temp.buf[1] & 0x3F) +#endif + return XZ_OPTIONS_ERROR; + + /* Compressed Size */ + if (s->temp.buf[1] & 0x40) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.compressed = s->vli; + } else { + s->block_header.compressed = VLI_UNKNOWN; + } + + /* Uncompressed Size */ + if (s->temp.buf[1] & 0x80) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.uncompressed = s->vli; + } else { + s->block_header.uncompressed = VLI_UNKNOWN; + } + +#ifdef XZ_DEC_BCJ + /* If there are two filters, the first one must be a BCJ filter. */ + s->bcj_active = s->temp.buf[1] & 0x01; + if (s->bcj_active) { + if (s->temp.size - s->temp.pos < 2) + return XZ_OPTIONS_ERROR; + + ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* + * We don't support custom start offset, + * so Size of Properties must be zero. + */ + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + } +#endif + + /* Valid Filter Flags always take at least two bytes. */ + if (s->temp.size - s->temp.pos < 2) + return XZ_DATA_ERROR; + + /* Filter ID = LZMA2 */ + if (s->temp.buf[s->temp.pos++] != 0x21) + return XZ_OPTIONS_ERROR; + + /* Size of Properties = 1-byte Filter Properties */ + if (s->temp.buf[s->temp.pos++] != 0x01) + return XZ_OPTIONS_ERROR; + + /* Filter Properties contains LZMA2 dictionary size. */ + if (s->temp.size - s->temp.pos < 1) + return XZ_DATA_ERROR; + + ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* The rest must be Header Padding. */ + while (s->temp.pos < s->temp.size) + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + + s->temp.pos = 0; + s->block.compressed = 0; + s->block.uncompressed = 0; + + return XZ_OK; +} + +static enum xz_ret XZ_FUNC dec_main(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + /* + * Store the start position for the case when we are in the middle + * of the Index field. + */ + s->in_start = b->in_pos; + + while (true) { + switch (s->sequence) { + case SEQ_STREAM_HEADER: + /* + * Stream Header is copied to s->temp, and then + * decoded from there. This way if the caller + * gives us only little input at a time, we can + * still keep the Stream Header decoding code + * simple. Similar approach is used in many places + * in this file. + */ + if (!fill_temp(s, b)) + return XZ_OK; + + /* + * If dec_stream_header() returns + * XZ_UNSUPPORTED_CHECK, it is still possible + * to continue decoding if working in multi-call + * mode. Thus, update s->sequence before calling + * dec_stream_header(). + */ + s->sequence = SEQ_BLOCK_START; + + ret = dec_stream_header(s); + if (ret != XZ_OK) + return ret; + + case SEQ_BLOCK_START: + /* We need one byte of input to continue. */ + if (b->in_pos == b->in_size) + return XZ_OK; + + /* See if this is the beginning of the Index field. */ + if (b->in[b->in_pos] == 0) { + s->in_start = b->in_pos++; + s->sequence = SEQ_INDEX; + break; + } + + /* + * Calculate the size of the Block Header and + * prepare to decode it. + */ + s->block_header.size + = ((uint32_t)b->in[b->in_pos] + 1) * 4; + + s->temp.size = s->block_header.size; + s->temp.pos = 0; + s->sequence = SEQ_BLOCK_HEADER; + + case SEQ_BLOCK_HEADER: + if (!fill_temp(s, b)) + return XZ_OK; + + ret = dec_block_header(s); + if (ret != XZ_OK) + return ret; + + s->sequence = SEQ_BLOCK_UNCOMPRESS; + + case SEQ_BLOCK_UNCOMPRESS: + ret = dec_block(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_BLOCK_PADDING; + + case SEQ_BLOCK_PADDING: + /* + * Size of Compressed Data + Block Padding + * must be a multiple of four. We don't need + * s->block.compressed for anything else + * anymore, so we use it here to test the size + * of the Block Padding field. + */ + while (s->block.compressed & 3) { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + + ++s->block.compressed; + } + + s->sequence = SEQ_BLOCK_CHECK; + + case SEQ_BLOCK_CHECK: + if (s->check_type == XZ_CHECK_CRC32) { + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + } +#ifdef XZ_DEC_ANY_CHECK + else if (!check_skip(s, b)) { + return XZ_OK; + } +#endif + + s->sequence = SEQ_BLOCK_START; + break; + + case SEQ_INDEX: + ret = dec_index(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_INDEX_PADDING; + + case SEQ_INDEX_PADDING: + while ((s->index.size + (b->in_pos - s->in_start)) + & 3) { + if (b->in_pos == b->in_size) { + index_update(s, b); + return XZ_OK; + } + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + } + + /* Finish the CRC32 value and Index size. */ + index_update(s, b); + + /* Compare the hashes to validate the Index field. */ + if (!memeq(&s->block.hash, &s->index.hash, + sizeof(s->block.hash))) + return XZ_DATA_ERROR; + + s->sequence = SEQ_INDEX_CRC32; + + case SEQ_INDEX_CRC32: + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->temp.size = STREAM_HEADER_SIZE; + s->sequence = SEQ_STREAM_FOOTER; + + case SEQ_STREAM_FOOTER: + if (!fill_temp(s, b)) + return XZ_OK; + + return dec_stream_footer(s); + } + } + + /* Never reached */ +} + +/* + * xz_dec_run() is a wrapper for dec_main() to handle some special cases in + * multi-call and single-call decoding. + * + * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we + * are not going to make any progress anymore. This is to prevent the caller + * from calling us infinitely when the input file is truncated or otherwise + * corrupt. Since zlib-style API allows that the caller fills the input buffer + * only when the decoder doesn't produce any new output, we have to be careful + * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only + * after the second consecutive call to xz_dec_run() that makes no progress. + * + * In single-call mode, if we couldn't decode everything and no error + * occurred, either the input is truncated or the output buffer is too small. + * Since we know that the last input byte never produces any output, we know + * that if all the input was consumed and decoding wasn't finished, the file + * must be corrupt. Otherwise the output buffer has to be too small or the + * file is corrupt in a way that decoding it produces too big output. + * + * If single-call decoding fails, we reset b->in_pos and b->out_pos back to + * their original values. This is because with some filter chains there won't + * be any valid uncompressed data in the output buffer unless the decoding + * actually succeeds (that's the price to pay of using the output buffer as + * the workspace). + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b) +{ + size_t in_start; + size_t out_start; + enum xz_ret ret; + + if (DEC_IS_SINGLE(s->mode)) + xz_dec_reset(s); + + in_start = b->in_pos; + out_start = b->out_pos; + ret = dec_main(s, b); + + if (DEC_IS_SINGLE(s->mode)) { + if (ret == XZ_OK) + ret = b->in_pos == b->in_size + ? XZ_DATA_ERROR : XZ_BUF_ERROR; + + if (ret != XZ_STREAM_END) { + b->in_pos = in_start; + b->out_pos = out_start; + } + + } else if (ret == XZ_OK && in_start == b->in_pos + && out_start == b->out_pos) { + if (s->allow_buf_error) + ret = XZ_BUF_ERROR; + + s->allow_buf_error = true; + } else { + s->allow_buf_error = false; + } + + return ret; +} + +XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init( + enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->mode = mode; + +#ifdef XZ_DEC_BCJ + s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode)); + if (s->bcj == NULL) + goto error_bcj; +#endif + + s->lzma2 = xz_dec_lzma2_create(mode, dict_max); + if (s->lzma2 == NULL) + goto error_lzma2; + + xz_dec_reset(s); + return s; + +error_lzma2: +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +error_bcj: +#endif + kfree(s); + return NULL; +} + +XZ_EXTERN void XZ_FUNC xz_dec_reset(struct xz_dec *s) +{ + s->sequence = SEQ_STREAM_HEADER; + s->allow_buf_error = false; + s->pos = 0; + s->crc32 = 0; + memzero(&s->block, sizeof(s->block)); + memzero(&s->index, sizeof(s->index)); + s->temp.pos = 0; + s->temp.size = STREAM_HEADER_SIZE; +} + +XZ_EXTERN void XZ_FUNC xz_dec_end(struct xz_dec *s) +{ + if (s != NULL) { + xz_dec_lzma2_end(s->lzma2); +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +#endif + kfree(s); + } +} diff --git a/archival/libarchive/unxz/xz_lzma2.h b/archival/libarchive/unxz/xz_lzma2.h new file mode 100644 index 000000000..47f21afbc --- /dev/null +++ b/archival/libarchive/unxz/xz_lzma2.h @@ -0,0 +1,204 @@ +/* + * LZMA2 definitions + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_LZMA2_H +#define XZ_LZMA2_H + +/* Range coder constants */ +#define RC_SHIFT_BITS 8 +#define RC_TOP_BITS 24 +#define RC_TOP_VALUE (1 << RC_TOP_BITS) +#define RC_BIT_MODEL_TOTAL_BITS 11 +#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS) +#define RC_MOVE_BITS 5 + +/* + * Maximum number of position states. A position state is the lowest pb + * number of bits of the current uncompressed offset. In some places there + * are different sets of probabilities for different position states. + */ +#define POS_STATES_MAX (1 << 4) + +/* + * This enum is used to track which LZMA symbols have occurred most recently + * and in which order. This information is used to predict the next symbol. + * + * Symbols: + * - Literal: One 8-bit byte + * - Match: Repeat a chunk of data at some distance + * - Long repeat: Multi-byte match at a recently seen distance + * - Short repeat: One-byte repeat at a recently seen distance + * + * The symbol names are in from STATE_oldest_older_previous. REP means + * either short or long repeated match, and NONLIT means any non-literal. + */ +enum lzma_state { + STATE_LIT_LIT, + STATE_MATCH_LIT_LIT, + STATE_REP_LIT_LIT, + STATE_SHORTREP_LIT_LIT, + STATE_MATCH_LIT, + STATE_REP_LIT, + STATE_SHORTREP_LIT, + STATE_LIT_MATCH, + STATE_LIT_LONGREP, + STATE_LIT_SHORTREP, + STATE_NONLIT_MATCH, + STATE_NONLIT_REP +}; + +/* Total number of states */ +#define STATES 12 + +/* The lowest 7 states indicate that the previous state was a literal. */ +#define LIT_STATES 7 + +/* Indicate that the latest symbol was a literal. */ +static inline void XZ_FUNC lzma_state_literal(enum lzma_state *state) +{ + if (*state <= STATE_SHORTREP_LIT_LIT) + *state = STATE_LIT_LIT; + else if (*state <= STATE_LIT_SHORTREP) + *state -= 3; + else + *state -= 6; +} + +/* Indicate that the latest symbol was a match. */ +static inline void XZ_FUNC lzma_state_match(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; +} + +/* Indicate that the latest state was a long repeated match. */ +static inline void XZ_FUNC lzma_state_long_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; +} + +/* Indicate that the latest symbol was a short match. */ +static inline void XZ_FUNC lzma_state_short_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; +} + +/* Test if the previous symbol was a literal. */ +static inline bool XZ_FUNC lzma_state_is_literal(enum lzma_state state) +{ + return state < LIT_STATES; +} + +/* Each literal coder is divided in three sections: + * - 0x001-0x0FF: Without match byte + * - 0x101-0x1FF: With match byte; match bit is 0 + * - 0x201-0x2FF: With match byte; match bit is 1 + * + * Match byte is used when the previous LZMA symbol was something else than + * a literal (that is, it was some kind of match). + */ +#define LITERAL_CODER_SIZE 0x300 + +/* Maximum number of literal coders */ +#define LITERAL_CODERS_MAX (1 << 4) + +/* Minimum length of a match is two bytes. */ +#define MATCH_LEN_MIN 2 + +/* Match length is encoded with 4, 5, or 10 bits. + * + * Length Bits + * 2-9 4 = Choice=0 + 3 bits + * 10-17 5 = Choice=1 + Choice2=0 + 3 bits + * 18-273 10 = Choice=1 + Choice2=1 + 8 bits + */ +#define LEN_LOW_BITS 3 +#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS) +#define LEN_MID_BITS 3 +#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS) +#define LEN_HIGH_BITS 8 +#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS) +#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS) + +/* + * Maximum length of a match is 273 which is a result of the encoding + * described above. + */ +#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1) + +/* + * Different sets of probabilities are used for match distances that have + * very short match length: Lengths of 2, 3, and 4 bytes have a separate + * set of probabilities for each length. The matches with longer length + * use a shared set of probabilities. + */ +#define DIST_STATES 4 + +/* + * Get the index of the appropriate probability array for decoding + * the distance slot. + */ +static inline uint32_t XZ_FUNC lzma_get_dist_state(uint32_t len) +{ + return len < DIST_STATES + MATCH_LEN_MIN + ? len - MATCH_LEN_MIN : DIST_STATES - 1; +} + +/* + * The highest two bits of a 32-bit match distance are encoded using six bits. + * This six-bit value is called a distance slot. This way encoding a 32-bit + * value takes 6-36 bits, larger values taking more bits. + */ +#define DIST_SLOT_BITS 6 +#define DIST_SLOTS (1 << DIST_SLOT_BITS) + +/* Match distances up to 127 are fully encoded using probabilities. Since + * the highest two bits (distance slot) are always encoded using six bits, + * the distances 0-3 don't need any additional bits to encode, since the + * distance slot itself is the same as the actual distance. DIST_MODEL_START + * indicates the first distance slot where at least one additional bit is + * needed. + */ +#define DIST_MODEL_START 4 + +/* + * Match distances greater than 127 are encoded in three pieces: + * - distance slot: the highest two bits + * - direct bits: 2-26 bits below the highest two bits + * - alignment bits: four lowest bits + * + * Direct bits don't use any probabilities. + * + * The distance slot value of 14 is for distances 128-191. + */ +#define DIST_MODEL_END 14 + +/* Distance slots that indicate a distance <= 127. */ +#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2) +#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS) + +/* + * For match distances greater than 127, only the highest two bits and the + * lowest four bits (alignment) is encoded using probabilities. + */ +#define ALIGN_BITS 4 +#define ALIGN_SIZE (1 << ALIGN_BITS) +#define ALIGN_MASK (ALIGN_SIZE - 1) + +/* Total number of all probability variables */ +#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE) + +/* + * LZMA remembers the four most recent match distances. Reusing these + * distances tends to take less space than re-encoding the actual + * distance value. + */ +#define REPS 4 + +#endif diff --git a/archival/libarchive/unxz/xz_private.h b/archival/libarchive/unxz/xz_private.h new file mode 100644 index 000000000..145649a83 --- /dev/null +++ b/archival/libarchive/unxz/xz_private.h @@ -0,0 +1,159 @@ +/* + * Private includes and definitions + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_PRIVATE_H +#define XZ_PRIVATE_H + +#ifdef __KERNEL__ + /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ +# ifndef XZ_PREBOOT +# include +# include +# include +# define memeq(a, b, size) (memcmp(a, b, size) == 0) +# define memzero(buf, size) memset(buf, 0, size) +# endif +# include +# include +# define get_le32(p) le32_to_cpup((const uint32_t *)(p)) + /* XZ_IGNORE_KCONFIG may be defined only via decompress_unxz.c. */ +# ifndef XZ_IGNORE_KCONFIG +# ifdef CONFIG_XZ_DEC_X86 +# define XZ_DEC_X86 +# endif +# ifdef CONFIG_XZ_DEC_POWERPC +# define XZ_DEC_POWERPC +# endif +# ifdef CONFIG_XZ_DEC_IA64 +# define XZ_DEC_IA64 +# endif +# ifdef CONFIG_XZ_DEC_ARM +# define XZ_DEC_ARM +# endif +# ifdef CONFIG_XZ_DEC_ARMTHUMB +# define XZ_DEC_ARMTHUMB +# endif +# ifdef CONFIG_XZ_DEC_SPARC +# define XZ_DEC_SPARC +# endif +# endif +# include +#else + /* + * For userspace builds, use a separate header to define the required + * macros and functions. This makes it easier to adapt the code into + * different environments and avoids clutter in the Linux kernel tree. + */ +# include "xz_config.h" +#endif + +/* If no specific decoding mode is requested, enable support for all modes. */ +#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ + && !defined(XZ_DEC_DYNALLOC) +# define XZ_DEC_SINGLE +# define XZ_DEC_PREALLOC +# define XZ_DEC_DYNALLOC +#endif + +/* + * The DEC_IS_foo(mode) macros are used in "if" statements. If only some + * of the supported modes are enabled, these macros will evaluate to true or + * false at compile time and thus allow the compiler to omit unneeded code. + */ +#ifdef XZ_DEC_SINGLE +# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE) +#else +# define DEC_IS_SINGLE(mode) (false) +#endif + +#ifdef XZ_DEC_PREALLOC +# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC) +#else +# define DEC_IS_PREALLOC(mode) (false) +#endif + +#ifdef XZ_DEC_DYNALLOC +# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC) +#else +# define DEC_IS_DYNALLOC(mode) (false) +#endif + +#if !defined(XZ_DEC_SINGLE) +# define DEC_IS_MULTI(mode) (true) +#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC) +# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE) +#else +# define DEC_IS_MULTI(mode) (false) +#endif + +/* + * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ. + * XZ_DEC_BCJ is used to enable generic support for BCJ decoders. + */ +#ifndef XZ_DEC_BCJ +# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ + || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ + || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ + || defined(XZ_DEC_SPARC) +# define XZ_DEC_BCJ +# endif +#endif + +/* + * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used + * before calling xz_dec_lzma2_run(). + */ +XZ_EXTERN struct xz_dec_lzma2 * XZ_FUNC xz_dec_lzma2_create( + enum xz_mode mode, uint32_t dict_max); + +/* + * Decode the LZMA2 properties (one byte) and reset the decoder. Return + * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not + * big enough, and XZ_OPTIONS_ERROR if props indicates something that this + * decoder doesn't support. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_reset( + struct xz_dec_lzma2 *s, uint8_t props); + +/* Decode raw LZMA2 stream from b->in to b->out. */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_run( + struct xz_dec_lzma2 *s, struct xz_buf *b); + +/* Free the memory allocated for the LZMA2 decoder. */ +XZ_EXTERN void XZ_FUNC xz_dec_lzma2_end(struct xz_dec_lzma2 *s); + +#ifdef XZ_DEC_BCJ +/* + * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before + * calling xz_dec_bcj_run(). + */ +XZ_EXTERN struct xz_dec_bcj * XZ_FUNC xz_dec_bcj_create(bool single_call); + +/* + * Decode the Filter ID of a BCJ filter. This implementation doesn't + * support custom start offsets, so no decoding of Filter Properties + * is needed. Returns XZ_OK if the given Filter ID is supported. + * Otherwise XZ_OPTIONS_ERROR is returned. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_reset( + struct xz_dec_bcj *s, uint8_t id); + +/* + * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is + * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run() + * must be called directly. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, struct xz_buf *b); + +/* Free the memory allocated for the BCJ filters. */ +#define xz_dec_bcj_end(s) kfree(s) +#endif + +#endif diff --git a/archival/libarchive/unxz/xz_stream.h b/archival/libarchive/unxz/xz_stream.h new file mode 100644 index 000000000..36f2a7cbf --- /dev/null +++ b/archival/libarchive/unxz/xz_stream.h @@ -0,0 +1,57 @@ +/* + * Definitions for handling the .xz file format + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_STREAM_H +#define XZ_STREAM_H + +#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32 +# include +# undef crc32 +# define xz_crc32(buf, size, crc) \ + (~crc32_le(~(uint32_t)(crc), buf, size)) +#endif + +/* + * See the .xz file format specification at + * http://tukaani.org/xz/xz-file-format.txt + * to understand the container format. + */ + +#define STREAM_HEADER_SIZE 12 + +#define HEADER_MAGIC "\3757zXZ\0" +#define HEADER_MAGIC_SIZE 6 + +#define FOOTER_MAGIC "YZ" +#define FOOTER_MAGIC_SIZE 2 + +/* + * Variable-length integer can hold a 63-bit unsigned integer, or a special + * value to indicate that the value is unknown. + */ +typedef uint64_t vli_type; + +#define VLI_MAX ((vli_type)-1 / 2) +#define VLI_UNKNOWN ((vli_type)-1) + +/* Maximum encoded size of a VLI */ +#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) + +/* Integrity Check types */ +enum xz_check { + XZ_CHECK_NONE = 0, + XZ_CHECK_CRC32 = 1, + XZ_CHECK_CRC64 = 4, + XZ_CHECK_SHA256 = 10 +}; + +/* Maximum possible Check ID */ +#define XZ_CHECK_MAX 15 + +#endif diff --git a/archival/lzop.c b/archival/lzop.c new file mode 100644 index 000000000..1326bd790 --- /dev/null +++ b/archival/lzop.c @@ -0,0 +1,1100 @@ +/* + This file is part of the lzop file compressor. + + Copyright (C) 1996..2003 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzop/ + + lzop and the LZO library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + "Minimalized" for busybox by Alain Knaff +*/ + +//usage:#define lzop_trivial_usage +//usage: "[-cfvd123456789CF] [FILE]..." +//usage:#define lzop_full_usage "\n\n" +//usage: " -1..9 Compression level" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -v Verbose" +//usage: "\n -F Don't store or verify checksum" +//usage: "\n -C Also write checksum of compressed block" +//usage: +//usage:#define lzopcat_trivial_usage +//usage: "[-vCF] [FILE]..." +//usage:#define lzopcat_full_usage "\n\n" +//usage: " -v Verbose" +//usage: "\n -F Don't store or verify checksum" +//usage: +//usage:#define unlzop_trivial_usage +//usage: "[-cfvCF] [FILE]..." +//usage:#define unlzop_full_usage "\n\n" +//usage: " -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -v Verbose" +//usage: "\n -F Don't store or verify checksum" + +#include "libbb.h" +#include "archive.h" +#include "liblzo_interface.h" + +/* lzo-2.03/src/lzo_ptr.h */ +#define pd(a,b) ((unsigned)((a)-(b))) + +#define lzo_version() LZO_VERSION +#define lzo_sizeof_dict_t (sizeof(uint8_t*)) + +/* lzo-2.03/include/lzo/lzo1x.h */ +#define LZO1X_1_MEM_COMPRESS (16384 * lzo_sizeof_dict_t) +#define LZO1X_1_15_MEM_COMPRESS (32768 * lzo_sizeof_dict_t) +#define LZO1X_999_MEM_COMPRESS (14 * 16384 * sizeof(short)) + +/* lzo-2.03/src/lzo1x_oo.c */ +#define NO_LIT UINT_MAX + +/**********************************************************************/ +static void copy2(uint8_t* ip, const uint8_t* m_pos, unsigned off) +{ + ip[0] = m_pos[0]; + if (off == 1) + ip[1] = m_pos[0]; + else + ip[1] = m_pos[1]; +} + +static void copy3(uint8_t* ip, const uint8_t* m_pos, unsigned off) +{ + ip[0] = m_pos[0]; + if (off == 1) { + ip[2] = ip[1] = m_pos[0]; + } + else if (off == 2) { + ip[1] = m_pos[1]; + ip[2] = m_pos[0]; + } + else { + ip[1] = m_pos[1]; + ip[2] = m_pos[2]; + } +} + +/**********************************************************************/ +// optimize a block of data. +/**********************************************************************/ +#define TEST_IP (ip < ip_end) +#define TEST_OP (op <= op_end) + +static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len, + uint8_t *out, unsigned *out_len, + void* wrkmem UNUSED_PARAM) +{ + uint8_t* op; + uint8_t* ip; + unsigned t; + uint8_t* m_pos; + uint8_t* const ip_end = in + in_len; + uint8_t* const op_end = out + *out_len; + uint8_t* litp = NULL; + unsigned lit = 0; + unsigned next_lit = NO_LIT; + unsigned nl; + unsigned long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0; + +// LZO_UNUSED(wrkmem); + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) { + t = *ip++ - 17; + if (t < 4) + goto match_next; + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + litp = ip - 1; + if (t == 0) { + t = 15; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + lit = t + 3; + /* copy literals */ + copy_literal_run: + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + first_literal_run: + do *op++ = *ip++; while (--t > 0); + + t = *ip++; + + if (t >= 16) + goto match; +#if defined(LZO1X) + m_pos = op - 1 - 0x800; +#elif defined(LZO1Y) + m_pos = op - 1 - 0x400; +#endif + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + lit = 0; + goto match_done; + + + /* handle matches */ + do { + if (t < 16) { /* a M1 match */ + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + + if (litp == NULL) + goto copy_m1; + + nl = ip[-2] & 3; + /* test if a match follows */ + if (nl == 0 && lit == 1 && ip[0] >= 16) { + next_lit = nl; + /* adjust length of previous short run */ + lit += 2; + *litp = (unsigned char)((*litp & ~3) | lit); + /* copy over the 2 literals that replace the match */ + copy2(ip-2, m_pos, pd(op, m_pos)); + o_m1_a++; + } + /* test if a literal run follows */ + else + if (nl == 0 + && ip[0] < 16 + && ip[0] != 0 + && (lit + 2 + ip[0] < 16) + ) { + t = *ip++; + /* remove short run */ + *litp &= ~3; + /* copy over the 2 literals that replace the match */ + copy2(ip-3+1,m_pos,pd(op,m_pos)); + /* move literals 1 byte ahead */ + litp += 2; + if (lit > 0) + memmove(litp+1, litp, lit); + /* insert new length of long literal run */ + lit += 2 + t + 3; + *litp = (unsigned char)(lit - 3); + + o_m1_b++; + *op++ = *m_pos++; *op++ = *m_pos++; + goto copy_literal_run; + } + copy_m1: + *op++ = *m_pos++; + *op++ = *m_pos++; + } else { + match: + if (t >= 64) { /* a M2 match */ + m_pos = op - 1; +#if defined(LZO1X) + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#endif + if (litp == NULL) + goto copy_m; + + nl = ip[-2] & 3; + /* test if in beetween two long literal runs */ + if (t == 1 && lit > 3 && nl == 0 + && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16) + ) { + t = *ip++; + /* copy over the 3 literals that replace the match */ + copy3(ip-1-2,m_pos,pd(op,m_pos)); + /* set new length of previous literal run */ + lit += 3 + t + 3; + *litp = (unsigned char)(lit - 3); + o_m2++; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + } else { + if (t >= 32) { /* a M3 match */ + t &= 31; + if (t == 0) { + t = 31; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos = op - 1; + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + } else { /* a M4 match */ + m_pos = op; + m_pos -= (t & 8) << 11; + t &= 7; + if (t == 0) { + t = 7; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + if (litp == NULL) + goto copy_m; + + nl = ip[-2] & 3; + /* test if in beetween two matches */ + if (t == 1 && lit == 0 && nl == 0 && ip[0] >= 16) { + next_lit = nl; + /* make a previous short run */ + lit += 3; + *litp = (unsigned char)((*litp & ~3) | lit); + /* copy over the 3 literals that replace the match */ + copy3(ip-3,m_pos,pd(op,m_pos)); + o_m3_a++; + } + /* test if a literal run follows */ + else if (t == 1 && lit <= 3 && nl == 0 + && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16) + ) { + t = *ip++; + /* remove short run */ + *litp &= ~3; + /* copy over the 3 literals that replace the match */ + copy3(ip-4+1,m_pos,pd(op,m_pos)); + /* move literals 1 byte ahead */ + litp += 2; + if (lit > 0) + memmove(litp+1,litp,lit); + /* insert new length of long literal run */ + lit += 3 + t + 3; + *litp = (unsigned char)(lit - 3); + + o_m3_b++; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + } + copy_m: + *op++ = *m_pos++; + *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + + match_done: + if (next_lit == NO_LIT) { + t = ip[-2] & 3; + lit = t; + litp = ip - 2; + } + else + t = next_lit; + next_lit = NO_LIT; + if (t == 0) + break; + /* copy literals */ + match_next: + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } while (TEST_IP && TEST_OP); + } + + /* no EOF code was found */ + *out_len = pd(op, out); + return LZO_E_EOF_NOT_FOUND; + + eof_found: +// LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2); +// LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b); + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); +} + +/**********************************************************************/ +#define F_OS F_OS_UNIX +#define F_CS F_CS_NATIVE + +/**********************************************************************/ +#define ADLER32_INIT_VALUE 1 +#define CRC32_INIT_VALUE 0 + +/**********************************************************************/ +enum { + M_LZO1X_1 = 1, + M_LZO1X_1_15 = 2, + M_LZO1X_999 = 3, +}; + +/**********************************************************************/ +/* header flags */ +#define F_ADLER32_D 0x00000001L +#define F_ADLER32_C 0x00000002L +#define F_H_EXTRA_FIELD 0x00000040L +#define F_H_GMTDIFF 0x00000080L +#define F_CRC32_D 0x00000100L +#define F_CRC32_C 0x00000200L +#define F_H_FILTER 0x00000800L +#define F_H_CRC32 0x00001000L +#define F_MASK 0x00003FFFL + +/* operating system & file system that created the file [mostly unused] */ +#define F_OS_UNIX 0x03000000L +#define F_OS_SHIFT 24 +#define F_OS_MASK 0xff000000L + +/* character set for file name encoding [mostly unused] */ +#define F_CS_NATIVE 0x00000000L +#define F_CS_SHIFT 20 +#define F_CS_MASK 0x00f00000L + +/* these bits must be zero */ +#define F_RESERVED ((F_MASK | F_OS_MASK | F_CS_MASK) ^ 0xffffffffL) + +typedef struct chksum_t { + uint32_t f_adler32; + uint32_t f_crc32; +} chksum_t; + +typedef struct header_t { + unsigned version; + unsigned lib_version; + unsigned version_needed_to_extract; + uint32_t flags; + uint32_t mode; + uint32_t mtime; + uint32_t gmtdiff; + uint32_t header_checksum; + + uint32_t extra_field_len; + uint32_t extra_field_checksum; + + unsigned char method; + unsigned char level; + + /* info */ + char name[255+1]; +} header_t; + +struct globals { + /*const uint32_t *lzo_crc32_table;*/ + chksum_t chksum_in; + chksum_t chksum_out; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { } while (0) +//#define G (*ptr_to_globals) +//#define INIT_G() do { +// SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); +//} while (0) + + +/**********************************************************************/ +#define LZOP_VERSION 0x1010 +//#define LZOP_VERSION_STRING "1.01" +//#define LZOP_VERSION_DATE "Apr 27th 2003" + +#define OPTION_STRING "cfvdt123456789CF" + +enum { + OPT_STDOUT = (1 << 0), + OPT_FORCE = (1 << 1), + OPT_VERBOSE = (1 << 2), + OPT_DECOMPRESS = (1 << 3), + OPT_TEST = (1 << 4), + OPT_1 = (1 << 5), + OPT_2 = (1 << 6), + OPT_3 = (1 << 7), + OPT_4 = (1 << 8), + OPT_5 = (1 << 9), + OPT_6 = (1 << 10), + OPT_789 = (7 << 11), + OPT_7 = (1 << 11), + OPT_8 = (1 << 12), + OPT_C = (1 << 14), + OPT_F = (1 << 15), +}; + +/**********************************************************************/ +// adler32 checksum +// adapted from free code by Mark Adler +// see http://www.zlib.org/ +/**********************************************************************/ +static FAST_FUNC uint32_t +lzo_adler32(uint32_t adler, const uint8_t* buf, unsigned len) +{ + enum { + LZO_BASE = 65521, /* largest prime smaller than 65536 */ + /* NMAX is the largest n such that + * 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + LZO_NMAX = 5552, + }; + uint32_t s1 = adler & 0xffff; + uint32_t s2 = (adler >> 16) & 0xffff; + unsigned k; + + if (buf == NULL) + return 1; + + while (len > 0) { + k = len < LZO_NMAX ? (unsigned) len : LZO_NMAX; + len -= k; + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k > 0); + s1 %= LZO_BASE; + s2 %= LZO_BASE; + } + return (s2 << 16) | s1; +} + +static FAST_FUNC uint32_t +lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len) +{ + //if (buf == NULL) - impossible + // return 0; + + return ~crc32_block_endian0(~c, buf, len, global_crc32_table); +} + +/**********************************************************************/ +static void init_chksum(chksum_t *ct) +{ + ct->f_adler32 = ADLER32_INIT_VALUE; + ct->f_crc32 = CRC32_INIT_VALUE; +} + +static void add_bytes_to_chksum(chksum_t *ct, const void* buf, int cnt) +{ + /* We need to handle the two checksums at once, because at the + * beginning of the header, we don't know yet which one we'll + * eventually need */ + ct->f_adler32 = lzo_adler32(ct->f_adler32, (const uint8_t*)buf, cnt); + ct->f_crc32 = lzo_crc32(ct->f_crc32, (const uint8_t*)buf, cnt); +} + +static uint32_t chksum_getresult(chksum_t *ct, const header_t *h) +{ + return (h->flags & F_H_CRC32) ? ct->f_crc32 : ct->f_adler32; +} + +/**********************************************************************/ +static uint32_t read32(void) +{ + uint32_t v; + xread(0, &v, 4); + return ntohl(v); +} + +static void write32(uint32_t v) +{ + v = htonl(v); + xwrite(1, &v, 4); +} + +static void f_write(const void* buf, int cnt) +{ + xwrite(1, buf, cnt); + add_bytes_to_chksum(&G.chksum_out, buf, cnt); +} + +static void f_read(void* buf, int cnt) +{ + xread(0, buf, cnt); + add_bytes_to_chksum(&G.chksum_in, buf, cnt); +} + +static int f_read8(void) +{ + uint8_t v; + f_read(&v, 1); + return v; +} + +static void f_write8(uint8_t v) +{ + f_write(&v, 1); +} + +static unsigned f_read16(void) +{ + uint16_t v; + f_read(&v, 2); + return ntohs(v); +} + +static void f_write16(uint16_t v) +{ + v = htons(v); + f_write(&v, 2); +} + +static uint32_t f_read32(void) +{ + uint32_t v; + f_read(&v, 4); + return ntohl(v); +} + +static void f_write32(uint32_t v) +{ + v = htonl(v); + f_write(&v, 4); +} + +/**********************************************************************/ +static int lzo_get_method(header_t *h) +{ + /* check method */ + if (h->method == M_LZO1X_1) { + if (h->level == 0) + h->level = 3; + } else if (h->method == M_LZO1X_1_15) { + if (h->level == 0) + h->level = 1; + } else if (h->method == M_LZO1X_999) { + if (h->level == 0) + h->level = 9; + } else + return -1; /* not a LZO method */ + + /* check compression level */ + if (h->level < 1 || h->level > 9) + return 15; + + return 0; +} + +/**********************************************************************/ +#define LZO_BLOCK_SIZE (256 * 1024l) +#define MAX_BLOCK_SIZE (64 * 1024l * 1024l) /* DO NOT CHANGE */ + +/* LZO may expand uncompressible data by a small amount */ +#define MAX_COMPRESSED_SIZE(x) ((x) + (x) / 16 + 64 + 3) + +/**********************************************************************/ +// compress a file +/**********************************************************************/ +static NOINLINE smallint lzo_compress(const header_t *h) +{ + unsigned block_size = LZO_BLOCK_SIZE; + int r = 0; /* LZO_E_OK */ + uint8_t *const b1 = xzalloc(block_size); + uint8_t *const b2 = xzalloc(MAX_COMPRESSED_SIZE(block_size)); + unsigned src_len = 0, dst_len = 0; + uint32_t d_adler32 = ADLER32_INIT_VALUE; + uint32_t d_crc32 = CRC32_INIT_VALUE; + int l; + smallint ok = 1; + uint8_t *wrk_mem = NULL; + + if (h->method == M_LZO1X_1) + wrk_mem = xzalloc(LZO1X_1_MEM_COMPRESS); + else if (h->method == M_LZO1X_1_15) + wrk_mem = xzalloc(LZO1X_1_15_MEM_COMPRESS); + else if (h->method == M_LZO1X_999) + wrk_mem = xzalloc(LZO1X_999_MEM_COMPRESS); + + for (;;) { + /* read a block */ + l = full_read(0, b1, block_size); + src_len = (l > 0 ? l : 0); + + /* write uncompressed block size */ + write32(src_len); + + /* exit if last block */ + if (src_len == 0) + break; + + /* compute checksum of uncompressed block */ + if (h->flags & F_ADLER32_D) + d_adler32 = lzo_adler32(ADLER32_INIT_VALUE, b1, src_len); + if (h->flags & F_CRC32_D) + d_crc32 = lzo_crc32(CRC32_INIT_VALUE, b1, src_len); + + /* compress */ + if (h->method == M_LZO1X_1) + r = lzo1x_1_compress(b1, src_len, b2, &dst_len, wrk_mem); + else if (h->method == M_LZO1X_1_15) + r = lzo1x_1_15_compress(b1, src_len, b2, &dst_len, wrk_mem); +#if ENABLE_LZOP_COMPR_HIGH + else if (h->method == M_LZO1X_999) + r = lzo1x_999_compress_level(b1, src_len, b2, &dst_len, + wrk_mem, h->level); +#endif + else + bb_error_msg_and_die("internal error"); + + if (r != 0) /* not LZO_E_OK */ + bb_error_msg_and_die("internal error - compression failed"); + + /* write compressed block size */ + if (dst_len < src_len) { + /* optimize */ + if (h->method == M_LZO1X_999) { + unsigned new_len = src_len; + r = lzo1x_optimize(b2, dst_len, b1, &new_len, NULL); + if (r != 0 /*LZO_E_OK*/ || new_len != src_len) + bb_error_msg_and_die("internal error - optimization failed"); + } + write32(dst_len); + } else { + /* data actually expanded => store data uncompressed */ + write32(src_len); + } + + /* write checksum of uncompressed block */ + if (h->flags & F_ADLER32_D) + write32(d_adler32); + if (h->flags & F_CRC32_D) + write32(d_crc32); + + if (dst_len < src_len) { + /* write checksum of compressed block */ + if (h->flags & F_ADLER32_C) + write32(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len)); + if (h->flags & F_CRC32_C) + write32(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len)); + /* write compressed block data */ + xwrite(1, b2, dst_len); + } else { + /* write uncompressed block data */ + xwrite(1, b1, src_len); + } + } + + free(wrk_mem); + free(b1); + free(b2); + return ok; +} + +static FAST_FUNC void lzo_check( + uint32_t init, + uint8_t* buf, unsigned len, + uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned), + uint32_t ref) +{ + /* This function, by having the same order of parameters + * as fn, and by being marked FAST_FUNC (same as fn), + * saves a dozen bytes of code. + */ + uint32_t c = fn(init, buf, len); + if (c != ref) + bb_error_msg_and_die("checksum error"); +} + +/**********************************************************************/ +// decompress a file +/**********************************************************************/ +static NOINLINE smallint lzo_decompress(const header_t *h) +{ + unsigned block_size = LZO_BLOCK_SIZE; + int r; + uint32_t src_len, dst_len; + uint32_t c_adler32 = ADLER32_INIT_VALUE; + uint32_t d_adler32 = ADLER32_INIT_VALUE; + uint32_t c_crc32 = CRC32_INIT_VALUE, d_crc32 = CRC32_INIT_VALUE; + smallint ok = 1; + uint8_t *b1; + uint32_t mcs_block_size = MAX_COMPRESSED_SIZE(block_size); + uint8_t *b2 = NULL; + + for (;;) { + uint8_t *dst; + + /* read uncompressed block size */ + dst_len = read32(); + + /* exit if last block */ + if (dst_len == 0) + break; + + /* error if split file */ + if (dst_len == 0xffffffffL) + /* should not happen - not yet implemented */ + bb_error_msg_and_die("this file is a split lzop file"); + + if (dst_len > MAX_BLOCK_SIZE) + bb_error_msg_and_die("corrupted data"); + + /* read compressed block size */ + src_len = read32(); + if (src_len <= 0 || src_len > dst_len) + bb_error_msg_and_die("corrupted data"); + + if (dst_len > block_size) { + if (b2) { + free(b2); + b2 = NULL; + } + block_size = dst_len; + mcs_block_size = MAX_COMPRESSED_SIZE(block_size); + } + + /* read checksum of uncompressed block */ + if (h->flags & F_ADLER32_D) + d_adler32 = read32(); + if (h->flags & F_CRC32_D) + d_crc32 = read32(); + + /* read checksum of compressed block */ + if (src_len < dst_len) { + if (h->flags & F_ADLER32_C) + c_adler32 = read32(); + if (h->flags & F_CRC32_C) + c_crc32 = read32(); + } + + if (b2 == NULL) + b2 = xzalloc(mcs_block_size); + /* read the block into the end of our buffer */ + b1 = b2 + mcs_block_size - src_len; + xread(0, b1, src_len); + + if (src_len < dst_len) { + unsigned d = dst_len; + + if (!(option_mask32 & OPT_F)) { + /* verify checksum of compressed block */ + if (h->flags & F_ADLER32_C) + lzo_check(ADLER32_INIT_VALUE, + b1, src_len, + lzo_adler32, c_adler32); + if (h->flags & F_CRC32_C) + lzo_check(CRC32_INIT_VALUE, + b1, src_len, + lzo_crc32, c_crc32); + } + + /* decompress */ +// if (option_mask32 & OPT_F) +// r = lzo1x_decompress(b1, src_len, b2, &d, NULL); +// else + r = lzo1x_decompress_safe(b1, src_len, b2, &d, NULL); + + if (r != 0 /*LZO_E_OK*/ || dst_len != d) { + bb_error_msg_and_die("corrupted data"); + } + dst = b2; + } else { + /* "stored" block => no decompression */ + dst = b1; + } + + if (!(option_mask32 & OPT_F)) { + /* verify checksum of uncompressed block */ + if (h->flags & F_ADLER32_D) + lzo_check(ADLER32_INIT_VALUE, + dst, dst_len, + lzo_adler32, d_adler32); + if (h->flags & F_CRC32_D) + lzo_check(CRC32_INIT_VALUE, + dst, dst_len, + lzo_crc32, d_crc32); + } + + /* write uncompressed block data */ + xwrite(1, dst, dst_len); + } + + free(b2); + return ok; +} + +/**********************************************************************/ +// lzop file signature (shamelessly borrowed from PNG) +/**********************************************************************/ +/* + * The first nine bytes of a lzop file always contain the following values: + * + * 0 1 2 3 4 5 6 7 8 + * --- --- --- --- --- --- --- --- --- + * (hex) 89 4c 5a 4f 00 0d 0a 1a 0a + * (decimal) 137 76 90 79 0 13 10 26 10 + * (C notation - ASCII) \211 L Z O \0 \r \n \032 \n + */ + +/* (vda) comparison with lzop v1.02rc1 ("lzop -1 version); + f_write16(h->lib_version); + f_write16(h->version_needed_to_extract); + f_write8(h->method); + f_write8(h->level); + f_write32(h->flags); + f_write32(h->mode); + f_write32(h->mtime); + f_write32(h->gmtdiff); + + l = (int) strlen(h->name); + f_write8(l); + if (l) + f_write(h->name, l); + + f_write32(chksum_getresult(&G.chksum_out, h)); +} + +static int read_header(header_t *h) +{ + int r; + int l; + uint32_t checksum; + + memset(h, 0, sizeof(*h)); + h->version_needed_to_extract = 0x0900; /* first lzop version */ + h->level = 0; + + init_chksum(&G.chksum_in); + + h->version = f_read16(); + if (h->version < 0x0900) + return 3; + h->lib_version = f_read16(); + if (h->version >= 0x0940) { + h->version_needed_to_extract = f_read16(); + if (h->version_needed_to_extract > LZOP_VERSION) + return 16; + if (h->version_needed_to_extract < 0x0900) + return 3; + } + h->method = f_read8(); + if (h->version >= 0x0940) + h->level = f_read8(); + h->flags = f_read32(); + if (h->flags & F_H_FILTER) + return 16; /* filter not supported */ + h->mode = f_read32(); + h->mtime = f_read32(); + if (h->version >= 0x0940) + h->gmtdiff = f_read32(); + + l = f_read8(); + if (l > 0) + f_read(h->name, l); + h->name[l] = 0; + + checksum = chksum_getresult(&G.chksum_in, h); + h->header_checksum = f_read32(); + if (h->header_checksum != checksum) + return 2; + + if (h->method <= 0) + return 14; + r = lzo_get_method(h); + if (r != 0) + return r; + + /* check reserved flags */ + if (h->flags & F_RESERVED) + return -13; + + /* skip extra field [not used yet] */ + if (h->flags & F_H_EXTRA_FIELD) { + uint32_t k; + + /* note: the checksum also covers the length */ + init_chksum(&G.chksum_in); + h->extra_field_len = f_read32(); + for (k = 0; k < h->extra_field_len; k++) + f_read8(); + checksum = chksum_getresult(&G.chksum_in, h); + h->extra_field_checksum = f_read32(); + if (h->extra_field_checksum != checksum) + return 3; + } + + return 0; +} + +static void p_header(header_t *h) +{ + int r; + + r = read_header(h); + if (r == 0) + return; + bb_error_msg_and_die("header_error %d", r); +} + +/**********************************************************************/ +// compress +/**********************************************************************/ +static void lzo_set_method(header_t *h) +{ + int level = 1; + + if (option_mask32 & OPT_1) { + h->method = M_LZO1X_1_15; + } else if (option_mask32 & OPT_789) { +#if ENABLE_LZOP_COMPR_HIGH + h->method = M_LZO1X_999; + if (option_mask32 & OPT_7) + level = 7; + else if (option_mask32 & OPT_8) + level = 8; + else + level = 9; +#else + bb_error_msg_and_die("high compression not compiled in"); +#endif + } else { /* levels 2..6 or none (defaults to level 3) */ + h->method = M_LZO1X_1; + level = 5; /* levels 2-6 are actually the same */ + } + + h->level = level; +} + +static smallint do_lzo_compress(void) +{ + header_t header; + +#define h (&header) + memset(h, 0, sizeof(*h)); + + lzo_set_method(h); + + h->version = (LZOP_VERSION & 0xffff); + h->version_needed_to_extract = 0x0940; + h->lib_version = lzo_version() & 0xffff; + + h->flags = (F_OS & F_OS_MASK) | (F_CS & F_CS_MASK); + + if (!(option_mask32 & OPT_F) || h->method == M_LZO1X_999) { + h->flags |= F_ADLER32_D; + if (option_mask32 & OPT_C) + h->flags |= F_ADLER32_C; + } + write_header(h); + return lzo_compress(h); +#undef h +} + +/**********************************************************************/ +// decompress +/**********************************************************************/ +static smallint do_lzo_decompress(void) +{ + header_t header; + + check_magic(); + p_header(&header); + return lzo_decompress(&header); +} + +static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM) +{ + if (option_mask32 & OPT_DECOMPRESS) { + char *extension = strrchr(filename, '.'); + if (!extension || strcmp(extension + 1, "lzo") != 0) + return xasprintf("%s.out", filename); + *extension = '\0'; + return filename; + } + return xasprintf("%s.lzo", filename); +} + +static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(unpack_info_t *info UNUSED_PARAM) +{ + if (option_mask32 & OPT_DECOMPRESS) + return do_lzo_decompress(); + return do_lzo_compress(); +} + +int lzop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lzop_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, OPTION_STRING); + argv += optind; + /* lzopcat? */ + if (applet_name[4] == 'c') + option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS); + /* unlzop? */ + if (applet_name[0] == 'u') + option_mask32 |= OPT_DECOMPRESS; + + global_crc32_table = crc32_filltable(NULL, 0); + return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL); +} diff --git a/archival/rpm.c b/archival/rpm.c new file mode 100644 index 000000000..8174f4869 --- /dev/null +++ b/archival/rpm.c @@ -0,0 +1,394 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rpm applet for busybox + * + * Copyright (C) 2001,2002 by Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define rpm_trivial_usage +//usage: "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm" +//usage:#define rpm_full_usage "\n\n" +//usage: "Manipulate RPM packages\n" +//usage: "\nCommands:" +//usage: "\n -i Install package" +//usage: "\n -qp Query package" +//usage: "\n -i Show information" +//usage: "\n -l List contents" +//usage: "\n -d List documents" +//usage: "\n -c List config files" + +#include "libbb.h" +#include "archive.h" +#include "rpm.h" + +#define RPM_CHAR_TYPE 1 +#define RPM_INT8_TYPE 2 +#define RPM_INT16_TYPE 3 +#define RPM_INT32_TYPE 4 +/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */ +#define RPM_STRING_TYPE 6 +#define RPM_BIN_TYPE 7 +#define RPM_STRING_ARRAY_TYPE 8 +#define RPM_I18NSTRING_TYPE 9 + +#define TAG_NAME 1000 +#define TAG_VERSION 1001 +#define TAG_RELEASE 1002 +#define TAG_SUMMARY 1004 +#define TAG_DESCRIPTION 1005 +#define TAG_BUILDTIME 1006 +#define TAG_BUILDHOST 1007 +#define TAG_SIZE 1009 +#define TAG_VENDOR 1011 +#define TAG_LICENSE 1014 +#define TAG_PACKAGER 1015 +#define TAG_GROUP 1016 +#define TAG_URL 1020 +#define TAG_PREIN 1023 +#define TAG_POSTIN 1024 +#define TAG_FILEFLAGS 1037 +#define TAG_FILEUSERNAME 1039 +#define TAG_FILEGROUPNAME 1040 +#define TAG_SOURCERPM 1044 +#define TAG_PREINPROG 1085 +#define TAG_POSTINPROG 1086 +#define TAG_PREFIXS 1098 +#define TAG_DIRINDEXES 1116 +#define TAG_BASENAMES 1117 +#define TAG_DIRNAMES 1118 + +#define RPMFILE_CONFIG (1 << 0) +#define RPMFILE_DOC (1 << 1) + +enum rpm_functions_e { + rpm_query = 1, + rpm_install = 2, + rpm_query_info = 4, + rpm_query_package = 8, + rpm_query_list = 16, + rpm_query_list_doc = 32, + rpm_query_list_config = 64 +}; + +typedef struct { + uint32_t tag; /* 4 byte tag */ + uint32_t type; /* 4 byte type */ + uint32_t offset; /* 4 byte offset */ + uint32_t count; /* 4 byte count */ +} rpm_index; + +static void *map; +static rpm_index **mytags; +static int tagcount; + +static void extract_cpio(int fd, const char *source_rpm); +static rpm_index **rpm_gettags(int fd, int *num_tags); +static int bsearch_rpmtag(const void *key, const void *item); +static char *rpm_getstr(int tag, int itemindex); +static int rpm_getint(int tag, int itemindex); +static int rpm_getcount(int tag); +static void fileaction_dobackup(char *filename, int fileref); +static void fileaction_setowngrp(char *filename, int fileref); +static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)); + +int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rpm_main(int argc, char **argv) +{ + int opt = 0, func = 0, rpm_fd, offset; + const int pagesize = getpagesize(); + + while ((opt = getopt(argc, argv, "iqpldc")) != -1) { + switch (opt) { + case 'i': /* First arg: Install mode, with q: Information */ + if (!func) func = rpm_install; + else func |= rpm_query_info; + break; + case 'q': /* First arg: Query mode */ + if (func) bb_show_usage(); + func = rpm_query; + break; + case 'p': /* Query a package */ + func |= rpm_query_package; + break; + case 'l': /* List files in a package */ + func |= rpm_query_list; + break; + case 'd': /* List doc files in a package (implies list) */ + func |= rpm_query_list; + func |= rpm_query_list_doc; + break; + case 'c': /* List config files in a package (implies list) */ + func |= rpm_query_list; + func |= rpm_query_list_config; + break; + default: + bb_show_usage(); + } + } + argv += optind; + //argc -= optind; + if (!argv[0]) { + bb_show_usage(); + } + + while (*argv) { + const char *source_rpm; + + rpm_fd = xopen(*argv++, O_RDONLY); + mytags = rpm_gettags(rpm_fd, &tagcount); + if (!mytags) + bb_error_msg_and_die("error reading rpm header"); + offset = xlseek(rpm_fd, 0, SEEK_CUR); + /* Mimimum is one page */ + map = mmap(0, offset > pagesize ? (offset + offset % pagesize) : pagesize, PROT_READ, MAP_PRIVATE, rpm_fd, 0); + + source_rpm = rpm_getstr(TAG_SOURCERPM, 0); + + if (func & rpm_install) { + /* Backup any config files */ + loop_through_files(TAG_BASENAMES, fileaction_dobackup); + /* Extact the archive */ + extract_cpio(rpm_fd, source_rpm); + /* Set the correct file uid/gid's */ + loop_through_files(TAG_BASENAMES, fileaction_setowngrp); + } + else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) { + if (!(func & (rpm_query_info|rpm_query_list))) { + /* If just a straight query, just give package name */ + printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0)); + } + if (func & rpm_query_info) { + /* Do the nice printout */ + time_t bdate_time; + struct tm *bdate_ptm; + char bdatestring[50]; + const char *p; + + p = rpm_getstr(TAG_PREFIXS, 0); + if (!p) p = "(not relocateable)"; + printf("Name : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME, 0), p); + p = rpm_getstr(TAG_VENDOR, 0); + if (!p) p = "(none)"; + printf("Version : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION, 0), p); + bdate_time = rpm_getint(TAG_BUILDTIME, 0); + bdate_ptm = localtime(&bdate_time); + strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm); + printf("Release : %-30sBuild Date: %s\n", rpm_getstr(TAG_RELEASE, 0), bdatestring); + printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST, 0)); + printf("Group : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP, 0), source_rpm); + printf("Size : %-33dLicense: %s\n", rpm_getint(TAG_SIZE, 0), rpm_getstr(TAG_LICENSE, 0)); + printf("URL : %s\n", rpm_getstr(TAG_URL, 0)); + printf("Summary : %s\n", rpm_getstr(TAG_SUMMARY, 0)); + printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0)); + } + if (func & rpm_query_list) { + int count, it, flags; + count = rpm_getcount(TAG_BASENAMES); + for (it = 0; it < count; it++) { + flags = rpm_getint(TAG_FILEFLAGS, it); + switch (func & (rpm_query_list_doc|rpm_query_list_config)) { + case rpm_query_list_doc: + if (!(flags & RPMFILE_DOC)) continue; + break; + case rpm_query_list_config: + if (!(flags & RPMFILE_CONFIG)) continue; + break; + case rpm_query_list_doc|rpm_query_list_config: + if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue; + break; + } + printf("%s%s\n", + rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)), + rpm_getstr(TAG_BASENAMES, it)); + } + } + } + free(mytags); + } + return 0; +} + +static void extract_cpio(int fd, const char *source_rpm) +{ + archive_handle_t *archive_handle; + + if (source_rpm != NULL) { + /* Binary rpm (it was built from some SRPM), install to root */ + xchdir("/"); + } /* else: SRPM, install to current dir */ + + /* Initialize */ + archive_handle = init_handle(); + archive_handle->seek = seek_by_read; + archive_handle->action_data = data_extract_all; +#if 0 /* For testing (rpm -i only lists the files in internal cpio): */ + archive_handle->action_header = header_list; + archive_handle->action_data = data_skip; +#endif + archive_handle->ah_flags = ARCHIVE_RESTORE_DATE | ARCHIVE_CREATE_LEADING_DIRS + /* compat: overwrite existing files. + * try "rpm -i foo.src.rpm" few times in a row - + * standard rpm will not complain. + * (TODO? real rpm creates "file;1234" and then renames it) */ + | ARCHIVE_UNLINK_OLD; + archive_handle->src_fd = fd; + /*archive_handle->offset = 0; - init_handle() did it */ + + setup_unzip_on_fd(archive_handle->src_fd /*, fail_if_not_detected: 1*/); + while (get_header_cpio(archive_handle) == EXIT_SUCCESS) + continue; +} + +static rpm_index **rpm_gettags(int fd, int *num_tags) +{ + /* We should never need more than 200 (shrink via realloc later) */ + rpm_index **tags = xzalloc(200 * sizeof(tags[0])); + int pass, tagindex = 0; + + xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */ + + /* 1st pass is the signature headers, 2nd is the main stuff */ + for (pass = 0; pass < 2; pass++) { + struct rpm_header header; + rpm_index *tmpindex; + int storepos; + + xread(fd, &header, sizeof(header)); + if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER)) + return NULL; /* Invalid magic, or not version 1 */ + header.size = ntohl(header.size); + header.entries = ntohl(header.entries); + storepos = xlseek(fd, 0, SEEK_CUR) + header.entries * 16; + + while (header.entries--) { + tmpindex = tags[tagindex++] = xmalloc(sizeof(*tmpindex)); + xread(fd, tmpindex, sizeof(*tmpindex)); + tmpindex->tag = ntohl(tmpindex->tag); + tmpindex->type = ntohl(tmpindex->type); + tmpindex->count = ntohl(tmpindex->count); + tmpindex->offset = storepos + ntohl(tmpindex->offset); + if (pass == 0) + tmpindex->tag -= 743; + } + storepos = xlseek(fd, header.size, SEEK_CUR); /* Seek past store */ + /* Skip padding to 8 byte boundary after reading signature headers */ + if (pass == 0) + xlseek(fd, (-storepos) & 0x7, SEEK_CUR); + } + /* realloc tags to save space */ + tags = xrealloc(tags, tagindex * sizeof(tags[0])); + *num_tags = tagindex; + /* All done, leave the file at the start of the gzipped cpio archive */ + return tags; +} + +static int bsearch_rpmtag(const void *key, const void *item) +{ + int *tag = (int *)key; + rpm_index **tmp = (rpm_index **) item; + return (*tag - tmp[0]->tag); +} + +static int rpm_getcount(int tag) +{ + rpm_index **found; + found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + if (!found) + return 0; + return found[0]->count; +} + +static char *rpm_getstr(int tag, int itemindex) +{ + rpm_index **found; + found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + if (!found || itemindex >= found[0]->count) + return NULL; + if (found[0]->type == RPM_STRING_TYPE + || found[0]->type == RPM_I18NSTRING_TYPE + || found[0]->type == RPM_STRING_ARRAY_TYPE + ) { + int n; + char *tmpstr = (char *) map + found[0]->offset; + for (n = 0; n < itemindex; n++) + tmpstr = tmpstr + strlen(tmpstr) + 1; + return tmpstr; + } + return NULL; +} + +static int rpm_getint(int tag, int itemindex) +{ + rpm_index **found; + int *tmpint; /* NB: using int8_t* would be easier to code */ + + /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ... + * it's ok to ignore it because tag won't be used as a pointer */ + found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + if (!found || itemindex >= found[0]->count) + return -1; + + tmpint = (int *) ((char *) map + found[0]->offset); + + if (found[0]->type == RPM_INT32_TYPE) { + tmpint = (int *) ((char *) tmpint + itemindex*4); + /*return ntohl(*tmpint);*/ + /* int can be != int32_t */ + return ntohl(*(int32_t*)tmpint); + } + if (found[0]->type == RPM_INT16_TYPE) { + tmpint = (int *) ((char *) tmpint + itemindex*2); + /* ??? read int, and THEN ntohs() it?? */ + /*return ntohs(*tmpint);*/ + return ntohs(*(int16_t*)tmpint); + } + if (found[0]->type == RPM_INT8_TYPE) { + tmpint = (int *) ((char *) tmpint + itemindex); + /* ??? why we don't read byte here??? */ + /*return ntohs(*tmpint);*/ + return *(int8_t*)tmpint; + } + return -1; +} + +static void fileaction_dobackup(char *filename, int fileref) +{ + struct stat oldfile; + int stat_res; + char *newname; + if (rpm_getint(TAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { + /* Only need to backup config files */ + stat_res = lstat(filename, &oldfile); + if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { + /* File already exists - really should check MD5's etc to see if different */ + newname = xasprintf("%s.rpmorig", filename); + copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS); + remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE); + free(newname); + } + } +} + +static void fileaction_setowngrp(char *filename, int fileref) +{ + /* real rpm warns: "user foo does not exist - using " */ + struct passwd *pw = getpwnam(rpm_getstr(TAG_FILEUSERNAME, fileref)); + int uid = pw ? pw->pw_uid : getuid(); /* or euid? */ + struct group *gr = getgrnam(rpm_getstr(TAG_FILEGROUPNAME, fileref)); + int gid = gr ? gr->gr_gid : getgid(); + chown(filename, uid, gid); +} + +static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)) +{ + int count = 0; + while (rpm_getstr(filetag, count)) { + char* filename = xasprintf("%s%s", + rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, count)), + rpm_getstr(TAG_BASENAMES, count)); + fileaction(filename, count++); + free(filename); + } +} diff --git a/archival/rpm.h b/archival/rpm.h new file mode 100644 index 000000000..afe2b550c --- /dev/null +++ b/archival/rpm.h @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * RPM structs and consts + * + * Copyright (C) 2001 by Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* RPM file starts with this struct: */ +struct rpm_lead { + uint32_t magic; + uint8_t major, minor; + uint16_t type; + uint16_t archnum; + char name[66]; + uint16_t osnum; + uint16_t signature_type; + char reserved[16]; +}; +struct BUG_rpm_lead { + char bug[sizeof(struct rpm_lead) == 96 ? 1 : -1]; +}; +#define RPM_LEAD_MAGIC 0xedabeedb +#define RPM_LEAD_MAGIC_STR "\355\253\356\333" + +/* Then follows the header: */ +struct rpm_header { + uint32_t magic_and_ver; /* 3 byte magic: 0x8e 0xad 0xe8; 1 byte version: 0x01 */ + uint32_t reserved; /* 4 bytes reserved */ + uint32_t entries; /* Number of entries in header (4 bytes) */ + uint32_t size; /* Size of store (4 bytes) */ +}; +struct BUG_rpm_header { + char bug[sizeof(struct rpm_header) == 16 ? 1 : -1]; +}; +#define RPM_HEADER_MAGICnVER 0x8eade801 +#define RPM_HEADER_MAGIC_STR "\216\255\350" diff --git a/archival/rpm2cpio.c b/archival/rpm2cpio.c new file mode 100644 index 000000000..ff4a0d1b0 --- /dev/null +++ b/archival/rpm2cpio.c @@ -0,0 +1,119 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rpm2cpio implementation for busybox + * + * Copyright (C) 2001 by Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define rpm2cpio_trivial_usage +//usage: "package.rpm" +//usage:#define rpm2cpio_full_usage "\n\n" +//usage: "Output a cpio archive of the rpm file" + +#include "libbb.h" +#include "archive.h" +#include "rpm.h" + +enum { rpm_fd = STDIN_FILENO }; + +static unsigned skip_header(void) +{ + struct rpm_header header; + unsigned len; + + xread(rpm_fd, &header, sizeof(header)); +// if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC_STR, 3) != 0) { +// bb_error_msg_and_die("invalid RPM header magic"); +// } +// if (header.version != 1) { +// bb_error_msg_and_die("unsupported RPM header version"); +// } + if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER)) { + bb_error_msg_and_die("invalid RPM header magic or unsupported version"); + // ": %x != %x", header.magic_and_ver, htonl(RPM_HEADER_MAGICnVER)); + } + + /* Seek past index entries, and past store */ + len = 16 * ntohl(header.entries) + ntohl(header.size); + seek_by_jump(rpm_fd, len); + + return sizeof(header) + len; +} + +/* No getopt required */ +int rpm2cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rpm2cpio_main(int argc UNUSED_PARAM, char **argv) +{ + struct rpm_lead lead; + unsigned pos; + + if (argv[1]) { + xmove_fd(xopen(argv[1], O_RDONLY), rpm_fd); + } + xread(rpm_fd, &lead, sizeof(lead)); + + /* Just check the magic, the rest is irrelevant */ + if (lead.magic != htonl(RPM_LEAD_MAGIC)) { + bb_error_msg_and_die("invalid RPM magic"); + } + + /* Skip the signature header, align to 8 bytes */ + pos = skip_header(); + seek_by_jump(rpm_fd, (-(int)pos) & 7); + + /* Skip the main header */ + skip_header(); + +#if 0 + /* This works, but doesn't report uncompress errors (they happen in child) */ + setup_unzip_on_fd(rpm_fd /*fail_if_not_detected: 1*/); + if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) + bb_error_msg_and_die("error unpacking"); +#else + /* BLOAT */ + { + union { + uint8_t b[4]; + uint16_t b16[2]; + uint32_t b32[1]; + } magic; + IF_DESKTOP(long long) int FAST_FUNC (*unpack)(int src_fd, int dst_fd); + + xread(rpm_fd, magic.b16, sizeof(magic.b16[0])); + if (magic.b16[0] == GZIP_MAGIC) { + unpack = unpack_gz_stream; + } else + if (ENABLE_FEATURE_SEAMLESS_BZ2 + && magic.b16[0] == BZIP2_MAGIC + ) { + unpack = unpack_bz2_stream; + } else + if (ENABLE_FEATURE_SEAMLESS_XZ + && magic.b16[0] == XZ_MAGIC1 + ) { + xread(rpm_fd, magic.b32, sizeof(magic.b32[0])); + if (magic.b32[0] != XZ_MAGIC2) + goto no_magic; + /* unpack_xz_stream wants fd at position 6, no need to seek */ + //xlseek(rpm_fd, -6, SEEK_CUR); + unpack = unpack_xz_stream; + } else { + no_magic: + bb_error_msg_and_die("no gzip" + IF_FEATURE_SEAMLESS_BZ2("/bzip2") + IF_FEATURE_SEAMLESS_XZ("/xz") + " magic"); + } + if (unpack(rpm_fd, STDOUT_FILENO) < 0) + bb_error_msg_and_die("error unpacking"); + } +#endif + + if (ENABLE_FEATURE_CLEAN_UP) { + close(rpm_fd); + } + + return 0; +} diff --git a/archival/tar.c b/archival/tar.c new file mode 100644 index 000000000..375e838d2 --- /dev/null +++ b/archival/tar.c @@ -0,0 +1,1135 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tar implementation for busybox + * + * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg + * by Glenn McGrath + * + * Note, that as of BusyBox-0.43, tar has been completely rewritten from the + * ground up. It still has remnants of the old code lying about, but it is + * very different now (i.e., cleaner, less global variables, etc.) + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Based in part in the tar implementation in sash + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * Permission to distribute sash derived code under GPL has been granted. + * + * Based in part on the tar implementation from busybox-0.28 + * Copyright (C) 1995 Bruce Perens + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* TODO: security with -C DESTDIR option can be enhanced. + * Consider tar file created via: + * $ tar cvf bug.tar anything.txt + * $ ln -s /tmp symlink + * $ tar --append -f bug.tar symlink + * $ rm symlink + * $ mkdir symlink + * $ tar --append -f bug.tar symlink/evil.py + * + * This will result in an archive which contains: + * $ tar --list -f bug.tar + * anything.txt + * symlink + * symlink/evil.py + * + * Untarring it puts evil.py in '/tmp' even if the -C DESTDIR is given. + * This doesn't feel right, and IIRC GNU tar doesn't do that. + */ + +#include +#include "libbb.h" +#include "archive.h" +/* FIXME: Stop using this non-standard feature */ +#ifndef FNM_LEADING_DIR +# define FNM_LEADING_DIR 0 +#endif + + +//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) +#define DBG(...) ((void)0) + + +#define block_buf bb_common_bufsiz1 + + +#if !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_FEATURE_SEAMLESS_BZ2 +/* Do not pass gzip flag to writeTarFile() */ +#define writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude, gzip) \ + writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude) +#endif + + +#if ENABLE_FEATURE_TAR_CREATE + +/* +** writeTarFile(), writeFileToTarball(), and writeTarHeader() are +** the only functions that deal with the HardLinkInfo structure. +** Even these functions use the xxxHardLinkInfo() functions. +*/ +typedef struct HardLinkInfo { + struct HardLinkInfo *next; /* Next entry in list */ + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ +// short linkCount; /* (Hard) Link Count */ + char name[1]; /* Start of filename (must be last) */ +} HardLinkInfo; + +/* Some info to be carried along when creating a new tarball */ +typedef struct TarBallInfo { + int tarFd; /* Open-for-write file descriptor + * for the tarball */ + int verboseFlag; /* Whether to print extra stuff or not */ + const llist_t *excludeList; /* List of files to not include */ + HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ + HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ +//TODO: save only st_dev + st_ino + struct stat tarFileStatBuf; /* Stat info for the tarball, letting + * us know the inode and device that the + * tarball lives, so we can avoid trying + * to include the tarball into itself */ +} TarBallInfo; + +/* A nice enum with all the possible tar file content types */ +enum { + REGTYPE = '0', /* regular file */ + REGTYPE0 = '\0', /* regular file (ancient bug compat) */ + LNKTYPE = '1', /* hard link */ + SYMTYPE = '2', /* symbolic link */ + CHRTYPE = '3', /* character special */ + BLKTYPE = '4', /* block special */ + DIRTYPE = '5', /* directory */ + FIFOTYPE = '6', /* FIFO special */ + CONTTYPE = '7', /* reserved */ + GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ + GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ +}; + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ +static void addHardLinkInfo(HardLinkInfo **hlInfoHeadPtr, + struct stat *statbuf, + const char *fileName) +{ + /* Note: hlInfoHeadPtr can never be NULL! */ + HardLinkInfo *hlInfo; + + hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName)); + hlInfo->next = *hlInfoHeadPtr; + *hlInfoHeadPtr = hlInfo; + hlInfo->dev = statbuf->st_dev; + hlInfo->ino = statbuf->st_ino; +// hlInfo->linkCount = statbuf->st_nlink; + strcpy(hlInfo->name, fileName); +} + +static void freeHardLinkInfo(HardLinkInfo **hlInfoHeadPtr) +{ + HardLinkInfo *hlInfo; + HardLinkInfo *hlInfoNext; + + if (hlInfoHeadPtr) { + hlInfo = *hlInfoHeadPtr; + while (hlInfo) { + hlInfoNext = hlInfo->next; + free(hlInfo); + hlInfo = hlInfoNext; + } + *hlInfoHeadPtr = NULL; + } +} + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order ;) */ +static HardLinkInfo *findHardLinkInfo(HardLinkInfo *hlInfo, struct stat *statbuf) +{ + while (hlInfo) { + if (statbuf->st_ino == hlInfo->ino + && statbuf->st_dev == hlInfo->dev + ) { + DBG("found hardlink:'%s'", hlInfo->name); + break; + } + hlInfo = hlInfo->next; + } + return hlInfo; +} + +/* Put an octal string into the specified buffer. + * The number is zero padded and possibly null terminated. + * Stores low-order bits only if whole value does not fit. */ +static void putOctal(char *cp, int len, off_t value) +{ + char tempBuffer[sizeof(off_t)*3 + 1]; + char *tempString = tempBuffer; + int width; + + width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value); + tempString += (width - len); + + /* If string has leading zeroes, we can drop one */ + /* and field will have trailing '\0' */ + /* (increases chances of compat with other tars) */ + if (tempString[0] == '0') + tempString++; + + /* Copy the string to the field */ + memcpy(cp, tempString, len); +} +#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b)) + +static void chksum_and_xwrite(int fd, struct tar_header_t* hp) +{ + /* POSIX says that checksum is done on unsigned bytes + * (Sun and HP-UX gets it wrong... more details in + * GNU tar source) */ + const unsigned char *cp; + int chksum, size; + + strcpy(hp->magic, "ustar "); + + /* Calculate and store the checksum (i.e., the sum of all of the bytes of + * the header). The checksum field must be filled with blanks for the + * calculation. The checksum field is formatted differently from the + * other fields: it has 6 digits, a null, then a space -- rather than + * digits, followed by a null like the other fields... */ + memset(hp->chksum, ' ', sizeof(hp->chksum)); + cp = (const unsigned char *) hp; + chksum = 0; + size = sizeof(*hp); + do { chksum += *cp++; } while (--size); + putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum); + + /* Now write the header out to disk */ + xwrite(fd, hp, sizeof(*hp)); +} + +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS +static void writeLongname(int fd, int type, const char *name, int dir) +{ + static const struct { + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + } prefilled = { + "0000000", + "0000000", + "0000000", + "00000000000", + "00000000000", + }; + struct tar_header_t header; + int size; + + dir = !!dir; /* normalize: 0/1 */ + size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */ + /* + dir: account for possible '/' */ + + memset(&header, 0, sizeof(header)); + strcpy(header.name, "././@LongLink"); + memcpy(header.mode, prefilled.mode, sizeof(prefilled)); + PUT_OCTAL(header.size, size); + header.typeflag = type; + chksum_and_xwrite(fd, &header); + + /* Write filename[/] and pad the block. */ + /* dir=0: writes 'name', pads */ + /* dir=1: writes 'name', writes '/', pads */ + dir *= 2; + xwrite(fd, name, size - dir); + xwrite(fd, "/", dir); + size = (-size) & (TAR_BLOCK_SIZE-1); + memset(&header, 0, size); + xwrite(fd, &header, size); +} +#endif + +/* Write out a tar header for the specified file/directory/whatever */ +static int writeTarHeader(struct TarBallInfo *tbInfo, + const char *header_name, const char *fileName, struct stat *statbuf) +{ + struct tar_header_t header; + + memset(&header, 0, sizeof(header)); + + strncpy(header.name, header_name, sizeof(header.name)); + + /* POSIX says to mask mode with 07777. */ + PUT_OCTAL(header.mode, statbuf->st_mode & 07777); + PUT_OCTAL(header.uid, statbuf->st_uid); + PUT_OCTAL(header.gid, statbuf->st_gid); + memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ + /* users report that files with negative st_mtime cause trouble, so: */ + PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0); + + /* Enter the user and group names */ + safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); + safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname)); + + if (tbInfo->hlInfo) { + /* This is a hard link */ + header.typeflag = LNKTYPE; + strncpy(header.linkname, tbInfo->hlInfo->name, + sizeof(header.linkname)); +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + /* Write out long linkname if needed */ + if (header.linkname[sizeof(header.linkname)-1]) + writeLongname(tbInfo->tarFd, GNULONGLINK, + tbInfo->hlInfo->name, 0); +#endif + } else if (S_ISLNK(statbuf->st_mode)) { + char *lpath = xmalloc_readlink_or_warn(fileName); + if (!lpath) + return FALSE; + header.typeflag = SYMTYPE; + strncpy(header.linkname, lpath, sizeof(header.linkname)); +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + /* Write out long linkname if needed */ + if (header.linkname[sizeof(header.linkname)-1]) + writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0); +#else + /* If it is larger than 100 bytes, bail out */ + if (header.linkname[sizeof(header.linkname)-1]) { + free(lpath); + bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); + return FALSE; + } +#endif + free(lpath); + } else if (S_ISDIR(statbuf->st_mode)) { + header.typeflag = DIRTYPE; + /* Append '/' only if there is a space for it */ + if (!header.name[sizeof(header.name)-1]) + header.name[strlen(header.name)] = '/'; + } else if (S_ISCHR(statbuf->st_mode)) { + header.typeflag = CHRTYPE; + PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); + PUT_OCTAL(header.devminor, minor(statbuf->st_rdev)); + } else if (S_ISBLK(statbuf->st_mode)) { + header.typeflag = BLKTYPE; + PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); + PUT_OCTAL(header.devminor, minor(statbuf->st_rdev)); + } else if (S_ISFIFO(statbuf->st_mode)) { + header.typeflag = FIFOTYPE; + } else if (S_ISREG(statbuf->st_mode)) { + /* header.size field is 12 bytes long */ + /* Does octal-encoded size fit? */ + uoff_t filesize = statbuf->st_size; + if (sizeof(filesize) <= 4 + || filesize <= (uoff_t)0777777777777LL + ) { + PUT_OCTAL(header.size, filesize); + } + /* Does base256-encoded size fit? + * It always does unless off_t is wider than 64 bits. + */ + else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS +#if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */ + && (filesize <= 0x3fffffffffffffffffffffffLL) +#endif + ) { + /* GNU tar uses "base-256 encoding" for very large numbers. + * Encoding is binary, with highest bit always set as a marker + * and sign in next-highest bit: + * 80 00 .. 00 - zero + * bf ff .. ff - largest positive number + * ff ff .. ff - minus 1 + * c0 00 .. 00 - smallest negative number + */ + char *p8 = header.size + sizeof(header.size); + do { + *--p8 = (uint8_t)filesize; + filesize >>= 8; + } while (p8 != header.size); + *p8 |= 0x80; + } else { + bb_error_msg_and_die("can't store file '%s' " + "of size %"OFF_FMT"u, aborting", + fileName, statbuf->st_size); + } + header.typeflag = REGTYPE; + } else { + bb_error_msg("%s: unknown file type", fileName); + return FALSE; + } + +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + /* Write out long name if needed */ + /* (we, like GNU tar, output long linkname *before* long name) */ + if (header.name[sizeof(header.name)-1]) + writeLongname(tbInfo->tarFd, GNULONGNAME, + header_name, S_ISDIR(statbuf->st_mode)); +#endif + + /* Now write the header out to disk */ + chksum_and_xwrite(tbInfo->tarFd, &header); + + /* Now do the verbose thing (or not) */ + if (tbInfo->verboseFlag) { + FILE *vbFd = stdout; + + /* If archive goes to stdout, verbose goes to stderr */ + if (tbInfo->tarFd == STDOUT_FILENO) + vbFd = stderr; + /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ + /* We don't have such excesses here: for us "v" == "vv" */ + /* '/' is probably a GNUism */ + fprintf(vbFd, "%s%s\n", header_name, + S_ISDIR(statbuf->st_mode) ? "/" : ""); + } + + return TRUE; +} + +#if ENABLE_FEATURE_TAR_FROM +static int exclude_file(const llist_t *excluded_files, const char *file) +{ + while (excluded_files) { + if (excluded_files->data[0] == '/') { + if (fnmatch(excluded_files->data, file, + FNM_PATHNAME | FNM_LEADING_DIR) == 0) + return 1; + } else { + const char *p; + + for (p = file; p[0] != '\0'; p++) { + if ((p == file || p[-1] == '/') + && p[0] != '/' + && fnmatch(excluded_files->data, p, + FNM_PATHNAME | FNM_LEADING_DIR) == 0 + ) { + return 1; + } + } + } + excluded_files = excluded_files->link; + } + + return 0; +} +#else +# define exclude_file(excluded_files, file) 0 +#endif + +static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, + void *userData, int depth UNUSED_PARAM) +{ + struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; + const char *header_name; + int inputFileFd = -1; + + DBG("writeFileToTarball('%s')", fileName); + + /* Strip leading '/' and such (must be before memorizing hardlink's name) */ + header_name = strip_unsafe_prefix(fileName); + + if (header_name[0] == '\0') + return TRUE; + + /* It is against the rules to archive a socket */ + if (S_ISSOCK(statbuf->st_mode)) { + bb_error_msg("%s: socket ignored", fileName); + return TRUE; + } + + /* + * Check to see if we are dealing with a hard link. + * If so - + * Treat the first occurance of a given dev/inode as a file while + * treating any additional occurances as hard links. This is done + * by adding the file information to the HardLinkInfo linked list. + */ + tbInfo->hlInfo = NULL; + if (!S_ISDIR(statbuf->st_mode) && statbuf->st_nlink > 1) { + DBG("'%s': st_nlink > 1", header_name); + tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); + if (tbInfo->hlInfo == NULL) { + DBG("'%s': addHardLinkInfo", header_name); + addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name); + } + } + + /* It is a bad idea to store the archive we are in the process of creating, + * so check the device and inode to be sure that this particular file isn't + * the new tarball */ + if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev + && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino + ) { + bb_error_msg("%s: file is the archive; skipping", fileName); + return TRUE; + } + + if (exclude_file(tbInfo->excludeList, header_name)) + return SKIP; + +#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS + if (strlen(header_name) >= NAME_SIZE) { + bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); + return TRUE; + } +#endif + + /* Is this a regular file? */ + if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { + /* open the file we want to archive, and make sure all is well */ + inputFileFd = open_or_warn(fileName, O_RDONLY); + if (inputFileFd < 0) { + return FALSE; + } + } + + /* Add an entry to the tarball */ + if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { + return FALSE; + } + + /* If it was a regular file, write out the body */ + if (inputFileFd >= 0) { + size_t readSize; + /* Write the file to the archive. */ + /* We record size into header first, */ + /* and then write out file. If file shrinks in between, */ + /* tar will be corrupted. So we don't allow for that. */ + /* NB: GNU tar 1.16 warns and pads with zeroes */ + /* or even seeks back and updates header */ + bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); + ////off_t readSize; + ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); + ////if (readSize != statbuf->st_size && readSize >= 0) { + //// bb_error_msg_and_die("short read from %s, aborting", fileName); + ////} + + /* Check that file did not grow in between? */ + /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */ + + close(inputFileFd); + + /* Pad the file up to the tar block size */ + /* (a few tricks here in the name of code size) */ + readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); + memset(block_buf, 0, readSize); + xwrite(tbInfo->tarFd, block_buf, readSize); + } + + return TRUE; +} + +#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 +# if !(ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2) +# define vfork_compressor(tar_fd, gzip) vfork_compressor(tar_fd) +# endif +/* Don't inline: vfork scares gcc and pessimizes code */ +static void NOINLINE vfork_compressor(int tar_fd, int gzip) +{ + pid_t gzipPid; +# if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2 + const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2"; +# elif ENABLE_FEATURE_SEAMLESS_GZ + const char *zip_exec = "gzip"; +# else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */ + const char *zip_exec = "bzip2"; +# endif + // On Linux, vfork never unpauses parent early, although standard + // allows for that. Do we want to waste bytes checking for it? +# define WAIT_FOR_CHILD 0 + volatile int vfork_exec_errno = 0; + struct fd_pair gzipDataPipe; +# if WAIT_FOR_CHILD + struct fd_pair gzipStatusPipe; + xpiped_pair(gzipStatusPipe); +# endif + xpiped_pair(gzipDataPipe); + + signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ + +# if defined(__GNUC__) && __GNUC__ + /* Avoid vfork clobbering */ + (void) &zip_exec; +# endif + + gzipPid = xvfork(); + + if (gzipPid == 0) { + /* child */ + /* NB: close _first_, then move fds! */ + close(gzipDataPipe.wr); +# if WAIT_FOR_CHILD + close(gzipStatusPipe.rd); + /* gzipStatusPipe.wr will close only on exec - + * parent waits for this close to happen */ + fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); +# endif + xmove_fd(gzipDataPipe.rd, 0); + xmove_fd(tar_fd, 1); + /* exec gzip/bzip2 program/applet */ + BB_EXECLP(zip_exec, zip_exec, "-f", NULL); + vfork_exec_errno = errno; + _exit(EXIT_FAILURE); + } + + /* parent */ + xmove_fd(gzipDataPipe.wr, tar_fd); + close(gzipDataPipe.rd); +# if WAIT_FOR_CHILD + close(gzipStatusPipe.wr); + while (1) { + char buf; + int n; + + /* Wait until child execs (or fails to) */ + n = full_read(gzipStatusPipe.rd, &buf, 1); + if (n < 0 /* && errno == EAGAIN */) + continue; /* try it again */ + } + close(gzipStatusPipe.rd); +# endif + if (vfork_exec_errno) { + errno = vfork_exec_errno; + bb_perror_msg_and_die("can't execute '%s'", zip_exec); + } +} +#endif /* ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 */ + + +/* gcc 4.2.1 inlines it, making code bigger */ +static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, + int dereferenceFlag, const llist_t *include, + const llist_t *exclude, int gzip) +{ + int errorFlag = FALSE; + struct TarBallInfo tbInfo; + + tbInfo.hlInfoHead = NULL; + tbInfo.tarFd = tar_fd; + tbInfo.verboseFlag = verboseFlag; + + /* Store the stat info for the tarball's file, so + * can avoid including the tarball into itself.... */ + xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file"); + +#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 + if (gzip) + vfork_compressor(tbInfo.tarFd, gzip); +#endif + + tbInfo.excludeList = exclude; + + /* Read the directory/files and iterate over them one at a time */ + while (include) { + if (!recursive_action(include->data, ACTION_RECURSE | + (dereferenceFlag ? ACTION_FOLLOWLINKS : 0), + writeFileToTarball, writeFileToTarball, &tbInfo, 0) + ) { + errorFlag = TRUE; + } + include = include->link; + } + /* Write two empty blocks to the end of the archive */ + memset(block_buf, 0, 2*TAR_BLOCK_SIZE); + xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE); + + /* To be pedantically correct, we would check if the tarball + * is smaller than 20 tar blocks, and pad it if it was smaller, + * but that isn't necessary for GNU tar interoperability, and + * so is considered a waste of space */ + + /* Close so the child process (if any) will exit */ + close(tbInfo.tarFd); + + /* Hang up the tools, close up shop, head home */ + if (ENABLE_FEATURE_CLEAN_UP) + freeHardLinkInfo(&tbInfo.hlInfoHead); + + if (errorFlag) + bb_error_msg("error exit delayed from previous errors"); + +#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 + if (gzip) { + int status; + if (safe_waitpid(-1, &status, 0) == -1) + bb_perror_msg("waitpid"); + else if (!WIFEXITED(status) || WEXITSTATUS(status)) + /* gzip was killed or has exited with nonzero! */ + errorFlag = TRUE; + } +#endif + return errorFlag; +} +#else +int writeTarFile(int tar_fd, int verboseFlag, + int dereferenceFlag, const llist_t *include, + const llist_t *exclude, int gzip); +#endif /* FEATURE_TAR_CREATE */ + +#if ENABLE_FEATURE_TAR_FROM +static llist_t *append_file_list_to_list(llist_t *list) +{ + FILE *src_stream; + char *line; + llist_t *newlist = NULL; + + while (list) { + src_stream = xfopen_stdin(llist_pop(&list)); + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + /* kill trailing '/' unless the string is just "/" */ + char *cp = last_char_is(line, '/'); + if (cp > line) + *cp = '\0'; + llist_add_to(&newlist, line); + } + fclose(src_stream); + } + return newlist; +} +#else +# define append_file_list_to_list(x) 0 +#endif + +#if ENABLE_FEATURE_SEAMLESS_Z +static char FAST_FUNC get_header_tar_Z(archive_handle_t *archive_handle) +{ + /* Can't lseek over pipes */ + archive_handle->seek = seek_by_read; + + /* do the decompression, and cleanup */ + if (xread_char(archive_handle->src_fd) != 0x1f + || xread_char(archive_handle->src_fd) != 0x9d + ) { + bb_error_msg_and_die("invalid magic"); + } + + open_transformer(archive_handle->src_fd, unpack_Z_stream, "uncompress"); + archive_handle->offset = 0; + while (get_header_tar(archive_handle) == EXIT_SUCCESS) + continue; + + /* Can only do one file at a time */ + return EXIT_FAILURE; +} +#else +# define get_header_tar_Z NULL +#endif + +#ifdef CHECK_FOR_CHILD_EXITCODE +/* Looks like it isn't needed - tar detects malformed (truncated) + * archive if e.g. bunzip2 fails */ +static int child_error; + +static void handle_SIGCHLD(int status) +{ + /* Actually, 'status' is a signo. We reuse it for other needs */ + + /* Wait for any child without blocking */ + if (wait_any_nohang(&status) < 0) + /* wait failed?! I'm confused... */ + return; + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + /* child exited with 0 */ + return; + /* Cannot happen? + if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */ + child_error = 1; +} +#endif + +//usage:#define tar_trivial_usage +//usage: "-[" IF_FEATURE_TAR_CREATE("c") "xt" +//usage: IF_FEATURE_SEAMLESS_Z("Z") +//usage: IF_FEATURE_SEAMLESS_GZ("z") +//usage: IF_FEATURE_SEAMLESS_BZ2("j") +//usage: IF_FEATURE_SEAMLESS_LZMA("a") +//usage: IF_FEATURE_TAR_CREATE("h") +//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m") +//usage: "vO] " +//usage: IF_FEATURE_TAR_FROM("[-X FILE] [-T FILE] ") +//usage: "[-f TARFILE] [-C DIR] [FILE]..." +//usage:#define tar_full_usage "\n\n" +//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") +//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ") +//usage: "or list files from a tar file\n" +//usage: "\nOperation:" +//usage: IF_FEATURE_TAR_CREATE( +//usage: "\n c Create" +//usage: ) +//usage: "\n x Extract" +//usage: "\n t List" +//usage: "\n f Name of TARFILE ('-' for stdin/out)" +//usage: "\n C Change to DIR before operation" +//usage: "\n v Verbose" +//usage: IF_FEATURE_SEAMLESS_Z( +//usage: "\n Z (De)compress using compress" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_GZ( +//usage: "\n z (De)compress using gzip" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_BZ2( +//usage: "\n j (De)compress using bzip2" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_LZMA( +//usage: "\n a (De)compress using lzma" +//usage: ) +//usage: "\n O Extract to stdout" +//usage: IF_FEATURE_TAR_CREATE( +//usage: "\n h Follow symlinks" +//usage: ) +//usage: IF_FEATURE_TAR_NOPRESERVE_TIME( +//usage: "\n m Don't restore mtime" +//usage: ) +//usage: IF_FEATURE_TAR_FROM( +//usage: IF_FEATURE_TAR_LONG_OPTIONS( +//usage: "\n exclude File to exclude" +//usage: ) +//usage: "\n X File with names to exclude" +//usage: "\n T File with names to include" +//usage: ) +//usage: +//usage:#define tar_example_usage +//usage: "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" +//usage: "$ tar -cf /tmp/tarball.tar /usr/local\n" + +// Supported but aren't in --help: +// o no-same-owner +// p same-permissions +// k keep-old +// numeric-owner +// no-same-permissions +// overwrite +//IF_FEATURE_TAR_TO_COMMAND( +// to-command +//) + +enum { + OPTBIT_KEEP_OLD = 8, + IF_FEATURE_TAR_CREATE( OPTBIT_CREATE ,) + IF_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,) + IF_FEATURE_SEAMLESS_BZ2( OPTBIT_BZIP2 ,) + IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,) + IF_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,) + IF_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) + IF_FEATURE_SEAMLESS_GZ( OPTBIT_GZIP ,) + IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) // 16th bit + IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) +#if ENABLE_FEATURE_TAR_LONG_OPTIONS + IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) + OPTBIT_NUMERIC_OWNER, + OPTBIT_NOPRESERVE_PERM, + OPTBIT_OVERWRITE, +#endif + OPT_TEST = 1 << 0, // t + OPT_EXTRACT = 1 << 1, // x + OPT_BASEDIR = 1 << 2, // C + OPT_TARNAME = 1 << 3, // f + OPT_2STDOUT = 1 << 4, // O + OPT_NOPRESERVE_OWNER = 1 << 5, // o == no-same-owner + OPT_P = 1 << 6, // p + OPT_VERBOSE = 1 << 7, // v + OPT_KEEP_OLD = 1 << 8, // k + OPT_CREATE = IF_FEATURE_TAR_CREATE( (1 << OPTBIT_CREATE )) + 0, // c + OPT_DEREFERENCE = IF_FEATURE_TAR_CREATE( (1 << OPTBIT_DEREFERENCE )) + 0, // h + OPT_BZIP2 = IF_FEATURE_SEAMLESS_BZ2( (1 << OPTBIT_BZIP2 )) + 0, // j + OPT_LZMA = IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA )) + 0, // a + OPT_INCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_INCLUDE_FROM)) + 0, // T + OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X + OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z + OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z + OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m + OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command + OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner + OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions + OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite +}; +#if ENABLE_FEATURE_TAR_LONG_OPTIONS +static const char tar_longopts[] ALIGN1 = + "list\0" No_argument "t" + "extract\0" No_argument "x" + "directory\0" Required_argument "C" + "file\0" Required_argument "f" + "to-stdout\0" No_argument "O" + /* do not restore owner */ + /* Note: GNU tar handles 'o' as no-same-owner only on extract, + * on create, 'o' is --old-archive. We do not support --old-archive. */ + "no-same-owner\0" No_argument "o" + "same-permissions\0" No_argument "p" + "verbose\0" No_argument "v" + "keep-old\0" No_argument "k" +# if ENABLE_FEATURE_TAR_CREATE + "create\0" No_argument "c" + "dereference\0" No_argument "h" +# endif +# if ENABLE_FEATURE_SEAMLESS_BZ2 + "bzip2\0" No_argument "j" +# endif +# if ENABLE_FEATURE_SEAMLESS_LZMA + "lzma\0" No_argument "a" +# endif +# if ENABLE_FEATURE_TAR_FROM + "files-from\0" Required_argument "T" + "exclude-from\0" Required_argument "X" +# endif +# if ENABLE_FEATURE_SEAMLESS_GZ + "gzip\0" No_argument "z" +# endif +# if ENABLE_FEATURE_SEAMLESS_Z + "compress\0" No_argument "Z" +# endif +# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME + "touch\0" No_argument "m" +# endif +# if ENABLE_FEATURE_TAR_TO_COMMAND + "to-command\0" Required_argument "\xfb" +# endif + /* use numeric uid/gid from tar header, not textual */ + "numeric-owner\0" No_argument "\xfc" + /* do not restore mode */ + "no-same-permissions\0" No_argument "\xfd" + /* on unpack, open with O_TRUNC and !O_EXCL */ + "overwrite\0" No_argument "\xfe" + /* --exclude takes next bit position in option mask, */ + /* therefore we have to put it _after_ --no-same-permissions */ +# if ENABLE_FEATURE_TAR_FROM + "exclude\0" Required_argument "\xff" +# endif + ; +#endif + +int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tar_main(int argc UNUSED_PARAM, char **argv) +{ + char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar; + archive_handle_t *tar_handle; + char *base_dir = NULL; + const char *tar_filename = "-"; + unsigned opt; + int verboseFlag = 0; +#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM + llist_t *excludes = NULL; +#endif + + /* Initialise default values */ + tar_handle = init_handle(); + tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS + | ARCHIVE_RESTORE_DATE + | ARCHIVE_UNLINK_OLD; + + /* Apparently only root's tar preserves perms (see bug 3844) */ + if (getuid() != 0) + tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; + + /* Prepend '-' to the first argument if required */ + opt_complementary = "--:" // first arg is options + "tt:vv:" // count -t,-v + IF_FEATURE_TAR_FROM("X::T::") // cumulative lists +#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM + "\xff::" // cumulative lists for --exclude +#endif + IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd + IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive + IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive +#if ENABLE_FEATURE_TAR_LONG_OPTIONS + applet_long_options = tar_longopts; +#endif +#if ENABLE_DESKTOP + if (argv[1] && argv[1][0] != '-') { + /* Compat: + * 1st argument without dash handles options with parameters + * differently from dashed one: it takes *next argv[i]* + * as paramenter even if there are more chars in 1st argument: + * "tar fx TARFILE" - "x" is not taken as f's param + * but is interpreted as -x option + * "tar -xf TARFILE" - dashed equivalent of the above + * "tar -fx ..." - "x" is taken as f's param + * getopt32 wouldn't handle 1st command correctly. + * Unfortunately, people do use such commands. + * We massage argv[1] to work around it by moving 'f' + * to the end of the string. + * More contrived "tar fCx TARFILE DIR" still fails, + * but such commands are much less likely to be used. + */ + char *f = strchr(argv[1], 'f'); + if (f) { + while (f[1] != '\0') { + *f = f[1]; + f++; + } + *f = 'f'; + } + } +#endif + opt = getopt32(argv, + "txC:f:Oopvk" + IF_FEATURE_TAR_CREATE( "ch" ) + IF_FEATURE_SEAMLESS_BZ2( "j" ) + IF_FEATURE_SEAMLESS_LZMA("a" ) + IF_FEATURE_TAR_FROM( "T:X:") + IF_FEATURE_SEAMLESS_GZ( "z" ) + IF_FEATURE_SEAMLESS_Z( "Z" ) + IF_FEATURE_TAR_NOPRESERVE_TIME("m") + , &base_dir // -C dir + , &tar_filename // -f filename + IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T + IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X + IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command +#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM + , &excludes // --exclude +#endif + , &verboseFlag // combined count for -t and -v + , &verboseFlag // combined count for -t and -v + ); + //bb_error_msg("opt:%08x", opt); + argv += optind; + + if (verboseFlag) tar_handle->action_header = header_verbose_list; + if (verboseFlag == 1) tar_handle->action_header = header_list; + + if (opt & OPT_EXTRACT) + tar_handle->action_data = data_extract_all; + + if (opt & OPT_2STDOUT) + tar_handle->action_data = data_extract_to_stdout; + + if (opt & OPT_2COMMAND) { + putenv((char*)"TAR_FILETYPE=f"); + signal(SIGPIPE, SIG_IGN); + tar_handle->action_data = data_extract_to_command; + IF_FEATURE_TAR_TO_COMMAND(tar_handle->tar__to_command_shell = xstrdup(get_shell_name());) + } + + if (opt & OPT_KEEP_OLD) + tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; + + if (opt & OPT_NUMERIC_OWNER) + tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER; + + if (opt & OPT_NOPRESERVE_OWNER) + tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_OWNER; + + if (opt & OPT_NOPRESERVE_PERM) + tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; + + if (opt & OPT_OVERWRITE) { + tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; + tar_handle->ah_flags |= ARCHIVE_O_TRUNC; + } + + if (opt & OPT_GZIP) + get_header_ptr = get_header_tar_gz; + + if (opt & OPT_BZIP2) + get_header_ptr = get_header_tar_bz2; + + if (opt & OPT_LZMA) + get_header_ptr = get_header_tar_lzma; + + if (opt & OPT_COMPRESS) + get_header_ptr = get_header_tar_Z; + + if (opt & OPT_NOPRESERVE_TIME) + tar_handle->ah_flags &= ~ARCHIVE_RESTORE_DATE; + +#if ENABLE_FEATURE_TAR_FROM + tar_handle->reject = append_file_list_to_list(tar_handle->reject); +# if ENABLE_FEATURE_TAR_LONG_OPTIONS + /* Append excludes to reject */ + while (excludes) { + llist_t *next = excludes->link; + excludes->link = tar_handle->reject; + tar_handle->reject = excludes; + excludes = next; + } +# endif + tar_handle->accept = append_file_list_to_list(tar_handle->accept); +#endif + + /* Setup an array of filenames to work with */ + /* TODO: This is the same as in ar, make a separate function? */ + while (*argv) { + /* kill trailing '/' unless the string is just "/" */ + char *cp = last_char_is(*argv, '/'); + if (cp > *argv) + *cp = '\0'; + llist_add_to_end(&tar_handle->accept, *argv); + argv++; + } + + if (tar_handle->accept || tar_handle->reject) + tar_handle->filter = filter_accept_reject_list; + + /* Open the tar file */ + { + int tar_fd = STDIN_FILENO; + int flags = O_RDONLY; + + if (opt & OPT_CREATE) { + /* Make sure there is at least one file to tar up */ + if (tar_handle->accept == NULL) + bb_error_msg_and_die("empty archive"); + + tar_fd = STDOUT_FILENO; + /* Mimicking GNU tar 1.15.1: */ + flags = O_WRONLY | O_CREAT | O_TRUNC; + } + + if (LONE_DASH(tar_filename)) { + tar_handle->src_fd = tar_fd; + tar_handle->seek = seek_by_read; + } else { + if (ENABLE_FEATURE_TAR_AUTODETECT + && flags == O_RDONLY + && get_header_ptr == get_header_tar + ) { + tar_handle->src_fd = open_zipped(tar_filename); + if (tar_handle->src_fd < 0) + bb_perror_msg_and_die("can't open '%s'", tar_filename); + } else { + tar_handle->src_fd = xopen(tar_filename, flags); + } + } + } + + if (base_dir) + xchdir(base_dir); + +#ifdef CHECK_FOR_CHILD_EXITCODE + /* We need to know whether child (gzip/bzip/etc) exits abnormally */ + signal(SIGCHLD, handle_SIGCHLD); +#endif + + /* Create an archive */ + if (opt & OPT_CREATE) { +#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 + int zipMode = 0; + if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP)) + zipMode = 1; + if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2)) + zipMode = 2; +#endif + /* NB: writeTarFile() closes tar_handle->src_fd */ + return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE, + tar_handle->accept, + tar_handle->reject, zipMode); + } + + while (get_header_ptr(tar_handle) == EXIT_SUCCESS) + continue; + + /* Check that every file that should have been extracted was */ + while (tar_handle->accept) { + if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) + && !find_list_entry(tar_handle->passed, tar_handle->accept->data) + ) { + bb_error_msg_and_die("%s: not found in archive", + tar_handle->accept->data); + } + tar_handle->accept = tar_handle->accept->link; + } + if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */) + close(tar_handle->src_fd); + + return EXIT_SUCCESS; +} diff --git a/archival/unzip.c b/archival/unzip.c new file mode 100644 index 000000000..4cc98cdb2 --- /dev/null +++ b/archival/unzip.c @@ -0,0 +1,704 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini unzip implementation for busybox + * + * Copyright (C) 2004 by Ed Clark + * + * Loosely based on original busybox unzip applet by Laurence Anderson. + * All options and features should work in this version. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* For reference see + * http://www.pkware.com/company/standards/appnote/ + * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip + */ + +/* TODO + * Zip64 + other methods + */ + +//usage:#define unzip_trivial_usage +//usage: "[-opts[modifiers]] FILE[.zip] [LIST] [-x XLIST] [-d DIR]" +//usage:#define unzip_full_usage "\n\n" +//usage: "Extract files from ZIP archives\n" +//usage: "\n -l List archive contents (with -q for short form)" +//usage: "\n -n Never overwrite files (default)" +//usage: "\n -o Overwrite" +//usage: "\n -p Send output to stdout" +//usage: "\n -q Quiet" +//usage: "\n -x XLST Exclude these files" +//usage: "\n -d DIR Extract files into DIR" + +#include "libbb.h" +#include "archive.h" + +enum { +#if BB_BIG_ENDIAN + ZIP_FILEHEADER_MAGIC = 0x504b0304, + ZIP_CDF_MAGIC = 0x504b0102, /* central directory's file header */ + ZIP_CDE_MAGIC = 0x504b0506, /* "end of central directory" record */ + ZIP_DD_MAGIC = 0x504b0708, +#else + ZIP_FILEHEADER_MAGIC = 0x04034b50, + ZIP_CDF_MAGIC = 0x02014b50, + ZIP_CDE_MAGIC = 0x06054b50, + ZIP_DD_MAGIC = 0x08074b50, +#endif +}; + +#define ZIP_HEADER_LEN 26 + +typedef union { + uint8_t raw[ZIP_HEADER_LEN]; + struct { + uint16_t version; /* 0-1 */ + uint16_t zip_flags; /* 2-3 */ + uint16_t method; /* 4-5 */ + uint16_t modtime; /* 6-7 */ + uint16_t moddate; /* 8-9 */ + uint32_t crc32 PACKED; /* 10-13 */ + uint32_t cmpsize PACKED; /* 14-17 */ + uint32_t ucmpsize PACKED; /* 18-21 */ + uint16_t filename_len; /* 22-23 */ + uint16_t extra_len; /* 24-25 */ + } formatted PACKED; +} zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ + +/* Check the offset of the last element, not the length. This leniency + * allows for poor packing, whereby the overall struct may be too long, + * even though the elements are all in the right place. + */ +struct BUG_zip_header_must_be_26_bytes { + char BUG_zip_header_must_be_26_bytes[ + offsetof(zip_header_t, formatted.extra_len) + 2 + == ZIP_HEADER_LEN ? 1 : -1]; +}; + +#define FIX_ENDIANNESS_ZIP(zip_header) do { \ + (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ + (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ + (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ + (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ + (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ + (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ + (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ + (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ + (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ +} while (0) + +#define CDF_HEADER_LEN 42 + +typedef union { + uint8_t raw[CDF_HEADER_LEN]; + struct { + /* uint32_t signature; 50 4b 01 02 */ + uint16_t version_made_by; /* 0-1 */ + uint16_t version_needed; /* 2-3 */ + uint16_t cdf_flags; /* 4-5 */ + uint16_t method; /* 6-7 */ + uint16_t mtime; /* 8-9 */ + uint16_t mdate; /* 10-11 */ + uint32_t crc32; /* 12-15 */ + uint32_t cmpsize; /* 16-19 */ + uint32_t ucmpsize; /* 20-23 */ + uint16_t file_name_length; /* 24-25 */ + uint16_t extra_field_length; /* 26-27 */ + uint16_t file_comment_length; /* 28-29 */ + uint16_t disk_number_start; /* 30-31 */ + uint16_t internal_file_attributes; /* 32-33 */ + uint32_t external_file_attributes PACKED; /* 34-37 */ + uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ + } formatted PACKED; +} cdf_header_t; + +struct BUG_cdf_header_must_be_42_bytes { + char BUG_cdf_header_must_be_42_bytes[ + offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 + == CDF_HEADER_LEN ? 1 : -1]; +}; + +#define FIX_ENDIANNESS_CDF(cdf_header) do { \ + (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ + (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ + (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ + (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ + (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ + (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ + IF_DESKTOP( \ + (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ + (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ + ) \ +} while (0) + +#define CDE_HEADER_LEN 16 + +typedef union { + uint8_t raw[CDE_HEADER_LEN]; + struct { + /* uint32_t signature; 50 4b 05 06 */ + uint16_t this_disk_no; + uint16_t disk_with_cdf_no; + uint16_t cdf_entries_on_this_disk; + uint16_t cdf_entries_total; + uint32_t cdf_size; + uint32_t cdf_offset; + /* uint16_t file_comment_length; */ + /* .ZIP file comment (variable size) */ + } formatted PACKED; +} cde_header_t; + +struct BUG_cde_header_must_be_16_bytes { + char BUG_cde_header_must_be_16_bytes[ + sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; +}; + +#define FIX_ENDIANNESS_CDE(cde_header) do { \ + (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ +} while (0) + +enum { zip_fd = 3 }; + + +#if ENABLE_DESKTOP + +#define PEEK_FROM_END 16384 + +/* NB: does not preserve file position! */ +static uint32_t find_cdf_offset(void) +{ + cde_header_t cde_header; + unsigned char *p; + off_t end; + unsigned char *buf = xzalloc(PEEK_FROM_END); + + end = xlseek(zip_fd, 0, SEEK_END); + end -= PEEK_FROM_END; + if (end < 0) + end = 0; + xlseek(zip_fd, end, SEEK_SET); + full_read(zip_fd, buf, PEEK_FROM_END); + + p = buf; + while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { + if (*p != 'P') { + p++; + continue; + } + if (*++p != 'K') + continue; + if (*++p != 5) + continue; + if (*++p != 6) + continue; + /* we found CDE! */ + memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); + FIX_ENDIANNESS_CDE(cde_header); + free(buf); + return cde_header.formatted.cdf_offset; + } + //free(buf); + bb_error_msg_and_die("can't find file table"); +}; + +static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) +{ + off_t org; + + org = xlseek(zip_fd, 0, SEEK_CUR); + + if (!cdf_offset) + cdf_offset = find_cdf_offset(); + + xlseek(zip_fd, cdf_offset + 4, SEEK_SET); + xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); + FIX_ENDIANNESS_CDF(*cdf_ptr); + cdf_offset += 4 + CDF_HEADER_LEN + + cdf_ptr->formatted.file_name_length + + cdf_ptr->formatted.extra_field_length + + cdf_ptr->formatted.file_comment_length; + + xlseek(zip_fd, org, SEEK_SET); + return cdf_offset; +}; +#endif + +static void unzip_skip(off_t skip) +{ + if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) + bb_copyfd_exact_size(zip_fd, -1, skip); +} + +static void unzip_create_leading_dirs(const char *fn) +{ + /* Create all leading directories */ + char *name = xstrdup(fn); + if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) { + xfunc_die(); /* bb_make_directory is noisy */ + } + free(name); +} + +static void unzip_extract(zip_header_t *zip_header, int dst_fd) +{ + if (zip_header->formatted.method == 0) { + /* Method 0 - stored (not compressed) */ + off_t size = zip_header->formatted.ucmpsize; + if (size) + bb_copyfd_exact_size(zip_fd, dst_fd, size); + } else { + /* Method 8 - inflate */ + inflate_unzip_result res; + if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0) + bb_error_msg_and_die("inflate error"); + /* Validate decompression - crc */ + if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) { + bb_error_msg_and_die("crc error"); + } + /* Validate decompression - size */ + if (zip_header->formatted.ucmpsize != res.bytes_out) { + /* Don't die. Who knows, maybe len calculation + * was botched somewhere. After all, crc matched! */ + bb_error_msg("bad length"); + } + } +} + +int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int unzip_main(int argc, char **argv) +{ + enum { O_PROMPT, O_NEVER, O_ALWAYS }; + + zip_header_t zip_header; + smallint quiet = 0; + IF_NOT_DESKTOP(const) smallint verbose = 0; + smallint listing = 0; + smallint overwrite = O_PROMPT; +#if ENABLE_DESKTOP + uint32_t cdf_offset; +#endif + unsigned long total_usize; + unsigned long total_size; + unsigned total_entries; + int dst_fd = -1; + char *src_fn = NULL; + char *dst_fn = NULL; + llist_t *zaccept = NULL; + llist_t *zreject = NULL; + char *base_dir = NULL; + int i, opt; + int opt_range = 0; + char key_buf[80]; + struct stat stat_buf; + +/* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP: + * + * # /usr/bin/unzip -qq -v decompress_unlzma.i.zip + * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i + * # /usr/bin/unzip -q -v decompress_unlzma.i.zip + * Length Method Size Ratio Date Time CRC-32 Name + * -------- ------ ------- ----- ---- ---- ------ ---- + * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i + * -------- ------- --- ------- + * 204372 35278 83% 1 file + * # /usr/bin/unzip -v decompress_unlzma.i.zip + * Archive: decompress_unlzma.i.zip + * Length Method Size Ratio Date Time CRC-32 Name + * -------- ------ ------- ----- ---- ---- ------ ---- + * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i + * -------- ------- --- ------- + * 204372 35278 83% 1 file + * # unzip -v decompress_unlzma.i.zip + * Archive: decompress_unlzma.i.zip + * Length Date Time Name + * -------- ---- ---- ---- + * 204372 09-06-09 14:23 decompress_unlzma.i + * -------- ------- + * 204372 1 files + * # /usr/bin/unzip -l -qq decompress_unlzma.i.zip + * 204372 09-06-09 14:23 decompress_unlzma.i + * # /usr/bin/unzip -l -q decompress_unlzma.i.zip + * Length Date Time Name + * -------- ---- ---- ---- + * 204372 09-06-09 14:23 decompress_unlzma.i + * -------- ------- + * 204372 1 file + * # /usr/bin/unzip -l decompress_unlzma.i.zip + * Archive: decompress_unlzma.i.zip + * Length Date Time Name + * -------- ---- ---- ---- + * 204372 09-06-09 14:23 decompress_unlzma.i + * -------- ------- + * 204372 1 file + */ + + /* '-' makes getopt return 1 for non-options */ + while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) { + switch (opt_range) { + case 0: /* Options */ + switch (opt) { + case 'l': /* List */ + listing = 1; + break; + + case 'n': /* Never overwrite existing files */ + overwrite = O_NEVER; + break; + + case 'o': /* Always overwrite existing files */ + overwrite = O_ALWAYS; + break; + + case 'p': /* Extract files to stdout and fall through to set verbosity */ + dst_fd = STDOUT_FILENO; + + case 'q': /* Be quiet */ + quiet++; + break; + + case 'v': /* Verbose list */ + IF_DESKTOP(verbose++;) + listing = 1; + break; + + case 1: /* The zip file */ + /* +5: space for ".zip" and NUL */ + src_fn = xmalloc(strlen(optarg) + 5); + strcpy(src_fn, optarg); + opt_range++; + break; + + default: + bb_show_usage(); + } + break; + + case 1: /* Include files */ + if (opt == 1) { + llist_add_to(&zaccept, optarg); + break; + } + if (opt == 'd') { + base_dir = optarg; + opt_range += 2; + break; + } + if (opt == 'x') { + opt_range++; + break; + } + bb_show_usage(); + + case 2 : /* Exclude files */ + if (opt == 1) { + llist_add_to(&zreject, optarg); + break; + } + if (opt == 'd') { /* Extract to base directory */ + base_dir = optarg; + opt_range++; + break; + } + /* fall through */ + + default: + bb_show_usage(); + } + } + + if (src_fn == NULL) { + bb_show_usage(); + } + + /* Open input file */ + if (LONE_DASH(src_fn)) { + xdup2(STDIN_FILENO, zip_fd); + /* Cannot use prompt mode since zip data is arriving on STDIN */ + if (overwrite == O_PROMPT) + overwrite = O_NEVER; + } else { + static const char extn[][5] = {"", ".zip", ".ZIP"}; + int orig_src_fn_len = strlen(src_fn); + int src_fd = -1; + + for (i = 0; (i < 3) && (src_fd == -1); i++) { + strcpy(src_fn + orig_src_fn_len, extn[i]); + src_fd = open(src_fn, O_RDONLY); + } + if (src_fd == -1) { + src_fn[orig_src_fn_len] = '\0'; + bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn); + } + xmove_fd(src_fd, zip_fd); + } + + /* Change dir if necessary */ + if (base_dir) + xchdir(base_dir); + + if (quiet <= 1) { /* not -qq */ + if (quiet == 0) + printf("Archive: %s\n", src_fn); + if (listing) { + puts(verbose ? + " Length Method Size Ratio Date Time CRC-32 Name\n" + "-------- ------ ------- ----- ---- ---- ------ ----" + : + " Length Date Time Name\n" + " -------- ---- ---- ----" + ); + } + } + +/* Example of an archive with one 0-byte long file named 'z' + * created by Zip 2.31 on Unix: + * 0000 [50 4b]03 04 0a 00 00 00 00 00 42 1a b8 3c 00 00 |PK........B..<..| + * sig........ vneed flags compr mtime mdate crc32> + * 0010 00 00 00 00 00 00 00 00 00 00 01 00 15 00 7a 55 |..............zU| + * >..... csize...... usize...... fnlen exlen fn ex> + * 0020 54 09 00 03 cc d3 f9 4b cc d3 f9 4b 55 78 04 00 |T......K...KUx..| + * >tra_field...................................... + * 0030 00 00 00 00[50 4b]01 02 17 03 0a 00 00 00 00 00 |....PK..........| + * ........... sig........ vmade vneed flags compr + * 0040 42 1a b8 3c 00 00 00 00 00 00 00 00 00 00 00 00 |B..<............| + * mtime mdate crc32...... csize...... usize...... + * 0050 01 00 0d 00 00 00 00 00 00 00 00 00 a4 81 00 00 |................| + * fnlen exlen clen. dnum. iattr eattr...... relofs> (eattr = rw-r--r--) + * 0060 00 00 7a 55 54 05 00 03 cc d3 f9 4b 55 78 00 00 |..zUT......KUx..| + * >..... fn extra_field........................... + * 0070 [50 4b]05 06 00 00 00 00 01 00 01 00 3c 00 00 00 |PK..........<...| + * 0080 34 00 00 00 00 00 |4.....| + */ + total_usize = 0; + total_size = 0; + total_entries = 0; +#if ENABLE_DESKTOP + cdf_offset = 0; +#endif + while (1) { + uint32_t magic; + mode_t dir_mode = 0777; +#if ENABLE_DESKTOP + mode_t file_mode = 0666; +#endif + + /* Check magic number */ + xread(zip_fd, &magic, 4); + /* Central directory? It's at the end, so exit */ + if (magic == ZIP_CDF_MAGIC) + break; +#if ENABLE_DESKTOP + /* Data descriptor? It was a streaming file, go on */ + if (magic == ZIP_DD_MAGIC) { + /* skip over duplicate crc32, cmpsize and ucmpsize */ + unzip_skip(3 * 4); + continue; + } +#endif + if (magic != ZIP_FILEHEADER_MAGIC) + bb_error_msg_and_die("invalid zip magic %08X", (int)magic); + + /* Read the file header */ + xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip_header); + if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { + bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); + } +#if !ENABLE_DESKTOP + if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { + bb_error_msg_and_die("zip flags 1 and 8 are not supported"); + } +#else + if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { + /* 0x0001 - encrypted */ + bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); + } + + { + cdf_header_t cdf_header; + cdf_offset = read_next_cdf(cdf_offset, &cdf_header); + if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { + /* 0x0008 - streaming. [u]cmpsize can be reliably gotten + * only from Central Directory. See unzip_doc.txt */ + zip_header.formatted.crc32 = cdf_header.formatted.crc32; + zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; + zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; + } + if ((cdf_header.formatted.version_made_by >> 8) == 3) { + /* this archive is created on Unix */ + dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); + } + } +#endif + + /* Read filename */ + free(dst_fn); + dst_fn = xzalloc(zip_header.formatted.filename_len + 1); + xread(zip_fd, dst_fn, zip_header.formatted.filename_len); + + /* Skip extra header bytes */ + unzip_skip(zip_header.formatted.extra_len); + + /* Filter zip entries */ + if (find_list_entry(zreject, dst_fn) + || (zaccept && !find_list_entry(zaccept, dst_fn)) + ) { /* Skip entry */ + i = 'n'; + + } else { /* Extract entry */ + if (listing) { /* List entry */ + unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); + if (!verbose) { + // " Length Date Time Name\n" + // " -------- ---- ---- ----" + printf( "%9u %02u-%02u-%02u %02u:%02u %s\n", + (unsigned)zip_header.formatted.ucmpsize, + (dostime & 0x01e00000) >> 21, + (dostime & 0x001f0000) >> 16, + (((dostime & 0xfe000000) >> 25) + 1980) % 100, + (dostime & 0x0000f800) >> 11, + (dostime & 0x000007e0) >> 5, + dst_fn); + total_usize += zip_header.formatted.ucmpsize; + } else { + unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize; + percents = percents * 100; + if (zip_header.formatted.ucmpsize) + percents /= zip_header.formatted.ucmpsize; + // " Length Method Size Ratio Date Time CRC-32 Name\n" + // "-------- ------ ------- ----- ---- ---- ------ ----" + printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x %s\n", + (unsigned)zip_header.formatted.ucmpsize, + (unsigned)zip_header.formatted.cmpsize, + (unsigned)percents, + (dostime & 0x01e00000) >> 21, + (dostime & 0x001f0000) >> 16, + (((dostime & 0xfe000000) >> 25) + 1980) % 100, + (dostime & 0x0000f800) >> 11, + (dostime & 0x000007e0) >> 5, + zip_header.formatted.crc32, + dst_fn); + total_usize += zip_header.formatted.ucmpsize; + total_size += zip_header.formatted.cmpsize; + } + i = 'n'; + } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ + i = -1; + } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ + if (stat(dst_fn, &stat_buf) == -1) { + if (errno != ENOENT) { + bb_perror_msg_and_die("can't stat '%s'", dst_fn); + } + if (!quiet) { + printf(" creating: %s\n", dst_fn); + } + unzip_create_leading_dirs(dst_fn); + if (bb_make_directory(dst_fn, dir_mode, 0)) { + xfunc_die(); + } + } else { + if (!S_ISDIR(stat_buf.st_mode)) { + bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); + } + } + i = 'n'; + + } else { /* Extract file */ + check_file: + if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ + if (errno != ENOENT) { + bb_perror_msg_and_die("can't stat '%s'", dst_fn); + } + i = 'y'; + } else { /* File already exists */ + if (overwrite == O_NEVER) { + i = 'n'; + } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ + if (overwrite == O_ALWAYS) { + i = 'y'; + } else { + printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); + fflush_all(); + if (!fgets(key_buf, sizeof(key_buf), stdin)) { + bb_perror_msg_and_die("can't read input"); + } + i = key_buf[0]; + } + } else { /* File is not regular file */ + bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); + } + } + } + } + + switch (i) { + case 'A': + overwrite = O_ALWAYS; + case 'y': /* Open file and fall into unzip */ + unzip_create_leading_dirs(dst_fn); +#if ENABLE_DESKTOP + dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); +#else + dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); +#endif + case -1: /* Unzip */ + if (!quiet) { + printf(" inflating: %s\n", dst_fn); + } + unzip_extract(&zip_header, dst_fd); + if (dst_fd != STDOUT_FILENO) { + /* closing STDOUT is potentially bad for future business */ + close(dst_fd); + } + break; + + case 'N': + overwrite = O_NEVER; + case 'n': + /* Skip entry data */ + unzip_skip(zip_header.formatted.cmpsize); + break; + + case 'r': + /* Prompt for new name */ + printf("new name: "); + if (!fgets(key_buf, sizeof(key_buf), stdin)) { + bb_perror_msg_and_die("can't read input"); + } + free(dst_fn); + dst_fn = xstrdup(key_buf); + chomp(dst_fn); + goto check_file; + + default: + printf("error: invalid response [%c]\n", (char)i); + goto check_file; + } + + total_entries++; + } + + if (listing && quiet <= 1) { + if (!verbose) { + // " Length Date Time Name\n" + // " -------- ---- ---- ----" + printf( " -------- -------\n" + "%9lu" " %u files\n", + total_usize, total_entries); + } else { + unsigned long percents = total_usize - total_size; + percents = percents * 100; + if (total_usize) + percents /= total_usize; + // " Length Method Size Ratio Date Time CRC-32 Name\n" + // "-------- ------ ------- ----- ---- ---- ------ ----" + printf( "-------- ------- --- -------\n" + "%8lu" "%17lu%4u%% %u files\n", + total_usize, total_size, (unsigned)percents, + total_entries); + } + } + + return 0; +} diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig new file mode 100644 index 000000000..15e12b1d2 --- /dev/null +++ b/configs/TEST_nommu_defconfig @@ -0,0 +1,926 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.16.0 +# Wed Jan 27 21:01:26 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +CONFIG_DESKTOP=y +CONFIG_EXTRA_COMPAT=y +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_SHOW_USAGE=y +CONFIG_FEATURE_VERBOSE_USAGE=y +CONFIG_FEATURE_COMPRESS_USAGE=y +CONFIG_FEATURE_INSTALLER=y +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_PIDFILE=y +CONFIG_FEATURE_SUID=y +CONFIG_FEATURE_SUID_CONFIG=y +CONFIG_FEATURE_SUID_CONFIG_QUIET=y +CONFIG_SELINUX=y +CONFIG_FEATURE_PREFER_APPLETS=y +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y +CONFIG_FEATURE_HAVE_RPC=y + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +CONFIG_NOMMU=y +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +CONFIG_LFS=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +# CONFIG_WERROR is not set +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +CONFIG_FEATURE_ETC_NETWORKS=y +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +CONFIG_FEATURE_EDITING_VI=y +CONFIG_FEATURE_EDITING_HISTORY=15 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +CONFIG_FEATURE_TAB_COMPLETION=y +CONFIG_FEATURE_USERNAME_COMPLETION=y +CONFIG_FEATURE_EDITING_FANCY_PROMPT=y +CONFIG_FEATURE_EDITING_ASK_TERMINAL=y +CONFIG_FEATURE_NON_POSIX_CP=y +CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y +CONFIG_FEATURE_COPYBUF_KB=4 +CONFIG_MONOTONIC_SYSCALL=y +CONFIG_IOCTL_HEX2STR_ERROR=y +CONFIG_FEATURE_HWIB=y + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y +CONFIG_DPKG=y +CONFIG_DPKG_DEB=y +CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY=y +CONFIG_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +CONFIG_LZOP_COMPR_HIGH=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_AUTODETECT=y +CONFIG_FEATURE_TAR_FROM=y +CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y +CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +CONFIG_FEATURE_TAR_LONG_OPTIONS=y +CONFIG_FEATURE_TAR_UNAME_GNAME=y +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +CONFIG_UNCOMPRESS=y +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAL=y +CONFIG_CAT=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +CONFIG_FEATURE_CP_LONG_OPTIONS=y +CONFIG_CUT=y +CONFIG_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +CONFIG_FEATURE_DATE_COMPAT=y +CONFIG_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=y +CONFIG_DF=y +CONFIG_FEATURE_DF_FANCY=y +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_FEATURE_ENV_LONG_OPTIONS=y +CONFIG_EXPAND=y +CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y +CONFIG_EXPR=y +CONFIG_EXPR_MATH_SUPPORT_64=y +CONFIG_FALSE=y +CONFIG_FOLD=y +CONFIG_FSYNC=y +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +CONFIG_HOSTID=y +CONFIG_ID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LN=y +CONFIG_LOGNAME=y +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +CONFIG_FEATURE_MV_LONG_OPTIONS=y +CONFIG_NICE=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PRINTENV=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_FEATURE_FLOAT_SLEEP=y +CONFIG_SORT=y +CONFIG_FEATURE_SORT_BIG=y +CONFIG_SPLIT=y +CONFIG_FEATURE_SPLIT_FANCY=y +CONFIG_STAT=y +CONFIG_FEATURE_STAT_FORMAT=y +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=y +CONFIG_TAC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +CONFIG_WHO=y +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls, more and telnet +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +CONFIG_KBD_MODE=y +CONFIG_LOADFONT=y +CONFIG_LOADKMAP=y +CONFIG_OPENVT=y +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +CONFIG_SETCONSOLE=y +CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y +CONFIG_SETFONT=y +CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y +CONFIG_DEFAULT_SETFONT_DIR="" +CONFIG_SETKEYCODES=y +CONFIG_SETLOGCONS=y +CONFIG_SHOWKEY=y + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +CONFIG_PIPE_PROGRESS=y +CONFIG_RUN_PARTS=y +CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y +CONFIG_FEATURE_RUN_PARTS_FANCY=y +CONFIG_START_STOP_DAEMON=y +CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y +CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_AWK=y +CONFIG_FEATURE_AWK_LIBM=y +CONFIG_CMP=y +CONFIG_DIFF=y +CONFIG_FEATURE_DIFF_LONG_OPTIONS=y +CONFIG_FEATURE_DIFF_DIR=y +CONFIG_ED=y +CONFIG_PATCH=y +CONFIG_SED=y +CONFIG_VI=y +CONFIG_FEATURE_VI_MAX_LEN=4096 +CONFIG_FEATURE_VI_8BIT=y +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +CONFIG_FEATURE_VI_USE_SIGNALS=y +CONFIG_FEATURE_VI_DOT_CMD=y +CONFIG_FEATURE_VI_READONLY=y +CONFIG_FEATURE_VI_SETOPTS=y +CONFIG_FEATURE_VI_SET=y +CONFIG_FEATURE_VI_WIN_RESIZE=y +CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y +CONFIG_FEATURE_ALLOW_EXEC=y + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_PRINT0=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_MMIN=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_MAXDEPTH=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_FEATURE_FIND_EXEC=y +CONFIG_FEATURE_FIND_USER=y +CONFIG_FEATURE_FIND_GROUP=y +CONFIG_FEATURE_FIND_NOT=y +CONFIG_FEATURE_FIND_DEPTH=y +CONFIG_FEATURE_FIND_PAREN=y +CONFIG_FEATURE_FIND_SIZE=y +CONFIG_FEATURE_FIND_PRUNE=y +CONFIG_FEATURE_FIND_DELETE=y +CONFIG_FEATURE_FIND_PATH=y +CONFIG_FEATURE_FIND_REGEX=y +CONFIG_FEATURE_FIND_CONTEXT=y +CONFIG_FEATURE_FIND_LINKS=y +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +CONFIG_FEATURE_KILL_REMOVED=y +CONFIG_FEATURE_KILL_DELAY=1 +CONFIG_FEATURE_INIT_SCTTY=y +CONFIG_FEATURE_INIT_SYSLOG=y +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_FEATURE_INITRD=y +CONFIG_HALT=y +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +CONFIG_MESG=y + +# +# Login/Password Management Utilities +# +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_PWD_GRP=y +CONFIG_USE_BB_SHADOW=y +CONFIG_USE_BB_CRYPT=y +CONFIG_USE_BB_CRYPT_SHA=y +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +CONFIG_FEATURE_CHECK_NAMES=y +CONFIG_ADDUSER=y +CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_DELUSER=y +CONFIG_GETTY=y +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y +CONFIG_LOGIN=y +# CONFIG_PAM is not set +CONFIG_LOGIN_SCRIPTS=y +CONFIG_FEATURE_NOLOGIN=y +CONFIG_FEATURE_SECURETTY=y +CONFIG_PASSWD=y +CONFIG_FEATURE_PASSWD_WEAK_CHECK=y +CONFIG_CRYPTPW=y +CONFIG_CHPASSWD=y +CONFIG_SU=y +CONFIG_FEATURE_SU_SYSLOG=y +CONFIG_FEATURE_SU_CHECKS_SHELLS=y +CONFIG_SULOGIN=y +CONFIG_VLOCK=y + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +CONFIG_FSCK=y +CONFIG_LSATTR=y + +# +# Linux Module Utilities +# +CONFIG_MODPROBE_SMALL=y +CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y +CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set + +# +# Options common to multiple modutils +# +# CONFIG_FEATURE_2_4_MODULES is not set +CONFIG_FEATURE_INSMOD_TRY_MMAP=y +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +CONFIG_DMESG=y +CONFIG_FEATURE_DMESG_PRETTY=y +CONFIG_FBSET=y +CONFIG_FEATURE_FBSET_FANCY=y +CONFIG_FEATURE_FBSET_READMODE=y +CONFIG_FDFLUSH=y +CONFIG_FDFORMAT=y +CONFIG_FDISK=y +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +CONFIG_FEATURE_FDISK_WRITABLE=y +CONFIG_FEATURE_AIX_LABEL=y +CONFIG_FEATURE_SGI_LABEL=y +CONFIG_FEATURE_SUN_LABEL=y +CONFIG_FEATURE_OSF_LABEL=y +CONFIG_FEATURE_FDISK_ADVANCED=y +CONFIG_FINDFS=y +CONFIG_FREERAMDISK=y +CONFIG_FSCK_MINIX=y +CONFIG_MKFS_EXT2=y +CONFIG_MKFS_MINIX=y + +# +# Minix filesystem support +# +CONFIG_FEATURE_MINIX2=y +CONFIG_MKFS_REISER=y +CONFIG_MKFS_VFAT=y +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +CONFIG_HWCLOCK=y +CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y +CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y +CONFIG_IPCRM=y +CONFIG_IPCS=y +CONFIG_LOSETUP=y +CONFIG_LSPCI=y +CONFIG_LSUSB=y +CONFIG_MDEV=y +CONFIG_FEATURE_MDEV_CONF=y +CONFIG_FEATURE_MDEV_RENAME=y +CONFIG_FEATURE_MDEV_RENAME_REGEXP=y +CONFIG_FEATURE_MDEV_EXEC=y +CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y +CONFIG_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_VOLUMEID=y +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=y +CONFIG_FEATURE_VOLUMEID_FAT=y +CONFIG_FEATURE_VOLUMEID_HFS=y +CONFIG_FEATURE_VOLUMEID_JFS=y +CONFIG_FEATURE_VOLUMEID_XFS=y +CONFIG_FEATURE_VOLUMEID_NTFS=y +CONFIG_FEATURE_VOLUMEID_ISO9660=y +CONFIG_FEATURE_VOLUMEID_UDF=y +CONFIG_FEATURE_VOLUMEID_LUKS=y +CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y +CONFIG_MOUNT=y +CONFIG_FEATURE_MOUNT_FAKE=y +CONFIG_FEATURE_MOUNT_VERBOSE=y +CONFIG_FEATURE_MOUNT_HELPERS=y +CONFIG_FEATURE_MOUNT_LABEL=y +CONFIG_FEATURE_MOUNT_NFS=y +CONFIG_FEATURE_MOUNT_CIFS=y +CONFIG_FEATURE_MOUNT_FLAGS=y +CONFIG_FEATURE_MOUNT_FSTAB=y +CONFIG_PIVOT_ROOT=y +CONFIG_RDATE=y +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +CONFIG_SETARCH=y +CONFIG_SWAPONOFF=y +CONFIG_FEATURE_SWAPON_PRI=y +CONFIG_SWITCH_ROOT=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_UMOUNT_ALL=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Miscellaneous Utilities +# +CONFIG_ADJTIMEX=y +CONFIG_BBCONFIG=y +CONFIG_BEEP=y +CONFIG_FEATURE_BEEP_FREQ=4000 +CONFIG_FEATURE_BEEP_LENGTH_MS=30 +CONFIG_CHAT=y +CONFIG_FEATURE_CHAT_NOFAIL=y +CONFIG_FEATURE_CHAT_TTY_HIFI=y +CONFIG_FEATURE_CHAT_IMPLICIT_CR=y +CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y +CONFIG_FEATURE_CHAT_SEND_ESCAPES=y +CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y +CONFIG_FEATURE_CHAT_CLR_ABORT=y +CONFIG_CHRT=y +CONFIG_CROND=y +CONFIG_FEATURE_CROND_D=y +CONFIG_FEATURE_CROND_CALL_SENDMAIL=y +CONFIG_FEATURE_CROND_DIR="/var/spool/cron" +CONFIG_CRONTAB=y +CONFIG_DC=y +CONFIG_FEATURE_DC_LIBM=y +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +CONFIG_DEVMEM=y +CONFIG_EJECT=y +CONFIG_FEATURE_EJECT_SCSI=y +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +# CONFIG_FLASH_LOCK is not set +# CONFIG_FLASH_UNLOCK is not set +# CONFIG_FLASH_ERASEALL is not set +CONFIG_IONICE=y +CONFIG_INOTIFYD=y +CONFIG_LAST=y +CONFIG_FEATURE_LAST_SMALL=y +# CONFIG_FEATURE_LAST_FANCY is not set +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +CONFIG_HDPARM=y +CONFIG_FEATURE_HDPARM_GET_IDENTITY=y +CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y +CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y +CONFIG_MAKEDEVS=y +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +CONFIG_FEATURE_MAKEDEVS_TABLE=y +CONFIG_MAN=y +CONFIG_MICROCOM=y +CONFIG_MOUNTPOINT=y +CONFIG_MT=y +CONFIG_RAIDAUTORUN=y +CONFIG_READAHEAD=y +CONFIG_RUNLEVEL=y +CONFIG_RX=y +CONFIG_SETSID=y +CONFIG_STRINGS=y +CONFIG_TASKSET=y +CONFIG_FEATURE_TASKSET_FANCY=y +CONFIG_TIME=y +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +CONFIG_WALL=y +CONFIG_WATCHDOG=y + +# +# Networking Utilities +# +CONFIG_FEATURE_IPV6=y +CONFIG_FEATURE_UNIX_LOCAL=y +CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y +CONFIG_VERBOSE_RESOLUTION_ERRORS=y +CONFIG_ARP=y +CONFIG_ARPING=y +CONFIG_BRCTL=y +CONFIG_FEATURE_BRCTL_FANCY=y +CONFIG_FEATURE_BRCTL_SHOW=y +CONFIG_DNSD=y +CONFIG_ETHER_WAKE=y +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y +CONFIG_HOSTNAME=y +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +CONFIG_FEATURE_HTTPD_USE_SENDFILE=y +CONFIG_FEATURE_HTTPD_SETUID=y +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +CONFIG_FEATURE_HTTPD_AUTH_MD5=y +CONFIG_FEATURE_HTTPD_CGI=y +CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y +CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y +CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y +CONFIG_FEATURE_HTTPD_ERROR_PAGES=y +CONFIG_FEATURE_HTTPD_PROXY=y +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +CONFIG_FEATURE_IFCONFIG_SLIP=y +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +CONFIG_IFENSLAVE=y +CONFIG_IFPLUGD=y +CONFIG_IFUPDOWN=y +CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" +CONFIG_FEATURE_IFUPDOWN_IP=y +CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +CONFIG_FEATURE_IFUPDOWN_IPV4=y +CONFIG_FEATURE_IFUPDOWN_IPV6=y +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y +CONFIG_INETD=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y +CONFIG_FEATURE_INETD_RPC=y +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +CONFIG_FEATURE_IP_TUNNEL=y +CONFIG_FEATURE_IP_RULE=y +CONFIG_FEATURE_IP_SHORT_FORMS=y +CONFIG_FEATURE_IP_RARE_PROTOCOLS=y +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IPTUNNEL=y +CONFIG_IPRULE=y +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +CONFIG_NAMEIF=y +CONFIG_FEATURE_NAMEIF_EXTENDED=y +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +CONFIG_NSLOOKUP=y +CONFIG_NTPD=y +CONFIG_FEATURE_NTPD_SERVER=y +CONFIG_PING=y +CONFIG_PING6=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_PSCAN=y +CONFIG_ROUTE=y +CONFIG_SLATTACH=y +CONFIG_TELNET=y +CONFIG_FEATURE_TELNET_TTYPE=y +CONFIG_FEATURE_TELNET_AUTOLOGIN=y +CONFIG_TELNETD=y +CONFIG_FEATURE_TELNETD_STANDALONE=y +CONFIG_FEATURE_TELNETD_INETD_WAIT=y +CONFIG_TFTP=y +CONFIG_TFTPD=y +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +CONFIG_FEATURE_TFTP_BLOCKSIZE=y +CONFIG_FEATURE_TFTP_PROGRESS_BAR=y +CONFIG_TFTP_DEBUG=y +CONFIG_TRACEROUTE=y +CONFIG_TRACEROUTE6=y +CONFIG_FEATURE_TRACEROUTE_VERBOSE=y +CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y +CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y +CONFIG_UDHCPD=y +CONFIG_DHCPRELAY=y +CONFIG_DUMPLEASES=y +CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y +CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +CONFIG_FEATURE_UDHCP_PORT=y +CONFIG_UDHCP_DEBUG=9 +CONFIG_FEATURE_UDHCP_RFC3397=y +CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" +CONFIG_VCONFIG=y +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_LONG_OPTIONS=y +CONFIG_ZCIP=y +CONFIG_TCPSVD=y +CONFIG_TUNCTL=y +CONFIG_FEATURE_TUNCTL_UG=y +CONFIG_UDPSVD=y + +# +# Print Utilities +# +CONFIG_LPD=y +CONFIG_LPR=y +CONFIG_LPQ=y + +# +# Mail Utilities +# +CONFIG_MAKEMIME=y +CONFIG_FEATURE_MIME_CHARSET="us-ascii" +CONFIG_POPMAILDIR=y +CONFIG_FEATURE_POPMAILDIR_DELIVERY=y +CONFIG_REFORMIME=y +CONFIG_FEATURE_REFORMIME_COMPAT=y +CONFIG_SENDMAIL=y + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_FUSER=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +CONFIG_NMETER=y +CONFIG_PGREP=y +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +CONFIG_PKILL=y +CONFIG_PS=y +CONFIG_FEATURE_PS_WIDE=y +CONFIG_FEATURE_PS_TIME=y +CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y +CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS=y +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +CONFIG_TOP=y +CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y +CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y +CONFIG_FEATURE_TOP_SMP_CPU=y +CONFIG_FEATURE_TOP_DECIMALS=y +CONFIG_FEATURE_TOP_SMP_PROCESS=y +CONFIG_FEATURE_TOPMEM=y +CONFIG_FEATURE_SHOW_THREADS=y +CONFIG_UPTIME=y +CONFIG_WATCH=y + +# +# Runit Utilities +# +CONFIG_RUNSV=y +CONFIG_RUNSVDIR=y +CONFIG_FEATURE_RUNSVDIR_LOG=y +CONFIG_SV=y +CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" +CONFIG_SVLOGD=y +CONFIG_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=y + +# +# SELinux Utilities +# +CONFIG_CHCON=y +CONFIG_FEATURE_CHCON_LONG_OPTIONS=y +CONFIG_GETENFORCE=y +CONFIG_GETSEBOOL=y +CONFIG_LOAD_POLICY=y +CONFIG_MATCHPATHCON=y +CONFIG_RESTORECON=y +CONFIG_RUNCON=y +CONFIG_FEATURE_RUNCON_LONG_OPTIONS=y +CONFIG_SELINUXENABLED=y +CONFIG_SETENFORCE=y +CONFIG_SETFILES=y +CONFIG_FEATURE_SETFILES_CHECK_OPTION=y +CONFIG_SETSEBOOL=y +CONFIG_SESTATUS=y + +# +# Shells +# +# CONFIG_FEATURE_SH_IS_ASH is not set +CONFIG_FEATURE_SH_IS_HUSH=y +# CONFIG_FEATURE_SH_IS_NONE is not set +# CONFIG_ASH is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_BUILTIN_ECHO is not set +# CONFIG_ASH_BUILTIN_PRINTF is not set +# CONFIG_ASH_BUILTIN_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +CONFIG_HUSH=y +CONFIG_HUSH_BASH_COMPAT=y +CONFIG_HUSH_HELP=y +CONFIG_HUSH_INTERACTIVE=y +CONFIG_HUSH_JOB=y +CONFIG_HUSH_TICK=y +CONFIG_HUSH_IF=y +CONFIG_HUSH_LOOPS=y +CONFIG_HUSH_CASE=y +CONFIG_HUSH_FUNCTIONS=y +CONFIG_HUSH_LOCAL=y +CONFIG_HUSH_EXPORT_N=y +CONFIG_HUSH_RANDOM_SUPPORT=y +CONFIG_MSH=y +CONFIG_SH_MATH_SUPPORT=y +CONFIG_SH_MATH_SUPPORT_64=y +CONFIG_FEATURE_SH_EXTRA_QUIET=y +CONFIG_FEATURE_SH_STANDALONE=y +CONFIG_FEATURE_SH_NOFORK=y +CONFIG_CTTYHACK=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_SYSLOGD_DUP=y +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +CONFIG_KLOGD=y +CONFIG_LOGGER=y diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig new file mode 100644 index 000000000..f4338df71 --- /dev/null +++ b/configs/TEST_noprintf_defconfig @@ -0,0 +1,927 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.17.0.git +# Mon Jun 7 13:37:55 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +CONFIG_DESKTOP=y +CONFIG_EXTRA_COMPAT=y +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_SHOW_USAGE=y +CONFIG_FEATURE_VERBOSE_USAGE=y +# CONFIG_FEATURE_COMPRESS_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_LOCALE_SUPPORT is not set +CONFIG_UNICODE_SUPPORT=y +# CONFIG_UNICODE_USING_LOCALE is not set +CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y +CONFIG_SUBST_WCHAR=63 +CONFIG_LAST_SUPPORTED_WCHAR=65535 +CONFIG_UNICODE_COMBINING_WCHARS=y +CONFIG_UNICODE_WIDE_WCHARS=y +CONFIG_UNICODE_BIDI_SUPPORT=y +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +CONFIG_UNICODE_PRESERVE_BROKEN=y +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y +CONFIG_FEATURE_PIDFILE=y +# CONFIG_FEATURE_SUID is not set +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +# CONFIG_FEATURE_SYSLOG is not set +# CONFIG_FEATURE_HAVE_RPC is not set + +# +# Build Options +# +CONFIG_STATIC=y +# CONFIG_PIE is not set +# CONFIG_NOMMU is not set +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +CONFIG_LFS=y +CONFIG_CROSS_COMPILER_PREFIX="i486-linux-uclibc-" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +CONFIG_WERROR=y +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +CONFIG_FEATURE_EDITING_VI=y +CONFIG_FEATURE_EDITING_HISTORY=15 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +CONFIG_FEATURE_TAB_COMPLETION=y +CONFIG_FEATURE_USERNAME_COMPLETION=y +CONFIG_FEATURE_EDITING_FANCY_PROMPT=y +CONFIG_FEATURE_EDITING_ASK_TERMINAL=y +CONFIG_FEATURE_NON_POSIX_CP=y +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=64 +CONFIG_MONOTONIC_SYSCALL=y +# CONFIG_IOCTL_HEX2STR_ERROR is not set +CONFIG_FEATURE_HWIB=y + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_XZ=y +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +# CONFIG_AR is not set +# CONFIG_FEATURE_AR_LONG_FILENAMES is not set +# CONFIG_FEATURE_AR_CREATE is not set +# CONFIG_BUNZIP2 is not set +# CONFIG_BZIP2 is not set +# CONFIG_CPIO is not set +# CONFIG_FEATURE_CPIO_O is not set +# CONFIG_FEATURE_CPIO_P is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +# CONFIG_GUNZIP is not set +# CONFIG_GZIP is not set +# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +# CONFIG_LZOP is not set +# CONFIG_LZOP_COMPR_HIGH is not set +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +# CONFIG_TAR is not set +# CONFIG_FEATURE_TAR_CREATE is not set +# CONFIG_FEATURE_TAR_AUTODETECT is not set +# CONFIG_FEATURE_TAR_FROM is not set +# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set +# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set +# CONFIG_FEATURE_TAR_GNU_EXTENSIONS is not set +# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set +# CONFIG_FEATURE_TAR_UNAME_GNAME is not set +# CONFIG_FEATURE_TAR_NOPRESERVE_TIME is not set +# CONFIG_FEATURE_TAR_SELINUX is not set +# CONFIG_UNCOMPRESS is not set +# CONFIG_UNLZMA is not set +# CONFIG_FEATURE_LZMA_FAST is not set +# CONFIG_LZMA is not set +# CONFIG_UNXZ is not set +# CONFIG_XZ is not set +# CONFIG_UNZIP is not set + +# +# Coreutils +# +CONFIG_BASENAME=y +# CONFIG_CAT is not set +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +# CONFIG_TEST is not set +# CONFIG_FEATURE_TEST_64 is not set +# CONFIG_TR is not set +# CONFIG_FEATURE_TR_CLASSES is not set +# CONFIG_FEATURE_TR_EQUIV is not set +# CONFIG_CAL is not set +# CONFIG_CATV is not set +# CONFIG_CHGRP is not set +# CONFIG_CHMOD is not set +# CONFIG_CHOWN is not set +# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set +# CONFIG_CHROOT is not set +# CONFIG_CKSUM is not set +# CONFIG_COMM is not set +# CONFIG_CP is not set +# CONFIG_FEATURE_CP_LONG_OPTIONS is not set +# CONFIG_CUT is not set +# CONFIG_DD is not set +# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set +# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set +# CONFIG_FEATURE_DD_IBS_OBS is not set +# CONFIG_DF is not set +# CONFIG_FEATURE_DF_FANCY is not set +# CONFIG_DIRNAME is not set +# CONFIG_DOS2UNIX is not set +# CONFIG_UNIX2DOS is not set +# CONFIG_DU is not set +# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set +# CONFIG_ECHO is not set +# CONFIG_FEATURE_FANCY_ECHO is not set +# CONFIG_ENV is not set +# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set +# CONFIG_EXPAND is not set +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +# CONFIG_EXPR is not set +# CONFIG_EXPR_MATH_SUPPORT_64 is not set +CONFIG_FALSE=y +# CONFIG_FOLD is not set +# CONFIG_FSYNC is not set +# CONFIG_HEAD is not set +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +# CONFIG_ID is not set +# CONFIG_INSTALL is not set +# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set +# CONFIG_LN is not set +# CONFIG_LOGNAME is not set +# CONFIG_LS is not set +# CONFIG_FEATURE_LS_FILETYPES is not set +# CONFIG_FEATURE_LS_FOLLOWLINKS is not set +# CONFIG_FEATURE_LS_RECURSIVE is not set +# CONFIG_FEATURE_LS_SORTFILES is not set +# CONFIG_FEATURE_LS_TIMESTAMPS is not set +# CONFIG_FEATURE_LS_USERNAME is not set +# CONFIG_FEATURE_LS_COLOR is not set +# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set +# CONFIG_MD5SUM is not set +# CONFIG_MKDIR is not set +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +# CONFIG_MKFIFO is not set +# CONFIG_MKNOD is not set +# CONFIG_MV is not set +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +# CONFIG_NICE is not set +# CONFIG_NOHUP is not set +# CONFIG_OD is not set +# CONFIG_PRINTENV is not set +# CONFIG_PRINTF is not set +# CONFIG_PWD is not set +# CONFIG_READLINK is not set +# CONFIG_FEATURE_READLINK_FOLLOW is not set +# CONFIG_REALPATH is not set +# CONFIG_RM is not set +# CONFIG_RMDIR is not set +# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set +# CONFIG_SEQ is not set +# CONFIG_SHA1SUM is not set +# CONFIG_SHA256SUM is not set +# CONFIG_SHA512SUM is not set +# CONFIG_SLEEP is not set +# CONFIG_FEATURE_FANCY_SLEEP is not set +# CONFIG_FEATURE_FLOAT_SLEEP is not set +# CONFIG_SORT is not set +# CONFIG_FEATURE_SORT_BIG is not set +# CONFIG_SPLIT is not set +# CONFIG_FEATURE_SPLIT_FANCY is not set +# CONFIG_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +# CONFIG_STTY is not set +# CONFIG_SUM is not set +# CONFIG_SYNC is not set +# CONFIG_TAC is not set +# CONFIG_TAIL is not set +# CONFIG_FEATURE_FANCY_TAIL is not set +# CONFIG_TEE is not set +# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set +# CONFIG_TOUCH is not set +CONFIG_TRUE=y +# CONFIG_TTY is not set +# CONFIG_UNAME is not set +# CONFIG_UNEXPAND is not set +# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set +# CONFIG_UNIQ is not set +# CONFIG_USLEEP is not set +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WC is not set +# CONFIG_FEATURE_WC_LARGE is not set +# CONFIG_WHO is not set +# CONFIG_WHOAMI is not set +# CONFIG_YES is not set +# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set +# CONFIG_FEATURE_AUTOWIDTH is not set +# CONFIG_FEATURE_HUMAN_READABLE is not set +# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_FGCONSOLE is not set +# CONFIG_CLEAR is not set +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +# CONFIG_RESET is not set +# CONFIG_RESIZE is not set +# CONFIG_FEATURE_RESIZE_PRINT is not set +# CONFIG_SETCONSOLE is not set +# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set +# CONFIG_SETFONT is not set +# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set +CONFIG_DEFAULT_SETFONT_DIR="" +# CONFIG_SETKEYCODES is not set +# CONFIG_SETLOGCONS is not set +# CONFIG_SHOWKEY is not set +# CONFIG_FEATURE_LOADFONT_PSF2 is not set +# CONFIG_FEATURE_LOADFONT_RAW is not set + +# +# Debian Utilities +# +# CONFIG_MKTEMP is not set +CONFIG_PIPE_PROGRESS=y +# CONFIG_RUN_PARTS is not set +# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set +# CONFIG_FEATURE_RUN_PARTS_FANCY is not set +# CONFIG_START_STOP_DAEMON is not set +# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set +# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set +# CONFIG_WHICH is not set + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_FEATURE_AWK_LIBM is not set +# CONFIG_CMP is not set +# CONFIG_DIFF is not set +# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set +# CONFIG_FEATURE_DIFF_DIR is not set +# CONFIG_ED is not set +# CONFIG_PATCH is not set +# CONFIG_SED is not set +# CONFIG_VI is not set +CONFIG_FEATURE_VI_MAX_LEN=0 +# CONFIG_FEATURE_VI_8BIT is not set +# CONFIG_FEATURE_VI_COLON is not set +# CONFIG_FEATURE_VI_YANKMARK is not set +# CONFIG_FEATURE_VI_SEARCH is not set +# CONFIG_FEATURE_VI_USE_SIGNALS is not set +# CONFIG_FEATURE_VI_DOT_CMD is not set +# CONFIG_FEATURE_VI_READONLY is not set +# CONFIG_FEATURE_VI_SETOPTS is not set +# CONFIG_FEATURE_VI_SET is not set +# CONFIG_FEATURE_VI_WIN_RESIZE is not set +# CONFIG_FEATURE_VI_ASK_TERMINAL is not set +# CONFIG_FEATURE_VI_OPTIMIZE_CURSOR is not set +# CONFIG_FEATURE_ALLOW_EXEC is not set + +# +# Finding Utilities +# +# CONFIG_FIND is not set +# CONFIG_FEATURE_FIND_PRINT0 is not set +# CONFIG_FEATURE_FIND_MTIME is not set +# CONFIG_FEATURE_FIND_MMIN is not set +# CONFIG_FEATURE_FIND_PERM is not set +# CONFIG_FEATURE_FIND_TYPE is not set +# CONFIG_FEATURE_FIND_XDEV is not set +# CONFIG_FEATURE_FIND_MAXDEPTH is not set +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +# CONFIG_FEATURE_FIND_EXEC is not set +# CONFIG_FEATURE_FIND_USER is not set +# CONFIG_FEATURE_FIND_GROUP is not set +# CONFIG_FEATURE_FIND_NOT is not set +# CONFIG_FEATURE_FIND_DEPTH is not set +# CONFIG_FEATURE_FIND_PAREN is not set +# CONFIG_FEATURE_FIND_SIZE is not set +# CONFIG_FEATURE_FIND_PRUNE is not set +# CONFIG_FEATURE_FIND_DELETE is not set +# CONFIG_FEATURE_FIND_PATH is not set +# CONFIG_FEATURE_FIND_REGEX is not set +# CONFIG_FEATURE_FIND_CONTEXT is not set +# CONFIG_FEATURE_FIND_LINKS is not set +# CONFIG_GREP is not set +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +# CONFIG_XARGS is not set +# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set +# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set +# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set +# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set + +# +# Init Utilities +# +# CONFIG_INIT is not set +# CONFIG_FEATURE_USE_INITTAB is not set +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +# CONFIG_FEATURE_INIT_SCTTY is not set +# CONFIG_FEATURE_INIT_SYSLOG is not set +# CONFIG_FEATURE_EXTRA_QUIET is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_FEATURE_INITRD is not set +# CONFIG_HALT is not set +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +# CONFIG_MESG is not set +# CONFIG_BOOTCHARTD is not set + +# +# Login/Password Management Utilities +# +# CONFIG_FEATURE_SHADOWPASSWDS is not set +# CONFIG_USE_BB_PWD_GRP is not set +# CONFIG_USE_BB_SHADOW is not set +# CONFIG_USE_BB_CRYPT is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDGROUP is not set +# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set +# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set +# CONFIG_DELGROUP is not set +# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +CONFIG_FIRST_SYSTEM_ID=0 +CONFIG_LAST_SYSTEM_ID=0 +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PAM is not set +# CONFIG_LOGIN_SCRIPTS is not set +# CONFIG_FEATURE_NOLOGIN is not set +# CONFIG_FEATURE_SECURETTY is not set +# CONFIG_PASSWD is not set +# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set +# CONFIG_CRYPTPW is not set +# CONFIG_CHPASSWD is not set +# CONFIG_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +# CONFIG_CHATTR is not set +# CONFIG_FSCK is not set +# CONFIG_LSATTR is not set +# CONFIG_TUNE2FS is not set + +# +# Linux Module Utilities +# +# CONFIG_MODINFO is not set +# CONFIG_MODPROBE_SMALL is not set +# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set +# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set + +# +# Options common to multiple modutils +# +# CONFIG_FEATURE_2_4_MODULES is not set +# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="" +CONFIG_DEFAULT_DEPMOD_FILE="" + +# +# Linux System Utilities +# +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +# CONFIG_BLKID is not set +# CONFIG_DMESG is not set +# CONFIG_FEATURE_DMESG_PRETTY is not set +# CONFIG_FBSET is not set +# CONFIG_FEATURE_FBSET_FANCY is not set +# CONFIG_FEATURE_FBSET_READMODE is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +# CONFIG_FEATURE_FDISK_WRITABLE is not set +# CONFIG_FEATURE_AIX_LABEL is not set +# CONFIG_FEATURE_SGI_LABEL is not set +# CONFIG_FEATURE_SUN_LABEL is not set +# CONFIG_FEATURE_OSF_LABEL is not set +# CONFIG_FEATURE_FDISK_ADVANCED is not set +# CONFIG_FINDFS is not set +# CONFIG_FLOCK is not set +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +# CONFIG_GETOPT is not set +# CONFIG_FEATURE_GETOPT_LONG is not set +# CONFIG_HEXDUMP is not set +# CONFIG_FEATURE_HEXDUMP_REVERSE is not set +# CONFIG_HD is not set +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +# CONFIG_IPCRM is not set +# CONFIG_IPCS is not set +# CONFIG_LOSETUP is not set +# CONFIG_LSPCI is not set +# CONFIG_LSUSB is not set +# CONFIG_MDEV is not set +# CONFIG_FEATURE_MDEV_CONF is not set +# CONFIG_FEATURE_MDEV_RENAME is not set +# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set +# CONFIG_FEATURE_MDEV_EXEC is not set +# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set +# CONFIG_MKSWAP is not set +# CONFIG_FEATURE_MKSWAP_UUID is not set +# CONFIG_MORE is not set +# CONFIG_FEATURE_USE_TERMIOS is not set +CONFIG_VOLUMEID=y +# CONFIG_FEATURE_VOLUMEID_EXT is not set +# CONFIG_FEATURE_VOLUMEID_BTRFS is not set +# CONFIG_FEATURE_VOLUMEID_REISERFS is not set +# CONFIG_FEATURE_VOLUMEID_FAT is not set +# CONFIG_FEATURE_VOLUMEID_HFS is not set +# CONFIG_FEATURE_VOLUMEID_JFS is not set +# CONFIG_FEATURE_VOLUMEID_XFS is not set +# CONFIG_FEATURE_VOLUMEID_NTFS is not set +# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set +# CONFIG_FEATURE_VOLUMEID_UDF is not set +# CONFIG_FEATURE_VOLUMEID_LUKS is not set +# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set +# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set +# CONFIG_FEATURE_VOLUMEID_ROMFS is not set +# CONFIG_FEATURE_VOLUMEID_SYSV is not set +# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set +# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set +# CONFIG_MOUNT is not set +# CONFIG_FEATURE_MOUNT_FAKE is not set +# CONFIG_FEATURE_MOUNT_VERBOSE is not set +# CONFIG_FEATURE_MOUNT_HELPERS is not set +# CONFIG_FEATURE_MOUNT_LABEL is not set +# CONFIG_FEATURE_MOUNT_NFS is not set +# CONFIG_FEATURE_MOUNT_CIFS is not set +# CONFIG_FEATURE_MOUNT_FLAGS is not set +# CONFIG_FEATURE_MOUNT_FSTAB is not set +# CONFIG_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +# CONFIG_RDEV is not set +# CONFIG_READPROFILE is not set +# CONFIG_RTCWAKE is not set +# CONFIG_SCRIPT is not set +# CONFIG_SCRIPTREPLAY is not set +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +CONFIG_BBCONFIG=y +# CONFIG_BEEP is not set +CONFIG_FEATURE_BEEP_FREQ=0 +CONFIG_FEATURE_BEEP_LENGTH_MS=0 +# CONFIG_CHAT is not set +# CONFIG_FEATURE_CHAT_NOFAIL is not set +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set +# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set +# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set +# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set +# CONFIG_FEATURE_CHAT_CLR_ABORT is not set +# CONFIG_CHRT is not set +# CONFIG_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="" +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_FEATURE_DC_LIBM is not set +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +# CONFIG_DEVMEM is not set +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +# CONFIG_FBSPLASH is not set +# CONFIG_FLASHCP is not set +# CONFIG_FLASH_LOCK is not set +# CONFIG_FLASH_UNLOCK is not set +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +# CONFIG_INOTIFYD is not set +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL is not set +# CONFIG_FEATURE_LAST_FANCY is not set +# CONFIG_LESS is not set +CONFIG_FEATURE_LESS_MAXLINES=0 +# CONFIG_FEATURE_LESS_BRACKETS is not set +# CONFIG_FEATURE_LESS_FLAGS is not set +# CONFIG_FEATURE_LESS_MARKS is not set +# CONFIG_FEATURE_LESS_REGEXP is not set +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS is not set +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set +# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set +# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +# CONFIG_FEATURE_MAKEDEVS_TABLE is not set +# CONFIG_MAN is not set +# CONFIG_MICROCOM is not set +# CONFIG_MOUNTPOINT is not set +# CONFIG_MT is not set +# CONFIG_RAIDAUTORUN is not set +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +# CONFIG_RX is not set +# CONFIG_SETSID is not set +# CONFIG_STRINGS is not set +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +# CONFIG_TIME is not set +# CONFIG_TIMEOUT is not set +# CONFIG_TTYSIZE is not set +# CONFIG_VOLNAME is not set +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +# CONFIG_ARP is not set +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +# CONFIG_DNSD is not set +# CONFIG_ETHER_WAKE is not set +# CONFIG_FAKEIDENTD is not set +# CONFIG_FTPD is not set +# CONFIG_FEATURE_FTP_WRITE is not set +# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set +# CONFIG_HOSTNAME is not set +# CONFIG_HTTPD is not set +# CONFIG_FEATURE_HTTPD_RANGES is not set +# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set +# CONFIG_FEATURE_HTTPD_SETUID is not set +# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set +# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set +# CONFIG_FEATURE_HTTPD_CGI is not set +# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set +# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set +# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set +# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set +# CONFIG_FEATURE_HTTPD_PROXY is not set +# CONFIG_IFCONFIG is not set +# CONFIG_FEATURE_IFCONFIG_STATUS is not set +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +# CONFIG_FEATURE_IFCONFIG_HW is not set +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +# CONFIG_IFUPDOWN is not set +CONFIG_IFUPDOWN_IFSTATE_PATH="" +# CONFIG_FEATURE_IFUPDOWN_IP is not set +# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set +# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set +# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# CONFIG_FEATURE_INETD_RPC is not set +# CONFIG_IP is not set +# CONFIG_FEATURE_IP_ADDRESS is not set +# CONFIG_FEATURE_IP_LINK is not set +# CONFIG_FEATURE_IP_ROUTE is not set +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_FEATURE_IP_RULE is not set +# CONFIG_FEATURE_IP_SHORT_FORMS is not set +# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_IPRULE is not set +# CONFIG_IPCALC is not set +# CONFIG_FEATURE_IPCALC_FANCY is not set +# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +# CONFIG_NC is not set +# CONFIG_NC_SERVER is not set +# CONFIG_NC_EXTRA is not set +# CONFIG_NETSTAT is not set +# CONFIG_FEATURE_NETSTAT_WIDE is not set +# CONFIG_FEATURE_NETSTAT_PRG is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +# CONFIG_PSCAN is not set +# CONFIG_ROUTE is not set +# CONFIG_SLATTACH is not set +# CONFIG_TCPSVD is not set +# CONFIG_TELNET is not set +# CONFIG_FEATURE_TELNET_TTYPE is not set +# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set +# CONFIG_TELNETD is not set +# CONFIG_FEATURE_TELNETD_STANDALONE is not set +# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set +# CONFIG_TFTP is not set +# CONFIG_TFTPD is not set +# CONFIG_FEATURE_TFTP_GET is not set +# CONFIG_FEATURE_TFTP_PUT is not set +# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set +# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +# CONFIG_TUNCTL is not set +# CONFIG_FEATURE_TUNCTL_UG is not set +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +CONFIG_DHCPD_LEASES_FILE="" +# CONFIG_UDHCPC is not set +# CONFIG_FEATURE_UDHCPC_ARPING is not set +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=0 +# CONFIG_FEATURE_UDHCP_RFC3397 is not set +CONFIG_UDHCPC_DEFAULT_SCRIPT="" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" +# CONFIG_UDPSVD is not set +# CONFIG_VCONFIG is not set +# CONFIG_WGET is not set +# CONFIG_FEATURE_WGET_STATUSBAR is not set +# CONFIG_FEATURE_WGET_AUTHENTICATION is not set +# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set +# CONFIG_ZCIP is not set + +# +# Print Utilities +# +# CONFIG_LPD is not set +# CONFIG_LPR is not set +# CONFIG_LPQ is not set + +# +# Mail Utilities +# +# CONFIG_MAKEMIME is not set +CONFIG_FEATURE_MIME_CHARSET="" +# CONFIG_POPMAILDIR is not set +# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set +# CONFIG_REFORMIME is not set +# CONFIG_FEATURE_REFORMIME_COMPAT is not set +# CONFIG_SENDMAIL is not set + +# +# Process Utilities +# +# CONFIG_FREE is not set +# CONFIG_FUSER is not set +# CONFIG_KILL is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_NMETER is not set +# CONFIG_PGREP is not set +# CONFIG_PIDOF is not set +# CONFIG_FEATURE_PIDOF_SINGLE is not set +# CONFIG_FEATURE_PIDOF_OMIT is not set +# CONFIG_PKILL is not set +# CONFIG_PS is not set +# CONFIG_FEATURE_PS_WIDE is not set +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +# CONFIG_RENICE is not set +# CONFIG_BB_SYSCTL is not set +# CONFIG_TOP is not set +# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set +# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set +# CONFIG_FEATURE_TOP_SMP_CPU is not set +# CONFIG_FEATURE_TOP_DECIMALS is not set +# CONFIG_FEATURE_TOP_SMP_PROCESS is not set +# CONFIG_FEATURE_TOPMEM is not set +# CONFIG_FEATURE_SHOW_THREADS is not set +# CONFIG_UPTIME is not set +# CONFIG_WATCH is not set + +# +# Runit Utilities +# +# CONFIG_RUNSV is not set +# CONFIG_RUNSVDIR is not set +# CONFIG_FEATURE_RUNSVDIR_LOG is not set +# CONFIG_SV is not set +CONFIG_SV_DEFAULT_SERVICE_DIR="" +# CONFIG_SVLOGD is not set +# CONFIG_CHPST is not set +# CONFIG_SETUIDGID is not set +# CONFIG_ENVUIDGID is not set +# CONFIG_ENVDIR is not set +# CONFIG_SOFTLIMIT is not set +# CONFIG_CHCON is not set +# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set +# CONFIG_GETENFORCE is not set +# CONFIG_GETSEBOOL is not set +# CONFIG_LOAD_POLICY is not set +# CONFIG_MATCHPATHCON is not set +# CONFIG_RESTORECON is not set +# CONFIG_RUNCON is not set +# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set +# CONFIG_SELINUXENABLED is not set +# CONFIG_SETENFORCE is not set +# CONFIG_SETFILES is not set +# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set +# CONFIG_SETSEBOOL is not set +# CONFIG_SESTATUS is not set + +# +# Shells +# +# CONFIG_ASH is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_BUILTIN_ECHO is not set +# CONFIG_ASH_BUILTIN_PRINTF is not set +# CONFIG_ASH_BUILTIN_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +# CONFIG_HUSH is not set +# CONFIG_HUSH_BASH_COMPAT is not set +# CONFIG_HUSH_HELP is not set +# CONFIG_HUSH_INTERACTIVE is not set +# CONFIG_HUSH_JOB is not set +# CONFIG_HUSH_TICK is not set +# CONFIG_HUSH_IF is not set +# CONFIG_HUSH_LOOPS is not set +# CONFIG_HUSH_CASE is not set +# CONFIG_HUSH_FUNCTIONS is not set +# CONFIG_HUSH_LOCAL is not set +# CONFIG_HUSH_EXPORT_N is not set +# CONFIG_HUSH_RANDOM_SUPPORT is not set +# CONFIG_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# CONFIG_FEATURE_BASH_IS_ASH is not set +# CONFIG_FEATURE_BASH_IS_HUSH is not set +CONFIG_FEATURE_BASH_IS_NONE=y +# CONFIG_MSH is not set +# CONFIG_SH_MATH_SUPPORT is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_CTTYHACK is not set + +# +# System Logging Utilities +# +# CONFIG_SYSLOGD is not set +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_SYSLOGD_DUP is not set +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 +# CONFIG_LOGREAD is not set +# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set +# CONFIG_KLOGD is not set +# CONFIG_LOGGER is not set diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig new file mode 100644 index 000000000..193d8f615 --- /dev/null +++ b/configs/TEST_rh9_defconfig @@ -0,0 +1,941 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.17.0.git +# Fri Apr 16 22:25:22 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# CONFIG_EXTRA_COMPAT is not set +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_SHOW_USAGE=y +CONFIG_FEATURE_VERBOSE_USAGE=y +CONFIG_FEATURE_COMPRESS_USAGE=y +CONFIG_FEATURE_INSTALLER=y +CONFIG_LOCALE_SUPPORT=y +CONFIG_UNICODE_SUPPORT=y +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=63 +CONFIG_LAST_SUPPORTED_WCHAR=767 +# CONFIG_UNICODE_COMBINING_WCHARS is not set +# CONFIG_UNICODE_WIDE_WCHARS is not set +# CONFIG_UNICODE_BIDI_SUPPORT is not set +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y +CONFIG_FEATURE_PIDFILE=y +CONFIG_FEATURE_SUID=y +CONFIG_FEATURE_SUID_CONFIG=y +CONFIG_FEATURE_SUID_CONFIG_QUIET=y +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y +CONFIG_FEATURE_HAVE_RPC=y + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +# CONFIG_NOMMU is not set +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +CONFIG_LFS=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +# CONFIG_WERROR is not set +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=15 +CONFIG_FEATURE_EDITING_SAVEHISTORY=y +CONFIG_FEATURE_TAB_COMPLETION=y +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +CONFIG_FEATURE_NON_POSIX_CP=y +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_MONOTONIC_SYSCALL is not set +CONFIG_IOCTL_HEX2STR_ERROR=y +# CONFIG_FEATURE_HWIB is not set + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +CONFIG_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +# CONFIG_LZOP_COMPR_HIGH is not set +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_AUTODETECT=y +CONFIG_FEATURE_TAR_FROM=y +CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y +CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +CONFIG_FEATURE_TAR_LONG_OPTIONS=y +CONFIG_FEATURE_TAR_UNAME_GNAME=y +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +# CONFIG_FEATURE_TAR_SELINUX is not set +CONFIG_UNCOMPRESS=y +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAL=y +CONFIG_CAT=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +CONFIG_FEATURE_CP_LONG_OPTIONS=y +CONFIG_CUT=y +CONFIG_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +CONFIG_FEATURE_DATE_COMPAT=y +CONFIG_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=y +CONFIG_DF=y +CONFIG_FEATURE_DF_FANCY=y +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_FEATURE_ENV_LONG_OPTIONS=y +CONFIG_EXPAND=y +CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y +CONFIG_EXPR=y +CONFIG_EXPR_MATH_SUPPORT_64=y +CONFIG_FALSE=y +CONFIG_FOLD=y +CONFIG_FSYNC=y +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +CONFIG_HOSTID=y +CONFIG_ID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LN=y +CONFIG_LOGNAME=y +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +CONFIG_FEATURE_MV_LONG_OPTIONS=y +CONFIG_NICE=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PRINTENV=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_FEATURE_FLOAT_SLEEP=y +CONFIG_SORT=y +CONFIG_FEATURE_SORT_BIG=y +CONFIG_SPLIT=y +CONFIG_FEATURE_SPLIT_FANCY=y +CONFIG_STAT=y +CONFIG_FEATURE_STAT_FORMAT=y +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=y +CONFIG_TAC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +CONFIG_WHO=y +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls, more and telnet +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +CONFIG_KBD_MODE=y +CONFIG_LOADFONT=y +CONFIG_LOADKMAP=y +CONFIG_OPENVT=y +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +CONFIG_SETCONSOLE=y +CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y +CONFIG_SETFONT=y +CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y +CONFIG_DEFAULT_SETFONT_DIR="" +CONFIG_SETKEYCODES=y +CONFIG_SETLOGCONS=y +CONFIG_SHOWKEY=y + +# +# Common options for loadfont and setfont +# +# CONFIG_FEATURE_LOADFONT_PSF2 is not set +# CONFIG_FEATURE_LOADFONT_RAW is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +CONFIG_PIPE_PROGRESS=y +CONFIG_RUN_PARTS=y +CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y +CONFIG_FEATURE_RUN_PARTS_FANCY=y +CONFIG_START_STOP_DAEMON=y +CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y +CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_AWK=y +CONFIG_FEATURE_AWK_LIBM=y +CONFIG_CMP=y +CONFIG_DIFF=y +CONFIG_FEATURE_DIFF_LONG_OPTIONS=y +CONFIG_FEATURE_DIFF_DIR=y +CONFIG_ED=y +CONFIG_PATCH=y +CONFIG_SED=y +CONFIG_VI=y +CONFIG_FEATURE_VI_MAX_LEN=4096 +# CONFIG_FEATURE_VI_8BIT is not set +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +CONFIG_FEATURE_VI_USE_SIGNALS=y +CONFIG_FEATURE_VI_DOT_CMD=y +CONFIG_FEATURE_VI_READONLY=y +CONFIG_FEATURE_VI_SETOPTS=y +CONFIG_FEATURE_VI_SET=y +CONFIG_FEATURE_VI_WIN_RESIZE=y +CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y +CONFIG_FEATURE_ALLOW_EXEC=y + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_PRINT0=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_MMIN=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_MAXDEPTH=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_FEATURE_FIND_EXEC=y +CONFIG_FEATURE_FIND_USER=y +CONFIG_FEATURE_FIND_GROUP=y +CONFIG_FEATURE_FIND_NOT=y +CONFIG_FEATURE_FIND_DEPTH=y +CONFIG_FEATURE_FIND_PAREN=y +CONFIG_FEATURE_FIND_SIZE=y +CONFIG_FEATURE_FIND_PRUNE=y +CONFIG_FEATURE_FIND_DELETE=y +CONFIG_FEATURE_FIND_PATH=y +CONFIG_FEATURE_FIND_REGEX=y +# CONFIG_FEATURE_FIND_CONTEXT is not set +CONFIG_FEATURE_FIND_LINKS=y +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +CONFIG_FEATURE_INIT_SCTTY=y +CONFIG_FEATURE_INIT_SYSLOG=y +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_FEATURE_INITRD=y +CONFIG_HALT=y +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +CONFIG_MESG=y + +# +# Login/Password Management Utilities +# +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_PWD_GRP=y +CONFIG_USE_BB_SHADOW=y +CONFIG_USE_BB_CRYPT=y +CONFIG_USE_BB_CRYPT_SHA=y +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_ADDUSER=y +CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_DELUSER=y +CONFIG_GETTY=y +CONFIG_LOGIN=y +# CONFIG_PAM is not set +CONFIG_LOGIN_SCRIPTS=y +CONFIG_FEATURE_NOLOGIN=y +CONFIG_FEATURE_SECURETTY=y +CONFIG_PASSWD=y +CONFIG_FEATURE_PASSWD_WEAK_CHECK=y +CONFIG_CRYPTPW=y +CONFIG_CHPASSWD=y +CONFIG_SU=y +CONFIG_FEATURE_SU_SYSLOG=y +CONFIG_FEATURE_SU_CHECKS_SHELLS=y +CONFIG_SULOGIN=y +CONFIG_VLOCK=y + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +CONFIG_FSCK=y +CONFIG_LSATTR=y + +# +# Linux Module Utilities +# +CONFIG_MODPROBE_SMALL=y +CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y +CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set + +# +# Options common to multiple modutils +# +# CONFIG_FEATURE_2_4_MODULES is not set +# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +CONFIG_DMESG=y +CONFIG_FEATURE_DMESG_PRETTY=y +CONFIG_FBSET=y +CONFIG_FEATURE_FBSET_FANCY=y +CONFIG_FEATURE_FBSET_READMODE=y +CONFIG_FDFLUSH=y +CONFIG_FDFORMAT=y +CONFIG_FDISK=y +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +CONFIG_FEATURE_FDISK_WRITABLE=y +# CONFIG_FEATURE_AIX_LABEL is not set +# CONFIG_FEATURE_SGI_LABEL is not set +# CONFIG_FEATURE_SUN_LABEL is not set +# CONFIG_FEATURE_OSF_LABEL is not set +CONFIG_FEATURE_FDISK_ADVANCED=y +CONFIG_FINDFS=y +# CONFIG_FLOCK is not set +CONFIG_FREERAMDISK=y +CONFIG_FSCK_MINIX=y +# CONFIG_MKFS_EXT2 is not set +CONFIG_MKFS_MINIX=y + +# +# Minix filesystem support +# +CONFIG_FEATURE_MINIX2=y +# CONFIG_MKFS_REISER is not set +CONFIG_MKFS_VFAT=y +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +CONFIG_HWCLOCK=y +CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y +CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y +CONFIG_IPCRM=y +CONFIG_IPCS=y +CONFIG_LOSETUP=y +CONFIG_LSPCI=y +CONFIG_LSUSB=y +CONFIG_MDEV=y +CONFIG_FEATURE_MDEV_CONF=y +CONFIG_FEATURE_MDEV_RENAME=y +CONFIG_FEATURE_MDEV_RENAME_REGEXP=y +CONFIG_FEATURE_MDEV_EXEC=y +CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y +CONFIG_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_VOLUMEID=y +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=y +CONFIG_FEATURE_VOLUMEID_FAT=y +CONFIG_FEATURE_VOLUMEID_HFS=y +CONFIG_FEATURE_VOLUMEID_JFS=y +CONFIG_FEATURE_VOLUMEID_XFS=y +CONFIG_FEATURE_VOLUMEID_NTFS=y +CONFIG_FEATURE_VOLUMEID_ISO9660=y +CONFIG_FEATURE_VOLUMEID_UDF=y +CONFIG_FEATURE_VOLUMEID_LUKS=y +CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y +CONFIG_MOUNT=y +CONFIG_FEATURE_MOUNT_FAKE=y +CONFIG_FEATURE_MOUNT_VERBOSE=y +# CONFIG_FEATURE_MOUNT_HELPERS is not set +CONFIG_FEATURE_MOUNT_LABEL=y +CONFIG_FEATURE_MOUNT_NFS=y +CONFIG_FEATURE_MOUNT_CIFS=y +CONFIG_FEATURE_MOUNT_FLAGS=y +CONFIG_FEATURE_MOUNT_FSTAB=y +CONFIG_PIVOT_ROOT=y +CONFIG_RDATE=y +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +CONFIG_SETARCH=y +CONFIG_SWAPONOFF=y +CONFIG_FEATURE_SWAPON_PRI=y +CONFIG_SWITCH_ROOT=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_UMOUNT_ALL=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Miscellaneous Utilities +# +CONFIG_ADJTIMEX=y +# CONFIG_BBCONFIG is not set +CONFIG_BEEP=y +CONFIG_FEATURE_BEEP_FREQ=4000 +CONFIG_FEATURE_BEEP_LENGTH_MS=30 +CONFIG_CHAT=y +CONFIG_FEATURE_CHAT_NOFAIL=y +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +CONFIG_FEATURE_CHAT_IMPLICIT_CR=y +CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y +CONFIG_FEATURE_CHAT_SEND_ESCAPES=y +CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y +CONFIG_FEATURE_CHAT_CLR_ABORT=y +CONFIG_CHRT=y +CONFIG_CROND=y +CONFIG_FEATURE_CROND_D=y +CONFIG_FEATURE_CROND_CALL_SENDMAIL=y +CONFIG_FEATURE_CROND_DIR="/var/spool/cron" +CONFIG_CRONTAB=y +CONFIG_DC=y +CONFIG_FEATURE_DC_LIBM=y +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +CONFIG_DEVMEM=y +CONFIG_EJECT=y +CONFIG_FEATURE_EJECT_SCSI=y +CONFIG_FBSPLASH=y +# CONFIG_FLASHCP is not set +# CONFIG_FLASH_LOCK is not set +# CONFIG_FLASH_UNLOCK is not set +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +# CONFIG_INOTIFYD is not set +CONFIG_LAST=y +# CONFIG_FEATURE_LAST_SMALL is not set +CONFIG_FEATURE_LAST_FANCY=y +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +CONFIG_HDPARM=y +CONFIG_FEATURE_HDPARM_GET_IDENTITY=y +CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y +CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y +CONFIG_MAKEDEVS=y +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +CONFIG_FEATURE_MAKEDEVS_TABLE=y +CONFIG_MAN=y +CONFIG_MICROCOM=y +CONFIG_MOUNTPOINT=y +CONFIG_MT=y +CONFIG_RAIDAUTORUN=y +# CONFIG_READAHEAD is not set +CONFIG_RUNLEVEL=y +CONFIG_RX=y +CONFIG_SETSID=y +CONFIG_STRINGS=y +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +CONFIG_TIME=y +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +CONFIG_WALL=y +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +CONFIG_FEATURE_IPV6=y +# CONFIG_FEATURE_UNIX_LOCAL is not set +CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +CONFIG_ARP=y +CONFIG_ARPING=y +CONFIG_BRCTL=y +CONFIG_FEATURE_BRCTL_FANCY=y +CONFIG_FEATURE_BRCTL_SHOW=y +CONFIG_DNSD=y +CONFIG_ETHER_WAKE=y +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y +CONFIG_HOSTNAME=y +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +CONFIG_FEATURE_HTTPD_USE_SENDFILE=y +CONFIG_FEATURE_HTTPD_SETUID=y +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +CONFIG_FEATURE_HTTPD_AUTH_MD5=y +CONFIG_FEATURE_HTTPD_CGI=y +CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y +CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y +CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y +CONFIG_FEATURE_HTTPD_ERROR_PAGES=y +CONFIG_FEATURE_HTTPD_PROXY=y +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +CONFIG_FEATURE_IFCONFIG_SLIP=y +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# CONFIG_IFENSLAVE is not set +CONFIG_IFPLUGD=y +CONFIG_IFUPDOWN=y +CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" +CONFIG_FEATURE_IFUPDOWN_IP=y +CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +CONFIG_FEATURE_IFUPDOWN_IPV4=y +CONFIG_FEATURE_IFUPDOWN_IPV6=y +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +CONFIG_INETD=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y +CONFIG_FEATURE_INETD_RPC=y +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +CONFIG_FEATURE_IP_TUNNEL=y +CONFIG_FEATURE_IP_RULE=y +CONFIG_FEATURE_IP_SHORT_FORMS=y +# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IPTUNNEL=y +CONFIG_IPRULE=y +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +CONFIG_NAMEIF=y +CONFIG_FEATURE_NAMEIF_EXTENDED=y +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +CONFIG_NSLOOKUP=y +CONFIG_NTPD=y +CONFIG_FEATURE_NTPD_SERVER=y +CONFIG_PING=y +CONFIG_PING6=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_PSCAN=y +CONFIG_ROUTE=y +CONFIG_SLATTACH=y +CONFIG_TCPSVD=y +CONFIG_TELNET=y +CONFIG_FEATURE_TELNET_TTYPE=y +CONFIG_FEATURE_TELNET_AUTOLOGIN=y +CONFIG_TELNETD=y +CONFIG_FEATURE_TELNETD_STANDALONE=y +CONFIG_FEATURE_TELNETD_INETD_WAIT=y +CONFIG_TFTP=y +CONFIG_TFTPD=y +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +CONFIG_FEATURE_TFTP_BLOCKSIZE=y +CONFIG_FEATURE_TFTP_PROGRESS_BAR=y +# CONFIG_TFTP_DEBUG is not set +CONFIG_TRACEROUTE=y +CONFIG_TRACEROUTE6=y +CONFIG_FEATURE_TRACEROUTE_VERBOSE=y +# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +CONFIG_TUNCTL=y +CONFIG_FEATURE_TUNCTL_UG=y +CONFIG_UDHCPD=y +CONFIG_DHCPRELAY=y +CONFIG_DUMPLEASES=y +CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y +CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +CONFIG_FEATURE_UDHCP_PORT=y +CONFIG_UDHCP_DEBUG=9 +CONFIG_FEATURE_UDHCP_RFC3397=y +CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" +CONFIG_UDPSVD=y +CONFIG_VCONFIG=y +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_LONG_OPTIONS=y +CONFIG_FEATURE_WGET_TIMEOUT=y +CONFIG_ZCIP=y + +# +# Print Utilities +# +CONFIG_LPD=y +CONFIG_LPR=y +CONFIG_LPQ=y + +# +# Mail Utilities +# +CONFIG_MAKEMIME=y +CONFIG_FEATURE_MIME_CHARSET="us-ascii" +CONFIG_POPMAILDIR=y +CONFIG_FEATURE_POPMAILDIR_DELIVERY=y +CONFIG_REFORMIME=y +CONFIG_FEATURE_REFORMIME_COMPAT=y +CONFIG_SENDMAIL=y + +# +# Process Utilities +# +CONFIG_FREE=y +CONFIG_FUSER=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +CONFIG_NMETER=y +CONFIG_PGREP=y +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +CONFIG_PKILL=y +CONFIG_PS=y +CONFIG_FEATURE_PS_WIDE=y +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +CONFIG_TOP=y +CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y +CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y +CONFIG_FEATURE_TOP_SMP_CPU=y +CONFIG_FEATURE_TOP_DECIMALS=y +CONFIG_FEATURE_TOP_SMP_PROCESS=y +CONFIG_FEATURE_TOPMEM=y +CONFIG_FEATURE_SHOW_THREADS=y +CONFIG_UPTIME=y +CONFIG_WATCH=y + +# +# Runit Utilities +# +CONFIG_RUNSV=y +CONFIG_RUNSVDIR=y +# CONFIG_FEATURE_RUNSVDIR_LOG is not set +CONFIG_SV=y +CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" +CONFIG_SVLOGD=y +CONFIG_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=y +# CONFIG_CHCON is not set +# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set +# CONFIG_GETENFORCE is not set +# CONFIG_GETSEBOOL is not set +# CONFIG_LOAD_POLICY is not set +# CONFIG_MATCHPATHCON is not set +# CONFIG_RESTORECON is not set +# CONFIG_RUNCON is not set +# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set +# CONFIG_SELINUXENABLED is not set +# CONFIG_SETENFORCE is not set +# CONFIG_SETFILES is not set +# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set +# CONFIG_SETSEBOOL is not set +# CONFIG_SESTATUS is not set + +# +# Shells +# +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y +CONFIG_ASH_BASH_COMPAT=y +CONFIG_ASH_JOB_CONTROL=y +CONFIG_ASH_ALIAS=y +CONFIG_ASH_GETOPTS=y +CONFIG_ASH_BUILTIN_ECHO=y +CONFIG_ASH_BUILTIN_PRINTF=y +CONFIG_ASH_BUILTIN_TEST=y +CONFIG_ASH_CMDCMD=y +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +CONFIG_ASH_RANDOM_SUPPORT=y +CONFIG_ASH_EXPAND_PRMT=y +CONFIG_HUSH=y +CONFIG_HUSH_BASH_COMPAT=y +CONFIG_HUSH_HELP=y +CONFIG_HUSH_INTERACTIVE=y +CONFIG_HUSH_JOB=y +CONFIG_HUSH_TICK=y +CONFIG_HUSH_IF=y +CONFIG_HUSH_LOOPS=y +CONFIG_HUSH_CASE=y +CONFIG_HUSH_FUNCTIONS=y +CONFIG_HUSH_LOCAL=y +CONFIG_HUSH_EXPORT_N=y +CONFIG_HUSH_RANDOM_SUPPORT=y +CONFIG_MSH=y +CONFIG_SH_MATH_SUPPORT=y +CONFIG_SH_MATH_SUPPORT_64=y +CONFIG_FEATURE_SH_EXTRA_QUIET=y +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +CONFIG_CTTYHACK=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_SYSLOGD_DUP=y +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +CONFIG_KLOGD=y +CONFIG_LOGGER=y diff --git a/configs/android2_defconfig b/configs/android2_defconfig new file mode 100644 index 000000000..b5166e0fc --- /dev/null +++ b/configs/android2_defconfig @@ -0,0 +1,997 @@ +# Run "make android2_defconfig", then "make". +# +# Tested with the standalone toolchain from ndk r6: +# android-ndk-r6/build/tools/make-standalone-toolchain.sh --platform=android-8 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# CONFIG_EXTRA_COMPAT is not set +# CONFIG_INCLUDE_SUSv2 is not set +# CONFIG_USE_PORTABLE_CODE is not set +CONFIG_PLATFORM_LINUX=y +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +# CONFIG_SHOW_USAGE is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_COMPRESS_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_INSTALL_NO_USR is not set +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=0 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# CONFIG_UNICODE_COMBINING_WCHARS is not set +# CONFIG_UNICODE_WIDE_WCHARS is not set +# CONFIG_UNICODE_BIDI_SUPPORT is not set +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +# CONFIG_UNICODE_PRESERVE_BROKEN is not set +# CONFIG_LONG_OPTS is not set +# CONFIG_FEATURE_DEVPTS is not set +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_UTMP is not set +# CONFIG_FEATURE_WTMP is not set +# CONFIG_FEATURE_PIDFILE is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y +# CONFIG_FEATURE_HAVE_RPC is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +# CONFIG_NOMMU is not set +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +# CONFIG_LFS is not set +CONFIG_CROSS_COMPILER_PREFIX="arm-linux-androideabi-" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +# CONFIG_WERROR is not set +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options ("make install" behavior) +# +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +# CONFIG_FEATURE_SYSTEMD is not set +# CONFIG_FEATURE_RTMINMAX is not set +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +# CONFIG_FEATURE_FAST_TOP is not set +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_USE_TERMIOS=y +# CONFIG_FEATURE_EDITING is not set +CONFIG_FEATURE_EDITING_MAX_LEN=0 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=0 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +# CONFIG_FEATURE_TAB_COMPLETION is not set +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +# CONFIG_FEATURE_NON_POSIX_CP is not set +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_FEATURE_SKIP_ROOTFS is not set +# CONFIG_MONOTONIC_SYSCALL is not set +# CONFIG_IOCTL_HEX2STR_ERROR is not set +# CONFIG_FEATURE_HWIB is not set + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_XZ=y +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y +CONFIG_DPKG=y +CONFIG_DPKG_DEB=y +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +CONFIG_GUNZIP=y +CONFIG_GZIP=y +# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +CONFIG_LZOP=y +CONFIG_LZOP_COMPR_HIGH=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_AUTODETECT=y +CONFIG_FEATURE_TAR_FROM=y +CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y +CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set +# CONFIG_FEATURE_TAR_TO_COMMAND is not set +CONFIG_FEATURE_TAR_UNAME_GNAME=y +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +# CONFIG_FEATURE_TAR_SELINUX is not set +CONFIG_UNCOMPRESS=y +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_LZMA=y +CONFIG_UNXZ=y +CONFIG_XZ=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +# CONFIG_ID is not set +# CONFIG_GROUPS is not set +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_BASE64=y +CONFIG_CAL=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +# CONFIG_FEATURE_CP_LONG_OPTIONS is not set +CONFIG_CUT=y +CONFIG_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=y +# CONFIG_DF is not set +# CONFIG_FEATURE_DF_FANCY is not set +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +# CONFIG_ENV is not set +# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set +CONFIG_EXPAND=y +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +# CONFIG_EXPR is not set +# CONFIG_EXPR_MATH_SUPPORT_64 is not set +CONFIG_FALSE=y +CONFIG_FOLD=y +# CONFIG_FSYNC is not set +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +# CONFIG_HOSTID is not set +CONFIG_INSTALL=y +# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +# CONFIG_FEATURE_LS_COLOR is not set +# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +CONFIG_NICE=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PRINTENV=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_FEATURE_FLOAT_SLEEP=y +CONFIG_SORT=y +CONFIG_FEATURE_SORT_BIG=y +CONFIG_SPLIT=y +CONFIG_FEATURE_SPLIT_FANCY=y +# CONFIG_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=y +CONFIG_TAC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TRUE=y +# CONFIG_TTY is not set +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set +CONFIG_UNIQ=y +# CONFIG_USLEEP is not set +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls, more and telnet +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_FGCONSOLE=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +CONFIG_LOADKMAP=y +CONFIG_OPENVT=y +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +CONFIG_SETCONSOLE=y +# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set +# CONFIG_SETFONT is not set +# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set +CONFIG_DEFAULT_SETFONT_DIR="" +CONFIG_SETKEYCODES=y +CONFIG_SETLOGCONS=y +CONFIG_SHOWKEY=y +# CONFIG_FEATURE_LOADFONT_PSF2 is not set +# CONFIG_FEATURE_LOADFONT_RAW is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +CONFIG_PIPE_PROGRESS=y +CONFIG_RUN_PARTS=y +# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set +CONFIG_FEATURE_RUN_PARTS_FANCY=y +CONFIG_START_STOP_DAEMON=y +CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y +# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +# CONFIG_VI is not set +CONFIG_FEATURE_VI_MAX_LEN=0 +# CONFIG_FEATURE_VI_8BIT is not set +# CONFIG_FEATURE_VI_COLON is not set +# CONFIG_FEATURE_VI_YANKMARK is not set +# CONFIG_FEATURE_VI_SEARCH is not set +# CONFIG_FEATURE_VI_REGEX_SEARCH is not set +# CONFIG_FEATURE_VI_USE_SIGNALS is not set +# CONFIG_FEATURE_VI_DOT_CMD is not set +# CONFIG_FEATURE_VI_READONLY is not set +# CONFIG_FEATURE_VI_SETOPTS is not set +# CONFIG_FEATURE_VI_SET is not set +# CONFIG_FEATURE_VI_WIN_RESIZE is not set +# CONFIG_FEATURE_VI_ASK_TERMINAL is not set +# CONFIG_FEATURE_VI_OPTIMIZE_CURSOR is not set +# CONFIG_AWK is not set +# CONFIG_FEATURE_AWK_LIBM is not set +CONFIG_CMP=y +CONFIG_DIFF=y +# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set +CONFIG_FEATURE_DIFF_DIR=y +# CONFIG_ED is not set +# CONFIG_SED is not set +# CONFIG_FEATURE_ALLOW_EXEC is not set + +# +# Finding Utilities +# +# CONFIG_FIND is not set +# CONFIG_FEATURE_FIND_PRINT0 is not set +# CONFIG_FEATURE_FIND_MTIME is not set +# CONFIG_FEATURE_FIND_MMIN is not set +# CONFIG_FEATURE_FIND_PERM is not set +# CONFIG_FEATURE_FIND_TYPE is not set +# CONFIG_FEATURE_FIND_XDEV is not set +# CONFIG_FEATURE_FIND_MAXDEPTH is not set +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +# CONFIG_FEATURE_FIND_EXEC is not set +# CONFIG_FEATURE_FIND_USER is not set +# CONFIG_FEATURE_FIND_GROUP is not set +# CONFIG_FEATURE_FIND_NOT is not set +# CONFIG_FEATURE_FIND_DEPTH is not set +# CONFIG_FEATURE_FIND_PAREN is not set +# CONFIG_FEATURE_FIND_SIZE is not set +# CONFIG_FEATURE_FIND_PRUNE is not set +# CONFIG_FEATURE_FIND_DELETE is not set +# CONFIG_FEATURE_FIND_PATH is not set +# CONFIG_FEATURE_FIND_REGEX is not set +# CONFIG_FEATURE_FIND_CONTEXT is not set +# CONFIG_FEATURE_FIND_LINKS is not set +# CONFIG_GREP is not set +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +CONFIG_XARGS=y +CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +CONFIG_HALT=y +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +CONFIG_FEATURE_INIT_SCTTY=y +CONFIG_FEATURE_INIT_SYSLOG=y +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_FEATURE_INITRD=y +CONFIG_INIT_TERMINAL_TYPE="linux" +CONFIG_MESG=y +CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y + +# +# Login/Password Management Utilities +# +# CONFIG_ADD_SHELL is not set +# CONFIG_REMOVE_SHELL is not set +# CONFIG_FEATURE_SHADOWPASSWDS is not set +# CONFIG_USE_BB_PWD_GRP is not set +# CONFIG_USE_BB_SHADOW is not set +# CONFIG_USE_BB_CRYPT is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_FIRST_SYSTEM_ID=0 +CONFIG_LAST_SYSTEM_ID=0 +# CONFIG_ADDGROUP is not set +# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set +# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set +# CONFIG_DELUSER is not set +# CONFIG_DELGROUP is not set +# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PAM is not set +# CONFIG_LOGIN_SCRIPTS is not set +# CONFIG_FEATURE_NOLOGIN is not set +# CONFIG_FEATURE_SECURETTY is not set +# CONFIG_PASSWD is not set +# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set +# CONFIG_CRYPTPW is not set +# CONFIG_CHPASSWD is not set +# CONFIG_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +CONFIG_LSATTR=y +CONFIG_TUNE2FS=y + +# +# Linux Module Utilities +# +CONFIG_MODINFO=y +CONFIG_MODPROBE_SMALL=y +CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y +CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set + +# +# Options common to multiple modutils +# +# CONFIG_FEATURE_2_4_MODULES is not set +# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +CONFIG_BLOCKDEV=y +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +# CONFIG_FEATURE_BLKID_TYPE is not set +CONFIG_DMESG=y +CONFIG_FEATURE_DMESG_PRETTY=y +CONFIG_FBSET=y +CONFIG_FEATURE_FBSET_FANCY=y +CONFIG_FEATURE_FBSET_READMODE=y +CONFIG_FDFLUSH=y +CONFIG_FDFORMAT=y +CONFIG_FDISK=y +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +CONFIG_FEATURE_FDISK_WRITABLE=y +# CONFIG_FEATURE_AIX_LABEL is not set +# CONFIG_FEATURE_SGI_LABEL is not set +# CONFIG_FEATURE_SUN_LABEL is not set +# CONFIG_FEATURE_OSF_LABEL is not set +# CONFIG_FEATURE_GPT_LABEL is not set +CONFIG_FEATURE_FDISK_ADVANCED=y +CONFIG_FINDFS=y +CONFIG_FLOCK=y +CONFIG_FREERAMDISK=y +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +# CONFIG_IPCRM is not set +# CONFIG_IPCS is not set +CONFIG_LOSETUP=y +CONFIG_LSPCI=y +CONFIG_LSUSB=y +# CONFIG_MDEV is not set +# CONFIG_FEATURE_MDEV_CONF is not set +# CONFIG_FEATURE_MDEV_RENAME is not set +# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set +# CONFIG_FEATURE_MDEV_EXEC is not set +# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set +CONFIG_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +# CONFIG_MOUNT is not set +# CONFIG_FEATURE_MOUNT_FAKE is not set +# CONFIG_FEATURE_MOUNT_VERBOSE is not set +# CONFIG_FEATURE_MOUNT_HELPERS is not set +# CONFIG_FEATURE_MOUNT_LABEL is not set +# CONFIG_FEATURE_MOUNT_NFS is not set +# CONFIG_FEATURE_MOUNT_CIFS is not set +# CONFIG_FEATURE_MOUNT_FLAGS is not set +# CONFIG_FEATURE_MOUNT_FSTAB is not set +# CONFIG_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +CONFIG_VOLUMEID=y + +# +# Filesystem/Volume identification +# +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=y +CONFIG_FEATURE_VOLUMEID_FAT=y +CONFIG_FEATURE_VOLUMEID_HFS=y +CONFIG_FEATURE_VOLUMEID_JFS=y +CONFIG_FEATURE_VOLUMEID_XFS=y +CONFIG_FEATURE_VOLUMEID_NTFS=y +CONFIG_FEATURE_VOLUMEID_ISO9660=y +CONFIG_FEATURE_VOLUMEID_UDF=y +CONFIG_FEATURE_VOLUMEID_LUKS=y +CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +CONFIG_NANDDUMP=y +CONFIG_SETSERIAL=y +# CONFIG_UBIATTACH is not set +# CONFIG_UBIDETACH is not set +# CONFIG_UBIMKVOL is not set +# CONFIG_UBIRMVOL is not set +# CONFIG_UBIRSVOL is not set +# CONFIG_UBIUPDATEVOL is not set +# CONFIG_ADJTIMEX is not set +# CONFIG_BBCONFIG is not set +# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set +CONFIG_BEEP=y +CONFIG_FEATURE_BEEP_FREQ=4000 +CONFIG_FEATURE_BEEP_LENGTH_MS=30 +CONFIG_CHAT=y +CONFIG_FEATURE_CHAT_NOFAIL=y +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +CONFIG_FEATURE_CHAT_IMPLICIT_CR=y +CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y +CONFIG_FEATURE_CHAT_SEND_ESCAPES=y +CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y +CONFIG_FEATURE_CHAT_CLR_ABORT=y +CONFIG_CHRT=y +# CONFIG_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="" +# CONFIG_CRONTAB is not set +CONFIG_DC=y +CONFIG_FEATURE_DC_LIBM=y +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +CONFIG_DEVMEM=y +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +CONFIG_FLASH_LOCK=y +CONFIG_FLASH_UNLOCK=y +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +CONFIG_INOTIFYD=y +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL is not set +# CONFIG_FEATURE_LAST_FANCY is not set +# CONFIG_LESS is not set +CONFIG_FEATURE_LESS_MAXLINES=0 +# CONFIG_FEATURE_LESS_BRACKETS is not set +# CONFIG_FEATURE_LESS_FLAGS is not set +# CONFIG_FEATURE_LESS_MARKS is not set +# CONFIG_FEATURE_LESS_REGEXP is not set +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS is not set +CONFIG_HDPARM=y +CONFIG_FEATURE_HDPARM_GET_IDENTITY=y +CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y +CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y +CONFIG_MAKEDEVS=y +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +CONFIG_FEATURE_MAKEDEVS_TABLE=y +CONFIG_MAN=y +# CONFIG_MICROCOM is not set +# CONFIG_MOUNTPOINT is not set +# CONFIG_MT is not set +CONFIG_RAIDAUTORUN=y +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +CONFIG_RX=y +CONFIG_SETSID=y +CONFIG_STRINGS=y +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +CONFIG_TIME=y +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +CONFIG_NBDCLIENT=y +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +# CONFIG_NC_110_COMPAT is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +CONFIG_WHOIS=y +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +CONFIG_ARP=y +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +CONFIG_DNSD=y +# CONFIG_ETHER_WAKE is not set +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set +# CONFIG_HOSTNAME is not set +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +CONFIG_FEATURE_HTTPD_USE_SENDFILE=y +CONFIG_FEATURE_HTTPD_SETUID=y +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set +CONFIG_FEATURE_HTTPD_CGI=y +CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y +CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y +CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y +CONFIG_FEATURE_HTTPD_ERROR_PAGES=y +CONFIG_FEATURE_HTTPD_PROXY=y +CONFIG_FEATURE_HTTPD_GZIP=y +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +CONFIG_IFUPDOWN=y +CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" +CONFIG_FEATURE_IFUPDOWN_IP=y +CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +CONFIG_FEATURE_IFUPDOWN_IPV4=y +# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# CONFIG_FEATURE_INETD_RPC is not set +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +CONFIG_FEATURE_IP_TUNNEL=y +CONFIG_FEATURE_IP_RULE=y +CONFIG_FEATURE_IP_SHORT_FORMS=y +# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IPTUNNEL=y +CONFIG_IPRULE=y +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +CONFIG_PSCAN=y +CONFIG_ROUTE=y +# CONFIG_SLATTACH is not set +CONFIG_TCPSVD=y +# CONFIG_TELNET is not set +# CONFIG_FEATURE_TELNET_TTYPE is not set +# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set +# CONFIG_TELNETD is not set +# CONFIG_FEATURE_TELNETD_STANDALONE is not set +# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set +# CONFIG_TFTP is not set +# CONFIG_TFTPD is not set +# CONFIG_FEATURE_TFTP_GET is not set +# CONFIG_FEATURE_TFTP_PUT is not set +# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set +# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +CONFIG_TUNCTL=y +CONFIG_FEATURE_TUNCTL_UG=y +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +CONFIG_DHCPD_LEASES_FILE="" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=9 +CONFIG_FEATURE_UDHCP_RFC3397=y +CONFIG_FEATURE_UDHCP_8021Q=y +CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" +# CONFIG_UDPSVD is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set +CONFIG_FEATURE_WGET_TIMEOUT=y +# CONFIG_ZCIP is not set + +# +# Print Utilities +# +CONFIG_LPD=y +CONFIG_LPR=y +CONFIG_LPQ=y + +# +# Mail Utilities +# +CONFIG_MAKEMIME=y +CONFIG_FEATURE_MIME_CHARSET="us-ascii" +CONFIG_POPMAILDIR=y +CONFIG_FEATURE_POPMAILDIR_DELIVERY=y +CONFIG_REFORMIME=y +CONFIG_FEATURE_REFORMIME_COMPAT=y +CONFIG_SENDMAIL=y + +# +# Process Utilities +# +CONFIG_IOSTAT=y +CONFIG_MPSTAT=y +CONFIG_NMETER=y +CONFIG_PMAP=y +CONFIG_POWERTOP=y +CONFIG_PSTREE=y +CONFIG_PWDX=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +CONFIG_FUSER=y +# CONFIG_KILL is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_PGREP is not set +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +# CONFIG_PKILL is not set +# CONFIG_PS is not set +# CONFIG_FEATURE_PS_WIDE is not set +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +CONFIG_TOP=y +CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y +CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y +CONFIG_FEATURE_TOP_SMP_CPU=y +CONFIG_FEATURE_TOP_DECIMALS=y +CONFIG_FEATURE_TOP_SMP_PROCESS=y +CONFIG_FEATURE_TOPMEM=y +CONFIG_FEATURE_SHOW_THREADS=y +# CONFIG_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +CONFIG_RUNSV=y +CONFIG_RUNSVDIR=y +# CONFIG_FEATURE_RUNSVDIR_LOG is not set +CONFIG_SV=y +CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" +CONFIG_SVLOGD=y +CONFIG_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=y +# CONFIG_CHCON is not set +# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set +# CONFIG_GETENFORCE is not set +# CONFIG_GETSEBOOL is not set +# CONFIG_LOAD_POLICY is not set +# CONFIG_MATCHPATHCON is not set +# CONFIG_RESTORECON is not set +# CONFIG_RUNCON is not set +# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set +# CONFIG_SELINUXENABLED is not set +# CONFIG_SETENFORCE is not set +# CONFIG_SETFILES is not set +# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set +# CONFIG_SETSEBOOL is not set +# CONFIG_SESTATUS is not set + +# +# Shells +# +# CONFIG_ASH is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_IDLE_TIMEOUT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_BUILTIN_ECHO is not set +# CONFIG_ASH_BUILTIN_PRINTF is not set +# CONFIG_ASH_BUILTIN_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +CONFIG_CTTYHACK=y +# CONFIG_HUSH is not set +# CONFIG_HUSH_BASH_COMPAT is not set +# CONFIG_HUSH_BRACE_EXPANSION is not set +# CONFIG_HUSH_HELP is not set +# CONFIG_HUSH_INTERACTIVE is not set +# CONFIG_HUSH_SAVEHISTORY is not set +# CONFIG_HUSH_JOB is not set +# CONFIG_HUSH_TICK is not set +# CONFIG_HUSH_IF is not set +# CONFIG_HUSH_LOOPS is not set +# CONFIG_HUSH_CASE is not set +# CONFIG_HUSH_FUNCTIONS is not set +# CONFIG_HUSH_LOCAL is not set +# CONFIG_HUSH_RANDOM_SUPPORT is not set +# CONFIG_HUSH_EXPORT_N is not set +# CONFIG_HUSH_MODE_X is not set +# CONFIG_MSH is not set +# CONFIG_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# CONFIG_FEATURE_BASH_IS_ASH is not set +# CONFIG_FEATURE_BASH_IS_HUSH is not set +CONFIG_FEATURE_BASH_IS_NONE=y +# CONFIG_SH_MATH_SUPPORT is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_FEATURE_SH_HISTFILESIZE is not set + +# +# System Logging Utilities +# +# CONFIG_SYSLOGD is not set +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_SYSLOGD_DUP is not set +# CONFIG_FEATURE_SYSLOGD_CFG is not set +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 +# CONFIG_LOGREAD is not set +# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set +CONFIG_KLOGD=y +CONFIG_FEATURE_KLOGD_KLOGCTL=y +# CONFIG_LOGGER is not set diff --git a/configs/android_defconfig b/configs/android_defconfig new file mode 100644 index 000000000..76068b040 --- /dev/null +++ b/configs/android_defconfig @@ -0,0 +1,1014 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.19.0.git +# Wed Jun 29 12:01:57 2011 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# CONFIG_EXTRA_COMPAT is not set +# CONFIG_INCLUDE_SUSv2 is not set +# CONFIG_USE_PORTABLE_CODE is not set +CONFIG_PLATFORM_LINUX=y +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +# CONFIG_SHOW_USAGE is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_COMPRESS_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_INSTALL_NO_USR is not set +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=0 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# CONFIG_UNICODE_COMBINING_WCHARS is not set +# CONFIG_UNICODE_WIDE_WCHARS is not set +# CONFIG_UNICODE_BIDI_SUPPORT is not set +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +# CONFIG_UNICODE_PRESERVE_BROKEN is not set +# CONFIG_LONG_OPTS is not set +# CONFIG_FEATURE_DEVPTS is not set +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_UTMP is not set +# CONFIG_FEATURE_WTMP is not set +# CONFIG_FEATURE_PIDFILE is not set +# CONFIG_FEATURE_SUID is not set +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y +# CONFIG_FEATURE_HAVE_RPC is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +# CONFIG_NOMMU is not set +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +# CONFIG_LFS is not set +CONFIG_CROSS_COMPILER_PREFIX="arm-eabi-" +# +# Removed: +# warning flags: +# -Wno-multichar -W -Wall -Wno-unused -Winit-self -Wpointer-arith +# -Werror=return-type -Werror=non-virtual-dtor -Werror=address +# -Werror=sequence-point -Wstrict-aliasing=2 -Wno-undef -Wno-shadow +# bbox already adds these: +# -ffunction-sections -fomit-frame-pointer +# should be not needed, or even increases code size: +# -finline-functions -fno-inline-functions-called-once -finline-limit=64 +# -fstack-protector -fno-strict-aliasing -fno-exceptions -funwind-tables +# -fmessage-length=0 (only affects error message format) +# todo: do we need these? - +# -fno-short-enums +# -fgcse-after-reload +# -frerun-cse-after-loop +# -frename-registers +CONFIG_EXTRA_CFLAGS="-I$A/system/core/include -I$A/bionic/libc/arch-arm/include -I$A/bionic/libc/include -I$A/bionic/libc/kernel/common -I$A/bionic/libc/kernel/arch-arm -I$A/bionic/libm/include -I$A/bionic/libm/include/arch/arm -include $A/system/core/include/arch/linux-arm/AndroidConfig.h -I$A/system/core/include/arch/linux-arm/ -DANDROID -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frerun-cse-after-loop -frename-registers" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +# CONFIG_WERROR is not set +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options ("make install" behavior) +# +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +# CONFIG_FEATURE_SYSTEMD is not set +# CONFIG_FEATURE_RTMINMAX is not set +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +# CONFIG_FEATURE_FAST_TOP is not set +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_USE_TERMIOS=y +# CONFIG_FEATURE_EDITING is not set +CONFIG_FEATURE_EDITING_MAX_LEN=0 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=0 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +# CONFIG_FEATURE_TAB_COMPLETION is not set +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +# CONFIG_FEATURE_NON_POSIX_CP is not set +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_FEATURE_SKIP_ROOTFS is not set +# CONFIG_MONOTONIC_SYSCALL is not set +# CONFIG_IOCTL_HEX2STR_ERROR is not set +# CONFIG_FEATURE_HWIB is not set + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_XZ=y +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y +CONFIG_DPKG=y +CONFIG_DPKG_DEB=y +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +CONFIG_GUNZIP=y +CONFIG_GZIP=y +# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +CONFIG_LZOP=y +CONFIG_LZOP_COMPR_HIGH=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_AUTODETECT=y +CONFIG_FEATURE_TAR_FROM=y +CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y +CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set +# CONFIG_FEATURE_TAR_TO_COMMAND is not set +CONFIG_FEATURE_TAR_UNAME_GNAME=y +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +# CONFIG_FEATURE_TAR_SELINUX is not set +CONFIG_UNCOMPRESS=y +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_LZMA=y +CONFIG_UNXZ=y +CONFIG_XZ=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +# CONFIG_ID is not set +# CONFIG_GROUPS is not set +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_BASE64=y +CONFIG_CAL=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +# CONFIG_FEATURE_CP_LONG_OPTIONS is not set +CONFIG_CUT=y +CONFIG_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=y +# CONFIG_DF is not set +# CONFIG_FEATURE_DF_FANCY is not set +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +# CONFIG_ENV is not set +# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set +CONFIG_EXPAND=y +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +# CONFIG_EXPR is not set +# CONFIG_EXPR_MATH_SUPPORT_64 is not set +CONFIG_FALSE=y +CONFIG_FOLD=y +# CONFIG_FSYNC is not set +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +# CONFIG_HOSTID is not set +CONFIG_INSTALL=y +# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set +CONFIG_LN=y +# CONFIG_LOGNAME is not set +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +# CONFIG_FEATURE_LS_COLOR is not set +# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +CONFIG_NICE=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PRINTENV=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_FEATURE_FLOAT_SLEEP=y +CONFIG_SORT=y +CONFIG_FEATURE_SORT_BIG=y +CONFIG_SPLIT=y +CONFIG_FEATURE_SPLIT_FANCY=y +# CONFIG_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=y +CONFIG_TAC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TRUE=y +# CONFIG_TTY is not set +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set +CONFIG_UNIQ=y +# CONFIG_USLEEP is not set +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls, more and telnet +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_FGCONSOLE=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +CONFIG_LOADKMAP=y +CONFIG_OPENVT=y +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +CONFIG_SETCONSOLE=y +# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set +# CONFIG_SETFONT is not set +# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set +CONFIG_DEFAULT_SETFONT_DIR="" +CONFIG_SETKEYCODES=y +CONFIG_SETLOGCONS=y +CONFIG_SHOWKEY=y +# CONFIG_FEATURE_LOADFONT_PSF2 is not set +# CONFIG_FEATURE_LOADFONT_RAW is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +CONFIG_PIPE_PROGRESS=y +CONFIG_RUN_PARTS=y +# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set +CONFIG_FEATURE_RUN_PARTS_FANCY=y +CONFIG_START_STOP_DAEMON=y +CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y +# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +# CONFIG_VI is not set +CONFIG_FEATURE_VI_MAX_LEN=0 +# CONFIG_FEATURE_VI_8BIT is not set +# CONFIG_FEATURE_VI_COLON is not set +# CONFIG_FEATURE_VI_YANKMARK is not set +# CONFIG_FEATURE_VI_SEARCH is not set +# CONFIG_FEATURE_VI_REGEX_SEARCH is not set +# CONFIG_FEATURE_VI_USE_SIGNALS is not set +# CONFIG_FEATURE_VI_DOT_CMD is not set +# CONFIG_FEATURE_VI_READONLY is not set +# CONFIG_FEATURE_VI_SETOPTS is not set +# CONFIG_FEATURE_VI_SET is not set +# CONFIG_FEATURE_VI_WIN_RESIZE is not set +# CONFIG_FEATURE_VI_ASK_TERMINAL is not set +# CONFIG_FEATURE_VI_OPTIMIZE_CURSOR is not set +# CONFIG_AWK is not set +# CONFIG_FEATURE_AWK_LIBM is not set +CONFIG_CMP=y +CONFIG_DIFF=y +# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set +CONFIG_FEATURE_DIFF_DIR=y +# CONFIG_ED is not set +# CONFIG_SED is not set +# CONFIG_FEATURE_ALLOW_EXEC is not set + +# +# Finding Utilities +# +# CONFIG_FIND is not set +# CONFIG_FEATURE_FIND_PRINT0 is not set +# CONFIG_FEATURE_FIND_MTIME is not set +# CONFIG_FEATURE_FIND_MMIN is not set +# CONFIG_FEATURE_FIND_PERM is not set +# CONFIG_FEATURE_FIND_TYPE is not set +# CONFIG_FEATURE_FIND_XDEV is not set +# CONFIG_FEATURE_FIND_MAXDEPTH is not set +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +# CONFIG_FEATURE_FIND_EXEC is not set +# CONFIG_FEATURE_FIND_USER is not set +# CONFIG_FEATURE_FIND_GROUP is not set +# CONFIG_FEATURE_FIND_NOT is not set +# CONFIG_FEATURE_FIND_DEPTH is not set +# CONFIG_FEATURE_FIND_PAREN is not set +# CONFIG_FEATURE_FIND_SIZE is not set +# CONFIG_FEATURE_FIND_PRUNE is not set +# CONFIG_FEATURE_FIND_DELETE is not set +# CONFIG_FEATURE_FIND_PATH is not set +# CONFIG_FEATURE_FIND_REGEX is not set +# CONFIG_FEATURE_FIND_CONTEXT is not set +# CONFIG_FEATURE_FIND_LINKS is not set +# CONFIG_GREP is not set +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +CONFIG_XARGS=y +CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +CONFIG_HALT=y +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +CONFIG_FEATURE_INIT_SCTTY=y +CONFIG_FEATURE_INIT_SYSLOG=y +CONFIG_FEATURE_EXTRA_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_FEATURE_INITRD=y +CONFIG_INIT_TERMINAL_TYPE="linux" +CONFIG_MESG=y +CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y + +# +# Login/Password Management Utilities +# +# CONFIG_ADD_SHELL is not set +# CONFIG_REMOVE_SHELL is not set +# CONFIG_FEATURE_SHADOWPASSWDS is not set +# CONFIG_USE_BB_PWD_GRP is not set +# CONFIG_USE_BB_SHADOW is not set +# CONFIG_USE_BB_CRYPT is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_FIRST_SYSTEM_ID=0 +CONFIG_LAST_SYSTEM_ID=0 +# CONFIG_ADDGROUP is not set +# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set +# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set +# CONFIG_DELUSER is not set +# CONFIG_DELGROUP is not set +# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PAM is not set +# CONFIG_LOGIN_SCRIPTS is not set +# CONFIG_FEATURE_NOLOGIN is not set +# CONFIG_FEATURE_SECURETTY is not set +# CONFIG_PASSWD is not set +# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set +# CONFIG_CRYPTPW is not set +# CONFIG_CHPASSWD is not set +# CONFIG_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +CONFIG_LSATTR=y +CONFIG_TUNE2FS=y + +# +# Linux Module Utilities +# +CONFIG_MODINFO=y +CONFIG_MODPROBE_SMALL=y +CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y +CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set + +# +# Options common to multiple modutils +# +# CONFIG_FEATURE_2_4_MODULES is not set +# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +CONFIG_BLOCKDEV=y +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +# CONFIG_FEATURE_BLKID_TYPE is not set +CONFIG_DMESG=y +CONFIG_FEATURE_DMESG_PRETTY=y +CONFIG_FBSET=y +CONFIG_FEATURE_FBSET_FANCY=y +CONFIG_FEATURE_FBSET_READMODE=y +CONFIG_FDFLUSH=y +CONFIG_FDFORMAT=y +CONFIG_FDISK=y +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +CONFIG_FEATURE_FDISK_WRITABLE=y +# CONFIG_FEATURE_AIX_LABEL is not set +# CONFIG_FEATURE_SGI_LABEL is not set +# CONFIG_FEATURE_SUN_LABEL is not set +# CONFIG_FEATURE_OSF_LABEL is not set +# CONFIG_FEATURE_GPT_LABEL is not set +CONFIG_FEATURE_FDISK_ADVANCED=y +CONFIG_FINDFS=y +CONFIG_FLOCK=y +CONFIG_FREERAMDISK=y +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +# CONFIG_IPCRM is not set +# CONFIG_IPCS is not set +CONFIG_LOSETUP=y +CONFIG_LSPCI=y +CONFIG_LSUSB=y +# CONFIG_MDEV is not set +# CONFIG_FEATURE_MDEV_CONF is not set +# CONFIG_FEATURE_MDEV_RENAME is not set +# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set +# CONFIG_FEATURE_MDEV_EXEC is not set +# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set +CONFIG_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +# CONFIG_MOUNT is not set +# CONFIG_FEATURE_MOUNT_FAKE is not set +# CONFIG_FEATURE_MOUNT_VERBOSE is not set +# CONFIG_FEATURE_MOUNT_HELPERS is not set +# CONFIG_FEATURE_MOUNT_LABEL is not set +# CONFIG_FEATURE_MOUNT_NFS is not set +# CONFIG_FEATURE_MOUNT_CIFS is not set +# CONFIG_FEATURE_MOUNT_FLAGS is not set +# CONFIG_FEATURE_MOUNT_FSTAB is not set +# CONFIG_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +CONFIG_VOLUMEID=y + +# +# Filesystem/Volume identification +# +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=y +CONFIG_FEATURE_VOLUMEID_FAT=y +CONFIG_FEATURE_VOLUMEID_HFS=y +CONFIG_FEATURE_VOLUMEID_JFS=y +CONFIG_FEATURE_VOLUMEID_XFS=y +CONFIG_FEATURE_VOLUMEID_NTFS=y +CONFIG_FEATURE_VOLUMEID_ISO9660=y +CONFIG_FEATURE_VOLUMEID_UDF=y +CONFIG_FEATURE_VOLUMEID_LUKS=y +CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +CONFIG_NANDDUMP=y +CONFIG_SETSERIAL=y +# CONFIG_UBIATTACH is not set +# CONFIG_UBIDETACH is not set +# CONFIG_UBIMKVOL is not set +# CONFIG_UBIRMVOL is not set +# CONFIG_UBIRSVOL is not set +# CONFIG_UBIUPDATEVOL is not set +# CONFIG_ADJTIMEX is not set +# CONFIG_BBCONFIG is not set +# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set +CONFIG_BEEP=y +CONFIG_FEATURE_BEEP_FREQ=4000 +CONFIG_FEATURE_BEEP_LENGTH_MS=30 +CONFIG_CHAT=y +CONFIG_FEATURE_CHAT_NOFAIL=y +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +CONFIG_FEATURE_CHAT_IMPLICIT_CR=y +CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y +CONFIG_FEATURE_CHAT_SEND_ESCAPES=y +CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y +CONFIG_FEATURE_CHAT_CLR_ABORT=y +CONFIG_CHRT=y +# CONFIG_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="" +# CONFIG_CRONTAB is not set +CONFIG_DC=y +CONFIG_FEATURE_DC_LIBM=y +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +CONFIG_DEVMEM=y +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +CONFIG_FLASH_LOCK=y +CONFIG_FLASH_UNLOCK=y +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +CONFIG_INOTIFYD=y +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL is not set +# CONFIG_FEATURE_LAST_FANCY is not set +# CONFIG_LESS is not set +CONFIG_FEATURE_LESS_MAXLINES=0 +# CONFIG_FEATURE_LESS_BRACKETS is not set +# CONFIG_FEATURE_LESS_FLAGS is not set +# CONFIG_FEATURE_LESS_MARKS is not set +# CONFIG_FEATURE_LESS_REGEXP is not set +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS is not set +CONFIG_HDPARM=y +CONFIG_FEATURE_HDPARM_GET_IDENTITY=y +CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y +CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y +CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y +CONFIG_MAKEDEVS=y +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +CONFIG_FEATURE_MAKEDEVS_TABLE=y +CONFIG_MAN=y +# CONFIG_MICROCOM is not set +# CONFIG_MOUNTPOINT is not set +# CONFIG_MT is not set +CONFIG_RAIDAUTORUN=y +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +CONFIG_RX=y +CONFIG_SETSID=y +CONFIG_STRINGS=y +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +CONFIG_TIME=y +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +CONFIG_NBDCLIENT=y +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +# CONFIG_NC_110_COMPAT is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +CONFIG_WHOIS=y +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +CONFIG_ARP=y +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +CONFIG_DNSD=y +# CONFIG_ETHER_WAKE is not set +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set +# CONFIG_HOSTNAME is not set +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +CONFIG_FEATURE_HTTPD_USE_SENDFILE=y +CONFIG_FEATURE_HTTPD_SETUID=y +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set +CONFIG_FEATURE_HTTPD_CGI=y +CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y +CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y +CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y +CONFIG_FEATURE_HTTPD_ERROR_PAGES=y +CONFIG_FEATURE_HTTPD_PROXY=y +CONFIG_FEATURE_HTTPD_GZIP=y +CONFIG_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +CONFIG_IFUPDOWN=y +CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" +CONFIG_FEATURE_IFUPDOWN_IP=y +CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +CONFIG_FEATURE_IFUPDOWN_IPV4=y +# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# CONFIG_FEATURE_INETD_RPC is not set +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +CONFIG_FEATURE_IP_TUNNEL=y +CONFIG_FEATURE_IP_RULE=y +CONFIG_FEATURE_IP_SHORT_FORMS=y +# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IPTUNNEL=y +CONFIG_IPRULE=y +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +CONFIG_PSCAN=y +CONFIG_ROUTE=y +# CONFIG_SLATTACH is not set +CONFIG_TCPSVD=y +# CONFIG_TELNET is not set +# CONFIG_FEATURE_TELNET_TTYPE is not set +# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set +# CONFIG_TELNETD is not set +# CONFIG_FEATURE_TELNETD_STANDALONE is not set +# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set +# CONFIG_TFTP is not set +# CONFIG_TFTPD is not set +# CONFIG_FEATURE_TFTP_GET is not set +# CONFIG_FEATURE_TFTP_PUT is not set +# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set +# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +CONFIG_TUNCTL=y +CONFIG_FEATURE_TUNCTL_UG=y +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +CONFIG_DHCPD_LEASES_FILE="" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=9 +CONFIG_FEATURE_UDHCP_RFC3397=y +CONFIG_FEATURE_UDHCP_8021Q=y +CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" +# CONFIG_UDPSVD is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set +CONFIG_FEATURE_WGET_TIMEOUT=y +# CONFIG_ZCIP is not set + +# +# Print Utilities +# +CONFIG_LPD=y +CONFIG_LPR=y +CONFIG_LPQ=y + +# +# Mail Utilities +# +CONFIG_MAKEMIME=y +CONFIG_FEATURE_MIME_CHARSET="us-ascii" +CONFIG_POPMAILDIR=y +CONFIG_FEATURE_POPMAILDIR_DELIVERY=y +CONFIG_REFORMIME=y +CONFIG_FEATURE_REFORMIME_COMPAT=y +CONFIG_SENDMAIL=y + +# +# Process Utilities +# +CONFIG_IOSTAT=y +CONFIG_MPSTAT=y +CONFIG_NMETER=y +CONFIG_PMAP=y +CONFIG_POWERTOP=y +CONFIG_PSTREE=y +CONFIG_PWDX=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +CONFIG_FUSER=y +# CONFIG_KILL is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_PGREP is not set +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +# CONFIG_PKILL is not set +# CONFIG_PS is not set +# CONFIG_FEATURE_PS_WIDE is not set +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +CONFIG_TOP=y +CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y +CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y +CONFIG_FEATURE_TOP_SMP_CPU=y +CONFIG_FEATURE_TOP_DECIMALS=y +CONFIG_FEATURE_TOP_SMP_PROCESS=y +CONFIG_FEATURE_TOPMEM=y +CONFIG_FEATURE_SHOW_THREADS=y +# CONFIG_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +CONFIG_RUNSV=y +CONFIG_RUNSVDIR=y +# CONFIG_FEATURE_RUNSVDIR_LOG is not set +CONFIG_SV=y +CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" +CONFIG_SVLOGD=y +CONFIG_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=y +# CONFIG_CHCON is not set +# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set +# CONFIG_GETENFORCE is not set +# CONFIG_GETSEBOOL is not set +# CONFIG_LOAD_POLICY is not set +# CONFIG_MATCHPATHCON is not set +# CONFIG_RESTORECON is not set +# CONFIG_RUNCON is not set +# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set +# CONFIG_SELINUXENABLED is not set +# CONFIG_SETENFORCE is not set +# CONFIG_SETFILES is not set +# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set +# CONFIG_SETSEBOOL is not set +# CONFIG_SESTATUS is not set + +# +# Shells +# +# CONFIG_ASH is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_IDLE_TIMEOUT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_BUILTIN_ECHO is not set +# CONFIG_ASH_BUILTIN_PRINTF is not set +# CONFIG_ASH_BUILTIN_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +CONFIG_CTTYHACK=y +# CONFIG_HUSH is not set +# CONFIG_HUSH_BASH_COMPAT is not set +# CONFIG_HUSH_BRACE_EXPANSION is not set +# CONFIG_HUSH_HELP is not set +# CONFIG_HUSH_INTERACTIVE is not set +# CONFIG_HUSH_SAVEHISTORY is not set +# CONFIG_HUSH_JOB is not set +# CONFIG_HUSH_TICK is not set +# CONFIG_HUSH_IF is not set +# CONFIG_HUSH_LOOPS is not set +# CONFIG_HUSH_CASE is not set +# CONFIG_HUSH_FUNCTIONS is not set +# CONFIG_HUSH_LOCAL is not set +# CONFIG_HUSH_RANDOM_SUPPORT is not set +# CONFIG_HUSH_EXPORT_N is not set +# CONFIG_HUSH_MODE_X is not set +# CONFIG_MSH is not set +# CONFIG_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# CONFIG_FEATURE_BASH_IS_ASH is not set +# CONFIG_FEATURE_BASH_IS_HUSH is not set +CONFIG_FEATURE_BASH_IS_NONE=y +# CONFIG_SH_MATH_SUPPORT is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_FEATURE_SH_HISTFILESIZE is not set + +# +# System Logging Utilities +# +# CONFIG_SYSLOGD is not set +# CONFIG_FEATURE_ROTATE_LOGFILE is not set +# CONFIG_FEATURE_REMOTE_LOG is not set +# CONFIG_FEATURE_SYSLOGD_DUP is not set +# CONFIG_FEATURE_SYSLOGD_CFG is not set +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 +# CONFIG_FEATURE_IPC_SYSLOG is not set +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 +# CONFIG_LOGREAD is not set +# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set +CONFIG_KLOGD=y +CONFIG_FEATURE_KLOGD_KLOGCTL=y +# CONFIG_LOGGER is not set diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig new file mode 100644 index 000000000..bdd0d66d0 --- /dev/null +++ b/configs/cygwin_defconfig @@ -0,0 +1,997 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.19.0.git +# Sun Jul 10 12:48:50 2011 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +CONFIG_DESKTOP=y +# CONFIG_EXTRA_COMPAT is not set +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +CONFIG_PLATFORM_LINUX=y +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_SHOW_USAGE=y +CONFIG_FEATURE_VERBOSE_USAGE=y +CONFIG_FEATURE_COMPRESS_USAGE=y +CONFIG_FEATURE_INSTALLER=y +# CONFIG_INSTALL_NO_USR is not set +# CONFIG_LOCALE_SUPPORT is not set +CONFIG_UNICODE_SUPPORT=y +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=65533 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# CONFIG_UNICODE_COMBINING_WCHARS is not set +# CONFIG_UNICODE_WIDE_WCHARS is not set +# CONFIG_UNICODE_BIDI_SUPPORT is not set +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +# CONFIG_UNICODE_PRESERVE_BROKEN is not set +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_UTMP is not set +# CONFIG_FEATURE_WTMP is not set +CONFIG_FEATURE_PIDFILE=y +CONFIG_FEATURE_SUID=y +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y +# CONFIG_FEATURE_HAVE_RPC is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +# CONFIG_NOMMU is not set +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +CONFIG_LFS=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +# CONFIG_WERROR is not set +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options ("make install" behavior) +# +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +# CONFIG_FEATURE_SYSTEMD is not set +CONFIG_FEATURE_RTMINMAX=y +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=255 +CONFIG_FEATURE_EDITING_SAVEHISTORY=y +CONFIG_FEATURE_TAB_COMPLETION=y +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +CONFIG_FEATURE_EDITING_FANCY_PROMPT=y +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +CONFIG_FEATURE_NON_POSIX_CP=y +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +CONFIG_FEATURE_SKIP_ROOTFS=y +# CONFIG_MONOTONIC_SYSCALL is not set +CONFIG_IOCTL_HEX2STR_ERROR=y +CONFIG_FEATURE_HWIB=y + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_XZ=y +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +# CONFIG_FEATURE_SEAMLESS_Z is not set +# CONFIG_AR is not set +# CONFIG_FEATURE_AR_LONG_FILENAMES is not set +# CONFIG_FEATURE_AR_CREATE is not set +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +CONFIG_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +# CONFIG_LZOP_COMPR_HIGH is not set +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_AUTODETECT=y +CONFIG_FEATURE_TAR_FROM=y +CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y +CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +CONFIG_FEATURE_TAR_LONG_OPTIONS=y +CONFIG_FEATURE_TAR_TO_COMMAND=y +CONFIG_FEATURE_TAR_UNAME_GNAME=y +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +# CONFIG_FEATURE_TAR_SELINUX is not set +# CONFIG_UNCOMPRESS is not set +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_LZMA=y +CONFIG_UNXZ=y +CONFIG_XZ=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +CONFIG_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +# CONFIG_FEATURE_DATE_NANO is not set +CONFIG_FEATURE_DATE_COMPAT=y +CONFIG_ID=y +CONFIG_GROUPS=y +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_BASE64=y +CONFIG_CAL=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +CONFIG_FEATURE_CP_LONG_OPTIONS=y +CONFIG_CUT=y +CONFIG_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=y +CONFIG_DF=y +CONFIG_FEATURE_DF_FANCY=y +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_FEATURE_ENV_LONG_OPTIONS=y +CONFIG_EXPAND=y +CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y +CONFIG_EXPR=y +CONFIG_EXPR_MATH_SUPPORT_64=y +CONFIG_FALSE=y +CONFIG_FOLD=y +CONFIG_FSYNC=y +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +CONFIG_HOSTID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LN=y +CONFIG_LOGNAME=y +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +CONFIG_FEATURE_MV_LONG_OPTIONS=y +CONFIG_NICE=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PRINTENV=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_FEATURE_FLOAT_SLEEP=y +CONFIG_SORT=y +CONFIG_FEATURE_SORT_BIG=y +CONFIG_SPLIT=y +CONFIG_FEATURE_SPLIT_FANCY=y +# CONFIG_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=y +CONFIG_TAC=y +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls, more and telnet +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_FGCONSOLE is not set +CONFIG_CLEAR=y +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +# CONFIG_SETCONSOLE is not set +# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set +# CONFIG_SETFONT is not set +# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set +CONFIG_DEFAULT_SETFONT_DIR="" +# CONFIG_SETKEYCODES is not set +# CONFIG_SETLOGCONS is not set +# CONFIG_SHOWKEY is not set +# CONFIG_FEATURE_LOADFONT_PSF2 is not set +# CONFIG_FEATURE_LOADFONT_RAW is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +CONFIG_PIPE_PROGRESS=y +CONFIG_RUN_PARTS=y +CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y +CONFIG_FEATURE_RUN_PARTS_FANCY=y +CONFIG_START_STOP_DAEMON=y +CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y +CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +CONFIG_VI=y +CONFIG_FEATURE_VI_MAX_LEN=4096 +# CONFIG_FEATURE_VI_8BIT is not set +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +# CONFIG_FEATURE_VI_REGEX_SEARCH is not set +CONFIG_FEATURE_VI_USE_SIGNALS=y +CONFIG_FEATURE_VI_DOT_CMD=y +CONFIG_FEATURE_VI_READONLY=y +CONFIG_FEATURE_VI_SETOPTS=y +CONFIG_FEATURE_VI_SET=y +CONFIG_FEATURE_VI_WIN_RESIZE=y +CONFIG_FEATURE_VI_ASK_TERMINAL=y +CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y +CONFIG_AWK=y +CONFIG_FEATURE_AWK_LIBM=y +CONFIG_CMP=y +CONFIG_DIFF=y +CONFIG_FEATURE_DIFF_LONG_OPTIONS=y +CONFIG_FEATURE_DIFF_DIR=y +CONFIG_ED=y +CONFIG_SED=y +CONFIG_FEATURE_ALLOW_EXEC=y + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_PRINT0=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_MMIN=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_MAXDEPTH=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_FEATURE_FIND_EXEC=y +CONFIG_FEATURE_FIND_USER=y +CONFIG_FEATURE_FIND_GROUP=y +CONFIG_FEATURE_FIND_NOT=y +CONFIG_FEATURE_FIND_DEPTH=y +CONFIG_FEATURE_FIND_PAREN=y +CONFIG_FEATURE_FIND_SIZE=y +CONFIG_FEATURE_FIND_PRUNE=y +CONFIG_FEATURE_FIND_DELETE=y +CONFIG_FEATURE_FIND_PATH=y +CONFIG_FEATURE_FIND_REGEX=y +# CONFIG_FEATURE_FIND_CONTEXT is not set +CONFIG_FEATURE_FIND_LINKS=y +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +# CONFIG_HALT is not set +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +# CONFIG_INIT is not set +# CONFIG_FEATURE_USE_INITTAB is not set +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +# CONFIG_FEATURE_INIT_SCTTY is not set +# CONFIG_FEATURE_INIT_SYSLOG is not set +# CONFIG_FEATURE_EXTRA_QUIET is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_FEATURE_INITRD is not set +CONFIG_INIT_TERMINAL_TYPE="" +CONFIG_MESG=y +CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y + +# +# Login/Password Management Utilities +# +CONFIG_ADD_SHELL=y +CONFIG_REMOVE_SHELL=y +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_PWD_GRP=y +CONFIG_USE_BB_SHADOW=y +CONFIG_USE_BB_CRYPT=y +CONFIG_USE_BB_CRYPT_SHA=y +CONFIG_ADDUSER=y +CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +CONFIG_DELUSER=y +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +# CONFIG_GETTY is not set +CONFIG_LOGIN=y +# CONFIG_PAM is not set +CONFIG_LOGIN_SCRIPTS=y +CONFIG_FEATURE_NOLOGIN=y +CONFIG_FEATURE_SECURETTY=y +CONFIG_PASSWD=y +CONFIG_FEATURE_PASSWD_WEAK_CHECK=y +CONFIG_CRYPTPW=y +CONFIG_CHPASSWD=y +CONFIG_SU=y +CONFIG_FEATURE_SU_SYSLOG=y +CONFIG_FEATURE_SU_CHECKS_SHELLS=y +CONFIG_SULOGIN=y +CONFIG_VLOCK=y + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +# CONFIG_LSATTR is not set +# CONFIG_TUNE2FS is not set + +# +# Linux Module Utilities +# +# CONFIG_MODINFO is not set +# CONFIG_MODPROBE_SMALL is not set +# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set +# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set + +# +# Options common to multiple modutils +# +# CONFIG_FEATURE_2_4_MODULES is not set +# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="" +CONFIG_DEFAULT_DEPMOD_FILE="" + +# +# Linux System Utilities +# +# CONFIG_BLOCKDEV is not set +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +# CONFIG_BLKID is not set +# CONFIG_FEATURE_BLKID_TYPE is not set +# CONFIG_DMESG is not set +# CONFIG_FEATURE_DMESG_PRETTY is not set +# CONFIG_FBSET is not set +# CONFIG_FEATURE_FBSET_FANCY is not set +# CONFIG_FEATURE_FBSET_READMODE is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set +# CONFIG_FEATURE_FDISK_WRITABLE is not set +# CONFIG_FEATURE_AIX_LABEL is not set +# CONFIG_FEATURE_SGI_LABEL is not set +# CONFIG_FEATURE_SUN_LABEL is not set +# CONFIG_FEATURE_OSF_LABEL is not set +# CONFIG_FEATURE_GPT_LABEL is not set +# CONFIG_FEATURE_FDISK_ADVANCED is not set +# CONFIG_FINDFS is not set +CONFIG_FLOCK=y +# CONFIG_FREERAMDISK is not set +CONFIG_FSCK_MINIX=y +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +CONFIG_FEATURE_MINIX2=y +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +CONFIG_IPCRM=y +# CONFIG_IPCS is not set +# CONFIG_LOSETUP is not set +# CONFIG_LSPCI is not set +# CONFIG_LSUSB is not set +# CONFIG_MDEV is not set +# CONFIG_FEATURE_MDEV_CONF is not set +# CONFIG_FEATURE_MDEV_RENAME is not set +# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set +# CONFIG_FEATURE_MDEV_EXEC is not set +# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set +CONFIG_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +# CONFIG_MOUNT is not set +# CONFIG_FEATURE_MOUNT_FAKE is not set +# CONFIG_FEATURE_MOUNT_VERBOSE is not set +# CONFIG_FEATURE_MOUNT_HELPERS is not set +# CONFIG_FEATURE_MOUNT_LABEL is not set +# CONFIG_FEATURE_MOUNT_NFS is not set +# CONFIG_FEATURE_MOUNT_CIFS is not set +# CONFIG_FEATURE_MOUNT_FLAGS is not set +# CONFIG_FEATURE_MOUNT_FSTAB is not set +# CONFIG_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +CONFIG_RDEV=y +CONFIG_READPROFILE=y +# CONFIG_RTCWAKE is not set +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +# CONFIG_VOLUMEID is not set +# CONFIG_FEATURE_VOLUMEID_EXT is not set +# CONFIG_FEATURE_VOLUMEID_BTRFS is not set +# CONFIG_FEATURE_VOLUMEID_REISERFS is not set +# CONFIG_FEATURE_VOLUMEID_FAT is not set +# CONFIG_FEATURE_VOLUMEID_HFS is not set +# CONFIG_FEATURE_VOLUMEID_JFS is not set +# CONFIG_FEATURE_VOLUMEID_XFS is not set +# CONFIG_FEATURE_VOLUMEID_NTFS is not set +# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set +# CONFIG_FEATURE_VOLUMEID_UDF is not set +# CONFIG_FEATURE_VOLUMEID_LUKS is not set +# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set +# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set +# CONFIG_FEATURE_VOLUMEID_ROMFS is not set +# CONFIG_FEATURE_VOLUMEID_SYSV is not set +# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set +# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +# CONFIG_NANDDUMP is not set +# CONFIG_SETSERIAL is not set +# CONFIG_UBIATTACH is not set +# CONFIG_UBIDETACH is not set +# CONFIG_UBIMKVOL is not set +# CONFIG_UBIRMVOL is not set +# CONFIG_UBIRSVOL is not set +# CONFIG_UBIUPDATEVOL is not set +# CONFIG_ADJTIMEX is not set +# CONFIG_BBCONFIG is not set +# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set +# CONFIG_BEEP is not set +CONFIG_FEATURE_BEEP_FREQ=0 +CONFIG_FEATURE_BEEP_LENGTH_MS=0 +CONFIG_CHAT=y +CONFIG_FEATURE_CHAT_NOFAIL=y +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +CONFIG_FEATURE_CHAT_IMPLICIT_CR=y +CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y +CONFIG_FEATURE_CHAT_SEND_ESCAPES=y +CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y +CONFIG_FEATURE_CHAT_CLR_ABORT=y +CONFIG_CHRT=y +CONFIG_CROND=y +CONFIG_FEATURE_CROND_D=y +CONFIG_FEATURE_CROND_CALL_SENDMAIL=y +CONFIG_FEATURE_CROND_DIR="/var/spool/cron" +CONFIG_CRONTAB=y +CONFIG_DC=y +CONFIG_FEATURE_DC_LIBM=y +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +CONFIG_DEVMEM=y +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +# CONFIG_FBSPLASH is not set +# CONFIG_FLASHCP is not set +# CONFIG_FLASH_LOCK is not set +# CONFIG_FLASH_UNLOCK is not set +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +# CONFIG_INOTIFYD is not set +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL is not set +# CONFIG_FEATURE_LAST_FANCY is not set +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set +# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set +# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +# CONFIG_FEATURE_MAKEDEVS_TABLE is not set +CONFIG_MAN=y +# CONFIG_MICROCOM is not set +# CONFIG_MOUNTPOINT is not set +CONFIG_MT=y +# CONFIG_RAIDAUTORUN is not set +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +# CONFIG_RX is not set +CONFIG_SETSID=y +CONFIG_STRINGS=y +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +CONFIG_TIME=y +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +# CONFIG_NBDCLIENT is not set +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +# CONFIG_NC_110_COMPAT is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +CONFIG_WHOIS=y +CONFIG_FEATURE_IPV6=y +# CONFIG_FEATURE_UNIX_LOCAL is not set +CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +# CONFIG_ARP is not set +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +CONFIG_DNSD=y +# CONFIG_ETHER_WAKE is not set +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y +CONFIG_HOSTNAME=y +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set +CONFIG_FEATURE_HTTPD_SETUID=y +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +CONFIG_FEATURE_HTTPD_AUTH_MD5=y +CONFIG_FEATURE_HTTPD_CGI=y +CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y +CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y +CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y +CONFIG_FEATURE_HTTPD_ERROR_PAGES=y +CONFIG_FEATURE_HTTPD_PROXY=y +CONFIG_FEATURE_HTTPD_GZIP=y +# CONFIG_IFCONFIG is not set +# CONFIG_FEATURE_IFCONFIG_STATUS is not set +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +# CONFIG_FEATURE_IFCONFIG_HW is not set +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +# CONFIG_IFUPDOWN is not set +CONFIG_IFUPDOWN_IFSTATE_PATH="" +# CONFIG_FEATURE_IFUPDOWN_IP is not set +# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set +# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set +# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +CONFIG_INETD=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y +CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y +# CONFIG_FEATURE_INETD_RPC is not set +# CONFIG_IP is not set +# CONFIG_FEATURE_IP_ADDRESS is not set +# CONFIG_FEATURE_IP_LINK is not set +# CONFIG_FEATURE_IP_ROUTE is not set +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_FEATURE_IP_RULE is not set +# CONFIG_FEATURE_IP_SHORT_FORMS is not set +# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_IPRULE is not set +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +# CONFIG_NETSTAT is not set +# CONFIG_FEATURE_NETSTAT_WIDE is not set +# CONFIG_FEATURE_NETSTAT_PRG is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +CONFIG_PSCAN=y +# CONFIG_ROUTE is not set +# CONFIG_SLATTACH is not set +CONFIG_TCPSVD=y +CONFIG_TELNET=y +CONFIG_FEATURE_TELNET_TTYPE=y +CONFIG_FEATURE_TELNET_AUTOLOGIN=y +CONFIG_TELNETD=y +CONFIG_FEATURE_TELNETD_STANDALONE=y +CONFIG_FEATURE_TELNETD_INETD_WAIT=y +CONFIG_TFTP=y +CONFIG_TFTPD=y + +# +# Common options for tftp/tftpd +# +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +CONFIG_FEATURE_TFTP_BLOCKSIZE=y +CONFIG_FEATURE_TFTP_PROGRESS_BAR=y +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +# CONFIG_TUNCTL is not set +# CONFIG_FEATURE_TUNCTL_UG is not set +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +CONFIG_DHCPD_LEASES_FILE="" +# CONFIG_UDHCPC is not set +# CONFIG_FEATURE_UDHCPC_ARPING is not set +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=0 +# CONFIG_FEATURE_UDHCP_RFC3397 is not set +# CONFIG_FEATURE_UDHCP_8021Q is not set +CONFIG_UDHCPC_DEFAULT_SCRIPT="" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" +CONFIG_UDPSVD=y +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_LONG_OPTIONS=y +CONFIG_FEATURE_WGET_TIMEOUT=y +# CONFIG_ZCIP is not set + +# +# Print Utilities +# +CONFIG_LPD=y +CONFIG_LPR=y +CONFIG_LPQ=y + +# +# Mail Utilities +# +CONFIG_MAKEMIME=y +CONFIG_FEATURE_MIME_CHARSET="us-ascii" +CONFIG_POPMAILDIR=y +CONFIG_FEATURE_POPMAILDIR_DELIVERY=y +CONFIG_REFORMIME=y +CONFIG_FEATURE_REFORMIME_COMPAT=y +CONFIG_SENDMAIL=y + +# +# Process Utilities +# +CONFIG_IOSTAT=y +CONFIG_MPSTAT=y +CONFIG_NMETER=y +# CONFIG_PMAP is not set +# CONFIG_POWERTOP is not set +CONFIG_PSTREE=y +CONFIG_PWDX=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +CONFIG_FUSER=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +CONFIG_PGREP=y +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +CONFIG_PKILL=y +CONFIG_PS=y +CONFIG_FEATURE_PS_WIDE=y +# CONFIG_FEATURE_PS_TIME is not set +CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +CONFIG_TOP=y +CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y +CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y +CONFIG_FEATURE_TOP_SMP_CPU=y +CONFIG_FEATURE_TOP_DECIMALS=y +CONFIG_FEATURE_TOP_SMP_PROCESS=y +CONFIG_FEATURE_TOPMEM=y +# CONFIG_FEATURE_SHOW_THREADS is not set +# CONFIG_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +CONFIG_RUNSV=y +CONFIG_RUNSVDIR=y +# CONFIG_FEATURE_RUNSVDIR_LOG is not set +CONFIG_SV=y +CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" +CONFIG_SVLOGD=y +CONFIG_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=y +# CONFIG_CHCON is not set +# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set +# CONFIG_GETENFORCE is not set +# CONFIG_GETSEBOOL is not set +# CONFIG_LOAD_POLICY is not set +# CONFIG_MATCHPATHCON is not set +# CONFIG_RESTORECON is not set +# CONFIG_RUNCON is not set +# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set +# CONFIG_SELINUXENABLED is not set +# CONFIG_SETENFORCE is not set +# CONFIG_SETFILES is not set +# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set +# CONFIG_SETSEBOOL is not set +# CONFIG_SESTATUS is not set + +# +# Shells +# +CONFIG_ASH=y +CONFIG_ASH_BASH_COMPAT=y +# CONFIG_ASH_IDLE_TIMEOUT is not set +CONFIG_ASH_JOB_CONTROL=y +CONFIG_ASH_ALIAS=y +CONFIG_ASH_GETOPTS=y +CONFIG_ASH_BUILTIN_ECHO=y +CONFIG_ASH_BUILTIN_PRINTF=y +CONFIG_ASH_BUILTIN_TEST=y +CONFIG_ASH_CMDCMD=y +# CONFIG_ASH_MAIL is not set +CONFIG_ASH_OPTIMIZE_FOR_SIZE=y +CONFIG_ASH_RANDOM_SUPPORT=y +CONFIG_ASH_EXPAND_PRMT=y +# CONFIG_CTTYHACK is not set +CONFIG_HUSH=y +CONFIG_HUSH_BASH_COMPAT=y +CONFIG_HUSH_BRACE_EXPANSION=y +CONFIG_HUSH_HELP=y +CONFIG_HUSH_INTERACTIVE=y +CONFIG_HUSH_SAVEHISTORY=y +CONFIG_HUSH_JOB=y +CONFIG_HUSH_TICK=y +CONFIG_HUSH_IF=y +CONFIG_HUSH_LOOPS=y +CONFIG_HUSH_CASE=y +CONFIG_HUSH_FUNCTIONS=y +CONFIG_HUSH_LOCAL=y +CONFIG_HUSH_RANDOM_SUPPORT=y +CONFIG_HUSH_EXPORT_N=y +CONFIG_HUSH_MODE_X=y +# CONFIG_MSH is not set +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +# CONFIG_FEATURE_BASH_IS_ASH is not set +# CONFIG_FEATURE_BASH_IS_HUSH is not set +CONFIG_FEATURE_BASH_IS_NONE=y +CONFIG_SH_MATH_SUPPORT=y +CONFIG_SH_MATH_SUPPORT_64=y +CONFIG_FEATURE_SH_EXTRA_QUIET=y +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +CONFIG_FEATURE_SH_HISTFILESIZE=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_SYSLOGD_DUP=y +CONFIG_FEATURE_SYSLOGD_CFG=y +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +# CONFIG_KLOGD is not set +# CONFIG_FEATURE_KLOGD_KLOGCTL is not set +CONFIG_LOGGER=y diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig new file mode 100644 index 000000000..dcb5d953c --- /dev/null +++ b/configs/freebsd_defconfig @@ -0,0 +1,970 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.18.1 +# Tue Dec 21 19:47:40 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# CONFIG_EXTRA_COMPAT is not set +CONFIG_INCLUDE_SUSv2=y +CONFIG_USE_PORTABLE_CODE=y +# CONFIG_PLATFORM_LINUX is not set +CONFIG_FEATURE_BUFFERS_USE_MALLOC=y +# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set +# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set +CONFIG_SHOW_USAGE=y +CONFIG_FEATURE_VERBOSE_USAGE=y +CONFIG_FEATURE_COMPRESS_USAGE=y +CONFIG_FEATURE_INSTALLER=y +# CONFIG_INSTALL_NO_USR is not set +CONFIG_LOCALE_SUPPORT=y +CONFIG_UNICODE_SUPPORT=y +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=63 +CONFIG_LAST_SUPPORTED_WCHAR=767 +# CONFIG_UNICODE_COMBINING_WCHARS is not set +# CONFIG_UNICODE_WIDE_WCHARS is not set +# CONFIG_UNICODE_BIDI_SUPPORT is not set +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +# CONFIG_UNICODE_PRESERVE_BROKEN is not set +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_WTMP is not set +# CONFIG_FEATURE_UTMP is not set +CONFIG_FEATURE_PIDFILE=y +CONFIG_FEATURE_SUID=y +CONFIG_FEATURE_SUID_CONFIG=y +CONFIG_FEATURE_SUID_CONFIG_QUIET=y +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y +# CONFIG_FEATURE_HAVE_RPC is not set + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +# CONFIG_NOMMU is not set +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +CONFIG_LFS=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +# CONFIG_WERROR is not set +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options ("make install" behavior) +# +CONFIG_INSTALL_APPLET_SYMLINKS=y +# CONFIG_INSTALL_APPLET_HARDLINKS is not set +# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set +# CONFIG_INSTALL_APPLET_DONT is not set +# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set +# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set +# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set +CONFIG_PREFIX="./_install" + +# +# Busybox Library Tuning +# +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_USE_TERMIOS=y +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=30 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +CONFIG_FEATURE_TAB_COMPLETION=y +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +CONFIG_FEATURE_NON_POSIX_CP=y +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_MONOTONIC_SYSCALL is not set +CONFIG_IOCTL_HEX2STR_ERROR=y +CONFIG_FEATURE_HWIB=y + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_XZ=y +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +# CONFIG_FEATURE_CPIO_O is not set +# CONFIG_FEATURE_CPIO_P is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set +CONFIG_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +# CONFIG_LZOP_COMPR_HIGH is not set +CONFIG_RPM2CPIO=y +CONFIG_RPM=y +CONFIG_TAR=y +CONFIG_FEATURE_TAR_CREATE=y +CONFIG_FEATURE_TAR_AUTODETECT=y +CONFIG_FEATURE_TAR_FROM=y +CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y +CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y +CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y +CONFIG_FEATURE_TAR_LONG_OPTIONS=y +CONFIG_FEATURE_TAR_TO_COMMAND=y +# CONFIG_FEATURE_TAR_UNAME_GNAME is not set +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +# CONFIG_FEATURE_TAR_SELINUX is not set +CONFIG_UNCOMPRESS=y +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_LZMA=y +CONFIG_UNXZ=y +CONFIG_XZ=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +# CONFIG_BASE64 is not set +CONFIG_CAL=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +CONFIG_FEATURE_CP_LONG_OPTIONS=y +CONFIG_CUT=y +CONFIG_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=y +# CONFIG_DF is not set +# CONFIG_FEATURE_DF_FANCY is not set +CONFIG_DIRNAME=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_DU=y +CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y +CONFIG_ECHO=y +CONFIG_FEATURE_FANCY_ECHO=y +CONFIG_ENV=y +CONFIG_FEATURE_ENV_LONG_OPTIONS=y +CONFIG_EXPAND=y +CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y +CONFIG_EXPR=y +CONFIG_EXPR_MATH_SUPPORT_64=y +CONFIG_FALSE=y +CONFIG_FOLD=y +CONFIG_FSYNC=y +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +CONFIG_HOSTID=y +CONFIG_ID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LENGTH=y +CONFIG_LN=y +CONFIG_LOGNAME=y +CONFIG_LS=y +CONFIG_FEATURE_LS_FILETYPES=y +CONFIG_FEATURE_LS_FOLLOWLINKS=y +CONFIG_FEATURE_LS_RECURSIVE=y +CONFIG_FEATURE_LS_SORTFILES=y +CONFIG_FEATURE_LS_TIMESTAMPS=y +CONFIG_FEATURE_LS_USERNAME=y +CONFIG_FEATURE_LS_COLOR=y +CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y +CONFIG_MKFIFO=y +# CONFIG_MKNOD is not set +CONFIG_MV=y +CONFIG_FEATURE_MV_LONG_OPTIONS=y +CONFIG_NICE=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PRINTENV=y +CONFIG_PRINTF=y +CONFIG_PWD=y +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y +CONFIG_RM=y +CONFIG_RMDIR=y +# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=y +CONFIG_SLEEP=y +CONFIG_FEATURE_FANCY_SLEEP=y +CONFIG_FEATURE_FLOAT_SLEEP=y +CONFIG_SORT=y +CONFIG_FEATURE_SORT_BIG=y +CONFIG_SPLIT=y +CONFIG_FEATURE_SPLIT_FANCY=y +# CONFIG_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +# CONFIG_STTY is not set +CONFIG_SUM=y +CONFIG_SYNC=y +# CONFIG_TAC is not set +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TOUCH=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y +CONFIG_UNIQ=y +CONFIG_USLEEP=y +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for ls, more and telnet +# +CONFIG_FEATURE_AUTOWIDTH=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_FGCONSOLE is not set +CONFIG_CLEAR=y +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +# CONFIG_SETCONSOLE is not set +# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set +# CONFIG_SETFONT is not set +# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set +CONFIG_DEFAULT_SETFONT_DIR="" +# CONFIG_SETKEYCODES is not set +# CONFIG_SETLOGCONS is not set +# CONFIG_SHOWKEY is not set +# CONFIG_FEATURE_LOADFONT_PSF2 is not set +# CONFIG_FEATURE_LOADFONT_RAW is not set + +# +# Debian Utilities +# +CONFIG_MKTEMP=y +CONFIG_PIPE_PROGRESS=y +CONFIG_RUN_PARTS=y +CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y +CONFIG_FEATURE_RUN_PARTS_FANCY=y +# CONFIG_START_STOP_DAEMON is not set +# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set +# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +CONFIG_AWK=y +CONFIG_FEATURE_AWK_LIBM=y +CONFIG_CMP=y +CONFIG_DIFF=y +CONFIG_FEATURE_DIFF_LONG_OPTIONS=y +CONFIG_FEATURE_DIFF_DIR=y +CONFIG_ED=y +CONFIG_SED=y +CONFIG_VI=y +CONFIG_FEATURE_VI_MAX_LEN=1024 +CONFIG_FEATURE_VI_8BIT=y +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +CONFIG_FEATURE_VI_USE_SIGNALS=y +CONFIG_FEATURE_VI_DOT_CMD=y +CONFIG_FEATURE_VI_READONLY=y +CONFIG_FEATURE_VI_SETOPTS=y +CONFIG_FEATURE_VI_SET=y +CONFIG_FEATURE_VI_WIN_RESIZE=y +CONFIG_FEATURE_VI_ASK_TERMINAL=y +CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y +CONFIG_FEATURE_ALLOW_EXEC=y + +# +# Finding Utilities +# +CONFIG_FIND=y +CONFIG_FEATURE_FIND_PRINT0=y +CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_MMIN=y +CONFIG_FEATURE_FIND_PERM=y +CONFIG_FEATURE_FIND_TYPE=y +CONFIG_FEATURE_FIND_XDEV=y +CONFIG_FEATURE_FIND_MAXDEPTH=y +CONFIG_FEATURE_FIND_NEWER=y +CONFIG_FEATURE_FIND_INUM=y +CONFIG_FEATURE_FIND_EXEC=y +CONFIG_FEATURE_FIND_USER=y +CONFIG_FEATURE_FIND_GROUP=y +CONFIG_FEATURE_FIND_NOT=y +CONFIG_FEATURE_FIND_DEPTH=y +CONFIG_FEATURE_FIND_PAREN=y +CONFIG_FEATURE_FIND_SIZE=y +CONFIG_FEATURE_FIND_PRUNE=y +CONFIG_FEATURE_FIND_DELETE=y +CONFIG_FEATURE_FIND_PATH=y +CONFIG_FEATURE_FIND_REGEX=y +# CONFIG_FEATURE_FIND_CONTEXT is not set +CONFIG_FEATURE_FIND_LINKS=y +CONFIG_GREP=y +CONFIG_FEATURE_GREP_EGREP_ALIAS=y +CONFIG_FEATURE_GREP_FGREP_ALIAS=y +CONFIG_FEATURE_GREP_CONTEXT=y +CONFIG_XARGS=y +CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y +CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y +CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y +CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +# CONFIG_HALT is not set +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +# CONFIG_INIT is not set +# CONFIG_FEATURE_USE_INITTAB is not set +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +# CONFIG_FEATURE_INIT_SCTTY is not set +# CONFIG_FEATURE_INIT_SYSLOG is not set +# CONFIG_FEATURE_EXTRA_QUIET is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_FEATURE_INITRD is not set +CONFIG_INIT_TERMINAL_TYPE="" +# CONFIG_MESG is not set + +# +# Login/Password Management Utilities +# +# CONFIG_ADD_SHELL is not set +# CONFIG_REMOVE_SHELL is not set +# CONFIG_FEATURE_SHADOWPASSWDS is not set +CONFIG_USE_BB_PWD_GRP=y +# CONFIG_USE_BB_SHADOW is not set +# CONFIG_USE_BB_CRYPT is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +# CONFIG_DELUSER is not set +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +# CONFIG_GETTY is not set +# CONFIG_LOGIN is not set +# CONFIG_PAM is not set +# CONFIG_LOGIN_SCRIPTS is not set +# CONFIG_FEATURE_NOLOGIN is not set +# CONFIG_FEATURE_SECURETTY is not set +# CONFIG_PASSWD is not set +# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set +# CONFIG_CRYPTPW is not set +# CONFIG_CHPASSWD is not set +# CONFIG_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +# CONFIG_LSATTR is not set +# CONFIG_TUNE2FS is not set +# CONFIG_MODINFO is not set +# CONFIG_MODPROBE_SMALL is not set +# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set +# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set +# CONFIG_INSMOD is not set +# CONFIG_RMMOD is not set +# CONFIG_LSMOD is not set +# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set +# CONFIG_MODPROBE is not set +# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set +# CONFIG_DEPMOD is not set +# CONFIG_FEATURE_2_4_MODULES is not set +# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set +# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set +# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set +# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set +# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set +# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set +# CONFIG_FEATURE_MODUTILS_ALIAS is not set +# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set +CONFIG_DEFAULT_MODULES_DIR="" +CONFIG_DEFAULT_DEPMOD_FILE="" + +# +# Linux System Utilities +# +# CONFIG_BLOCKDEV is not set +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +# CONFIG_BLKID is not set +# CONFIG_DMESG is not set +# CONFIG_FEATURE_DMESG_PRETTY is not set +# CONFIG_FBSET is not set +# CONFIG_FEATURE_FBSET_FANCY is not set +# CONFIG_FEATURE_FBSET_READMODE is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +# CONFIG_FEATURE_FDISK_WRITABLE is not set +# CONFIG_FEATURE_AIX_LABEL is not set +# CONFIG_FEATURE_SGI_LABEL is not set +# CONFIG_FEATURE_SUN_LABEL is not set +# CONFIG_FEATURE_OSF_LABEL is not set +# CONFIG_FEATURE_GPT_LABEL is not set +# CONFIG_FEATURE_FDISK_ADVANCED is not set +# CONFIG_FINDFS is not set +CONFIG_FLOCK=y +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +# CONFIG_FEATURE_HEXDUMP_REVERSE is not set +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +# CONFIG_IPCRM is not set +# CONFIG_IPCS is not set +# CONFIG_LOSETUP is not set +CONFIG_LSPCI=y +CONFIG_LSUSB=y +# CONFIG_MDEV is not set +# CONFIG_FEATURE_MDEV_CONF is not set +# CONFIG_FEATURE_MDEV_RENAME is not set +# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set +# CONFIG_FEATURE_MDEV_EXEC is not set +# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set +# CONFIG_MKSWAP is not set +# CONFIG_FEATURE_MKSWAP_UUID is not set +CONFIG_MORE=y +# CONFIG_MOUNT is not set +# CONFIG_FEATURE_MOUNT_FAKE is not set +# CONFIG_FEATURE_MOUNT_VERBOSE is not set +# CONFIG_FEATURE_MOUNT_HELPERS is not set +# CONFIG_FEATURE_MOUNT_LABEL is not set +# CONFIG_FEATURE_MOUNT_NFS is not set +# CONFIG_FEATURE_MOUNT_CIFS is not set +# CONFIG_FEATURE_MOUNT_FLAGS is not set +# CONFIG_FEATURE_MOUNT_FSTAB is not set +# CONFIG_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +# CONFIG_RDEV is not set +CONFIG_READPROFILE=y +# CONFIG_RTCWAKE is not set +# CONFIG_SCRIPT is not set +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +# CONFIG_VOLUMEID is not set +# CONFIG_FEATURE_VOLUMEID_EXT is not set +# CONFIG_FEATURE_VOLUMEID_BTRFS is not set +# CONFIG_FEATURE_VOLUMEID_REISERFS is not set +# CONFIG_FEATURE_VOLUMEID_FAT is not set +# CONFIG_FEATURE_VOLUMEID_HFS is not set +# CONFIG_FEATURE_VOLUMEID_JFS is not set +# CONFIG_FEATURE_VOLUMEID_XFS is not set +# CONFIG_FEATURE_VOLUMEID_NTFS is not set +# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set +# CONFIG_FEATURE_VOLUMEID_UDF is not set +# CONFIG_FEATURE_VOLUMEID_LUKS is not set +# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set +# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set +# CONFIG_FEATURE_VOLUMEID_ROMFS is not set +# CONFIG_FEATURE_VOLUMEID_SYSV is not set +# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set +# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +# CONFIG_NANDDUMP is not set +# CONFIG_UBIATTACH is not set +# CONFIG_UBIDETACH is not set +# CONFIG_ADJTIMEX is not set +# CONFIG_BBCONFIG is not set +# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set +# CONFIG_BEEP is not set +CONFIG_FEATURE_BEEP_FREQ=0 +CONFIG_FEATURE_BEEP_LENGTH_MS=0 +# CONFIG_CHAT is not set +# CONFIG_FEATURE_CHAT_NOFAIL is not set +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set +# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set +# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set +# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set +# CONFIG_FEATURE_CHAT_CLR_ABORT is not set +CONFIG_CHRT=y +# CONFIG_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="/var/spool/cron" +CONFIG_CRONTAB=y +CONFIG_DC=y +CONFIG_FEATURE_DC_LIBM=y +# CONFIG_DEVFSD is not set +# CONFIG_DEVFSD_MODLOAD is not set +# CONFIG_DEVFSD_FG_NP is not set +# CONFIG_DEVFSD_VERBOSE is not set +# CONFIG_FEATURE_DEVFS is not set +# CONFIG_DEVMEM is not set +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +# CONFIG_FBSPLASH is not set +# CONFIG_FLASHCP is not set +# CONFIG_FLASH_LOCK is not set +# CONFIG_FLASH_UNLOCK is not set +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +# CONFIG_INOTIFYD is not set +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL is not set +# CONFIG_FEATURE_LAST_FANCY is not set +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS is not set +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set +# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set +# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set +# CONFIG_MAKEDEVS is not set +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +# CONFIG_FEATURE_MAKEDEVS_TABLE is not set +# CONFIG_MAN is not set +CONFIG_MICROCOM=y +# CONFIG_MOUNTPOINT is not set +# CONFIG_MT is not set +# CONFIG_RAIDAUTORUN is not set +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +# CONFIG_RX is not set +CONFIG_SETSID=y +CONFIG_STRINGS=y +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +# CONFIG_TIME is not set +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_NBDCLIENT is not set +CONFIG_NC=y +# CONFIG_NC_SERVER is not set +# CONFIG_NC_EXTRA is not set +# CONFIG_NC_110_COMPAT is not set +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +# CONFIG_ARP is not set +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +CONFIG_DNSD=y +# CONFIG_ETHER_WAKE is not set +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y +CONFIG_FTPGET=y +CONFIG_FTPPUT=y +CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y +CONFIG_HOSTNAME=y +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set +# CONFIG_FEATURE_HTTPD_SETUID is not set +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +CONFIG_FEATURE_HTTPD_AUTH_MD5=y +CONFIG_FEATURE_HTTPD_CGI=y +CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y +CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y +CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y +CONFIG_FEATURE_HTTPD_ERROR_PAGES=y +CONFIG_FEATURE_HTTPD_PROXY=y +CONFIG_FEATURE_HTTPD_GZIP=y +# CONFIG_IFCONFIG is not set +# CONFIG_FEATURE_IFCONFIG_STATUS is not set +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set +# CONFIG_FEATURE_IFCONFIG_HW is not set +# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +# CONFIG_IFUPDOWN is not set +CONFIG_IFUPDOWN_IFSTATE_PATH="" +# CONFIG_FEATURE_IFUPDOWN_IP is not set +# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set +# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set +# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set +# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set +# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# CONFIG_FEATURE_INETD_RPC is not set +# CONFIG_IP is not set +# CONFIG_FEATURE_IP_ADDRESS is not set +# CONFIG_FEATURE_IP_LINK is not set +# CONFIG_FEATURE_IP_ROUTE is not set +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_FEATURE_IP_RULE is not set +# CONFIG_FEATURE_IP_SHORT_FORMS is not set +# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set +# CONFIG_IPADDR is not set +# CONFIG_IPLINK is not set +# CONFIG_IPROUTE is not set +# CONFIG_IPTUNNEL is not set +# CONFIG_IPRULE is not set +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +# CONFIG_NETSTAT is not set +# CONFIG_FEATURE_NETSTAT_WIDE is not set +# CONFIG_FEATURE_NETSTAT_PRG is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +CONFIG_PSCAN=y +# CONFIG_ROUTE is not set +# CONFIG_SLATTACH is not set +# CONFIG_TCPSVD is not set +CONFIG_TELNET=y +CONFIG_FEATURE_TELNET_TTYPE=y +CONFIG_FEATURE_TELNET_AUTOLOGIN=y +CONFIG_TELNETD=y +CONFIG_FEATURE_TELNETD_STANDALONE=y +CONFIG_FEATURE_TELNETD_INETD_WAIT=y +CONFIG_TFTP=y +CONFIG_TFTPD=y + +# +# Common options for tftp/tftpd +# +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +CONFIG_FEATURE_TFTP_BLOCKSIZE=y +CONFIG_FEATURE_TFTP_PROGRESS_BAR=y +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +# CONFIG_TUNCTL is not set +# CONFIG_FEATURE_TUNCTL_UG is not set +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +CONFIG_DHCPD_LEASES_FILE="" +# CONFIG_UDHCPC is not set +# CONFIG_FEATURE_UDHCPC_ARPING is not set +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=0 +# CONFIG_FEATURE_UDHCP_RFC3397 is not set +CONFIG_UDHCPC_DEFAULT_SCRIPT="" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" +# CONFIG_UDPSVD is not set +# CONFIG_VCONFIG is not set +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_LONG_OPTIONS=y +CONFIG_FEATURE_WGET_TIMEOUT=y +# CONFIG_ZCIP is not set + +# +# Print Utilities +# +# CONFIG_LPD is not set +CONFIG_LPR=y +CONFIG_LPQ=y + +# +# Mail Utilities +# +# CONFIG_MAKEMIME is not set +CONFIG_FEATURE_MIME_CHARSET="" +# CONFIG_POPMAILDIR is not set +# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set +# CONFIG_REFORMIME is not set +# CONFIG_FEATURE_REFORMIME_COMPAT is not set +# CONFIG_SENDMAIL is not set + +# +# Process Utilities +# +CONFIG_IOSTAT=y +CONFIG_MPSTAT=y +CONFIG_PMAP=y +CONFIG_POWERTOP=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +# CONFIG_FUSER is not set +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +# CONFIG_NMETER is not set +CONFIG_PGREP=y +# CONFIG_PIDOF is not set +# CONFIG_FEATURE_PIDOF_SINGLE is not set +# CONFIG_FEATURE_PIDOF_OMIT is not set +CONFIG_PKILL=y +CONFIG_PS=y +CONFIG_FEATURE_PS_WIDE=y +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +# CONFIG_TOP is not set +# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set +# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set +# CONFIG_FEATURE_TOP_SMP_CPU is not set +# CONFIG_FEATURE_TOP_DECIMALS is not set +# CONFIG_FEATURE_TOP_SMP_PROCESS is not set +# CONFIG_FEATURE_TOPMEM is not set +CONFIG_FEATURE_SHOW_THREADS=y +# CONFIG_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +# CONFIG_RUNSV is not set +# CONFIG_RUNSVDIR is not set +# CONFIG_FEATURE_RUNSVDIR_LOG is not set +# CONFIG_SV is not set +CONFIG_SV_DEFAULT_SERVICE_DIR="" +# CONFIG_SVLOGD is not set +# CONFIG_CHPST is not set +# CONFIG_SETUIDGID is not set +# CONFIG_ENVUIDGID is not set +# CONFIG_ENVDIR is not set +# CONFIG_SOFTLIMIT is not set +# CONFIG_CHCON is not set +# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set +# CONFIG_GETENFORCE is not set +# CONFIG_GETSEBOOL is not set +# CONFIG_LOAD_POLICY is not set +# CONFIG_MATCHPATHCON is not set +# CONFIG_RESTORECON is not set +# CONFIG_RUNCON is not set +# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set +# CONFIG_SELINUXENABLED is not set +# CONFIG_SETENFORCE is not set +# CONFIG_SETFILES is not set +# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set +# CONFIG_SETSEBOOL is not set +# CONFIG_SESTATUS is not set + +# +# Shells +# +CONFIG_ASH=y +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_BUILTIN_ECHO is not set +# CONFIG_ASH_BUILTIN_PRINTF is not set +# CONFIG_ASH_BUILTIN_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +# CONFIG_CTTYHACK is not set +# CONFIG_HUSH is not set +# CONFIG_HUSH_BASH_COMPAT is not set +# CONFIG_HUSH_BRACE_EXPANSION is not set +# CONFIG_HUSH_HELP is not set +# CONFIG_HUSH_INTERACTIVE is not set +# CONFIG_HUSH_SAVEHISTORY is not set +# CONFIG_HUSH_JOB is not set +# CONFIG_HUSH_TICK is not set +# CONFIG_HUSH_IF is not set +# CONFIG_HUSH_LOOPS is not set +# CONFIG_HUSH_CASE is not set +# CONFIG_HUSH_FUNCTIONS is not set +# CONFIG_HUSH_LOCAL is not set +# CONFIG_HUSH_RANDOM_SUPPORT is not set +# CONFIG_HUSH_EXPORT_N is not set +# CONFIG_HUSH_MODE_X is not set +# CONFIG_MSH is not set +CONFIG_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +# CONFIG_FEATURE_BASH_IS_ASH is not set +# CONFIG_FEATURE_BASH_IS_HUSH is not set +CONFIG_FEATURE_BASH_IS_NONE=y +CONFIG_SH_MATH_SUPPORT=y +CONFIG_SH_MATH_SUPPORT_64=y +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +# CONFIG_FEATURE_SYSLOGD_DUP is not set +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +# CONFIG_KLOGD is not set +# CONFIG_FEATURE_KLOGD_KLOGCTL is not set +CONFIG_LOGGER=y diff --git a/console-tools/Config.src b/console-tools/Config.src new file mode 100644 index 000000000..c65704478 --- /dev/null +++ b/console-tools/Config.src @@ -0,0 +1,176 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Console Utilities" + +INSERT + +config CHVT + bool "chvt" + default y + select PLATFORM_LINUX + help + This program is used to change to another terminal. + Example: chvt 4 (change to terminal /dev/tty4) + +config FGCONSOLE + bool "fgconsole" + default y + select PLATFORM_LINUX + help + This program prints active (foreground) console number. + +config CLEAR + bool "clear" + default y + help + This program clears the terminal screen. + +config DEALLOCVT + bool "deallocvt" + default y + select PLATFORM_LINUX + help + This program deallocates unused virtual consoles. + +config DUMPKMAP + bool "dumpkmap" + default y + select PLATFORM_LINUX + help + This program dumps the kernel's keyboard translation table to + stdout, in binary format. You can then use loadkmap to load it. + +config KBD_MODE + bool "kbd_mode" + default y + select PLATFORM_LINUX + help + This program reports and sets keyboard mode. + +config LOADFONT + bool "loadfont" + default y + select PLATFORM_LINUX + help + This program loads a console font from standard input. + +config LOADKMAP + bool "loadkmap" + default y + select PLATFORM_LINUX + help + This program loads a keyboard translation table from + standard input. + +config OPENVT + bool "openvt" + default y + select PLATFORM_LINUX + help + This program is used to start a command on an unused + virtual terminal. + +config RESET + bool "reset" + default y + help + This program is used to reset the terminal screen, if it + gets messed up. + +config RESIZE + bool "resize" + default y + help + This program is used to (re)set the width and height of your current + terminal. + +config FEATURE_RESIZE_PRINT + bool "Print environment variables" + default y + depends on RESIZE + help + Prints the newly set size (number of columns and rows) of + the terminal. + E.g.: + COLUMNS=80;LINES=44;export COLUMNS LINES; + +config SETCONSOLE + bool "setconsole" + default y + select PLATFORM_LINUX + help + This program redirects the system console to another device, + like the current tty while logged in via telnet. + +config FEATURE_SETCONSOLE_LONG_OPTIONS + bool "Enable long options" + default y + depends on SETCONSOLE && LONG_OPTS + help + Support long options for the setconsole applet. + +config SETFONT + bool "setfont" + default y + select PLATFORM_LINUX + help + Allows to load console screen map. Useful for i18n. + +config FEATURE_SETFONT_TEXTUAL_MAP + bool "Support reading textual screen maps" + default y + depends on SETFONT + help + Support reading textual screen maps. + +config DEFAULT_SETFONT_DIR + string "Default directory for console-tools files" + default "" + depends on SETFONT + help + Directory to use if setfont's params are simple filenames + (not /path/to/file or ./file). Default is "" (no default directory). + +config SETKEYCODES + bool "setkeycodes" + default y + select PLATFORM_LINUX + help + This program loads entries into the kernel's scancode-to-keycode + map, allowing unusual keyboards to generate usable keycodes. + +config SETLOGCONS + bool "setlogcons" + default y + select PLATFORM_LINUX + help + This program redirects the output console of kernel messages. + +config SHOWKEY + bool "showkey" + default y + select PLATFORM_LINUX + help + Shows keys pressed. + +comment "Common options for loadfont and setfont" + depends on LOADFONT || SETFONT + +config FEATURE_LOADFONT_PSF2 + bool "Support for PSF2 console fonts" + default y + depends on LOADFONT || SETFONT + help + Support PSF2 console fonts. + +config FEATURE_LOADFONT_RAW + bool "Support for old (raw) console fonts" + default y + depends on LOADFONT || SETFONT + help + Support old (raw) console fonts. + +endmenu diff --git a/console-tools/Kbuild.src b/console-tools/Kbuild.src new file mode 100644 index 000000000..94de9ad9f --- /dev/null +++ b/console-tools/Kbuild.src @@ -0,0 +1,25 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT +lib-$(CONFIG_CHVT) += chvt.o +lib-$(CONFIG_FGCONSOLE) += fgconsole.o +lib-$(CONFIG_CLEAR) += clear.o +lib-$(CONFIG_DEALLOCVT) += deallocvt.o +lib-$(CONFIG_DUMPKMAP) += dumpkmap.o +lib-$(CONFIG_SETCONSOLE) += setconsole.o +lib-$(CONFIG_KBD_MODE) += kbd_mode.o +lib-$(CONFIG_LOADFONT) += loadfont.o +lib-$(CONFIG_LOADKMAP) += loadkmap.o +lib-$(CONFIG_OPENVT) += openvt.o +lib-$(CONFIG_RESET) += reset.o +lib-$(CONFIG_RESIZE) += resize.o +lib-$(CONFIG_SETFONT) += loadfont.o +lib-$(CONFIG_SETKEYCODES) += setkeycodes.o +lib-$(CONFIG_SETLOGCONS) += setlogcons.o +lib-$(CONFIG_SHOWKEY) += showkey.o diff --git a/console-tools/chvt.c b/console-tools/chvt.c new file mode 100644 index 000000000..b9c974f4a --- /dev/null +++ b/console-tools/chvt.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chvt implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define chvt_trivial_usage +//usage: "N" +//usage:#define chvt_full_usage "\n\n" +//usage: "Change the foreground virtual terminal to /dev/ttyN" + +#include "libbb.h" + +int chvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chvt_main(int argc UNUSED_PARAM, char **argv) +{ + int num = xatou_range(single_argv(argv), 1, 63); + console_make_active(get_console_fd_or_die(), num); + return EXIT_SUCCESS; +} diff --git a/console-tools/clear.c b/console-tools/clear.c new file mode 100644 index 000000000..ac22b787e --- /dev/null +++ b/console-tools/clear.c @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini clear implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define clear_trivial_usage +//usage: "" +//usage:#define clear_full_usage "\n\n" +//usage: "Clear screen" + +#include "libbb.h" + +int clear_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int clear_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + /* home; clear to the end of screen */ + return full_write1_str("\033[H""\033[J") != 6; +} diff --git a/console-tools/deallocvt.c b/console-tools/deallocvt.c new file mode 100644 index 000000000..b131c0a64 --- /dev/null +++ b/console-tools/deallocvt.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Disallocate virtual terminal(s) + * + * Copyright (C) 2003 by Tito Ragusa + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* no options, no getopt */ + +//usage:#define deallocvt_trivial_usage +//usage: "[N]" +//usage:#define deallocvt_full_usage "\n\n" +//usage: "Deallocate unused virtual terminal /dev/ttyN" + +#include "libbb.h" + +/* From */ +enum { VT_DISALLOCATE = 0x5608 }; /* free memory associated to vt */ + +int deallocvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int deallocvt_main(int argc UNUSED_PARAM, char **argv) +{ + /* num = 0 deallocate all unused consoles */ + int num = 0; + + if (argv[1]) { + if (argv[2]) + bb_show_usage(); + num = xatou_range(argv[1], 1, 63); + } + + /* double cast suppresses "cast to ptr from int of different size" */ + xioctl(get_console_fd_or_die(), VT_DISALLOCATE, (void *)(ptrdiff_t)num); + return EXIT_SUCCESS; +} diff --git a/console-tools/dumpkmap.c b/console-tools/dumpkmap.c new file mode 100644 index 000000000..6b923d2d4 --- /dev/null +++ b/console-tools/dumpkmap.c @@ -0,0 +1,82 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dumpkmap implementation for busybox + * + * Copyright (C) Arne Bernin + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + */ +/* no options, no getopt */ + +//usage:#define dumpkmap_trivial_usage +//usage: "> keymap" +//usage:#define dumpkmap_full_usage "\n\n" +//usage: "Print a binary keyboard translation table to stdout" +//usage: +//usage:#define dumpkmap_example_usage +//usage: "$ dumpkmap > keymap\n" + +#include "libbb.h" + +/* From */ +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short kb_value; +}; +#define KDGKBENT 0x4B46 /* gets one entry in translation table */ + +/* From */ +#define NR_KEYS 128 +#define MAX_NR_KEYMAPS 256 + +int dumpkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dumpkmap_main(int argc UNUSED_PARAM, char **argv) +{ + struct kbentry ke; + int i, j, fd; + RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS); + + /* When user accidentally runs "dumpkmap FILE" + * instead of "dumpkmap >FILE", we'd dump binary stuff to tty. + * Let's prevent it: */ + if (argv[1]) + bb_show_usage(); +/* bb_warn_ignoring_args(argv[1]);*/ + + fd = get_console_fd_or_die(); + + write(STDOUT_FILENO, "bkeymap", 7); + + /* Here we want to set everything to 0 except for indexes: + * [0-2] [4-6] [8-10] [12] */ + memset(flags, 0x00, MAX_NR_KEYMAPS); + memset(flags, 0x01, 13); + flags[3] = flags[7] = flags[11] = 0; + + /* dump flags */ + write(STDOUT_FILENO, flags, MAX_NR_KEYMAPS); + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + if (flags[i] == 1) { + for (j = 0; j < NR_KEYS; j++) { + ke.kb_index = j; + ke.kb_table = i; + if (!ioctl_or_perror(fd, KDGKBENT, &ke, + "ioctl failed with %s, %s, %p", + (char *)&ke.kb_index, + (char *)&ke.kb_table, + &ke.kb_value) + ) { + write(STDOUT_FILENO, (void*)&ke.kb_value, 2); + } + } + } + } + if (ENABLE_FEATURE_CLEAN_UP) { + close(fd); + RELEASE_CONFIG_BUFFER(flags); + } + return EXIT_SUCCESS; +} diff --git a/console-tools/fgconsole.c b/console-tools/fgconsole.c new file mode 100644 index 000000000..54355bee6 --- /dev/null +++ b/console-tools/fgconsole.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fgconsole implementation for busybox + * + * Copyright (C) 2010 by Grigory Batalov + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define fgconsole_trivial_usage +//usage: "" +//usage:#define fgconsole_full_usage "\n\n" +//usage: "Get active console" + +#include "libbb.h" + +/* From */ +struct vt_stat { + unsigned short v_active; /* active vt */ + unsigned short v_signal; /* signal to send */ + unsigned short v_state; /* vt bitmask */ +}; +enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ + +int fgconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fgconsole_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + struct vt_stat vtstat; + + vtstat.v_active = 0; + xioctl(get_console_fd_or_die(), VT_GETSTATE, &vtstat); + printf("%d\n", vtstat.v_active); + + return EXIT_SUCCESS; +} diff --git a/console-tools/kbd_mode.c b/console-tools/kbd_mode.c new file mode 100644 index 000000000..138536721 --- /dev/null +++ b/console-tools/kbd_mode.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini kbd_mode implementation for busybox + * + * Copyright (C) 2007 Loic Grenie + * written using Andries Brouwer 's kbd_mode from + * console-utils v0.2.3, licensed under GNU GPLv2 + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define kbd_mode_trivial_usage +//usage: "[-a|k|s|u] [-C TTY]" +//usage:#define kbd_mode_full_usage "\n\n" +//usage: "Report or set the keyboard mode\n" +//usage: "\n -a Default (ASCII)" +//usage: "\n -k Medium-raw (keyboard)" +//usage: "\n -s Raw (scancode)" +//usage: "\n -u Unicode (utf-8)" +//usage: "\n -C TTY Affect TTY instead of /dev/tty" + +#include "libbb.h" +#include + +int kbd_mode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int kbd_mode_main(int argc UNUSED_PARAM, char **argv) +{ + enum { + SCANCODE = (1 << 0), + ASCII = (1 << 1), + MEDIUMRAW = (1 << 2), + UNICODE = (1 << 3), + }; + int fd; + unsigned opt; + const char *tty_name = CURRENT_TTY; + + opt = getopt32(argv, "sakuC:", &tty_name); + fd = xopen_nonblocking(tty_name); + opt &= 0xf; /* clear -C bit, see (*) */ + + if (!opt) { /* print current setting */ + const char *mode = "unknown"; + int m; + + xioctl(fd, KDGKBMODE, &m); + if (m == K_RAW) + mode = "raw (scancode)"; + else if (m == K_XLATE) + mode = "default (ASCII)"; + else if (m == K_MEDIUMRAW) + mode = "mediumraw (keycode)"; + else if (m == K_UNICODE) + mode = "Unicode (UTF-8)"; + printf("The keyboard is in %s mode\n", mode); + } else { + /* here we depend on specific bits assigned to options (*) */ + opt = opt & UNICODE ? 3 : opt >> 1; + /* double cast prevents warnings about widening conversion */ + xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt); + } + + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + return EXIT_SUCCESS; +} diff --git a/console-tools/loadfont.c b/console-tools/loadfont.c new file mode 100644 index 000000000..9e887f256 --- /dev/null +++ b/console-tools/loadfont.c @@ -0,0 +1,492 @@ +/* vi: set sw=4 ts=4: */ +/* + * loadfont.c - Eugene Crosser & Andries Brouwer + * + * Version 0.96bb + * + * Loads the console font, and possibly the corresponding screen map(s). + * (Adapted for busybox by Matej Vela.) + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +//usage:#define loadfont_trivial_usage +//usage: "< font" +//usage:#define loadfont_full_usage "\n\n" +//usage: "Load a console font from stdin" +/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ +//usage: +//usage:#define loadfont_example_usage +//usage: "$ loadfont < /etc/i18n/fontname\n" +//usage: +//usage:#define setfont_trivial_usage +//usage: "FONT [-m MAPFILE] [-C TTY]" +//usage:#define setfont_full_usage "\n\n" +//usage: "Load a console font\n" +//usage: "\n -m MAPFILE Load console screen map" +//usage: "\n -C TTY Affect TTY instead of /dev/tty" +//usage: +//usage:#define setfont_example_usage +//usage: "$ setfont -m koi8-r /etc/i18n/fontname\n" + +#include "libbb.h" +#include + +#ifndef KDFONTOP +# define KDFONTOP 0x4B72 +struct console_font_op { + unsigned op; /* KD_FONT_OP_* */ + unsigned flags; /* KD_FONT_FLAG_* */ + unsigned width, height; + unsigned charcount; + unsigned char *data; /* font data with height fixed to 32 */ +}; +# define KD_FONT_OP_SET 0 /* Set font */ +# define KD_FONT_OP_GET 1 /* Get font */ +# define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */ +# define KD_FONT_OP_COPY 3 /* Copy from another console */ +# define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */ +# define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */ + /* (Used internally for PIO_FONT support) */ +#endif /* KDFONTOP */ + + +enum { + PSF1_MAGIC0 = 0x36, + PSF1_MAGIC1 = 0x04, + PSF1_MODE512 = 0x01, + PSF1_MODEHASTAB = 0x02, + PSF1_MODEHASSEQ = 0x04, + PSF1_MAXMODE = 0x05, + PSF1_STARTSEQ = 0xfffe, + PSF1_SEPARATOR = 0xffff, +}; + +struct psf1_header { + unsigned char magic[2]; /* Magic number */ + unsigned char mode; /* PSF font mode */ + unsigned char charsize; /* Character size */ +}; + +#define psf1h(x) ((struct psf1_header*)(x)) + +#define PSF1_MAGIC_OK(x) ( \ + (x)->magic[0] == PSF1_MAGIC0 \ + && (x)->magic[1] == PSF1_MAGIC1 \ +) + +#if ENABLE_FEATURE_LOADFONT_PSF2 +enum { + PSF2_MAGIC0 = 0x72, + PSF2_MAGIC1 = 0xb5, + PSF2_MAGIC2 = 0x4a, + PSF2_MAGIC3 = 0x86, + PSF2_HAS_UNICODE_TABLE = 0x01, + PSF2_MAXVERSION = 0, + PSF2_STARTSEQ = 0xfe, + PSF2_SEPARATOR = 0xff +}; + +struct psf2_header { + unsigned char magic[4]; + unsigned int version; + unsigned int headersize; /* offset of bitmaps in file */ + unsigned int flags; + unsigned int length; /* number of glyphs */ + unsigned int charsize; /* number of bytes for each character */ + unsigned int height; /* max dimensions of glyphs */ + unsigned int width; /* charsize = height * ((width + 7) / 8) */ +}; + +#define psf2h(x) ((struct psf2_header*)(x)) + +#define PSF2_MAGIC_OK(x) ( \ + (x)->magic[0] == PSF2_MAGIC0 \ + && (x)->magic[1] == PSF2_MAGIC1 \ + && (x)->magic[2] == PSF2_MAGIC2 \ + && (x)->magic[3] == PSF2_MAGIC3 \ +) +#endif /* ENABLE_FEATURE_LOADFONT_PSF2 */ + + +static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize) +{ + unsigned char *buf; + int charwidth = 32 * ((width+7)/8); + int i; + + if (height < 1 || height > 32 || width < 1 || width > 32) + bb_error_msg_and_die("bad character size %dx%d", height, width); + + buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize)); + for (i = 0; i < fontsize; i++) + memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize); + + { /* KDFONTOP */ + struct console_font_op cfo; + cfo.op = KD_FONT_OP_SET; + cfo.flags = 0; + cfo.width = width; + cfo.height = height; + cfo.charcount = fontsize; + cfo.data = buf; + xioctl(fd, KDFONTOP, &cfo); + } + + free(buf); +} + +/* + * Format of the Unicode information: + * + * For each font position ** + * where is a 2-byte little endian Unicode value (PSF1) + * or an UTF-8 coded value (PSF2), + * = *, = psf1 ? 0xFFFE : 0xFE, + * = psf1 ? 0xFFFF : 0xFF. + * and * denotes zero or more occurrences of the preceding item. + * + * Semantics: + * The leading * part gives Unicode symbols that are all + * represented by this font position. The following sequences + * are sequences of Unicode symbols - probably a symbol + * together with combining accents - also represented by + * this font position. + * + * Example: + * At the font position for a capital A-ring glyph, we + * may have: + * 00C5,212B,FFFE,0041,030A,FFFF + * Some font positions may be described by sequences only, + * namely when there is no precomposed Unicode value for the glyph. + */ +#if !ENABLE_FEATURE_LOADFONT_PSF2 +#define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \ + do_loadtable(fd, inbuf, tailsz, fontsize) +#endif +static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2) +{ +#if !ENABLE_FEATURE_LOADFONT_PSF2 +/* gcc 4.3.1 code size: */ +# define psf2 0 /* +0 bytes */ +// const int psf2 = 0; /* +8 bytes */ +// enum { psf2 = 0 }; /* +13 bytes */ +#endif + struct unimapinit advice; + struct unimapdesc ud; + struct unipair *up; + int ct = 0, maxct; + int glyph; + uint16_t unicode; + + maxct = tailsz; /* more than enough */ + up = xmalloc(maxct * sizeof(*up)); + + for (glyph = 0; glyph < fontsize; glyph++) { + while (tailsz > 0) { + if (!psf2) { /* PSF1 */ + unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0]; + tailsz -= 2; + inbuf += 2; + if (unicode == PSF1_SEPARATOR) + break; + } else { /* PSF2 */ +#if ENABLE_FEATURE_LOADFONT_PSF2 + --tailsz; + unicode = *inbuf++; + if (unicode == PSF2_SEPARATOR) { + break; + } else if (unicode == PSF2_STARTSEQ) { + bb_error_msg_and_die("unicode sequences not implemented"); + } else if (unicode >= 0xC0) { + if (unicode >= 0xFC) + unicode &= 0x01, maxct = 5; + else if (unicode >= 0xF8) + unicode &= 0x03, maxct = 4; + else if (unicode >= 0xF0) + unicode &= 0x07, maxct = 3; + else if (unicode >= 0xE0) + unicode &= 0x0F, maxct = 2; + else + unicode &= 0x1F, maxct = 1; + do { + if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF) + bb_error_msg_and_die("illegal UTF-8 character"); + --tailsz; + unicode = (unicode << 6) + (*inbuf++ & 0x3F); + } while (--maxct > 0); + } else if (unicode >= 0x80) { + bb_error_msg_and_die("illegal UTF-8 character"); + } +#else + return; +#endif + } + up[ct].unicode = unicode; + up[ct].fontpos = glyph; + ct++; + } + } + + /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP + this printf did not work on many kernels */ + + advice.advised_hashsize = 0; + advice.advised_hashstep = 0; + advice.advised_hashlevel = 0; + xioctl(fd, PIO_UNIMAPCLR, &advice); + ud.entry_ct = ct; + ud.entries = up; + xioctl(fd, PIO_UNIMAP, &ud); +#undef psf2 +} + +static void do_load(int fd, unsigned char *buffer, size_t len) +{ + int height; + int width = 8; + int charsize; + int fontsize = 256; + int has_table = 0; + unsigned char *font = buffer; + unsigned char *table; + + if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) { + if (psf1h(buffer)->mode > PSF1_MAXMODE) + bb_error_msg_and_die("unsupported psf file mode"); + if (psf1h(buffer)->mode & PSF1_MODE512) + fontsize = 512; + if (psf1h(buffer)->mode & PSF1_MODEHASTAB) + has_table = 1; + height = charsize = psf1h(buffer)->charsize; + font += sizeof(struct psf1_header); + } else +#if ENABLE_FEATURE_LOADFONT_PSF2 + if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) { + if (psf2h(buffer)->version > PSF2_MAXVERSION) + bb_error_msg_and_die("unsupported psf file version"); + fontsize = psf2h(buffer)->length; + if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE) + has_table = 2; + charsize = psf2h(buffer)->charsize; + height = psf2h(buffer)->height; + width = psf2h(buffer)->width; + font += psf2h(buffer)->headersize; + } else +#endif +#if ENABLE_FEATURE_LOADFONT_RAW + if (len == 9780) { /* file with three code pages? */ + charsize = height = 16; + font += 40; + } else if ((len & 0377) == 0) { /* bare font */ + charsize = height = len / 256; + } else +#endif + { + bb_error_msg_and_die("input file: bad length or unsupported font type"); + } + +#if !defined(PIO_FONTX) || defined(__sparc__) + if (fontsize != 256) + bb_error_msg_and_die("only fontsize 256 supported"); +#endif + + table = font + fontsize * charsize; + buffer += len; + + if (table > buffer || (!has_table && table != buffer)) + bb_error_msg_and_die("input file: bad length"); + + do_loadfont(fd, font, height, width, charsize, fontsize); + + if (has_table) + do_loadtable(fd, table, buffer - table, fontsize, has_table - 1); +} + + +#if ENABLE_LOADFONT +int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int loadfont_main(int argc UNUSED_PARAM, char **argv) +{ + size_t len; + unsigned char *buffer; + + // no arguments allowed! + opt_complementary = "=0"; + getopt32(argv, ""); + + /* + * We used to look at the length of the input file + * with stat(); now that we accept compressed files, + * just read the entire file. + */ + len = 32*1024; // can't be larger + buffer = xmalloc_read(STDIN_FILENO, &len); + // xmalloc_open_zipped_read_close(filename, &len); + if (!buffer) + bb_perror_msg_and_die("error reading input font"); + do_load(get_console_fd_or_die(), buffer, len); + + return EXIT_SUCCESS; +} +#endif + +#if ENABLE_SETFONT + +/* +kbd-1.12: + +setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig] +[-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console] +[-hNN] [-v] [-V] + +-h NN Override font height +-o file + Save previous font in file +-O file + Save previous font and Unicode map in file +-om file + Store console map in file +-ou file + Save previous Unicode map in file +-m file + Load console map or Unicode console map from file +-u file + Load Unicode table describing the font from file + Example: + # cp866 + 0x00-0x7f idem + # + 0x80 U+0410 # CYRILLIC CAPITAL LETTER A + 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE + 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE +-C console + Set the font for the indicated console +-v Verbose +-V Version +*/ + +#if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP +static int ctoi(char *s) +{ + if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0') + return s[1]; + // U+ means 0x + if (s[0] == 'U' && s[1] == '+') { + s[0] = '0'; + s[1] = 'x'; + } + if (!isdigit(s[0])) + return -1; + return xstrtoul(s, 0); +} +#endif + +int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setfont_main(int argc UNUSED_PARAM, char **argv) +{ + size_t len; + unsigned opts; + int fd; + unsigned char *buffer; + char *mapfilename; + const char *tty_name = CURRENT_TTY; + + opt_complementary = "=1"; + opts = getopt32(argv, "m:C:", &mapfilename, &tty_name); + argv += optind; + + fd = xopen_nonblocking(tty_name); + + if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not "" + if (*argv[0] != '/') { + // goto default fonts location. don't die if doesn't exist + chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts"); + } + } + // load font + len = 32*1024; // can't be larger + buffer = xmalloc_open_zipped_read_close(*argv, &len); + if (!buffer) + bb_simple_perror_msg_and_die(*argv); + do_load(fd, buffer, len); + + // load the screen map, if any + if (opts & 1) { // -m + unsigned mode = PIO_SCRNMAP; + void *map; + + if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not "" + if (mapfilename[0] != '/') { + // goto default keymaps location + chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans"); + } + } + // fetch keymap + map = xmalloc_open_zipped_read_close(mapfilename, &len); + if (!map) + bb_simple_perror_msg_and_die(mapfilename); + // file size is 256 or 512 bytes? -> assume binary map + if (len == E_TABSZ || len == 2*E_TABSZ) { + if (len == 2*E_TABSZ) + mode = PIO_UNISCRNMAP; + } +#if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP + // assume textual Unicode console maps: + // 0x00 U+0000 # NULL (NUL) + // 0x01 U+0001 # START OF HEADING (SOH) + // 0x02 U+0002 # START OF TEXT (STX) + // 0x03 U+0003 # END OF TEXT (ETX) + else { + int i; + char *token[2]; + parser_t *parser; + + if (ENABLE_FEATURE_CLEAN_UP) + free(map); + map = xmalloc(E_TABSZ * sizeof(unsigned short)); + +#define unicodes ((unsigned short *)map) + // fill vanilla map + for (i = 0; i < E_TABSZ; i++) + unicodes[i] = 0xf000 + i; + + parser = config_open(mapfilename); + while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) { + // parse code/value pair + int a = ctoi(token[0]); + int b = ctoi(token[1]); + if (a < 0 || a >= E_TABSZ + || b < 0 || b > 65535 + ) { + bb_error_msg_and_die("map format"); + } + // patch map + unicodes[a] = b; + // unicode character is met? + if (b > 255) + mode = PIO_UNISCRNMAP; + } + if (ENABLE_FEATURE_CLEAN_UP) + config_close(parser); + + if (mode != PIO_UNISCRNMAP) { +#define asciis ((unsigned char *)map) + for (i = 0; i < E_TABSZ; i++) + asciis[i] = unicodes[i]; +#undef asciis + } +#undef unicodes + } +#endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP + + // do set screen map + xioctl(fd, mode, map); + + if (ENABLE_FEATURE_CLEAN_UP) + free(map); + } + + return EXIT_SUCCESS; +} +#endif diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c new file mode 100644 index 000000000..bcffe16b1 --- /dev/null +++ b/console-tools/loadkmap.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini loadkmap implementation for busybox + * + * Copyright (C) 1998 Enrique Zanardi + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define loadkmap_trivial_usage +//usage: "< keymap" +//usage:#define loadkmap_full_usage "\n\n" +//usage: "Load a binary keyboard translation table from stdin\n" +/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ +//usage: +//usage:#define loadkmap_example_usage +//usage: "$ loadkmap < /etc/i18n/lang-keymap\n" + +#include "libbb.h" + +#define BINARY_KEYMAP_MAGIC "bkeymap" + +/* From */ +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short kb_value; +}; +/* sets one entry in translation table */ +#define KDSKBENT 0x4B47 + +/* From */ +#define NR_KEYS 128 +#define MAX_NR_KEYMAPS 256 + +int loadkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int loadkmap_main(int argc UNUSED_PARAM, char **argv) +{ + struct kbentry ke; + int i, j, fd; + uint16_t ibuff[NR_KEYS]; +/* const char *tty_name = CURRENT_TTY; */ + RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS); + + /* When user accidentally runs "loadkmap FILE" + * instead of "loadkmap + * hacked by Tito + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define openvt_trivial_usage +//usage: "[-c N] [-sw] [PROG ARGS]" +//usage:#define openvt_full_usage "\n\n" +//usage: "Start PROG on a new virtual terminal\n" +//usage: "\n -c N Use specified VT" +//usage: "\n -s Switch to the VT" +/* //usage: "\n -l Run PROG as login shell (by prepending '-')" */ +//usage: "\n -w Wait for PROG to exit" +//usage: +//usage:#define openvt_example_usage +//usage: "openvt 2 /bin/ash\n" + +#include +#include "libbb.h" + +/* "Standard" openvt's man page (we do not support all of this): + +openvt [-c NUM] [-fsulv] [--] [command [args]] + +Find the first available VT, and run command on it. Stdio is directed +to that VT. If no command is specified then $SHELL is used. + +-c NUM + Use the given VT number, not the first free one. +-f + Force opening a VT: don't try to check if VT is already in use. +-s + Switch to the new VT when starting the command. + The VT of the new command will be made the new current VT. +-u + Figure out the owner of the current VT, and run login as that user. + Suitable to be called by init. Shouldn't be used with -c or -l. +-l + Make the command a login shell: a "-" is prepended to the argv[0] + when command is executed. +-v + Verbose. +-w + Wait for command to complete. If -w and -s are used together, + switch back to the controlling terminal when the command completes. + +bbox: +-u: not implemented +-f: always in effect +-l: not implemented, ignored +-v: ignored +-ws: does NOT switch back +*/ + +/* Helper: does this fd understand VT_xxx? */ +static int not_vt_fd(int fd) +{ + struct vt_stat vtstat; + return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */ +} + +/* Helper: get a fd suitable for VT_xxx */ +static int get_vt_fd(void) +{ + int fd; + + /* Do we, by chance, already have it? */ + for (fd = 0; fd < 3; fd++) + if (!not_vt_fd(fd)) + return fd; + fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK); + if (fd >= 0 && !not_vt_fd(fd)) + return fd; + bb_error_msg_and_die("can't find open VT"); +} + +static int find_free_vtno(void) +{ + int vtno; + int fd = get_vt_fd(); + + errno = 0; + /*xfunc_error_retval = 3; - do we need compat? */ + if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0) + bb_perror_msg_and_die("can't find open VT"); +// Not really needed, grep for DAEMON_ONLY_SANITIZE +// if (fd > 2) +// close(fd); + return vtno; +} + +/* vfork scares gcc, it generates bigger code. + * Keep it away from main program. + * TODO: move to libbb; or adapt existing libbb's spawn(). + */ +static NOINLINE void vfork_child(char **argv) +{ + if (vfork() == 0) { + /* CHILD */ + /* Try to make this VT our controlling tty */ + setsid(); /* lose old ctty */ + ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */); + //bb_error_msg("our sid %d", getsid(0)); + //bb_error_msg("our pgrp %d", getpgrp()); + //bb_error_msg("VT's sid %d", tcgetsid(0)); + //bb_error_msg("VT's pgrp %d", tcgetpgrp(0)); + BB_EXECVP_or_die(argv); + } +} + +int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int openvt_main(int argc UNUSED_PARAM, char **argv) +{ + char vtname[sizeof(VC_FORMAT) + sizeof(int)*3]; + struct vt_stat vtstat; + char *str_c; + int vtno; + int flags; + enum { + OPT_c = (1 << 0), + OPT_w = (1 << 1), + OPT_s = (1 << 2), + OPT_l = (1 << 3), + OPT_f = (1 << 4), + OPT_v = (1 << 5), + }; + + /* "+" - stop on first non-option */ + flags = getopt32(argv, "+c:wslfv", &str_c); + argv += optind; + + if (flags & OPT_c) { + /* Check for illegal vt number: < 1 or > 63 */ + vtno = xatou_range(str_c, 1, 63); + } else { + vtno = find_free_vtno(); + } + + /* Grab new VT */ + sprintf(vtname, VC_FORMAT, vtno); + /* (Try to) clean up stray open fds above fd 2 */ + bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL); + close(STDIN_FILENO); + /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ + xopen(vtname, O_RDWR); + xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat); + + if (flags & OPT_s) { + console_make_active(STDIN_FILENO, vtno); + } + + if (!argv[0]) { + argv--; + argv[0] = (char *) get_shell_name(); + /*argv[1] = NULL; - already is */ + } + + xdup2(STDIN_FILENO, STDOUT_FILENO); + xdup2(STDIN_FILENO, STDERR_FILENO); + +#ifdef BLOAT + { + /* Handle -l (login shell) option */ + const char *prog = argv[0]; + if (flags & OPT_l) + argv[0] = xasprintf("-%s", argv[0]); + } +#endif + + vfork_child(argv); + if (flags & OPT_w) { + /* We have only one child, wait for it */ + safe_waitpid(-1, NULL, 0); /* loops on EINTR */ + if (flags & OPT_s) { + console_make_active(STDIN_FILENO, vtstat.v_active); + // Compat: even with -c N (try to) disallocate: + // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5 + // openvt: could not deallocate console 9 + xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno); + } + } + return EXIT_SUCCESS; +} diff --git a/console-tools/reset.c b/console-tools/reset.c new file mode 100644 index 000000000..65940bdec --- /dev/null +++ b/console-tools/reset.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reset implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * Written by Erik Andersen and Kent Robotti + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BTW, which "standard" package has this utility? It doesn't seem + * to be ncurses, coreutils, console-tools... then what? */ + +//usage:#define reset_trivial_usage +//usage: "" +//usage:#define reset_full_usage "\n\n" +//usage: "Reset the screen" + +#include "libbb.h" + +#define ESC "\033" + +#if ENABLE_STTY +int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +#endif + +int reset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + static const char *const args[] = { + "stty", "sane", NULL + }; + + /* no options, no getopt */ + + if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) { + /* See 'man 4 console_codes' for details: + * "ESC c" -- Reset + * "ESC ( B" -- Select G0 Character Set (B = US) + * "ESC [ 0 m" -- Reset all display attributes + * "ESC [ J" -- Erase to the end of screen + * "ESC [ ? 25 h" -- Make cursor visible + */ + printf(ESC"c" ESC"(B" ESC"[0m" ESC"[J" ESC"[?25h"); + /* http://bugs.busybox.net/view.php?id=1414: + * people want it to reset echo etc: */ +#if ENABLE_STTY + return stty_main(2, (char**)args); +#else + execvp("stty", (char**)args); +#endif + } + return EXIT_SUCCESS; +} diff --git a/console-tools/resize.c b/console-tools/resize.c new file mode 100644 index 000000000..4b0d63a03 --- /dev/null +++ b/console-tools/resize.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * resize - set terminal width and height. + * + * Copyright 2006 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* no options, no getopt */ + +//usage:#define resize_trivial_usage +//usage: "" +//usage:#define resize_full_usage "\n\n" +//usage: "Resize the screen" + +#include "libbb.h" + +#define ESC "\033" + +#define old_termios_p ((struct termios*)&bb_common_bufsiz1) + +static void +onintr(int sig UNUSED_PARAM) +{ + tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p); + _exit(EXIT_FAILURE); +} + +int resize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + struct termios new; + struct winsize w = { 0, 0, 0, 0 }; + int ret; + + /* We use _stderr_ in order to make resize usable + * in shell backticks (those redirect stdout away from tty). + * NB: other versions of resize open "/dev/tty" + * and operate on it - should we do the same? + */ + + tcgetattr(STDERR_FILENO, old_termios_p); /* fiddle echo */ + memcpy(&new, old_termios_p, sizeof(new)); + new.c_cflag |= (CLOCAL | CREAD); + new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + bb_signals(0 + + (1 << SIGINT) + + (1 << SIGQUIT) + + (1 << SIGTERM) + + (1 << SIGALRM) + , onintr); + tcsetattr(STDERR_FILENO, TCSANOW, &new); + + /* save_cursor_pos 7 + * scroll_whole_screen [r + * put_cursor_waaaay_off [$x;$yH + * get_cursor_pos [6n + * restore_cursor_pos 8 + */ + fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n"); + alarm(3); /* Just in case terminal won't answer */ +//BUG: death by signal won't restore termios + scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col); + fprintf(stderr, ESC"8"); + + /* BTW, other versions of resize recalculate w.ws_xpixel, ws.ws_ypixel + * by calculating character cell HxW from old values + * (gotten via TIOCGWINSZ) and recomputing *pixel values */ + ret = ioctl(STDERR_FILENO, TIOCSWINSZ, &w); + + tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p); + + if (ENABLE_FEATURE_RESIZE_PRINT) + printf("COLUMNS=%d;LINES=%d;export COLUMNS LINES;\n", + w.ws_col, w.ws_row); + + return ret; +} diff --git a/console-tools/setconsole.c b/console-tools/setconsole.c new file mode 100644 index 000000000..c0051dcc8 --- /dev/null +++ b/console-tools/setconsole.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * setconsole.c - redirect system console output + * + * Copyright (C) 2004,2005 Enrik Berkhan + * Copyright (C) 2008 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define setconsole_trivial_usage +//usage: "[-r" IF_FEATURE_SETCONSOLE_LONG_OPTIONS("|--reset") "] [DEVICE]" +//usage:#define setconsole_full_usage "\n\n" +//usage: "Redirect system console output to DEVICE (default: /dev/tty)\n" +//usage: "\n -r Reset output to /dev/console" + +#include "libbb.h" + +int setconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setconsole_main(int argc UNUSED_PARAM, char **argv) +{ + const char *device = CURRENT_TTY; + bool reset; + +#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS + static const char setconsole_longopts[] ALIGN1 = + "reset\0" No_argument "r" + ; + applet_long_options = setconsole_longopts; +#endif + /* at most one non-option argument */ + opt_complementary = "?1"; + reset = getopt32(argv, "r"); + + argv += 1 + reset; + if (*argv) { + device = *argv; + } else { + if (reset) + device = DEV_CONSOLE; + } + + xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL); + return EXIT_SUCCESS; +} diff --git a/console-tools/setkeycodes.c b/console-tools/setkeycodes.c new file mode 100644 index 000000000..a6a7c2374 --- /dev/null +++ b/console-tools/setkeycodes.c @@ -0,0 +1,59 @@ +/* vi: set sw=4 ts=4: */ +/* + * setkeycodes + * + * Copyright (C) 1994-1998 Andries E. Brouwer + * + * Adjusted for BusyBox by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define setkeycodes_trivial_usage +//usage: "SCANCODE KEYCODE..." +//usage:#define setkeycodes_full_usage "\n\n" +//usage: "Set entries into the kernel's scancode-to-keycode map,\n" +//usage: "allowing unusual keyboards to generate usable keycodes.\n\n" +//usage: "SCANCODE may be either xx or e0xx (hexadecimal),\n" +//usage: "and KEYCODE is given in decimal." +//usage: +//usage:#define setkeycodes_example_usage +//usage: "$ setkeycodes e030 127\n" + +#include "libbb.h" + +/* From */ +struct kbkeycode { + unsigned scancode, keycode; +}; +enum { + KDSETKEYCODE = 0x4B4D /* write kernel keycode table entry */ +}; + +int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setkeycodes_main(int argc, char **argv) +{ + int fd; + struct kbkeycode a; + + if (!(argc & 1) /* if even */ || argc < 2) { + bb_show_usage(); + } + + fd = get_console_fd_or_die(); + + while (argv[1]) { + int sc = xstrtoul_range(argv[1], 16, 0, 0xe07f); + if (sc >= 0xe000) { + sc -= 0xe000; + sc += 0x0080; + } + a.scancode = sc; + a.keycode = xatou_range(argv[2], 0, 255); + ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a, + "can't set SCANCODE %x to KEYCODE %d", + sc, a.keycode); + argv += 2; + } + return EXIT_SUCCESS; +} diff --git a/console-tools/setlogcons.c b/console-tools/setlogcons.c new file mode 100644 index 000000000..83a895407 --- /dev/null +++ b/console-tools/setlogcons.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * setlogcons: Send kernel messages to the current console or to console N + * + * Copyright (C) 2006 by Jan Kiszka + * + * Based on setlogcons (kbd-1.12) by Andries E. Brouwer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define setlogcons_trivial_usage +//usage: "N" +//usage:#define setlogcons_full_usage "\n\n" +//usage: "Redirect the kernel output to console N (0 for current)" + +#include "libbb.h" + +int setlogcons_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setlogcons_main(int argc UNUSED_PARAM, char **argv) +{ + struct { + char fn; + char subarg; + } arg = { 11, /* redirect kernel messages */ + 0 /* to specified console (current as default) */ + }; + + if (argv[1]) + arg.subarg = xatou_range(argv[1], 0, 63); + + xioctl(xopen(VC_1, O_RDONLY), TIOCLINUX, &arg); + + return EXIT_SUCCESS; +} diff --git a/console-tools/showkey.c b/console-tools/showkey.c new file mode 100644 index 000000000..69b785ec6 --- /dev/null +++ b/console-tools/showkey.c @@ -0,0 +1,150 @@ +/* vi: set sw=4 ts=4: */ +/* + * shows keys pressed. inspired by kbd package + * + * Copyright (C) 2008 by Vladimir Dronnikov + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +//usage:#define showkey_trivial_usage +//usage: "[-a | -k | -s]" +//usage:#define showkey_full_usage "\n\n" +//usage: "Show keys pressed\n" +//usage: "\n -a Display decimal/octal/hex values of the keys" +//usage: "\n -k Display interpreted keycodes (default)" +//usage: "\n -s Display raw scan-codes" + +#include "libbb.h" +#include + + +struct globals { + int kbmode; + struct termios tio, tio0; +}; +#define G (*ptr_to_globals) +#define kbmode (G.kbmode) +#define tio (G.tio) +#define tio0 (G.tio0) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ +} while (0) + + +// set raw tty mode +// also used by microcom +// libbb candidates? +static void xget1(struct termios *t, struct termios *oldt) +{ + tcgetattr(STDIN_FILENO, oldt); + *t = *oldt; + cfmakeraw(t); +} + +static void xset1(struct termios *t) +{ + int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, t); + if (ret) { + bb_perror_msg("can't tcsetattr for stdin"); + } +} + +int showkey_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int showkey_main(int argc UNUSED_PARAM, char **argv) +{ + enum { + OPT_a = (1<<0), // display the decimal/octal/hex values of the keys + OPT_k = (1<<1), // display only the interpreted keycodes (default) + OPT_s = (1<<2), // display only the raw scan-codes + }; + + INIT_G(); + + // FIXME: aks are all mutually exclusive + getopt32(argv, "aks"); + + // prepare for raw mode + xget1(&tio, &tio0); + // put stdin in raw mode + xset1(&tio); + +#define press_keys "Press any keys, program terminates %s:\r\n\n" + + if (option_mask32 & OPT_a) { + // just read stdin char by char + unsigned char c; + + printf(press_keys, "on EOF (ctrl-D)"); + + // read and show byte values + while (1 == read(STDIN_FILENO, &c, 1)) { + printf("%3u 0%03o 0x%02x\r\n", c, c, c); + if (04 /*CTRL-D*/ == c) + break; + } + + } else { + // we assume a PC keyboard + xioctl(STDIN_FILENO, KDGKBMODE, &kbmode); + printf("Keyboard mode was %s.\r\n\n", + kbmode == K_RAW ? "RAW" : + (kbmode == K_XLATE ? "XLATE" : + (kbmode == K_MEDIUMRAW ? "MEDIUMRAW" : + (kbmode == K_UNICODE ? "UNICODE" : "UNKNOWN"))) + ); + + // set raw keyboard mode + xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); + + // we should exit on any signal; signals should interrupt read + bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); + + // inform user that program ends after time of inactivity + printf(press_keys, "10s after last keypress"); + + // read and show scancodes + while (!bb_got_signal) { + char buf[18]; + int i, n; + + // setup 10s watchdog + alarm(10); + + // read scancodes + n = read(STDIN_FILENO, buf, sizeof(buf)); + i = 0; + while (i < n) { + if (option_mask32 & OPT_s) { + // show raw scancodes + printf("0x%02x ", buf[i++]); + } else { + // show interpreted scancodes (default) + char c = buf[i]; + int kc; + if (i+2 < n + && (c & 0x7f) == 0 + && (buf[i+1] & 0x80) != 0 + && (buf[i+2] & 0x80) != 0 + ) { + kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f); + i += 3; + } else { + kc = (c & 0x7f); + i++; + } + printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press"); + } + } + puts("\r"); + } + + // restore keyboard mode + xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode); + } + + // restore console settings + xset1(&tio0); + + return EXIT_SUCCESS; +} diff --git a/coreutils/Config.src b/coreutils/Config.src new file mode 100644 index 000000000..a28449b11 --- /dev/null +++ b/coreutils/Config.src @@ -0,0 +1,782 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Coreutils" + +INSERT + +config CAL + bool "cal" + default y + help + cal is used to display a monthly calender. + +config CATV + bool "catv" + default y + help + Display nonprinting characters as escape sequences (like some + implementations' cat -v option). + +config CHGRP + bool "chgrp" + default y + help + chgrp is used to change the group ownership of files. + +config CHMOD + bool "chmod" + default y + help + chmod is used to change the access permission of files. + +config CHOWN + bool "chown" + default y + help + chown is used to change the user and/or group ownership + of files. + +config FEATURE_CHOWN_LONG_OPTIONS + bool "Enable long options" + default y + depends on CHOWN && LONG_OPTS + help + Enable use of long options + +config CHROOT + bool "chroot" + default y + help + chroot is used to change the root directory and run a command. + The default command is `/bin/sh'. + +config CKSUM + bool "cksum" + default y + help + cksum is used to calculate the CRC32 checksum of a file. + +config COMM + bool "comm" + default y + help + comm is used to compare two files line by line and return + a three-column output. + +config CP + bool "cp" + default y + help + cp is used to copy files and directories. + +config FEATURE_CP_LONG_OPTIONS + bool "Enable long options for cp" + default y + depends on CP && LONG_OPTS + help + Enable long options for cp. + Also add support for --parents option. + +config CUT + bool "cut" + default y + help + cut is used to print selected parts of lines from + each file to stdout. + +config DD + bool "dd" + default y + help + dd copies a file (from standard input to standard output, + by default) using specific input and output blocksizes, + while optionally performing conversions on it. + +config FEATURE_DD_SIGNAL_HANDLING + bool "Enable DD signal handling for status reporting" + default y + depends on DD + help + Sending a SIGUSR1 signal to a running `dd' process makes it + print to standard error the number of records read and written + so far, then to resume copying. + + $ dd if=/dev/zero of=/dev/null& + $ pid=$! kill -USR1 $pid; sleep 1; kill $pid + 10899206+0 records in + 10899206+0 records out + +config FEATURE_DD_THIRD_STATUS_LINE + bool "Enable the third status line upon signal" + default y + depends on DD && FEATURE_DD_SIGNAL_HANDLING + help + Displays a coreutils-like third status line with transferred bytes, + elapsed time and speed. + +config FEATURE_DD_IBS_OBS + bool "Enable ibs, obs and conv options" + default y + depends on DD + help + Enables support for writing a certain number of bytes in and out, + at a time, and performing conversions on the data stream. + +config DF + bool "df" + default y + help + df reports the amount of disk space used and available + on filesystems. + +config FEATURE_DF_FANCY + bool "Enable -a, -i, -B" + default y + depends on DF + help + This option enables -a, -i and -B. + + -a Show all filesystems + -i Inodes + -B Blocksize + +config DIRNAME + bool "dirname" + default y + help + dirname is used to strip a non-directory suffix from + a file name. + +config DOS2UNIX + bool "dos2unix/unix2dos" + default y + help + dos2unix is used to convert a text file from DOS format to + UNIX format, and vice versa. + +config UNIX2DOS + bool + default y + depends on DOS2UNIX + help + unix2dos is used to convert a text file from UNIX format to + DOS format, and vice versa. + +config DU + bool "du (default blocksize of 512 bytes)" + default y + help + du is used to report the amount of disk space used + for specified files. + +config FEATURE_DU_DEFAULT_BLOCKSIZE_1K + bool "Use a default blocksize of 1024 bytes (1K)" + default y + depends on DU + help + Use a blocksize of (1K) instead of the default 512b. + +config ECHO + bool "echo (basic SuSv3 version taking no options)" + default y + help + echo is used to print a specified string to stdout. + +# this entry also appears in shell/Config.in, next to the echo builtin +config FEATURE_FANCY_ECHO + bool "Enable echo options (-n and -e)" + default y + depends on ECHO || ASH_BUILTIN_ECHO || HUSH + help + This adds options (-n and -e) to echo. + +config ENV + bool "env" + default y + help + env is used to set an environment variable and run + a command; without options it displays the current + environment. + +config FEATURE_ENV_LONG_OPTIONS + bool "Enable long options" + default y + depends on ENV && LONG_OPTS + help + Support long options for the env applet. + +config EXPAND + bool "expand" + default y + help + By default, convert all tabs to spaces. + +config FEATURE_EXPAND_LONG_OPTIONS + bool "Enable long options" + default y + depends on EXPAND && LONG_OPTS + help + Support long options for the expand applet. + +config EXPR + bool "expr" + default y + help + expr is used to calculate numbers and print the result + to standard output. + +config EXPR_MATH_SUPPORT_64 + bool "Extend Posix numbers support to 64 bit" + default y + depends on EXPR + help + Enable 64-bit math support in the expr applet. This will make + the applet slightly larger, but will allow computation with very + large numbers. + +config FALSE + bool "false" + default y + help + false returns an exit code of FALSE (1). + +config FOLD + bool "fold" + default y + help + Wrap text to fit a specific width. + +config FSYNC + bool "fsync" + default y + help + fsync is used to flush file-related cached blocks to disk. + +config HEAD + bool "head" + default y + help + head is used to print the first specified number of lines + from files. + +config FEATURE_FANCY_HEAD + bool "Enable head options (-c, -q, and -v)" + default y + depends on HEAD + help + This enables the head options (-c, -q, and -v). + +config INSTALL + bool "install" + default y + help + Copy files and set attributes. + +config FEATURE_INSTALL_LONG_OPTIONS + bool "Enable long options" + default y + depends on INSTALL && LONG_OPTS + help + Support long options for the install applet. + +####config LENGTH +#### bool "length" +#### default y +#### help +#### length is used to print out the length of a specified string. + +config LN + bool "ln" + default y + help + ln is used to create hard or soft links between files. + +config LOGNAME + bool "logname" + default y + help + logname is used to print the current user's login name. + +config LS + bool "ls" + default y + help + ls is used to list the contents of directories. + +config FEATURE_LS_FILETYPES + bool "Enable filetyping options (-p and -F)" + default y + depends on LS + help + Enable the ls options (-p and -F). + +config FEATURE_LS_FOLLOWLINKS + bool "Enable symlinks dereferencing (-L)" + default y + depends on LS + help + Enable the ls option (-L). + +config FEATURE_LS_RECURSIVE + bool "Enable recursion (-R)" + default y + depends on LS + help + Enable the ls option (-R). + +config FEATURE_LS_SORTFILES + bool "Sort the file names" + default y + depends on LS + help + Allow ls to sort file names alphabetically. + +config FEATURE_LS_TIMESTAMPS + bool "Show file timestamps" + default y + depends on LS + help + Allow ls to display timestamps for files. + +config FEATURE_LS_USERNAME + bool "Show username/groupnames" + default y + depends on LS + help + Allow ls to display username/groupname for files. + +config FEATURE_LS_COLOR + bool "Allow use of color to identify file types" + default y + depends on LS && LONG_OPTS + help + This enables the --color option to ls. + +config FEATURE_LS_COLOR_IS_DEFAULT + bool "Produce colored ls output by default" + default y + depends on FEATURE_LS_COLOR + help + Saying yes here will turn coloring on by default, + even if no "--color" option is given to the ls command. + This is not recommended, since the colors are not + configurable, and the output may not be legible on + many output screens. + +config MD5SUM + bool "md5sum" + default y + help + md5sum is used to print or check MD5 checksums. + +config MKDIR + bool "mkdir" + default y + help + mkdir is used to create directories with the specified names. + +config FEATURE_MKDIR_LONG_OPTIONS + bool "Enable long options" + default y + depends on MKDIR && LONG_OPTS + help + Support long options for the mkdir applet. + +config MKFIFO + bool "mkfifo" + default y + help + mkfifo is used to create FIFOs (named pipes). + The `mknod' program can also create FIFOs. + +config MKNOD + bool "mknod" + default y + help + mknod is used to create FIFOs or block/character special + files with the specified names. + +config MV + bool "mv" + default y + help + mv is used to move or rename files or directories. + +config FEATURE_MV_LONG_OPTIONS + bool "Enable long options" + default y + depends on MV && LONG_OPTS + help + Support long options for the mv applet. + +config NICE + bool "nice" + default y + help + nice runs a program with modified scheduling priority. + +config NOHUP + bool "nohup" + default y + help + run a command immune to hangups, with output to a non-tty. + +config OD + bool "od" + default y + help + od is used to dump binary files in octal and other formats. + +config PRINTENV + bool "printenv" + default y + help + printenv is used to print all or part of environment. + +config PRINTF + bool "printf" + default y + help + printf is used to format and print specified strings. + It's similar to `echo' except it has more options. + +config PWD + bool "pwd" + default y + help + pwd is used to print the current directory. + +config READLINK + bool "readlink" + default y + help + This program reads a symbolic link and returns the name + of the file it points to + +config FEATURE_READLINK_FOLLOW + bool "Enable canonicalization by following all symlinks (-f)" + default y + depends on READLINK + help + Enable the readlink option (-f). + +config REALPATH + bool "realpath" + default y + help + Return the canonicalized absolute pathname. + This isn't provided by GNU shellutils, but where else does it belong. + +config RM + bool "rm" + default y + help + rm is used to remove files or directories. + +config RMDIR + bool "rmdir" + default y + help + rmdir is used to remove empty directories. + +config FEATURE_RMDIR_LONG_OPTIONS + bool "Enable long options" + default y + depends on RMDIR && LONG_OPTS + help + Support long options for the rmdir applet, including + --ignore-fail-on-non-empty for compatibility with GNU rmdir. + +config SEQ + bool "seq" + default y + help + print a sequence of numbers + +config SHA1SUM + bool "sha1sum" + default y + help + Compute and check SHA1 message digest + +config SHA256SUM + bool "sha256sum" + default y + help + Compute and check SHA256 message digest + +config SHA512SUM + bool "sha512sum" + default y + help + Compute and check SHA512 message digest + +config SLEEP + bool "sleep" + default y + help + sleep is used to pause for a specified number of seconds. + It comes in 3 versions: + - small: takes one integer parameter + - fancy: takes multiple integer arguments with suffixes: + sleep 1d 2h 3m 15s + - fancy with fractional numbers: + sleep 2.3s 4.5h sleeps for 16202.3 seconds + Last one is "the most compatible" with coreutils sleep, + but it adds around 1k of code. + +config FEATURE_FANCY_SLEEP + bool "Enable multiple arguments and s/m/h/d suffixes" + default y + depends on SLEEP + help + Allow sleep to pause for specified minutes, hours, and days. + +config FEATURE_FLOAT_SLEEP + bool "Enable fractional arguments" + default y + depends on FEATURE_FANCY_SLEEP + help + Allow for fractional numeric parameters. + +config SORT + bool "sort" + default y + help + sort is used to sort lines of text in specified files. + +config FEATURE_SORT_BIG + bool "Full SuSv3 compliant sort (support -ktcsbdfiozgM)" + default y + depends on SORT + help + Without this, sort only supports -r, -u, and an integer version + of -n. Selecting this adds sort keys, floating point support, and + more. This adds a little over 3k to a nonstatic build on x86. + + The SuSv3 sort standard is available at: + http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html + +config SPLIT + bool "split" + default y + help + split a file into pieces. + +config FEATURE_SPLIT_FANCY + bool "Fancy extensions" + default y + depends on SPLIT + help + Add support for features not required by SUSv3. + Supports additional suffixes 'b' for 512 bytes, + 'g' for 1GiB for the -b option. + +config STAT + bool "stat" + default y + select PLATFORM_LINUX # statfs() + help + display file or filesystem status. + +config FEATURE_STAT_FORMAT + bool "Enable custom formats (-c)" + default y + depends on STAT + help + Without this, stat will not support the '-c format' option where + users can pass a custom format string for output. This adds about + 7k to a nonstatic build on amd64. + +config STTY + bool "stty" + default y + help + stty is used to change and print terminal line settings. + +config SUM + bool "sum" + default y + help + checksum and count the blocks in a file + +config SYNC + bool "sync" + default y + help + sync is used to flush filesystem buffers. + +config TAC + bool "tac" + default y + help + tac is used to concatenate and print files in reverse. + +config TAIL + bool "tail" + default y + help + tail is used to print the last specified number of lines + from files. + +config FEATURE_FANCY_TAIL + bool "Enable extra tail options (-q, -s, -v, and -F)" + default y + depends on TAIL + help + The options (-q, -s, and -v) are provided by GNU tail, but + are not specific in the SUSv3 standard. + + -q Never output headers giving file names + -s SEC Wait SEC seconds between reads with -f + -v Always output headers giving file names + +config TEE + bool "tee" + default y + help + tee is used to read from standard input and write + to standard output and files. + +config FEATURE_TEE_USE_BLOCK_IO + bool "Enable block I/O (larger/faster) instead of byte I/O" + default y + depends on TEE + help + Enable this option for a faster tee, at expense of size. + +config TRUE + bool "true" + default y + help + true returns an exit code of TRUE (0). + +config TTY + bool "tty" + default y + help + tty is used to print the name of the current terminal to + standard output. + +config UNAME + bool "uname" + default y + help + uname is used to print system information. + +config UNEXPAND + bool "unexpand" + default y + help + By default, convert only leading sequences of blanks to tabs. + +config FEATURE_UNEXPAND_LONG_OPTIONS + bool "Enable long options" + default y + depends on UNEXPAND && LONG_OPTS + help + Support long options for the unexpand applet. + +config UNIQ + bool "uniq" + default y + help + uniq is used to remove duplicate lines from a sorted file. + +config USLEEP + bool "usleep" + default y + help + usleep is used to pause for a specified number of microseconds. + +config UUDECODE + bool "uudecode" + default y + help + uudecode is used to decode a uuencoded file. + +config UUENCODE + bool "uuencode" + default y + help + uuencode is used to uuencode a file. + +config WC + bool "wc" + default y + help + wc is used to print the number of bytes, words, and lines, + in specified files. + +config FEATURE_WC_LARGE + bool "Support very large files in wc" + default y + depends on WC + help + Use "unsigned long long" in wc for counter variables. + +config WHOAMI + bool "whoami" + default y + help + whoami is used to print the username of the current + user id (same as id -un). + +config YES + bool "yes" + default y + help + yes is used to repeatedly output a specific string, or + the default string `y'. + +comment "Common options for cp and mv" + depends on CP || MV + +config FEATURE_PRESERVE_HARDLINKS + bool "Preserve hard links" + default y + depends on CP || MV + help + Allow cp and mv to preserve hard links. + +comment "Common options for ls, more and telnet" + depends on LS || MORE || TELNET + +config FEATURE_AUTOWIDTH + bool "Calculate terminal & column widths" + default y + depends on LS || MORE || TELNET + help + This option allows utilities such as 'ls', 'more' and 'telnet' + to determine the width of the screen, which can allow them to + display additional text or avoid wrapping text onto the next line. + If you leave this disabled, your utilities will be especially + primitive and will be unable to determine the current screen width. + +comment "Common options for df, du, ls" + depends on DF || DU || LS + +config FEATURE_HUMAN_READABLE + bool "Support for human readable output (example 13k, 23M, 235G)" + default y + depends on DF || DU || LS + help + Allow df, du, and ls to have human readable output. + +comment "Common options for md5sum, sha1sum, sha256sum, sha512sum" + depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM + +config FEATURE_MD5_SHA1_SUM_CHECK + bool "Enable -c, -s and -w options" + default y + depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM + help + Enabling the -c options allows files to be checked + against pre-calculated hash values. + + -s and -w are useful options when verifying checksums. + +endmenu diff --git a/coreutils/Kbuild.src b/coreutils/Kbuild.src new file mode 100644 index 000000000..d6453f014 --- /dev/null +++ b/coreutils/Kbuild.src @@ -0,0 +1,85 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +libs-y += libcoreutils/ + +lib-y:= + +INSERT +lib-$(CONFIG_CAL) += cal.o +lib-$(CONFIG_CATV) += catv.o +lib-$(CONFIG_CHGRP) += chgrp.o chown.o +lib-$(CONFIG_CHMOD) += chmod.o +lib-$(CONFIG_CHOWN) += chown.o +lib-$(CONFIG_ADDUSER) += chown.o # used by adduser +lib-$(CONFIG_ADDGROUP) += chown.o # used by adduser +lib-$(CONFIG_CHROOT) += chroot.o +lib-$(CONFIG_CKSUM) += cksum.o +lib-$(CONFIG_COMM) += comm.o +lib-$(CONFIG_CP) += cp.o +lib-$(CONFIG_CUT) += cut.o +lib-$(CONFIG_DD) += dd.o +lib-$(CONFIG_DF) += df.o +lib-$(CONFIG_DIRNAME) += dirname.o +lib-$(CONFIG_DOS2UNIX) += dos2unix.o +lib-$(CONFIG_DU) += du.o +lib-$(CONFIG_ECHO) += echo.o +lib-$(CONFIG_ASH) += echo.o # used by ash +lib-$(CONFIG_HUSH) += echo.o # used by hush +lib-$(CONFIG_ENV) += env.o +lib-$(CONFIG_EXPR) += expr.o +lib-$(CONFIG_EXPAND) += expand.o +lib-$(CONFIG_FALSE) += false.o +lib-$(CONFIG_FOLD) += fold.o +lib-$(CONFIG_FSYNC) += fsync.o +lib-$(CONFIG_HEAD) += head.o +lib-$(CONFIG_INSTALL) += install.o +#lib-$(CONFIG_LENGTH) += length.o +lib-$(CONFIG_LN) += ln.o +lib-$(CONFIG_LOGNAME) += logname.o +lib-$(CONFIG_LS) += ls.o +lib-$(CONFIG_FTPD) += ls.o +lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o +lib-$(CONFIG_MKDIR) += mkdir.o +lib-$(CONFIG_MKFIFO) += mkfifo.o +lib-$(CONFIG_MKNOD) += mknod.o +lib-$(CONFIG_MV) += mv.o +lib-$(CONFIG_NICE) += nice.o +lib-$(CONFIG_NOHUP) += nohup.o +lib-$(CONFIG_OD) += od.o +lib-$(CONFIG_PRINTENV) += printenv.o +lib-$(CONFIG_PRINTF) += printf.o +lib-$(CONFIG_ASH_BUILTIN_PRINTF) += printf.o +lib-$(CONFIG_PWD) += pwd.o +lib-$(CONFIG_READLINK) += readlink.o +lib-$(CONFIG_REALPATH) += realpath.o +lib-$(CONFIG_RM) += rm.o +lib-$(CONFIG_RMDIR) += rmdir.o +lib-$(CONFIG_SEQ) += seq.o +lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o +lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o +lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o +lib-$(CONFIG_SLEEP) += sleep.o +lib-$(CONFIG_SPLIT) += split.o +lib-$(CONFIG_SORT) += sort.o +lib-$(CONFIG_STAT) += stat.o +lib-$(CONFIG_STTY) += stty.o +lib-$(CONFIG_SUM) += sum.o +lib-$(CONFIG_SYNC) += sync.o +lib-$(CONFIG_TAC) += tac.o +lib-$(CONFIG_TAIL) += tail.o +lib-$(CONFIG_TEE) += tee.o +lib-$(CONFIG_TRUE) += true.o +lib-$(CONFIG_TTY) += tty.o +lib-$(CONFIG_UNAME) += uname.o +lib-$(CONFIG_UNEXPAND) += expand.o +lib-$(CONFIG_UNIQ) += uniq.o +lib-$(CONFIG_USLEEP) += usleep.o +lib-$(CONFIG_UUDECODE) += uudecode.o +lib-$(CONFIG_UUENCODE) += uuencode.o +lib-$(CONFIG_WC) += wc.o +lib-$(CONFIG_WHOAMI) += whoami.o +lib-$(CONFIG_YES) += yes.o diff --git a/coreutils/basename.c b/coreutils/basename.c new file mode 100644 index 000000000..1f7a13713 --- /dev/null +++ b/coreutils/basename.c @@ -0,0 +1,79 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Changes: + * 1) Now checks for too many args. Need at least one and at most two. + * 2) Don't check for options, as per SUSv3. + * 3) Save some space by using strcmp(). Calling strncmp() here was silly. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */ + +//kbuild:lib-$(CONFIG_BASENAME) += basename.o + +//config:config BASENAME +//config: bool "basename" +//config: default y +//config: help +//config: basename is used to strip the directory and suffix from filenames, +//config: leaving just the filename itself. Enable this option if you wish +//config: to enable the 'basename' utility. + +//usage:#define basename_trivial_usage +//usage: "FILE [SUFFIX]" +//usage:#define basename_full_usage "\n\n" +//usage: "Strip directory path and .SUFFIX from FILE\n" +//usage: +//usage:#define basename_example_usage +//usage: "$ basename /usr/local/bin/foo\n" +//usage: "foo\n" +//usage: "$ basename /usr/local/bin/\n" +//usage: "bin\n" +//usage: "$ basename /foo/bar.txt .txt\n" +//usage: "bar" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int basename_main(int argc, char **argv) +{ + size_t m, n; + char *s; + + if (argv[1] && strcmp(argv[1], "--") == 0) { + argv++; + argc--; + } + + if ((unsigned)(argc-2) >= 2) { + bb_show_usage(); + } + + /* It should strip slash: /abc/def/ -> def */ + s = bb_get_last_path_component_strip(*++argv); + + m = strlen(s); + if (*++argv) { + n = strlen(*argv); + if ((m > n) && (strcmp(s+m-n, *argv) == 0)) { + m -= n; + /*s[m] = '\0'; - redundant */ + } + } + + /* puts(s) will do, but we can do without stdio this way: */ + s[m++] = '\n'; + /* NB: != is correct here: */ + return full_write(STDOUT_FILENO, s, m) != (ssize_t)m; +} diff --git a/coreutils/cal.c b/coreutils/cal.c new file mode 100644 index 000000000..b470ad968 --- /dev/null +++ b/coreutils/cal.c @@ -0,0 +1,381 @@ +/* vi: set sw=4 ts=4: */ +/* + * Calendar implementation for busybox + * + * See original copyright at the end of this file + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */ +/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream + * BB_AUDIT BUG: version in util-linux seems to be broken as well. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Major size reduction... over 50% (>1.5k) on i386. + */ + +//usage:#define cal_trivial_usage +//usage: "[-jy] [[MONTH] YEAR]" +//usage:#define cal_full_usage "\n\n" +//usage: "Display a calendar\n" +//usage: "\n -j Use julian dates" +//usage: "\n -y Display the entire year" + +#include "libbb.h" +#include "unicode.h" + +/* We often use "unsigned" intead of "int", it's easier to div on most CPUs */ + +#define THURSDAY 4 /* for reformation */ +#define SATURDAY 6 /* 1 Jan 1 was a Saturday */ + +#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ +#define NUMBER_MISSING_DAYS 11 /* 11 day correction */ + +#define MAXDAYS 42 /* max slots in a month array */ +#define SPACE -1 /* used in day array */ + +static const unsigned char days_in_month[] ALIGN1 = { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const unsigned char sep1752[] ALIGN1 = { + 1, 2, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30 +}; + +/* Set to 0 or 1 in main */ +#define julian ((unsigned)option_mask32) + +/* leap year -- account for Gregorian reformation in 1752 */ +static int leap_year(unsigned yr) +{ + if (yr <= 1752) + return !(yr % 4); + return (!(yr % 4) && (yr % 100)) || !(yr % 400); +} + +/* number of centuries since 1700, not inclusive */ +#define centuries_since_1700(yr) \ + ((yr) > 1700 ? (yr) / 100 - 17 : 0) + +/* number of centuries since 1700 whose modulo of 400 is 0 */ +#define quad_centuries_since_1700(yr) \ + ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) + +/* number of leap years between year 1 and this year, not inclusive */ +#define leap_years_since_year_1(yr) \ + ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) + +static void center(char *, unsigned, unsigned); +static void day_array(unsigned, unsigned, unsigned *); +static void trim_trailing_spaces_and_print(char *); + +static void blank_string(char *buf, size_t buflen); +static char *build_row(char *p, unsigned *dp); + +#define DAY_LEN 3 /* 3 spaces per day */ +#define J_DAY_LEN (DAY_LEN + 1) +#define WEEK_LEN 20 /* 7 * 3 - one space at the end */ +#define J_WEEK_LEN (WEEK_LEN + 7) +#define HEAD_SEP 2 /* spaces between day headings */ + +int cal_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cal_main(int argc UNUSED_PARAM, char **argv) +{ + struct tm zero_tm; + time_t now; + unsigned month, year, flags, i; + char *month_names[12]; + /* normal heading: */ + /* "Su Mo Tu We Th Fr Sa" */ + /* -j heading: */ + /* " Su Mo Tu We Th Fr Sa" */ + char day_headings[ENABLE_UNICODE_SUPPORT ? 28 * 6 : 28]; + IF_UNICODE_SUPPORT(char *hp = day_headings;) + char buf[40]; + + init_unicode(); + + flags = getopt32(argv, "jy"); + /* This sets julian = flags & 1: */ + option_mask32 &= 1; + month = 0; + argv += optind; + + if (!argv[0]) { + struct tm *ptm; + + time(&now); + ptm = localtime(&now); + year = ptm->tm_year + 1900; + if (!(flags & 2)) { /* no -y */ + month = ptm->tm_mon + 1; + } + } else { + if (argv[1]) { + if (argv[2]) { + bb_show_usage(); + } + if (!(flags & 2)) { /* no -y */ + month = xatou_range(*argv, 1, 12); + } + argv++; + } + year = xatou_range(*argv, 1, 9999); + } + + blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian); + + i = 0; + do { + zero_tm.tm_mon = i; + /* full month name according to locale */ + strftime(buf, sizeof(buf), "%B", &zero_tm); + month_names[i] = xstrdup(buf); + + if (i < 7) { + zero_tm.tm_wday = i; + /* abbreviated weekday name according to locale */ + strftime(buf, sizeof(buf), "%a", &zero_tm); +#if ENABLE_UNICODE_SUPPORT + if (julian) + *hp++ = ' '; + { + char *two_wchars = unicode_conv_to_printable_fixedwidth(/*NULL,*/ buf, 2); + strcpy(hp, two_wchars); + free(two_wchars); + } + hp += strlen(hp); + *hp++ = ' '; +#else + strncpy(day_headings + i * (3+julian) + julian, buf, 2); +#endif + } + } while (++i < 12); + IF_UNICODE_SUPPORT(hp[-1] = '\0';) + + if (month) { + unsigned row, len, days[MAXDAYS]; + unsigned *dp = days; + char lineout[30]; + + day_array(month, year, dp); + len = sprintf(lineout, "%s %d", month_names[month - 1], year); + printf("%*s%s\n%s\n", + ((7*julian + WEEK_LEN) - len) / 2, "", + lineout, day_headings); + for (row = 0; row < 6; row++) { + build_row(lineout, dp)[0] = '\0'; + dp += 7; + trim_trailing_spaces_and_print(lineout); + } + } else { + unsigned row, which_cal, week_len, days[12][MAXDAYS]; + unsigned *dp; + char lineout[80]; + + sprintf(lineout, "%u", year); + center(lineout, + (WEEK_LEN * 3 + HEAD_SEP * 2) + + julian * (J_WEEK_LEN * 2 + HEAD_SEP + - (WEEK_LEN * 3 + HEAD_SEP * 2)), + 0); + puts("\n"); /* two \n's */ + for (i = 0; i < 12; i++) { + day_array(i + 1, year, days[i]); + } + blank_string(lineout, sizeof(lineout)); + week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN); + for (month = 0; month < 12; month += 3-julian) { + center(month_names[month], week_len, HEAD_SEP); + if (!julian) { + center(month_names[month + 1], week_len, HEAD_SEP); + } + center(month_names[month + 2 - julian], week_len, 0); + printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings); + if (!julian) { + printf("%*s%s", HEAD_SEP, "", day_headings); + } + bb_putchar('\n'); + for (row = 0; row < (6*7); row += 7) { + for (which_cal = 0; which_cal < 3-julian; which_cal++) { + dp = days[month + which_cal] + row; + build_row(lineout + which_cal * (week_len + 2), dp); + } + /* blank_string took care of nul termination. */ + trim_trailing_spaces_and_print(lineout); + } + } + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/* + * day_array -- + * Fill in an array of 42 integers with a calendar. Assume for a moment + * that you took the (maximum) 6 rows in a calendar and stretched them + * out end to end. You would have 42 numbers or spaces. This routine + * builds that array for any month from Jan. 1 through Dec. 9999. + */ +static void day_array(unsigned month, unsigned year, unsigned *days) +{ + unsigned long temp; + unsigned i; + unsigned day, dw, dm; + + memset(days, SPACE, MAXDAYS * sizeof(int)); + + if ((month == 9) && (year == 1752)) { + /* Assumes the Gregorian reformation eliminates + * 3 Sep. 1752 through 13 Sep. 1752. + */ + unsigned j_offset = julian * 244; + size_t oday = 0; + + do { + days[oday+2] = sep1752[oday] + j_offset; + } while (++oday < sizeof(sep1752)); + + return; + } + + /* day_in_year + * return the 1 based day number within the year + */ + day = 1; + if ((month > 2) && leap_year(year)) { + ++day; + } + + i = month; + while (i) { + day += days_in_month[--i]; + } + + /* day_in_week + * return the 0 based day number for any date from 1 Jan. 1 to + * 31 Dec. 9999. Assumes the Gregorian reformation eliminates + * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all + * missing days. + */ + temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + day; + if (temp < FIRST_MISSING_DAY) { + dw = ((temp - 1 + SATURDAY) % 7); + } else { + dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); + } + + if (!julian) { + day = 1; + } + + dm = days_in_month[month]; + if ((month == 2) && leap_year(year)) { + ++dm; + } + + do { + days[dw++] = day++; + } while (--dm); +} + +static void trim_trailing_spaces_and_print(char *s) +{ + char *p = s; + + while (*p) { + ++p; + } + while (p != s) { + --p; + if (!isspace(*p)) { + p[1] = '\0'; + break; + } + } + + puts(s); +} + +static void center(char *str, unsigned len, unsigned separate) +{ + unsigned n = strlen(str); + len -= n; + printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, ""); +} + +static void blank_string(char *buf, size_t buflen) +{ + memset(buf, ' ', buflen); + buf[buflen-1] = '\0'; +} + +static char *build_row(char *p, unsigned *dp) +{ + unsigned col, val, day; + + memset(p, ' ', (julian + DAY_LEN) * 7); + + col = 0; + do { + day = *dp++; + if (day != SPACE) { + if (julian) { + ++p; + if (day >= 100) { + *p = '0'; + p[-1] = (day / 100) + '0'; + day %= 100; + } + } + val = day / 10; + if (val > 0) { + *p = val + '0'; + } + *++p = day % 10 + '0'; + p += 2; + } else { + p += DAY_LEN + julian; + } + } while (++col < 7); + + return p; +} + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kim Letkeman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/coreutils/cat.c b/coreutils/cat.c new file mode 100644 index 000000000..00c38d486 --- /dev/null +++ b/coreutils/cat.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */ + +//kbuild:lib-$(CONFIG_CAT) += cat.o +//kbuild:lib-$(CONFIG_MORE) += cat.o # more uses it if stdout isn't a tty +//kbuild:lib-$(CONFIG_LESS) += cat.o # less too +//kbuild:lib-$(CONFIG_CRONTAB) += cat.o # crontab -l + +//config:config CAT +//config: bool "cat" +//config: default y +//config: help +//config: cat is used to concatenate files and print them to the standard +//config: output. Enable this option if you wish to enable the 'cat' utility. + +//usage:#define cat_trivial_usage +//usage: "[FILE]..." +//usage:#define cat_full_usage "\n\n" +//usage: "Concatenate FILEs and print them to stdout" +//usage: +//usage:#define cat_example_usage +//usage: "$ cat /proc/uptime\n" +//usage: "110716.72 17.67" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +int bb_cat(char **argv) +{ + int fd; + int retval = EXIT_SUCCESS; + + if (!*argv) + argv = (char**) &bb_argv_dash; + + do { + fd = open_or_warn_stdin(*argv); + if (fd >= 0) { + /* This is not a xfunc - never exits */ + off_t r = bb_copyfd_eof(fd, STDOUT_FILENO); + if (fd != STDIN_FILENO) + close(fd); + if (r >= 0) + continue; + } + retval = EXIT_FAILURE; + } while (*++argv); + + return retval; +} + +int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cat_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "u"); + argv += optind; + return bb_cat(argv); +} diff --git a/coreutils/catv.c b/coreutils/catv.c new file mode 100644 index 000000000..214b4311a --- /dev/null +++ b/coreutils/catv.c @@ -0,0 +1,83 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat -v implementation for busybox + * + * Copyright (C) 2006 Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* See "Cat -v considered harmful" at + * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */ + +//usage:#define catv_trivial_usage +//usage: "[-etv] [FILE]..." +//usage:#define catv_full_usage "\n\n" +//usage: "Display nonprinting characters as ^x or M-x\n" +//usage: "\n -e End each line with $" +//usage: "\n -t Show tabs as ^I" +//usage: "\n -v Don't use ^x or M-x escapes" + +#include "libbb.h" + +int catv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int catv_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + int fd; + unsigned flags; + + flags = getopt32(argv, "etv"); +#define CATV_OPT_e (1<<0) +#define CATV_OPT_t (1<<1) +#define CATV_OPT_v (1<<2) + flags ^= CATV_OPT_v; + argv += optind; + + /* Read from stdin if there's nothing else to do. */ + if (!argv[0]) + *--argv = (char*)"-"; + do { + fd = open_or_warn_stdin(*argv); + if (fd < 0) { + retval = EXIT_FAILURE; + continue; + } + for (;;) { + int i, res; + +#define read_buf bb_common_bufsiz1 + res = read(fd, read_buf, COMMON_BUFSIZE); + if (res < 0) + retval = EXIT_FAILURE; + if (res < 1) + break; + for (i = 0; i < res; i++) { + unsigned char c = read_buf[i]; + + if (c > 126 && (flags & CATV_OPT_v)) { + if (c == 127) { + printf("^?"); + continue; + } + printf("M-"); + c -= 128; + } + if (c < 32) { + if (c == 10) { + if (flags & CATV_OPT_e) + bb_putchar('$'); + } else if (flags & (c==9 ? CATV_OPT_t : CATV_OPT_v)) { + printf("^%c", c+'@'); + continue; + } + } + bb_putchar(c); + } + } + if (ENABLE_FEATURE_CLEAN_UP && fd) + close(fd); + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c new file mode 100644 index 000000000..7076db62f --- /dev/null +++ b/coreutils/chgrp.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 defects - none? */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ + +//usage:#define chgrp_trivial_usage +//usage: "[-RhLHP"IF_DESKTOP("cvf")"]... GROUP FILE..." +//usage:#define chgrp_full_usage "\n\n" +//usage: "Change the group membership of each FILE to GROUP\n" +//usage: "\n -R Recurse" +//usage: "\n -h Affect symlinks instead of symlink targets" +//usage: "\n -L Traverse all symlinks to directories" +//usage: "\n -H Traverse symlinks on command line only" +//usage: "\n -P Don't traverse symlinks (default)" +//usage: IF_DESKTOP( +//usage: "\n -c List changed files" +//usage: "\n -v Verbose" +//usage: "\n -f Hide errors" +//usage: ) +//usage: +//usage:#define chgrp_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chgrp root /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +int chgrp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chgrp_main(int argc, char **argv) +{ + /* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */ + char **p = argv; + while (*++p) { + if (p[0][0] != '-') { + p[0] = xasprintf(":%s", p[0]); + break; + } + } + return chown_main(argc, argv); +} diff --git a/coreutils/chmod.c b/coreutils/chmod.c new file mode 100644 index 000000000..5ee45b942 --- /dev/null +++ b/coreutils/chmod.c @@ -0,0 +1,182 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chmod implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Reworked by (C) 2002 Vladimir Oleynik + * to correctly parse '-rwxgoa' + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +//usage:#define chmod_trivial_usage +//usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..." +//usage:#define chmod_full_usage "\n\n" +//usage: "Each MODE is one or more of the letters ugoa, one of the\n" +//usage: "symbols +-= and one or more of the letters rwxst\n" +//usage: "\n -R Recurse" +//usage: IF_DESKTOP( +//usage: "\n -c List changed files" +//usage: "\n -v List all files" +//usage: "\n -f Hide errors" +//usage: ) +//usage: +//usage:#define chmod_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chmod u+x /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n" +//usage: "$ chmod 444 /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define OPT_RECURSE (option_mask32 & 1) +#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0)) +#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0)) +#define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0)) +#define OPT_STR "R" IF_DESKTOP("vcf") + +/* coreutils: + * chmod never changes the permissions of symbolic links; the chmod + * system call cannot change their permissions. This is not a problem + * since the permissions of symbolic links are never used. + * However, for each symbolic link listed on the command line, chmod changes + * the permissions of the pointed-to file. In contrast, chmod ignores + * symbolic links encountered during recursive directory traversals. + */ + +static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) +{ + mode_t newmode; + + /* match coreutils behavior */ + if (depth == 0) { + /* statbuf holds lstat result, but we need stat (follow link) */ + if (stat(fileName, statbuf)) + goto err; + } else { /* depth > 0: skip links */ + if (S_ISLNK(statbuf->st_mode)) + return TRUE; + } + newmode = statbuf->st_mode; + + if (!bb_parse_mode((char *)param, &newmode)) + bb_error_msg_and_die("invalid mode '%s'", (char *)param); + + if (chmod(fileName, newmode) == 0) { + if (OPT_VERBOSE + || (OPT_CHANGED && statbuf->st_mode != newmode) + ) { + printf("mode of '%s' changed to %04o (%s)\n", fileName, + newmode & 07777, bb_mode_string(newmode)+1); + } + return TRUE; + } + err: + if (!OPT_QUIET) + bb_simple_perror_msg(fileName); + return FALSE; +} + +int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chmod_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + char *arg, **argp; + char *smode; + + /* Convert first encountered -r into ar, -w into aw etc + * so that getopt would not eat it */ + argp = argv; + while ((arg = *++argp)) { + /* Mode spec must be the first arg (sans -R etc) */ + /* (protect against mishandling e.g. "chmod 644 -r") */ + if (arg[0] != '-') { + arg = NULL; + break; + } + /* An option. Not a -- or valid option? */ + if (arg[1] && !strchr("-"OPT_STR, arg[1])) { + arg[0] = 'a'; + break; + } + } + + /* Parse options */ + opt_complementary = "-2"; + getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */ + argv += optind; + + /* Restore option-like mode if needed */ + if (arg) arg[0] = '-'; + + /* Ok, ready to do the deed now */ + smode = *argv++; + do { + if (!recursive_action(*argv, + OPT_RECURSE, // recurse + fileAction, // file action + fileAction, // dir action + smode, // user data + 0) // depth + ) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Security: chmod is too important and too subtle. +This is a test script (busybox chmod versus coreutils). +Run it in empty directory. + +#!/bin/sh +t1="/tmp/busybox chmod" +t2="/usr/bin/chmod" +create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir + >up + >file + >dir/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/up + ) +} +tst() { + (cd test1; $t1 $1) + (cd test2; $t2 $1) + (cd test1; ls -lR) >out1 + (cd test2; ls -lR) >out2 + echo "chmod $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + rm out.diff +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +create test1; create test2 +tst "a+w file" +tst "a-w dir" +tst "a+w linkfile" +tst "a-w linkdir" +tst "-R a+w file" +tst "-R a-w dir" +tst "-R a+w linkfile" +tst "-R a-w linkdir" +tst "a-r,a+x linkfile" +*/ diff --git a/coreutils/chown.c b/coreutils/chown.c new file mode 100644 index 000000000..bb166d8fe --- /dev/null +++ b/coreutils/chown.c @@ -0,0 +1,221 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 defects - none? */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ + +//usage:#define chown_trivial_usage +//usage: "[-RhLHP"IF_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..." +//usage:#define chown_full_usage "\n\n" +//usage: "Change the owner and/or group of each FILE to OWNER and/or GROUP\n" +//usage: "\n -R Recurse" +//usage: "\n -h Affect symlinks instead of symlink targets" +//usage: "\n -L Traverse all symlinks to directories" +//usage: "\n -H Traverse symlinks on command line only" +//usage: "\n -P Don't traverse symlinks (default)" +//usage: IF_DESKTOP( +//usage: "\n -c List changed files" +//usage: "\n -v List all files" +//usage: "\n -f Hide errors" +//usage: ) +//usage: +//usage:#define chown_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chown root /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chown root.root /tmp/foo\n" +//usage: "ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define OPT_STR ("Rh" IF_DESKTOP("vcfLHP")) +#define BIT_RECURSE 1 +#define OPT_RECURSE (opt & 1) +#define OPT_NODEREF (opt & 2) +#define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0)) +#define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0)) +#define OPT_QUIET (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0)) +/* POSIX options + * -L traverse every symbolic link to a directory encountered + * -H if a command line argument is a symbolic link to a directory, traverse it + * -P do not traverse any symbolic links (default) + * We do not conform to the following: + * "Specifying more than one of -H, -L, and -P is not an error. + * The last option specified shall determine the behavior of the utility." */ +/* -L */ +#define BIT_TRAVERSE 0x20 +#define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0)) +/* -H or -L */ +#define BIT_TRAVERSE_TOP (0x20|0x40) +#define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0)) + +#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS +static const char chown_longopts[] ALIGN1 = + "recursive\0" No_argument "R" + "dereference\0" No_argument "\xff" + "no-dereference\0" No_argument "h" +# if ENABLE_DESKTOP + "changes\0" No_argument "c" + "silent\0" No_argument "f" + "quiet\0" No_argument "f" + "verbose\0" No_argument "v" +# endif + ; +#endif + +typedef int (*chown_fptr)(const char *, uid_t, gid_t); + +struct param_t { + struct bb_uidgid_t ugid; + chown_fptr chown_func; +}; + +static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, + void *vparam, int depth UNUSED_PARAM) +{ +#define param (*(struct param_t*)vparam) +#define opt option_mask32 + uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid; + gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid; + + if (param.chown_func(fileName, u, g) == 0) { + if (OPT_VERBOSE + || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g)) + ) { + printf("changed ownership of '%s' to %u:%u\n", + fileName, (unsigned)u, (unsigned)g); + } + return TRUE; + } + if (!OPT_QUIET) + bb_simple_perror_msg(fileName); + return FALSE; +#undef opt +#undef param +} + +int chown_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + int opt, flags; + struct param_t param; + + /* Just -1 might not work: uid_t may be unsigned long */ + param.ugid.uid = -1L; + param.ugid.gid = -1L; + +#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS + applet_long_options = chown_longopts; +#endif + opt_complementary = "-2"; + opt = getopt32(argv, OPT_STR); + argv += optind; + + /* This matches coreutils behavior (almost - see below) */ + param.chown_func = chown; + if (OPT_NODEREF + /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */ + IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE) + ) { + param.chown_func = lchown; + } + + flags = ACTION_DEPTHFIRST; /* match coreutils order */ + if (OPT_RECURSE) + flags |= ACTION_RECURSE; + if (OPT_TRAVERSE_TOP) + flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */ + if (OPT_TRAVERSE) + flags |= ACTION_FOLLOWLINKS; /* follow links if -L */ + + parse_chown_usergroup_or_die(¶m.ugid, argv[0]); + + /* Ok, ready to do the deed now */ + while (*++argv) { + if (!recursive_action(*argv, + flags, /* flags */ + fileAction, /* file action */ + fileAction, /* dir action */ + ¶m, /* user data */ + 0) /* depth */ + ) { + retval = EXIT_FAILURE; + } + } + + return retval; +} + +/* +Testcase. Run in empty directory. + +#!/bin/sh +t1="/tmp/busybox chown" +t2="/usr/bin/chown" +create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir dir2 + >up + >file + >dir/file + >dir2/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/linkup + ln -s ../dir2 dir/linkupdir2 + ) + chown -R 0:0 $1 +} +tst() { + create test1 + create test2 + echo "[$1]" >>test1.out + echo "[$1]" >>test2.out + (cd test1; $t1 $1) >>test1.out 2>&1 + (cd test2; $t2 $1) >>test2.out 2>&1 + (cd test1; ls -lnR) >out1 + (cd test2; ls -lnR) >out2 + echo "chown $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + rm out.diff +} +tst_for_each() { + tst "$1 1:1 file" + tst "$1 1:1 dir" + tst "$1 1:1 linkdir" + tst "$1 1:1 linkfile" +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +>test1.out +>test2.out +# These match coreutils 6.8: +tst_for_each "-v" +tst_for_each "-vR" +tst_for_each "-vRP" +tst_for_each "-vRL" +tst_for_each "-vRH" +tst_for_each "-vh" +tst_for_each "-vhR" +tst_for_each "-vhRP" +tst_for_each "-vhRL" +tst_for_each "-vhRH" +# Fix `name' in coreutils output +sed 's/`/'"'"'/g' -i test2.out +# Compare us with coreutils output +diff -u test1.out test2.out + +*/ diff --git a/coreutils/chroot.c b/coreutils/chroot.c new file mode 100644 index 000000000..ab8beb023 --- /dev/null +++ b/coreutils/chroot.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chroot implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +//usage:#define chroot_trivial_usage +//usage: "NEWROOT [PROG ARGS]" +//usage:#define chroot_full_usage "\n\n" +//usage: "Run PROG with root directory set to NEWROOT" +//usage: +//usage:#define chroot_example_usage +//usage: "$ ls -l /bin/ls\n" +//usage: "lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox\n" +//usage: "# mount /dev/hdc1 /mnt -t minix\n" +//usage: "# chroot /mnt\n" +//usage: "# ls -l /bin/ls\n" +//usage: "-rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls*\n" + +#include "libbb.h" + +int chroot_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chroot_main(int argc UNUSED_PARAM, char **argv) +{ + ++argv; + if (!*argv) + bb_show_usage(); + xchroot(*argv); + xchdir("/"); + + ++argv; + if (!*argv) { /* no 2nd param (PROG), use shell */ + argv -= 2; + argv[0] = (char *) get_shell_name(); + argv[1] = (char *) "-i"; /* GNU coreutils 8.4 compat */ + /*argv[2] = NULL; - already is */ + } + + BB_EXECVP_or_die(argv); +} diff --git a/coreutils/cksum.c b/coreutils/cksum.c new file mode 100644 index 000000000..ac0b0c319 --- /dev/null +++ b/coreutils/cksum.c @@ -0,0 +1,71 @@ +/* vi: set sw=4 ts=4: */ +/* + * cksum - calculate the CRC32 checksum of a file + * + * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define cksum_trivial_usage +//usage: "FILES..." +//usage:#define cksum_full_usage "\n\n" +//usage: "Calculate the CRC32 checksums of FILES" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cksum_main(int argc UNUSED_PARAM, char **argv) +{ + uint32_t *crc32_table = crc32_filltable(NULL, 1); + uint32_t crc; + off_t length, filesize; + int bytes_read; + int exit_code = EXIT_SUCCESS; + +#if ENABLE_DESKTOP + getopt32(argv, ""); /* coreutils 6.9 compat */ + argv += optind; +#else + argv++; +#endif + + do { + int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input); + + if (fd < 0) { + exit_code = EXIT_FAILURE; + continue; + } + crc = 0; + length = 0; + +#define read_buf bb_common_bufsiz1 + while ((bytes_read = safe_read(fd, read_buf, sizeof(read_buf))) > 0) { + length += bytes_read; + crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table); + } + close(fd); + + filesize = length; + + while (length) { + crc = (crc << 8) ^ crc32_table[(uint8_t)(crc >> 24) ^ (uint8_t)length]; + /* must ensure that shift is unsigned! */ + if (sizeof(length) <= sizeof(unsigned)) + length = (unsigned)length >> 8; + else if (sizeof(length) <= sizeof(unsigned long)) + length = (unsigned long)length >> 8; + else + length = (unsigned long long)length >> 8; + } + crc = ~crc; + + printf((*argv ? "%"PRIu32" %"OFF_FMT"i %s\n" : "%"PRIu32" %"OFF_FMT"i\n"), + crc, filesize, *argv); + } while (*argv && *++argv); + + fflush_stdout_and_exit(exit_code); +} diff --git a/coreutils/comm.c b/coreutils/comm.c new file mode 100644 index 000000000..cd450950b --- /dev/null +++ b/coreutils/comm.c @@ -0,0 +1,108 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini comm implementation for busybox + * + * Copyright (C) 2005 by Robert Sullivan + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define comm_trivial_usage +//usage: "[-123] FILE1 FILE2" +//usage:#define comm_full_usage "\n\n" +//usage: "Compare FILE1 with FILE2\n" +//usage: "\n -1 Suppress lines unique to FILE1" +//usage: "\n -2 Suppress lines unique to FILE2" +//usage: "\n -3 Suppress lines common to both files" + +#include "libbb.h" + +#define COMM_OPT_1 (1 << 0) +#define COMM_OPT_2 (1 << 1) +#define COMM_OPT_3 (1 << 2) + +/* writeline outputs the input given, appropriately aligned according to class */ +static void writeline(char *line, int class) +{ + int flags = option_mask32; + if (class == 0) { + if (flags & COMM_OPT_1) + return; + } else if (class == 1) { + if (flags & COMM_OPT_2) + return; + if (!(flags & COMM_OPT_1)) + putchar('\t'); + } else /*if (class == 2)*/ { + if (flags & COMM_OPT_3) + return; + if (!(flags & COMM_OPT_1)) + putchar('\t'); + if (!(flags & COMM_OPT_2)) + putchar('\t'); + } + puts(line); +} + +int comm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int comm_main(int argc UNUSED_PARAM, char **argv) +{ + char *thisline[2]; + FILE *stream[2]; + int i; + int order; + + opt_complementary = "=2"; + getopt32(argv, "123"); + argv += optind; + + for (i = 0; i < 2; ++i) { + stream[i] = xfopen_stdin(argv[i]); + } + + order = 0; + thisline[1] = thisline[0] = NULL; + while (1) { + if (order <= 0) { + free(thisline[0]); + thisline[0] = xmalloc_fgetline(stream[0]); + } + if (order >= 0) { + free(thisline[1]); + thisline[1] = xmalloc_fgetline(stream[1]); + } + + i = !thisline[0] + (!thisline[1] << 1); + if (i) + break; + order = strcmp(thisline[0], thisline[1]); + + if (order >= 0) + writeline(thisline[1], order ? 1 : 2); + else + writeline(thisline[0], 0); + } + + /* EOF at least on one of the streams */ + i &= 1; + if (thisline[i]) { + /* stream[i] is not at EOF yet */ + /* we did not print thisline[i] yet */ + char *p = thisline[i]; + writeline(p, i); + while (1) { + free(p); + p = xmalloc_fgetline(stream[i]); + if (!p) + break; + writeline(p, i); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) { + fclose(stream[0]); + fclose(stream[1]); + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/cp.c b/coreutils/cp.c new file mode 100644 index 000000000..e48e21c97 --- /dev/null +++ b/coreutils/cp.c @@ -0,0 +1,208 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cp implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ + +//usage:#define cp_trivial_usage +//usage: "[OPTIONS] SOURCE DEST" +//usage:#define cp_full_usage "\n\n" +//usage: "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY\n" +//usage: "\n -a Same as -dpR" +//usage: IF_SELINUX( +//usage: "\n -c Preserve security context" +//usage: ) +//usage: "\n -R,-r Recurse" +//usage: "\n -d,-P Preserve symlinks (default if -R)" +//usage: "\n -L Follow all symlinks" +//usage: "\n -H Follow symlinks on command line" +//usage: "\n -p Preserve file attributes if possible" +//usage: "\n -f Overwrite" +//usage: "\n -i Prompt before overwrite" +//usage: "\n -l,-s Create (sym)links" + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cp_main(int argc, char **argv) +{ + struct stat source_stat; + struct stat dest_stat; + const char *last; + const char *dest; + int s_flags; + int d_flags; + int flags; + int status; + enum { + OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), + OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), + OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), + OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), +#if ENABLE_FEATURE_CP_LONG_OPTIONS + OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3), +#endif + }; + + // Need at least two arguments + // Soft- and hardlinking doesn't mix + // -P and -d are the same (-P is POSIX, -d is GNU) + // -r and -R are the same + // -R (and therefore -r) turns on -d (coreutils does this) + // -a = -pdR + opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR"; +#if ENABLE_FEATURE_CP_LONG_OPTIONS + applet_long_options = + "archive\0" No_argument "a" + "force\0" No_argument "f" + "interactive\0" No_argument "i" + "link\0" No_argument "l" + "dereference\0" No_argument "L" + "no-dereference\0" No_argument "P" + "recursive\0" No_argument "R" + "symbolic-link\0" No_argument "s" + "verbose\0" No_argument "v" + "parents\0" No_argument "\xff" + ; +#endif + // -v (--verbose) is ignored + flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv"); + /* Options of cp from GNU coreutils 6.10: + * -a, --archive + * -f, --force + * -i, --interactive + * -l, --link + * -L, --dereference + * -P, --no-dereference + * -R, -r, --recursive + * -s, --symbolic-link + * -v, --verbose + * -H follow command-line symbolic links in SOURCE + * -d same as --no-dereference --preserve=links + * -p same as --preserve=mode,ownership,timestamps + * -c same as --preserve=context + * --parents + * use full source file name under DIRECTORY + * NOT SUPPORTED IN BBOX: + * --backup[=CONTROL] + * make a backup of each existing destination file + * -b like --backup but does not accept an argument + * --copy-contents + * copy contents of special files when recursive + * --preserve[=ATTR_LIST] + * preserve attributes (default: mode,ownership,timestamps), + * if possible additional attributes: security context,links,all + * --no-preserve=ATTR_LIST + * --remove-destination + * remove each existing destination file before attempting to open + * --sparse=WHEN + * control creation of sparse files + * --strip-trailing-slashes + * remove any trailing slashes from each SOURCE argument + * -S, --suffix=SUFFIX + * override the usual backup suffix + * -t, --target-directory=DIRECTORY + * copy all SOURCE arguments into DIRECTORY + * -T, --no-target-directory + * treat DEST as a normal file + * -u, --update + * copy only when the SOURCE file is newer than the destination + * file or when the destination file is missing + * -x, --one-file-system + * stay on this file system + * -Z, --context=CONTEXT + * (SELinux) set SELinux security context of copy to CONTEXT + */ + argc -= optind; + argv += optind; + /* Reverse this bit. If there is -d, bit is not set: */ + flags ^= FILEUTILS_DEREFERENCE; + /* coreutils 6.9 compat: + * by default, "cp" derefs symlinks (creates regular dest files), + * but "cp -R" does not. We switch off deref if -r or -R (see above). + * However, "cp -RL" must still deref symlinks: */ + if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ + flags |= FILEUTILS_DEREFERENCE; + +#if ENABLE_SELINUX + if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { + selinux_or_die(); + } +#endif + + status = EXIT_SUCCESS; + last = argv[argc - 1]; + /* If there are only two arguments and... */ + if (argc == 2) { + s_flags = cp_mv_stat2(*argv, &source_stat, + (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); + if (s_flags < 0) + return EXIT_FAILURE; + d_flags = cp_mv_stat(last, &dest_stat); + if (d_flags < 0) + return EXIT_FAILURE; + +#if ENABLE_FEATURE_CP_LONG_OPTIONS + if (flags & OPT_parents) { + if (!(d_flags & 2)) { + bb_error_msg_and_die("with --parents, the destination must be a directory"); + } + } +#endif + + /* ...if neither is a directory... */ + if (!((s_flags | d_flags) & 2) + /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */ + || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) + ) { + /* Do a simple copy */ + dest = last; + goto DO_COPY; /* NB: argc==2 -> *++argv==last */ + } + } + + while (1) { +#if ENABLE_FEATURE_CP_LONG_OPTIONS + if (flags & OPT_parents) { + char *dest_dup; + char *dest_dir; + dest = concat_path_file(last, *argv); + dest_dup = xstrdup(dest); + dest_dir = dirname(dest_dup); + if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) { + return EXIT_FAILURE; + } + free(dest_dup); + goto DO_COPY; + } +#endif + dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); + DO_COPY: + if (copy_file(*argv, dest, flags) < 0) { + status = EXIT_FAILURE; + } + if (*++argv == last) { + /* possibly leaking dest... */ + break; + } + /* don't move up: dest may be == last and not malloced! */ + free((void*)dest); + } + + /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */ + return status; +} diff --git a/coreutils/cut.c b/coreutils/cut.c new file mode 100644 index 000000000..2c27b704f --- /dev/null +++ b/coreutils/cut.c @@ -0,0 +1,306 @@ +/* vi: set sw=4 ts=4: */ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley + * debloated by Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define cut_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define cut_full_usage "\n\n" +//usage: "Print selected fields from each input FILE to stdout\n" +//usage: "\n -b LIST Output only bytes from LIST" +//usage: "\n -c LIST Output only characters from LIST" +//usage: "\n -d CHAR Use CHAR instead of tab as the field delimiter" +//usage: "\n -s Output only the lines containing delimiter" +//usage: "\n -f N Print only these fields" +//usage: "\n -n Ignored" +//usage: +//usage:#define cut_example_usage +//usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" +//usage: "Hello\n" +//usage: "$ echo \"Hello world\" | cut -f 2 -d ' '\n" +//usage: "world\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +/* option vars */ +static const char optstring[] ALIGN1 = "b:c:f:d:sn"; +#define CUT_OPT_BYTE_FLGS (1 << 0) +#define CUT_OPT_CHAR_FLGS (1 << 1) +#define CUT_OPT_FIELDS_FLGS (1 << 2) +#define CUT_OPT_DELIM_FLGS (1 << 3) +#define CUT_OPT_SUPPRESS_FLGS (1 << 4) + +struct cut_list { + int startpos; + int endpos; +}; + +enum { + BOL = 0, + EOL = INT_MAX, + NON_RANGE = -1 +}; + +static int cmpfunc(const void *a, const void *b) +{ + return (((struct cut_list *) a)->startpos - + ((struct cut_list *) b)->startpos); +} + +static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, unsigned nlists) +{ + char *line; + unsigned linenum = 0; /* keep these zero-based to be consistent */ + + /* go through every line in the file */ + while ((line = xmalloc_fgetline(file)) != NULL) { + + /* set up a list so we can keep track of what's been printed */ + int linelen = strlen(line); + char *printed = xzalloc(linelen + 1); + char *orig_line = line; + unsigned cl_pos = 0; + int spos; + + /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ + if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { + /* print the chars specified in each cut list */ + for (; cl_pos < nlists; cl_pos++) { + spos = cut_lists[cl_pos].startpos; + while (spos < linelen) { + if (!printed[spos]) { + printed[spos] = 'X'; + putchar(line[spos]); + } + spos++; + if (spos > cut_lists[cl_pos].endpos + /* NON_RANGE is -1, so if below is true, + * the above was true too (spos is >= 0) */ + /* || cut_lists[cl_pos].endpos == NON_RANGE */ + ) { + break; + } + } + } + } else if (delim == '\n') { /* cut by lines */ + spos = cut_lists[cl_pos].startpos; + + /* get out if we have no more lists to process or if the lines + * are lower than what we're interested in */ + if (((int)linenum < spos) || (cl_pos >= nlists)) + goto next_line; + + /* if the line we're looking for is lower than the one we were + * passed, it means we displayed it already, so move on */ + while (spos < (int)linenum) { + spos++; + /* go to the next list if we're at the end of this one */ + if (spos > cut_lists[cl_pos].endpos + || cut_lists[cl_pos].endpos == NON_RANGE + ) { + cl_pos++; + /* get out if there's no more lists to process */ + if (cl_pos >= nlists) + goto next_line; + spos = cut_lists[cl_pos].startpos; + /* get out if the current line is lower than the one + * we just became interested in */ + if ((int)linenum < spos) + goto next_line; + } + } + + /* If we made it here, it means we've found the line we're + * looking for, so print it */ + puts(line); + goto next_line; + } else { /* cut by fields */ + int ndelim = -1; /* zero-based / one-based problem */ + int nfields_printed = 0; + char *field = NULL; + char delimiter[2]; + + delimiter[0] = delim; + delimiter[1] = 0; + + /* does this line contain any delimiters? */ + if (strchr(line, delim) == NULL) { + if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) + puts(line); + goto next_line; + } + + /* process each list on this line, for as long as we've got + * a line to process */ + for (; cl_pos < nlists && line; cl_pos++) { + spos = cut_lists[cl_pos].startpos; + do { + /* find the field we're looking for */ + while (line && ndelim < spos) { + field = strsep(&line, delimiter); + ndelim++; + } + + /* we found it, and it hasn't been printed yet */ + if (field && ndelim == spos && !printed[ndelim]) { + /* if this isn't our first time through, we need to + * print the delimiter after the last field that was + * printed */ + if (nfields_printed > 0) + putchar(delim); + fputs(field, stdout); + printed[ndelim] = 'X'; + nfields_printed++; /* shouldn't overflow.. */ + } + + spos++; + + /* keep going as long as we have a line to work with, + * this is a list, and we're not at the end of that + * list */ + } while (spos <= cut_lists[cl_pos].endpos && line + && cut_lists[cl_pos].endpos != NON_RANGE); + } + } + /* if we printed anything at all, we need to finish it with a + * newline cuz we were handed a chomped line */ + putchar('\n'); + next_line: + linenum++; + free(printed); + free(orig_line); + } +} + +int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cut_main(int argc UNUSED_PARAM, char **argv) +{ + /* growable array holding a series of lists */ + struct cut_list *cut_lists = NULL; + unsigned nlists = 0; /* number of elements in above list */ + char delim = '\t'; /* delimiter, default is tab */ + char *sopt, *ltok; + unsigned opt; + + opt_complementary = "b--bcf:c--bcf:f--bcf"; + opt = getopt32(argv, optstring, &sopt, &sopt, &sopt, <ok); +// argc -= optind; + argv += optind; + if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) + bb_error_msg_and_die("expected a list of bytes, characters, or fields"); + + if (opt & CUT_OPT_DELIM_FLGS) { + if (ltok[0] && ltok[1]) { /* more than 1 char? */ + bb_error_msg_and_die("the delimiter must be a single character"); + } + delim = ltok[0]; + } + + /* non-field (char or byte) cutting has some special handling */ + if (!(opt & CUT_OPT_FIELDS_FLGS)) { + static const char _op_on_field[] ALIGN1 = " only when operating on fields"; + + if (opt & CUT_OPT_SUPPRESS_FLGS) { + bb_error_msg_and_die + ("suppressing non-delimited lines makes sense%s", + _op_on_field); + } + if (delim != '\t') { + bb_error_msg_and_die + ("a delimiter may be specified%s", _op_on_field); + } + } + + /* + * parse list and put values into startpos and endpos. + * valid list formats: N, N-, N-M, -M + * more than one list can be separated by commas + */ + { + char *ntok; + int s = 0, e = 0; + + /* take apart the lists, one by one (they are separated with commas) */ + while ((ltok = strsep(&sopt, ",")) != NULL) { + + /* it's actually legal to pass an empty list */ + if (!ltok[0]) + continue; + + /* get the start pos */ + ntok = strsep(<ok, "-"); + if (!ntok[0]) { + s = BOL; + } else { + s = xatoi_positive(ntok); + /* account for the fact that arrays are zero based, while + * the user expects the first char on the line to be char #1 */ + if (s != 0) + s--; + } + + /* get the end pos */ + if (ltok == NULL) { + e = NON_RANGE; + } else if (!ltok[0]) { + e = EOL; + } else { + e = xatoi_positive(ltok); + /* if the user specified and end position of 0, + * that means "til the end of the line" */ + if (e == 0) + e = EOL; + e--; /* again, arrays are zero based, lines are 1 based */ + if (e == s) + e = NON_RANGE; + } + + /* add the new list */ + cut_lists = xrealloc_vector(cut_lists, 4, nlists); + /* NB: startpos is always >= 0, + * while endpos may be = NON_RANGE (-1) */ + cut_lists[nlists].startpos = s; + cut_lists[nlists].endpos = e; + nlists++; + } + + /* make sure we got some cut positions out of all that */ + if (nlists == 0) + bb_error_msg_and_die("missing list of positions"); + + /* now that the lists are parsed, we need to sort them to make life + * easier on us when it comes time to print the chars / fields / lines + */ + qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc); + } + + { + int retval = EXIT_SUCCESS; + + if (!*argv) + *--argv = (char *)"-"; + + do { + FILE *file = fopen_or_warn_stdin(*argv); + if (!file) { + retval = EXIT_FAILURE; + continue; + } + cut_file(file, delim, cut_lists, nlists); + fclose_if_not_stdin(file); + } while (*++argv); + + if (ENABLE_FEATURE_CLEAN_UP) + free(cut_lists); + fflush_stdout_and_exit(retval); + } +} diff --git a/coreutils/date.c b/coreutils/date.c new file mode 100644 index 000000000..6a7d5fac3 --- /dev/null +++ b/coreutils/date.c @@ -0,0 +1,382 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant + * + * iso-format handling added by Robert Griebl + * bugfixes and cleanup by Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. +*/ + +/* This 'date' command supports only 2 time setting formats, + all the GNU strftime stuff (its in libc, lets use it), + setting time using UTC and displaying it, as well as + an RFC 2822 compliant date output for shell scripting + mail commands */ + +/* Input parsing code is always bulky - used heavy duty libc stuff as + much as possible, missed out a lot of bounds checking */ + +//applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_DATE) += date.o + +//config:config DATE +//config: bool "date" +//config: default y +//config: help +//config: date is used to set the system date or display the +//config: current time in the given format. +//config: +//config:config FEATURE_DATE_ISOFMT +//config: bool "Enable ISO date format output (-I)" +//config: default y +//config: depends on DATE +//config: help +//config: Enable option (-I) to output an ISO-8601 compliant +//config: date/time string. +//config: +//config:# defaults to "no": stat's nanosecond field is a bit non-portable +//config:config FEATURE_DATE_NANO +//config: bool "Support %[num]N nanosecond format specifier" +//config: default n +//config: depends on DATE # syscall(__NR_clock_gettime) +//config: select PLATFORM_LINUX +//config: help +//config: Support %[num]N format specifier. Adds ~250 bytes of code. +//config: +//config:config FEATURE_DATE_COMPAT +//config: bool "Support weird 'date MMDDhhmm[[YY]YY][.ss]' format" +//config: default y +//config: depends on DATE +//config: help +//config: System time can be set by 'date -s DATE' and simply 'date DATE', +//config: but formats of DATE string are different. 'date DATE' accepts +//config: a rather weird MMDDhhmm[[YY]YY][.ss] format with completely +//config: unnatural placement of year between minutes and seconds. +//config: date -s (and other commands like touch -d) use more sensible +//config: formats (for one, ISO format YYYY-MM-DD hh:mm:ss.ssssss). +//config: +//config: With this option off, 'date DATE' is 'date -s DATE' support +//config: the same format. With it on, 'date DATE' additionally supports +//config: MMDDhhmm[[YY]YY][.ss] format. + +/* GNU coreutils 6.9 man page: + * date [OPTION]... [+FORMAT] + * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] + * -d, --date=STRING + * display time described by STRING, not `now' + * -f, --file=DATEFILE + * like --date once for each line of DATEFILE + * -r, --reference=FILE + * display the last modification time of FILE + * -R, --rfc-2822 + * output date and time in RFC 2822 format. + * Example: Mon, 07 Aug 2006 12:34:56 -0600 + * --rfc-3339=TIMESPEC + * output date and time in RFC 3339 format. + * TIMESPEC='date', 'seconds', or 'ns' + * Date and time components are separated by a single space: + * 2006-08-07 12:34:56-06:00 + * -s, --set=STRING + * set time described by STRING + * -u, --utc, --universal + * print or set Coordinated Universal Time + * + * Busybox: + * long options are not supported + * -f is not supported + * -I seems to roughly match --rfc-3339, but -I has _optional_ param + * (thus "-I seconds" doesn't work, only "-Iseconds"), + * and does not support -Ins + * -D FMT is a bbox extension for _input_ conversion of -d DATE + */ + +//usage:#define date_trivial_usage +//usage: "[OPTIONS] [+FMT] [TIME]" +//usage:#define date_full_usage "\n\n" +//usage: "Display time (using +FMT), or set time\n" +//usage: IF_NOT_LONG_OPTS( +//usage: "\n [-s] TIME Set time to TIME" +//usage: "\n -u Work in UTC (don't convert to local time)" +//usage: "\n -R Output RFC-2822 compliant date string" +//usage: ) IF_LONG_OPTS( +//usage: "\n [-s,--set] TIME Set time to TIME" +//usage: "\n -u,--utc Work in UTC (don't convert to local time)" +//usage: "\n -R,--rfc-2822 Output RFC-2822 compliant date string" +//usage: ) +//usage: IF_FEATURE_DATE_ISOFMT( +//usage: "\n -I[SPEC] Output ISO-8601 compliant date string" +//usage: "\n SPEC='date' (default) for date only," +//usage: "\n 'hours', 'minutes', or 'seconds' for date and" +//usage: "\n time to the indicated precision" +//usage: ) +//usage: IF_NOT_LONG_OPTS( +//usage: "\n -r FILE Display last modification time of FILE" +//usage: "\n -d TIME Display TIME, not 'now'" +//usage: ) IF_LONG_OPTS( +//usage: "\n -r,--reference FILE Display last modification time of FILE" +//usage: "\n -d,--date TIME Display TIME, not 'now'" +//usage: ) +//usage: IF_FEATURE_DATE_ISOFMT( +//usage: "\n -D FMT Use FMT for -d TIME conversion" +//usage: ) +//usage: "\n" +//usage: "\nRecognized TIME formats:" +//usage: "\n hh:mm[:ss]" +//usage: "\n [YYYY.]MM.DD-hh:mm[:ss]" +//usage: "\n YYYY-MM-DD hh:mm[:ss]" +//usage: "\n [[[[[YY]YY]MM]DD]hh]mm[.ss]" +//usage: +//usage:#define date_example_usage +//usage: "$ date\n" +//usage: "Wed Apr 12 18:52:41 MDT 2000\n" + +#include "libbb.h" +#if ENABLE_FEATURE_DATE_NANO +# include +#endif + +enum { + OPT_RFC2822 = (1 << 0), /* R */ + OPT_SET = (1 << 1), /* s */ + OPT_UTC = (1 << 2), /* u */ + OPT_DATE = (1 << 3), /* d */ + OPT_REFERENCE = (1 << 4), /* r */ + OPT_TIMESPEC = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */ + OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */ +}; + +static void maybe_set_utc(int opt) +{ + if (opt & OPT_UTC) + putenv((char*)"TZ=UTC0"); +} + +#if ENABLE_LONG_OPTS +static const char date_longopts[] ALIGN1 = + "rfc-822\0" No_argument "R" + "rfc-2822\0" No_argument "R" + "set\0" Required_argument "s" + "utc\0" No_argument "u" + /* "universal\0" No_argument "u" */ + "date\0" Required_argument "d" + "reference\0" Required_argument "r" + ; +#endif + +int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int date_main(int argc UNUSED_PARAM, char **argv) +{ + struct timespec ts; + struct tm tm_time; + char buf_fmt_dt2str[64]; + unsigned opt; + int ifmt = -1; + char *date_str; + char *fmt_dt2str; + char *fmt_str2dt; + char *filename; + char *isofmt_arg = NULL; + + opt_complementary = "d--s:s--d" + IF_FEATURE_DATE_ISOFMT(":R--I:I--R"); + IF_LONG_OPTS(applet_long_options = date_longopts;) + opt = getopt32(argv, "Rs:ud:r:" + IF_FEATURE_DATE_ISOFMT("I::D:"), + &date_str, &date_str, &filename + IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt)); + argv += optind; + maybe_set_utc(opt); + + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_TIMESPEC)) { + ifmt = 0; /* default is date */ + if (isofmt_arg) { + static const char isoformats[] ALIGN1 = + "date\0""hours\0""minutes\0""seconds\0"; /* ns? */ + ifmt = index_in_substrings(isoformats, isofmt_arg); + if (ifmt < 0) + bb_show_usage(); + } + } + + fmt_dt2str = NULL; + if (argv[0] && argv[0][0] == '+') { + fmt_dt2str = &argv[0][1]; /* skip over the '+' */ + argv++; + } + if (!(opt & (OPT_SET | OPT_DATE))) { + opt |= OPT_SET; + date_str = argv[0]; /* can be NULL */ + if (date_str) { +#if ENABLE_FEATURE_DATE_COMPAT + int len = strspn(date_str, "0123456789"); + if (date_str[len] == '\0' + || (date_str[len] == '.' + && isdigit(date_str[len+1]) + && isdigit(date_str[len+2]) + && date_str[len+3] == '\0' + ) + ) { + /* Dreaded MMDDhhmm[[CC]YY][.ss] format! + * It does not match -d or -s format. + * Some users actually do use it. + */ + len -= 8; + if (len < 0 || len > 4 || (len & 1)) + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + if (len != 0) { /* move YY or CCYY to front */ + char buf[4]; + memcpy(buf, date_str + 8, len); + memmove(date_str + len, date_str, 8); + memcpy(date_str, buf, len); + } + } +#endif + argv++; + } + } + if (*argv) + bb_show_usage(); + + /* Now we have parsed all the information except the date format + * which depends on whether the clock is being set or read */ + + if (opt & OPT_REFERENCE) { + struct stat statbuf; + xstat(filename, &statbuf); + ts.tv_sec = statbuf.st_mtime; +#if ENABLE_FEATURE_DATE_NANO + ts.tv_nsec = statbuf.st_mtim.tv_nsec; + /* Some toolchains use .st_mtimensec instead of st_mtim.tv_nsec. + * If you need #define _SVID_SOURCE 1 to enable st_mtim.tv_nsec, + * drop a mail to project mailing list please + */ +#endif + } else { +#if ENABLE_FEATURE_DATE_NANO + /* libc has incredibly messy way of doing this, + * typically requiring -lrt. We just skip all this mess */ + syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts); +#else + time(&ts.tv_sec); +#endif + } + localtime_r(&ts.tv_sec, &tm_time); + + /* If date string is given, update tm_time, and maybe set date */ + if (date_str != NULL) { + /* Zero out fields - take her back to midnight! */ + tm_time.tm_sec = 0; + tm_time.tm_min = 0; + tm_time.tm_hour = 0; + + /* Process any date input to UNIX time since 1 Jan 1970 */ + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_HINT)) { + if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } else { + parse_datestr(date_str, &tm_time); + } + + /* Correct any day of week and day of year etc. fields */ + /* Be sure to recheck dst (but not if date is time_t format) */ + if (date_str[0] != '@') + tm_time.tm_isdst = -1; + ts.tv_sec = validate_tm_time(date_str, &tm_time); + + maybe_set_utc(opt); + + /* if setting time, set it */ + if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) { + bb_perror_msg("can't set date"); + } + } + + /* Display output */ + + /* Deal with format string */ + if (fmt_dt2str == NULL) { + int i; + fmt_dt2str = buf_fmt_dt2str; + if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) { + /* -I[SPEC]: 0:date 1:hours 2:minutes 3:seconds */ + strcpy(fmt_dt2str, "%Y-%m-%dT%H:%M:%S"); + i = 8 + 3 * ifmt; + if (ifmt != 0) { + /* TODO: if (ifmt==4) i += sprintf(&fmt_dt2str[i], ",%09u", nanoseconds); */ + format_utc: + fmt_dt2str[i++] = '%'; + fmt_dt2str[i++] = (opt & OPT_UTC) ? 'Z' : 'z'; + } + fmt_dt2str[i] = '\0'; + } else if (opt & OPT_RFC2822) { + /* -R. undo busybox.c setlocale */ + if (ENABLE_LOCALE_SUPPORT) + setlocale(LC_TIME, "C"); + strcpy(fmt_dt2str, "%a, %d %b %Y %H:%M:%S "); + i = sizeof("%a, %d %b %Y %H:%M:%S ")-1; + goto format_utc; + } else { /* default case */ + fmt_dt2str = (char*)"%a %b %e %H:%M:%S %Z %Y"; + } + } +#if ENABLE_FEATURE_DATE_NANO + else { + /* User-specified fmt_dt2str */ + /* Search for and process "%N" */ + char *p = fmt_dt2str; + while ((p = strchr(p, '%')) != NULL) { + int n, m; + unsigned pres, scale; + + p++; + if (*p == '%') { + p++; + continue; + } + n = strspn(p, "0123456789"); + if (p[n] != 'N') { + p += n; + continue; + } + /* We have "%[nnn]N" */ + p[-1] = '\0'; + p[n] = '\0'; + scale = 1; + pres = 9; + if (n) { + pres = xatoi_positive(p); + if (pres == 0) + pres = 9; + m = 9 - pres; + while (--m >= 0) + scale *= 10; + } + + m = p - fmt_dt2str; + p += n + 1; + fmt_dt2str = xasprintf("%s%0*u%s", fmt_dt2str, pres, (unsigned)ts.tv_nsec / scale, p); + p = fmt_dt2str + m; + } + } +#endif + +#define date_buf bb_common_bufsiz1 + if (*fmt_dt2str == '\0') { + /* With no format string, just print a blank line */ + date_buf[0] = '\0'; + } else { + /* Handle special conversions */ + if (strncmp(fmt_dt2str, "%f", 2) == 0) { + fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S"; + } + /* Generate output string */ + strftime(date_buf, sizeof(date_buf), fmt_dt2str, &tm_time); + } + puts(date_buf); + + return EXIT_SUCCESS; +} diff --git a/coreutils/dd.c b/coreutils/dd.c new file mode 100644 index 000000000..96602ebdd --- /dev/null +++ b/coreutils/dd.c @@ -0,0 +1,439 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dd implementation for busybox + * + * + * Copyright (C) 2000,2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define dd_trivial_usage +//usage: "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n" +//usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]") +//usage:#define dd_full_usage "\n\n" +//usage: "Copy a file with converting and formatting\n" +//usage: "\n if=FILE Read from FILE instead of stdin" +//usage: "\n of=FILE Write to FILE instead of stdout" +//usage: "\n bs=N Read and write N bytes at a time" +//usage: IF_FEATURE_DD_IBS_OBS( +//usage: "\n ibs=N Read N bytes at a time" +//usage: ) +//usage: IF_FEATURE_DD_IBS_OBS( +//usage: "\n obs=N Write N bytes at a time" +//usage: ) +//usage: "\n count=N Copy only N input blocks" +//usage: "\n skip=N Skip N input blocks" +//usage: "\n seek=N Skip N output blocks" +//usage: IF_FEATURE_DD_IBS_OBS( +//usage: "\n conv=notrunc Don't truncate output file" +//usage: "\n conv=noerror Continue after read errors" +//usage: "\n conv=sync Pad blocks with zeros" +//usage: "\n conv=fsync Physically write data out before finishing" +//usage: ) +//usage: "\n" +//usage: "\nNumbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024)," +//usage: "\nMD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)" +//usage: +//usage:#define dd_example_usage +//usage: "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" +//usage: "4+0 records in\n" +//usage: "4+0 records out\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +enum { + ifd = STDIN_FILENO, + ofd = STDOUT_FILENO, +}; + +static const struct suffix_mult dd_suffixes[] = { + { "c", 1 }, + { "w", 2 }, + { "b", 512 }, + { "kD", 1000 }, + { "k", 1024 }, + { "K", 1024 }, /* compat with coreutils dd */ + { "MD", 1000000 }, + { "M", 1048576 }, + { "GD", 1000000000 }, + { "G", 1073741824 }, + { "", 0 } +}; + +struct globals { + off_t out_full, out_part, in_full, in_part; +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + unsigned long long total_bytes; + unsigned long long begin_time_us; +#endif +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { \ + /* we have to zero it out because of NOEXEC */ \ + memset(&G, 0, sizeof(G)); \ +} while (0) + + +static void dd_output_status(int UNUSED_PARAM cur_signal) +{ +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + double seconds; + unsigned long long bytes_sec; + unsigned long long now_us = monotonic_us(); /* before fprintf */ +#endif + + /* Deliberately using %u, not %d */ + fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n" + "%"OFF_FMT"u+%"OFF_FMT"u records out\n", + G.in_full, G.in_part, + G.out_full, G.out_part); + +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + fprintf(stderr, "%llu bytes (%sB) copied, ", + G.total_bytes, + /* show fractional digit, use suffixes */ + make_human_readable_str(G.total_bytes, 1, 0) + ); + /* Corner cases: + * ./busybox dd /dev/null + * ./busybox dd bs=1M count=2000 /dev/null + * (echo DONE) | ./busybox dd >/dev/null + * (sleep 1; echo DONE) | ./busybox dd >/dev/null + */ + seconds = (now_us - G.begin_time_us) / 1000000.0; + bytes_sec = G.total_bytes / seconds; + fprintf(stderr, "%f seconds, %sB/s\n", + seconds, + /* show fractional digit, use suffixes */ + make_human_readable_str(bytes_sec, 1, 0) + ); +#endif +} + +static ssize_t full_write_or_warn(const void *buf, size_t len, + const char *const filename) +{ + ssize_t n = full_write(ofd, buf, len); + if (n < 0) + bb_perror_msg("writing '%s'", filename); + return n; +} + +static bool write_and_stats(const void *buf, size_t len, size_t obs, + const char *filename) +{ + ssize_t n = full_write_or_warn(buf, len, filename); + if (n < 0) + return 1; + if ((size_t)n == obs) + G.out_full++; + else if (n) /* > 0 */ + G.out_part++; +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + G.total_bytes += n; +#endif + return 0; +} + +#if ENABLE_LFS +# define XATOU_SFX xatoull_sfx +#else +# define XATOU_SFX xatoul_sfx +#endif + +int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dd_main(int argc UNUSED_PARAM, char **argv) +{ + enum { + /* Must be in the same order as OP_conv_XXX! */ + /* (see "flags |= (1 << what)" below) */ + FLAG_NOTRUNC = 1 << 0, + FLAG_SYNC = 1 << 1, + FLAG_NOERROR = 1 << 2, + FLAG_FSYNC = 1 << 3, + /* end of conv flags */ + FLAG_TWOBUFS = 1 << 4, + FLAG_COUNT = 1 << 5, + }; + static const char keywords[] ALIGN1 = + "bs\0""count\0""seek\0""skip\0""if\0""of\0" +#if ENABLE_FEATURE_DD_IBS_OBS + "ibs\0""obs\0""conv\0" +#endif + ; +#if ENABLE_FEATURE_DD_IBS_OBS + static const char conv_words[] ALIGN1 = + "notrunc\0""sync\0""noerror\0""fsync\0"; +#endif + enum { + OP_bs = 0, + OP_count, + OP_seek, + OP_skip, + OP_if, + OP_of, +#if ENABLE_FEATURE_DD_IBS_OBS + OP_ibs, + OP_obs, + OP_conv, + /* Must be in the same order as FLAG_XXX! */ + OP_conv_notrunc = 0, + OP_conv_sync, + OP_conv_noerror, + OP_conv_fsync, + /* Unimplemented conv=XXX: */ + //nocreat do not create the output file + //excl fail if the output file already exists + //fdatasync physically write output file data before finishing + //swab swap every pair of input bytes + //lcase change upper case to lower case + //ucase change lower case to upper case + //block pad newline-terminated records with spaces to cbs-size + //unblock replace trailing spaces in cbs-size records with newline + //ascii from EBCDIC to ASCII + //ebcdic from ASCII to EBCDIC + //ibm from ASCII to alternate EBCDIC +#endif + }; + int exitcode = EXIT_FAILURE; + size_t ibs = 512, obs = 512; + ssize_t n, w; + char *ibuf, *obuf; + /* And these are all zeroed at once! */ + struct { + int flags; + size_t oc; + off_t count; + off_t seek, skip; + const char *infile, *outfile; + } Z; +#define flags (Z.flags ) +#define oc (Z.oc ) +#define count (Z.count ) +#define seek (Z.seek ) +#define skip (Z.skip ) +#define infile (Z.infile ) +#define outfile (Z.outfile) + + memset(&Z, 0, sizeof(Z)); + INIT_G(); + //fflush_all(); - is this needed because of NOEXEC? + + for (n = 1; argv[n]; n++) { + int what; + char *val; + char *arg = argv[n]; + +#if ENABLE_DESKTOP + /* "dd --". NB: coreutils 6.9 will complain if they see + * more than one of them. We wouldn't. */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') + continue; +#endif + val = strchr(arg, '='); + if (val == NULL) + bb_show_usage(); + *val = '\0'; + what = index_in_strings(keywords, arg); + if (what < 0) + bb_show_usage(); + /* *val = '='; - to preserve ps listing? */ + val++; +#if ENABLE_FEATURE_DD_IBS_OBS + if (what == OP_ibs) { + /* Must fit into positive ssize_t */ + ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); + /*continue;*/ + } + if (what == OP_obs) { + obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); + /*continue;*/ + } + if (what == OP_conv) { + while (1) { + /* find ',', replace them with NUL so we can use val for + * index_in_strings() without copying. + * We rely on val being non-null, else strchr would fault. + */ + arg = strchr(val, ','); + if (arg) + *arg = '\0'; + what = index_in_strings(conv_words, val); + if (what < 0) + bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv"); + flags |= (1 << what); + if (!arg) /* no ',' left, so this was the last specifier */ + break; + /* *arg = ','; - to preserve ps listing? */ + val = arg + 1; /* skip this keyword and ',' */ + } + continue; /* we trashed 'what', can't fall through */ + } +#endif + if (what == OP_bs) { + ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); + /*continue;*/ + } + /* These can be large: */ + if (what == OP_count) { + flags |= FLAG_COUNT; + count = XATOU_SFX(val, dd_suffixes); + /*continue;*/ + } + if (what == OP_seek) { + seek = XATOU_SFX(val, dd_suffixes); + /*continue;*/ + } + if (what == OP_skip) { + skip = XATOU_SFX(val, dd_suffixes); + /*continue;*/ + } + if (what == OP_if) { + infile = val; + /*continue;*/ + } + if (what == OP_of) { + outfile = val; + /*continue;*/ + } + } /* end of "for (argv[n])" */ + +//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever + ibuf = obuf = xmalloc(ibs); + if (ibs != obs) { + flags |= FLAG_TWOBUFS; + obuf = xmalloc(obs); + } + +#if ENABLE_FEATURE_DD_SIGNAL_HANDLING + signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status); +#endif +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + G.begin_time_us = monotonic_us(); +#endif + + if (infile != NULL) + xmove_fd(xopen(infile, O_RDONLY), ifd); + else { + infile = bb_msg_standard_input; + } + if (outfile != NULL) { + int oflag = O_WRONLY | O_CREAT; + + if (!seek && !(flags & FLAG_NOTRUNC)) + oflag |= O_TRUNC; + + xmove_fd(xopen(outfile, oflag), ofd); + + if (seek && !(flags & FLAG_NOTRUNC)) { + if (ftruncate(ofd, seek * obs) < 0) { + struct stat st; + + if (fstat(ofd, &st) < 0 + || S_ISREG(st.st_mode) + || S_ISDIR(st.st_mode) + ) { + goto die_outfile; + } + } + } + } else { + outfile = bb_msg_standard_output; + } + if (skip) { + if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) { + while (skip-- > 0) { + n = safe_read(ifd, ibuf, ibs); + if (n < 0) + goto die_infile; + if (n == 0) + break; + } + } + } + if (seek) { + if (lseek(ofd, seek * obs, SEEK_CUR) < 0) + goto die_outfile; + } + + while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { + n = safe_read(ifd, ibuf, ibs); + if (n == 0) + break; + if (n < 0) { + /* "Bad block" */ + if (!(flags & FLAG_NOERROR)) + goto die_infile; + bb_simple_perror_msg(infile); + /* GNU dd with conv=noerror skips over bad blocks */ + xlseek(ifd, ibs, SEEK_CUR); + /* conv=noerror,sync writes NULs, + * conv=noerror just ignores input bad blocks */ + n = 0; + } + if ((size_t)n == ibs) + G.in_full++; + else { + G.in_part++; + if (flags & FLAG_SYNC) { + memset(ibuf + n, 0, ibs - n); + n = ibs; + } + } + if (flags & FLAG_TWOBUFS) { + char *tmp = ibuf; + while (n) { + size_t d = obs - oc; + + if (d > (size_t)n) + d = n; + memcpy(obuf + oc, tmp, d); + n -= d; + tmp += d; + oc += d; + if (oc == obs) { + if (write_and_stats(obuf, obs, obs, outfile)) + goto out_status; + oc = 0; + } + } + } else if (write_and_stats(ibuf, n, obs, outfile)) + goto out_status; + + if (flags & FLAG_FSYNC) { + if (fsync(ofd) < 0) + goto die_outfile; + } + } + + if (ENABLE_FEATURE_DD_IBS_OBS && oc) { + w = full_write_or_warn(obuf, oc, outfile); + if (w < 0) goto out_status; + if (w > 0) G.out_part++; + } + if (close(ifd) < 0) { + die_infile: + bb_simple_perror_msg_and_die(infile); + } + + if (close(ofd) < 0) { + die_outfile: + bb_simple_perror_msg_and_die(outfile); + } + + exitcode = EXIT_SUCCESS; + out_status: + dd_output_status(0); + + if (ENABLE_FEATURE_CLEAN_UP) { + free(obuf); + if (flags & FLAG_TWOBUFS) + free(ibuf); + } + + return exitcode; +} diff --git a/coreutils/df.c b/coreutils/df.c new file mode 100644 index 000000000..63dbd61bd --- /dev/null +++ b/coreutils/df.c @@ -0,0 +1,255 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * based on original code by (I think) Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. Removed floating point dependency. Added error checking + * on output. Output stats on 0-sized filesystems if specifically listed on + * the command line. Properly round *-blocks, Used, and Available quantities. + * + * Aug 28, 2008 Bernhard Reutner-Fischer + * + * Implement -P and -B; better coreutils compat; cleanup + */ + +//usage:#define df_trivial_usage +//usage: "[-Pk" +//usage: IF_FEATURE_HUMAN_READABLE("mh") +//usage: IF_FEATURE_DF_FANCY("ai] [-B SIZE") +//usage: "] [FILESYSTEM]..." +//usage:#define df_full_usage "\n\n" +//usage: "Print filesystem usage statistics\n" +//usage: "\n -P POSIX output format" +//usage: "\n -k 1024-byte blocks (default)" +//usage: IF_FEATURE_HUMAN_READABLE( +//usage: "\n -m 1M-byte blocks" +//usage: "\n -h Human readable (e.g. 1K 243M 2G)" +//usage: ) +//usage: IF_FEATURE_DF_FANCY( +//usage: "\n -a Show all filesystems" +//usage: "\n -i Inodes" +//usage: "\n -B SIZE Blocksize" +//usage: ) +//usage: +//usage:#define df_example_usage +//usage: "$ df\n" +//usage: "Filesystem 1K-blocks Used Available Use% Mounted on\n" +//usage: "/dev/sda3 8690864 8553540 137324 98% /\n" +//usage: "/dev/sda1 64216 36364 27852 57% /boot\n" +//usage: "$ df /dev/sda3\n" +//usage: "Filesystem 1K-blocks Used Available Use% Mounted on\n" +//usage: "/dev/sda3 8690864 8553540 137324 98% /\n" +//usage: "$ POSIXLY_CORRECT=sure df /dev/sda3\n" +//usage: "Filesystem 512B-blocks Used Available Use% Mounted on\n" +//usage: "/dev/sda3 17381728 17107080 274648 98% /\n" +//usage: "$ POSIXLY_CORRECT=yep df -P /dev/sda3\n" +//usage: "Filesystem 512-blocks Used Available Capacity Mounted on\n" +//usage: "/dev/sda3 17381728 17107080 274648 98% /\n" + +#include +#include +#include "libbb.h" +#include "unicode.h" + +#if !ENABLE_FEATURE_HUMAN_READABLE +static unsigned long kscale(unsigned long b, unsigned long bs) +{ + return (b * (unsigned long long) bs + 1024/2) / 1024; +} +#endif + +int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int df_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned long blocks_used; + unsigned blocks_percent_used; + unsigned long df_disp_hr = 1024; + int status = EXIT_SUCCESS; + unsigned opt; + FILE *mount_table; + struct mntent *mount_entry; + struct statfs s; + + enum { + OPT_KILO = (1 << 0), + OPT_POSIX = (1 << 1), + OPT_ALL = (1 << 2) * ENABLE_FEATURE_DF_FANCY, + OPT_INODE = (1 << 3) * ENABLE_FEATURE_DF_FANCY, + OPT_BSIZE = (1 << 4) * ENABLE_FEATURE_DF_FANCY, + OPT_HUMAN = (1 << (2 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, + OPT_MEGA = (1 << (3 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, + }; + const char *disp_units_hdr = NULL; + char *chp; + + init_unicode(); + +#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY + opt_complementary = "k-mB:m-Bk:B-km"; +#elif ENABLE_FEATURE_HUMAN_READABLE + opt_complementary = "k-m:m-k"; +#endif + opt = getopt32(argv, "kP" + IF_FEATURE_DF_FANCY("aiB:") + IF_FEATURE_HUMAN_READABLE("hm") + IF_FEATURE_DF_FANCY(, &chp)); + if (opt & OPT_MEGA) + df_disp_hr = 1024*1024; + + if (opt & OPT_BSIZE) + df_disp_hr = xatoul_range(chp, 1, ULONG_MAX); /* disallow 0 */ + + /* From the manpage of df from coreutils-6.10: + Disk space is shown in 1K blocks by default, unless the environment + variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used. + */ + if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ + df_disp_hr = 512; + + if (opt & OPT_HUMAN) { + df_disp_hr = 0; + disp_units_hdr = " Size"; + } + if (opt & OPT_INODE) + disp_units_hdr = " Inodes"; + + if (disp_units_hdr == NULL) { +#if ENABLE_FEATURE_HUMAN_READABLE + disp_units_hdr = xasprintf("%s-blocks", + /* print df_disp_hr, show no fractionals, + * use suffixes if OPT_POSIX is set in opt */ + make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) + ); +#else + disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr); +#endif + } + printf("Filesystem %-15sUsed Available %s Mounted on\n", + disp_units_hdr, (opt & OPT_POSIX) ? "Capacity" : "Use%"); + + mount_table = NULL; + argv += optind; + if (!argv[0]) { + mount_table = setmntent(bb_path_mtab_file, "r"); + if (!mount_table) + bb_perror_msg_and_die(bb_path_mtab_file); + } + + while (1) { + const char *device; + const char *mount_point; + + if (mount_table) { + mount_entry = getmntent(mount_table); + if (!mount_entry) { + endmntent(mount_table); + break; + } + } else { + mount_point = *argv++; + if (!mount_point) + break; + mount_entry = find_mount_point(mount_point, 1); + if (!mount_entry) { + bb_error_msg("%s: can't find mount point", mount_point); + set_error: + status = EXIT_FAILURE; + continue; + } + } + + device = mount_entry->mnt_fsname; + mount_point = mount_entry->mnt_dir; + + if (statfs(mount_point, &s) != 0) { + bb_simple_perror_msg(mount_point); + goto set_error; + } + + if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) { + if (opt & OPT_INODE) { + s.f_blocks = s.f_files; + s.f_bavail = s.f_bfree = s.f_ffree; + s.f_bsize = 1; + + if (df_disp_hr) + df_disp_hr = 1; + } + blocks_used = s.f_blocks - s.f_bfree; + blocks_percent_used = 0; + if (blocks_used + s.f_bavail) { + blocks_percent_used = (blocks_used * 100ULL + + (blocks_used + s.f_bavail)/2 + ) / (blocks_used + s.f_bavail); + } + + /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */ + if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0) + continue; + +#ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY + if (strcmp(device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + device = find_block_device("/"); + if (!device) { + goto set_error; + } + } +#endif + +#if ENABLE_UNICODE_SUPPORT + { + uni_stat_t uni_stat; + char *uni_dev = unicode_conv_to_printable(&uni_stat, device); + if (uni_stat.unicode_width > 20 && !(opt & OPT_POSIX)) { + printf("%s\n%20s", uni_dev, ""); + } else { + printf("%s%*s", uni_dev, 20 - (int)uni_stat.unicode_width, ""); + } + free(uni_dev); + } +#else + if (printf("\n%-20s" + 1, device) > 20 && !(opt & OPT_POSIX)) + printf("\n%-20s", ""); +#endif + +#if ENABLE_FEATURE_HUMAN_READABLE + printf(" %9s ", + /* f_blocks x f_bsize / df_disp_hr, show one fractional, + * use suffixes if df_disp_hr == 0 */ + make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); + + printf(" %9s " + 1, + /* EXPR x f_bsize / df_disp_hr, show one fractional, + * use suffixes if df_disp_hr == 0 */ + make_human_readable_str((s.f_blocks - s.f_bfree), + s.f_bsize, df_disp_hr)); + + printf("%9s %3u%% %s\n", + /* f_bavail x f_bsize / df_disp_hr, show one fractional, + * use suffixes if df_disp_hr == 0 */ + make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + printf(" %9lu %9lu %9lu %3u%% %s\n", + kscale(s.f_blocks, s.f_bsize), + kscale(s.f_blocks - s.f_bfree, s.f_bsize), + kscale(s.f_bavail, s.f_bsize), + blocks_percent_used, mount_point); +#endif + } + } + + return status; +} diff --git a/coreutils/dirname.c b/coreutils/dirname.c new file mode 100644 index 000000000..101067c90 --- /dev/null +++ b/coreutils/dirname.c @@ -0,0 +1,33 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */ + +//usage:#define dirname_trivial_usage +//usage: "FILENAME" +//usage:#define dirname_full_usage "\n\n" +//usage: "Strip non-directory suffix from FILENAME" +//usage: +//usage:#define dirname_example_usage +//usage: "$ dirname /tmp/foo\n" +//usage: "/tmp\n" +//usage: "$ dirname /tmp/foo/\n" +//usage: "/tmp\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int dirname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dirname_main(int argc UNUSED_PARAM, char **argv) +{ + puts(dirname(single_argv(argv))); + return fflush_all(); +} diff --git a/coreutils/dos2unix.c b/coreutils/dos2unix.c new file mode 100644 index 000000000..07398bdfa --- /dev/null +++ b/coreutils/dos2unix.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * dos2unix for BusyBox + * + * dos2unix '\n' convertor 0.5.0 + * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997) + * Copyright 1997,.. by Peter Hanecak . + * All rights reserved. + * + * dos2unix filters reading input from stdin and writing output to stdout. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. +*/ + +//usage:#define dos2unix_trivial_usage +//usage: "[-ud] [FILE]" +//usage:#define dos2unix_full_usage "\n\n" +//usage: "Convert FILE in-place from DOS to Unix format.\n" +//usage: "When no file is given, use stdin/stdout.\n" +//usage: "\n -u dos2unix" +//usage: "\n -d unix2dos" +//usage: +//usage:#define unix2dos_trivial_usage +//usage: "[-ud] [FILE]" +//usage:#define unix2dos_full_usage "\n\n" +//usage: "Convert FILE in-place from Unix to DOS format.\n" +//usage: "When no file is given, use stdin/stdout.\n" +//usage: "\n -u dos2unix" +//usage: "\n -d unix2dos" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +enum { + CT_UNIX2DOS = 1, + CT_DOS2UNIX +}; + +/* if fn is NULL then input is stdin and output is stdout */ +static void convert(char *fn, int conv_type) +{ + FILE *in, *out; + int i; + char *temp_fn = temp_fn; /* for compiler */ + char *resolved_fn = resolved_fn; + + in = stdin; + out = stdout; + if (fn != NULL) { + struct stat st; + + resolved_fn = xmalloc_follow_symlinks(fn); + if (resolved_fn == NULL) + bb_simple_perror_msg_and_die(fn); + in = xfopen_for_read(resolved_fn); + fstat(fileno(in), &st); + + temp_fn = xasprintf("%sXXXXXX", resolved_fn); + i = xmkstemp(temp_fn); + if (fchmod(i, st.st_mode) == -1) + bb_simple_perror_msg_and_die(temp_fn); + + out = xfdopen_for_write(i); + } + + while ((i = fgetc(in)) != EOF) { + if (i == '\r') + continue; + if (i == '\n') + if (conv_type == CT_UNIX2DOS) + fputc('\r', out); + fputc(i, out); + } + + if (fn != NULL) { + if (fclose(in) < 0 || fclose(out) < 0) { + unlink(temp_fn); + bb_perror_nomsg_and_die(); + } + xrename(temp_fn, resolved_fn); + free(temp_fn); + free(resolved_fn); + } +} + +int dos2unix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dos2unix_main(int argc UNUSED_PARAM, char **argv) +{ + int o, conv_type; + + /* See if we are supposed to be doing dos2unix or unix2dos */ + conv_type = CT_UNIX2DOS; + if (applet_name[0] == 'd') { + conv_type = CT_DOS2UNIX; + } + + /* -u convert to unix, -d convert to dos */ + opt_complementary = "u--d:d--u"; /* mutually exclusive */ + o = getopt32(argv, "du"); + + /* Do the conversion requested by an argument else do the default + * conversion depending on our name. */ + if (o) + conv_type = o; + + argv += optind; + do { + /* might be convert(NULL) if there is no filename given */ + convert(*argv, conv_type); + } while (*argv && *++argv); + + return 0; +} diff --git a/coreutils/du.c b/coreutils/du.c new file mode 100644 index 000000000..b8bbe3d9e --- /dev/null +++ b/coreutils/du.c @@ -0,0 +1,267 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini du implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * Copyright (C) 2002 Edward Betts + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Mostly rewritten for SUSv3 compliance and to fix bugs/defects. + * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options. + * The -d option allows setting of max depth (similar to gnu --max-depth). + * 2) Fixed incorrect size calculations for links and directories, especially + * when errors occurred. Calculates sizes should now match gnu du output. + * 3) Added error checking of output. + * 4) Fixed busybox bug #1284 involving long overflow with human_readable. + */ + +//usage:#define du_trivial_usage +//usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..." +//usage:#define du_full_usage "\n\n" +//usage: "Summarize disk space used for each FILE and/or directory.\n" +//usage: "Disk space is printed in units of " +//usage: IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("1024") +//usage: IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("512") +//usage: " bytes.\n" +//usage: "\n -a Show file sizes too" +//usage: "\n -L Follow all symlinks" +//usage: "\n -H Follow symlinks on command line" +//usage: "\n -d N Limit output to directories (and files with -a) of depth < N" +//usage: "\n -c Show grand total" +//usage: "\n -l Count sizes many times if hard linked" +//usage: "\n -s Display only a total for each argument" +//usage: "\n -x Skip directories on different filesystems" +//usage: IF_FEATURE_HUMAN_READABLE( +//usage: "\n -h Sizes in human readable format (e.g., 1K 243M 2G )" +//usage: "\n -m Sizes in megabytes" +//usage: ) +//usage: "\n -k Sizes in kilobytes" +//usage: IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)") +//usage: +//usage:#define du_example_usage +//usage: "$ du\n" +//usage: "16 ./CVS\n" +//usage: "12 ./kernel-patches/CVS\n" +//usage: "80 ./kernel-patches\n" +//usage: "12 ./tests/CVS\n" +//usage: "36 ./tests\n" +//usage: "12 ./scripts/CVS\n" +//usage: "16 ./scripts\n" +//usage: "12 ./docs/CVS\n" +//usage: "104 ./docs\n" +//usage: "2417 .\n" + +#include "libbb.h" + +enum { + OPT_a_files_too = (1 << 0), + OPT_H_follow_links = (1 << 1), + OPT_k_kbytes = (1 << 2), + OPT_L_follow_links = (1 << 3), + OPT_s_total_norecurse = (1 << 4), + OPT_x_one_FS = (1 << 5), + OPT_d_maxdepth = (1 << 6), + OPT_l_hardlinks = (1 << 7), + OPT_c_total = (1 << 8), + OPT_h_for_humans = (1 << 9), + OPT_m_mbytes = (1 << 10), +}; + +struct globals { +#if ENABLE_FEATURE_HUMAN_READABLE + unsigned long disp_hr; +#else + unsigned disp_k; +#endif + int max_print_depth; + bool status; + int slink_depth; + int du_depth; + dev_t dir_dev; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) + + +static void print(unsigned long size, const char *filename) +{ + /* TODO - May not want to defer error checking here. */ +#if ENABLE_FEATURE_HUMAN_READABLE + printf("%s\t%s\n", + /* size x 512 / G.disp_hr, show one fractional, + * use suffixes if G.disp_hr == 0 */ + make_human_readable_str(size, 512, G.disp_hr), + filename); +#else + if (G.disp_k) { + size++; + size >>= 1; + } + printf("%lu\t%s\n", size, filename); +#endif +} + +/* tiny recursive du */ +static unsigned long du(const char *filename) +{ + struct stat statbuf; + unsigned long sum; + + if (lstat(filename, &statbuf) != 0) { + bb_simple_perror_msg(filename); + G.status = EXIT_FAILURE; + return 0; + } + + if (option_mask32 & OPT_x_one_FS) { + if (G.du_depth == 0) { + G.dir_dev = statbuf.st_dev; + } else if (G.dir_dev != statbuf.st_dev) { + return 0; + } + } + + sum = statbuf.st_blocks; + + if (S_ISLNK(statbuf.st_mode)) { + if (G.slink_depth > G.du_depth) { /* -H or -L */ + if (stat(filename, &statbuf) != 0) { + bb_simple_perror_msg(filename); + G.status = EXIT_FAILURE; + return 0; + } + sum = statbuf.st_blocks; + if (G.slink_depth == 1) { + /* Convert -H to -L */ + G.slink_depth = INT_MAX; + } + } + } + + if (!(option_mask32 & OPT_l_hardlinks) + && statbuf.st_nlink > 1 + ) { + /* Add files/directories with links only once */ + if (is_in_ino_dev_hashtable(&statbuf)) { + return 0; + } + add_to_ino_dev_hashtable(&statbuf, NULL); + } + + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + struct dirent *entry; + char *newfile; + + dir = warn_opendir(filename); + if (!dir) { + G.status = EXIT_FAILURE; + return sum; + } + + while ((entry = readdir(dir))) { + newfile = concat_subpath_file(filename, entry->d_name); + if (newfile == NULL) + continue; + ++G.du_depth; + sum += du(newfile); + --G.du_depth; + free(newfile); + } + closedir(dir); + } else { + if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0) + return sum; + } + if (G.du_depth <= G.max_print_depth) { + print(sum, filename); + } + return sum; +} + +int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int du_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned long total; + int slink_depth_save; + unsigned opt; + +#if ENABLE_FEATURE_HUMAN_READABLE + IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;) + IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;) + if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ + G.disp_hr = 512; +#else + IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;) + /* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */ +#endif + G.max_print_depth = INT_MAX; + + /* Note: SUSv3 specifies that -a and -s options cannot be used together + * in strictly conforming applications. However, it also says that some + * du implementations may produce output when -a and -s are used together. + * gnu du exits with an error code in this case. We choose to simply + * ignore -a. This is consistent with -s being equivalent to -d 0. + */ +#if ENABLE_FEATURE_HUMAN_READABLE + opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+"; + opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth); + argv += optind; + if (opt & OPT_h_for_humans) { + G.disp_hr = 0; + } + if (opt & OPT_m_mbytes) { + G.disp_hr = 1024*1024; + } + if (opt & OPT_k_kbytes) { + G.disp_hr = 1024; + } +#else + opt_complementary = "H-L:L-H:s-d:d-s:d+"; + opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth); + argv += optind; +#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K + if (opt & OPT_k_kbytes) { + G.disp_k = 1; + } +#endif +#endif + if (opt & OPT_H_follow_links) { + G.slink_depth = 1; + } + if (opt & OPT_L_follow_links) { + G.slink_depth = INT_MAX; + } + if (opt & OPT_s_total_norecurse) { + G.max_print_depth = 0; + } + + /* go through remaining args (if any) */ + if (!*argv) { + *--argv = (char*)"."; + if (G.slink_depth == 1) { + G.slink_depth = 0; + } + } + + slink_depth_save = G.slink_depth; + total = 0; + do { + total += du(*argv); + /* otherwise du /dir /dir won't show /dir twice: */ + reset_ino_dev_hashtable(); + G.slink_depth = slink_depth_save; + } while (*++argv); + + if (opt & OPT_c_total) + print(total, "total"); + + fflush_stdout_and_exit(G.status); +} diff --git a/coreutils/echo.c b/coreutils/echo.c new file mode 100644 index 000000000..9663894ec --- /dev/null +++ b/coreutils/echo.c @@ -0,0 +1,332 @@ +/* vi: set sw=4 ts=4: */ +/* + * echo implementation for busybox + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice is retained at the end of this file. + */ + +/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Because of behavioral differences, implemented configurable SUSv3 + * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs. + * 1) In handling '\c' escape, the previous version only suppressed the + * trailing newline. SUSv3 specifies _no_ output after '\c'. + * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. + * The previous version did not allow 4-digit octals. + */ + +//usage:#define echo_trivial_usage +//usage: IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..." +//usage:#define echo_full_usage "\n\n" +//usage: "Print the specified ARGs to stdout" +//usage: IF_FEATURE_FANCY_ECHO( "\n" +//usage: "\n -n Suppress trailing newline" +//usage: "\n -e Interpret backslash escapes (i.e., \\t=tab)" +//usage: "\n -E Don't interpret backslash escapes (default)" +//usage: ) +//usage: +//usage:#define echo_example_usage +//usage: "$ echo \"Erik is cool\"\n" +//usage: "Erik is cool\n" +//usage: IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n" +//usage: "Erik\n" +//usage: "is\n" +//usage: "cool\n" +//usage: "$ echo \"Erik\\nis\\ncool\"\n" +//usage: "Erik\\nis\\ncool\n") + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +/* NB: can be used by shell even if not enabled as applet */ + +/* + * NB2: we don't use stdio, we need better error handing. + * Examples include writing into non-opened stdout and error on write. + * + * With stdio, output gets shoveled into stdout buffer, and even + * fflush cannot clear it out. It seems that even if libc receives + * EBADF on write attempts, it feels determined to output data no matter what. + * If echo is called by shell, it will try writing again later, and possibly + * will clobber future output. Not good. + * + * Solaris has fpurge which discards buffered input. glibc has __fpurge. + * But this function is not standard. + */ + +int echo_main(int argc UNUSED_PARAM, char **argv) +{ + char **pp; + const char *arg; + char *out; + char *buffer; + unsigned buflen; +#if !ENABLE_FEATURE_FANCY_ECHO + enum { + eflag = '\\', + nflag = 1, /* 1 -- print '\n' */ + }; + + argv++; +#else + char nflag = 1; + char eflag = 0; + + while ((arg = *++argv) != NULL) { + char n, e; + + if (arg[0] != '-') + break; /* not an option arg, echo it */ + + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + arg++; + n = nflag; + e = eflag; + do { + if (*arg == 'n') + n = 0; + else if (*arg == 'e') + e = '\\'; + else if (*arg != 'E') { + /* "-ccc" arg with one of c's invalid, echo it */ + /* arg consisting from just "-" also handled here */ + goto just_echo; + } + } while (*++arg); + nflag = n; + eflag = e; + } + just_echo: +#endif + + buflen = 0; + pp = argv; + while ((arg = *pp) != NULL) { + buflen += strlen(arg) + 1; + pp++; + } + out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */ + + while ((arg = *argv) != NULL) { + int c; + + if (!eflag) { + /* optimization for very common case */ + out = stpcpy(out, arg); + } else + while ((c = *arg++) != '\0') { + if (c == eflag) { + /* This is an "\x" sequence */ + + if (*arg == 'c') { + /* "\c" means cancel newline and + * ignore all subsequent chars. */ + goto do_write; + } + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0') { + if ((unsigned char)(arg[1] - '0') < 8) { + /* 2nd char is 0..7: skip leading '0' */ + arg++; + } + } + /* bb_process_escape_sequence handles NUL correctly + * ("...\" case). */ + { + /* optimization: don't force arg to be on-stack, + * use another variable for that. ~30 bytes win */ + const char *z = arg; + c = bb_process_escape_sequence(&z); + arg = z; + } + } + *out++ = c; + } + + if (!*++argv) + break; + *out++ = ' '; + } + + if (nflag) { + *out++ = '\n'; + } + + do_write: + /* Careful to error out on partial writes too (think ENOSPC!) */ + errno = 0; + /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer); + free(buffer); + if (/*WRONG:r < 0*/ errno) { + bb_perror_msg(bb_msg_write_error); + return 1; + } + return 0; +} + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)echo.c 8.1 (Berkeley) 5/31/93 + */ + +#ifdef VERSION_WITH_WRITEV +/* We can't use stdio. + * The reason for this is highly non-obvious. + * echo_main is used from shell. Shell must correctly handle "echo foo" + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. + * + * Using writev instead, with 'direct' conversion of argv vector. + */ + +int echo_main(int argc, char **argv) +{ + struct iovec io[argc]; + struct iovec *cur_io = io; + char *arg; + char *p; +#if !ENABLE_FEATURE_FANCY_ECHO + enum { + eflag = '\\', + nflag = 1, /* 1 -- print '\n' */ + }; + arg = *++argv; + if (!arg) + goto newline_ret; +#else + char nflag = 1; + char eflag = 0; + + while (1) { + arg = *++argv; + if (!arg) + goto newline_ret; + if (*arg != '-') + break; + + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + p = arg + 1; + if (!*p) /* A single '-', so echo it. */ + goto just_echo; + + do { + if (!strchr("neE", *p)) + goto just_echo; + } while (*++p); + + /* All of the options in this arg are valid, so handle them. */ + p = arg + 1; + do { + if (*p == 'n') + nflag = 0; + if (*p == 'e') + eflag = '\\'; + } while (*++p); + } + just_echo: +#endif + + while (1) { + /* arg is already == *argv and isn't NULL */ + int c; + + cur_io->iov_base = p = arg; + + if (!eflag) { + /* optimization for very common case */ + p += strlen(arg); + } else while ((c = *arg++)) { + if (c == eflag) { + /* This is an "\x" sequence */ + + if (*arg == 'c') { + /* "\c" means cancel newline and + * ignore all subsequent chars. */ + cur_io->iov_len = p - (char*)cur_io->iov_base; + cur_io++; + goto ret; + } + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) { + arg++; + } + /* bb_process_escape_sequence can handle nul correctly */ + c = bb_process_escape_sequence( (void*) &arg); + } + *p++ = c; + } + + arg = *++argv; + if (arg) + *p++ = ' '; + cur_io->iov_len = p - (char*)cur_io->iov_base; + cur_io++; + if (!arg) + break; + } + + newline_ret: + if (nflag) { + cur_io->iov_base = (char*)"\n"; + cur_io->iov_len = 1; + cur_io++; + } + ret: + /* TODO: implement and use full_writev? */ + return writev(1, io, (cur_io - io)) >= 0; +} +#endif diff --git a/coreutils/env.c b/coreutils/env.c new file mode 100644 index 000000000..807ef13e9 --- /dev/null +++ b/coreutils/env.c @@ -0,0 +1,132 @@ +/* vi: set sw=4 ts=4: */ +/* + * env implementation for busybox + * + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice is retained at the end of this file. + * + * Modified for BusyBox by Erik Andersen + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed bug involving exit return codes if execvp fails. Also added + * output error checking. + */ + +/* + * Modified by Vladimir Oleynik (C) 2003 + * - correct "-" option usage + * - multiple "-u unsetenv" support + * - GNU long option support + * - use xfunc_error_retval + */ + +/* This is a NOEXEC applet. Be very careful! */ + +//usage:#define env_trivial_usage +//usage: "[-iu] [-] [name=value]... [PROG ARGS]" +//usage:#define env_full_usage "\n\n" +//usage: "Print the current environment or run PROG after setting up\n" +//usage: "the specified environment\n" +//usage: "\n -, -i Start with an empty environment" +//usage: "\n -u Remove variable from the environment" + +#include "libbb.h" + +#if ENABLE_FEATURE_ENV_LONG_OPTIONS +static const char env_longopts[] ALIGN1 = + "ignore-environment\0" No_argument "i" + "unset\0" Required_argument "u" + ; +#endif + +int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int env_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opts; + llist_t *unset_env = NULL; + + opt_complementary = "u::"; +#if ENABLE_FEATURE_ENV_LONG_OPTIONS + applet_long_options = env_longopts; +#endif + opts = getopt32(argv, "+iu:", &unset_env); + argv += optind; + if (argv[0] && LONE_DASH(argv[0])) { + opts |= 1; + ++argv; + } + if (opts & 1) { + clearenv(); + } + while (unset_env) { + char *var = llist_pop(&unset_env); + /* This does not handle -uVAR=VAL + * (coreutils _sets_ the variable in that case): */ + /*unsetenv(var);*/ + /* This does, but uses somewhan undocumented feature that + * putenv("name_without_equal_sign") unsets the variable: */ + putenv(var); + } + + while (*argv && (strchr(*argv, '=') != NULL)) { + if (putenv(*argv) < 0) { + bb_perror_msg_and_die("putenv"); + } + ++argv; + } + + if (argv[0]) { + BB_EXECVP_or_die(argv); + } + + if (environ) { /* clearenv() may set environ == NULL! */ + char **ep; + for (ep = environ; *ep; ep++) { + puts(*ep); + } + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change + * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/coreutils/expand.c b/coreutils/expand.c new file mode 100644 index 000000000..25bbffc66 --- /dev/null +++ b/coreutils/expand.c @@ -0,0 +1,233 @@ +/* expand - convert tabs to spaces + * unexpand - convert spaces to tabs + * + * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * David MacKenzie + * + * Options for expand: + * -t num --tabs=NUM Convert tabs to num spaces (default 8 spaces). + * -i --initial Only convert initial tabs on each line to spaces. + * + * Options for unexpand: + * -a --all Convert all blanks, instead of just initial blanks. + * -f --first-only Convert only leading sequences of blanks (default). + * -t num --tabs=NUM Have tabs num characters apart instead of 8. + * + * Busybox version (C) 2007 by Tito Ragusa + * + * Caveat: this versions of expand and unexpand don't accept tab lists. + */ + +//usage:#define expand_trivial_usage +//usage: "[-i] [-t N] [FILE]..." +//usage:#define expand_full_usage "\n\n" +//usage: "Convert tabs to spaces, writing to stdout\n" +//usage: IF_FEATURE_EXPAND_LONG_OPTIONS( +//usage: "\n -i,--initial Don't convert tabs after non blanks" +//usage: "\n -t,--tabs=N Tabstops every N chars" +//usage: ) +//usage: IF_NOT_FEATURE_EXPAND_LONG_OPTIONS( +//usage: "\n -i Don't convert tabs after non blanks" +//usage: "\n -t Tabstops every N chars" +//usage: ) + +//usage:#define unexpand_trivial_usage +//usage: "[-fa][-t N] [FILE]..." +//usage:#define unexpand_full_usage "\n\n" +//usage: "Convert spaces to tabs, writing to stdout\n" +//usage: IF_FEATURE_UNEXPAND_LONG_OPTIONS( +//usage: "\n -a,--all Convert all blanks" +//usage: "\n -f,--first-only Convert only leading blanks" +//usage: "\n -t,--tabs=N Tabstops every N chars" +//usage: ) +//usage: IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS( +//usage: "\n -a Convert all blanks" +//usage: "\n -f Convert only leading blanks" +//usage: "\n -t N Tabstops every N chars" +//usage: ) + +#include "libbb.h" +#include "unicode.h" + +enum { + OPT_INITIAL = 1 << 0, + OPT_TABS = 1 << 1, + OPT_ALL = 1 << 2, +}; + +#if ENABLE_EXPAND +static void expand(FILE *file, unsigned tab_size, unsigned opt) +{ + char *line; + + while ((line = xmalloc_fgets(file)) != NULL) { + unsigned char c; + char *ptr; + char *ptr_strbeg; + + ptr = ptr_strbeg = line; + while ((c = *ptr) != '\0') { + if ((opt & OPT_INITIAL) && !isblank(c)) { + /* not space or tab */ + break; + } + if (c == '\t') { + unsigned len; + *ptr = '\0'; +# if ENABLE_UNICODE_SUPPORT + { + uni_stat_t uni_stat; + printable_string(&uni_stat, ptr_strbeg); + len = uni_stat.unicode_width; + } +# else + len = ptr - ptr_strbeg; +# endif + len = tab_size - (len % tab_size); + /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */ + printf("%s%*s", ptr_strbeg, len, ""); + ptr_strbeg = ptr + 1; + } + ptr++; + } + fputs(ptr_strbeg, stdout); + free(line); + } +} +#endif + +#if ENABLE_UNEXPAND +static void unexpand(FILE *file, unsigned tab_size, unsigned opt) +{ + char *line; + + while ((line = xmalloc_fgets(file)) != NULL) { + char *ptr = line; + unsigned column = 0; + + while (*ptr) { + unsigned n; + unsigned len = 0; + + while (*ptr == ' ') { + ptr++; + len++; + } + column += len; + if (*ptr == '\t') { + column += tab_size - (column % tab_size); + ptr++; + continue; + } + + n = column / tab_size; + if (n) { + len = column = column % tab_size; + while (n--) + putchar('\t'); + } + + if ((opt & OPT_INITIAL) && ptr != line) { + printf("%*s%s", len, "", ptr); + break; + } + n = strcspn(ptr, "\t "); + printf("%*s%.*s", len, "", n, ptr); +# if ENABLE_UNICODE_SUPPORT + { + char c; + uni_stat_t uni_stat; + c = ptr[n]; + ptr[n] = '\0'; + printable_string(&uni_stat, ptr); + len = uni_stat.unicode_width; + ptr[n] = c; + } +# else + len = n; +# endif + ptr += n; + column = (column + len) % tab_size; + } + free(line); + } +} +#endif + +int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int expand_main(int argc UNUSED_PARAM, char **argv) +{ + /* Default 8 spaces for 1 tab */ + const char *opt_t = "8"; + FILE *file; + unsigned tab_size; + unsigned opt; + int exit_status = EXIT_SUCCESS; + +#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS + static const char expand_longopts[] ALIGN1 = + /* name, has_arg, val */ + "initial\0" No_argument "i" + "tabs\0" Required_argument "t" + ; +#endif +#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS + static const char unexpand_longopts[] ALIGN1 = + /* name, has_arg, val */ + "first-only\0" No_argument "i" + "tabs\0" Required_argument "t" + "all\0" No_argument "a" + ; +#endif + init_unicode(); + + if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) { + IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts); + opt = getopt32(argv, "it:", &opt_t); + } else { + IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts); + /* -t NUM sets also -a */ + opt_complementary = "ta"; + opt = getopt32(argv, "ft:a", &opt_t); + /* -f --first-only is the default */ + if (!(opt & OPT_ALL)) opt |= OPT_INITIAL; + } + tab_size = xatou_range(opt_t, 1, UINT_MAX); + + argv += optind; + + if (!*argv) { + *--argv = (char*)bb_msg_standard_input; + } + do { + file = fopen_or_warn_stdin(*argv); + if (!file) { + exit_status = EXIT_FAILURE; + continue; + } + + if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) + IF_EXPAND(expand(file, tab_size, opt)); + else + IF_UNEXPAND(unexpand(file, tab_size, opt)); + + /* Check and close the file */ + if (fclose_if_not_stdin(file)) { + bb_simple_perror_msg(*argv); + exit_status = EXIT_FAILURE; + } + /* If stdin also clear EOF */ + if (file == stdin) + clearerr(file); + } while (*++argv); + + /* Now close stdin also */ + /* (if we didn't read from it, it's a no-op) */ + if (fclose(stdin)) + bb_perror_msg_and_die(bb_msg_standard_input); + + fflush_stdout_and_exit(exit_status); +} diff --git a/coreutils/expr.c b/coreutils/expr.c new file mode 100644 index 000000000..24e75b556 --- /dev/null +++ b/coreutils/expr.c @@ -0,0 +1,535 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * Copyright (C) 2003-2005 Vladimir Oleynik + * - reduced 464 bytes. + * - 64 math support + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* This program evaluates expressions. Each token (operator, operand, + * parenthesis) of the expression must be a separate argument. The + * parser used is a reasonably general one, though any incarnation of + * it is language-specific. It is especially nice for expressions. + * + * No parse tree is needed; a new node is evaluated immediately. + * One function can handle multiple operators all of equal precedence, + * provided they all associate ((x op x) op x). */ + +/* no getopt needed */ + +//usage:#define expr_trivial_usage +//usage: "EXPRESSION" +//usage:#define expr_full_usage "\n\n" +//usage: "Print the value of EXPRESSION to stdout\n" +//usage: "\n" +//usage: "EXPRESSION may be:\n" +//usage: " ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" +//usage: " ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" +//usage: " ARG1 < ARG2 1 if ARG1 is less than ARG2, else 0. Similarly:\n" +//usage: " ARG1 <= ARG2\n" +//usage: " ARG1 = ARG2\n" +//usage: " ARG1 != ARG2\n" +//usage: " ARG1 >= ARG2\n" +//usage: " ARG1 > ARG2\n" +//usage: " ARG1 + ARG2 Sum of ARG1 and ARG2. Similarly:\n" +//usage: " ARG1 - ARG2\n" +//usage: " ARG1 * ARG2\n" +//usage: " ARG1 / ARG2\n" +//usage: " ARG1 % ARG2\n" +//usage: " STRING : REGEXP Anchored pattern match of REGEXP in STRING\n" +//usage: " match STRING REGEXP Same as STRING : REGEXP\n" +//usage: " substr STRING POS LENGTH Substring of STRING, POS counted from 1\n" +//usage: " index STRING CHARS Index in STRING where any CHARS is found, or 0\n" +//usage: " length STRING Length of STRING\n" +//usage: " quote TOKEN Interpret TOKEN as a string, even if\n" +//usage: " it is a keyword like 'match' or an\n" +//usage: " operator like '/'\n" +//usage: " (EXPRESSION) Value of EXPRESSION\n" +//usage: "\n" +//usage: "Beware that many operators need to be escaped or quoted for shells.\n" +//usage: "Comparisons are arithmetic if both ARGs are numbers, else\n" +//usage: "lexicographical. Pattern matches return the string matched between\n" +//usage: "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" +//usage: "of characters matched or 0." + +#include "libbb.h" +#include "xregex.h" + +#if ENABLE_EXPR_MATH_SUPPORT_64 +typedef int64_t arith_t; + +#define PF_REZ "ll" +#define PF_REZ_TYPE (long long) +#define STRTOL(s, e, b) strtoll(s, e, b) +#else +typedef long arith_t; + +#define PF_REZ "l" +#define PF_REZ_TYPE (long) +#define STRTOL(s, e, b) strtol(s, e, b) +#endif + +/* TODO: use bb_strtol[l]? It's easier to check for errors... */ + +/* The kinds of value we can have. */ +enum { + INTEGER, + STRING +}; + +/* A value is.... */ +struct valinfo { + smallint type; /* Which kind. */ + union { /* The value itself. */ + arith_t i; + char *s; + } u; +}; +typedef struct valinfo VALUE; + +/* The arguments given to the program, minus the program name. */ +struct globals { + char **args; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) + +/* forward declarations */ +static VALUE *eval(void); + + +/* Return a VALUE for I. */ + +static VALUE *int_value(arith_t i) +{ + VALUE *v; + + v = xzalloc(sizeof(VALUE)); + if (INTEGER) /* otherwise xzaaloc did it already */ + v->type = INTEGER; + v->u.i = i; + return v; +} + +/* Return a VALUE for S. */ + +static VALUE *str_value(const char *s) +{ + VALUE *v; + + v = xzalloc(sizeof(VALUE)); + if (STRING) /* otherwise xzaaloc did it already */ + v->type = STRING; + v->u.s = xstrdup(s); + return v; +} + +/* Free VALUE V, including structure components. */ + +static void freev(VALUE *v) +{ + if (v->type == STRING) + free(v->u.s); + free(v); +} + +/* Return nonzero if V is a null-string or zero-number. */ + +static int null(VALUE *v) +{ + if (v->type == INTEGER) + return v->u.i == 0; + /* STRING: */ + return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); +} + +/* Coerce V to a STRING value (can't fail). */ + +static void tostring(VALUE *v) +{ + if (v->type == INTEGER) { + v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i); + v->type = STRING; + } +} + +/* Coerce V to an INTEGER value. Return 1 on success, 0 on failure. */ + +static bool toarith(VALUE *v) +{ + if (v->type == STRING) { + arith_t i; + char *e; + + /* Don't interpret the empty string as an integer. */ + /* Currently does not worry about overflow or int/long differences. */ + i = STRTOL(v->u.s, &e, 10); + if ((v->u.s == e) || *e) + return 0; + free(v->u.s); + v->u.i = i; + v->type = INTEGER; + } + return 1; +} + +/* Return str[0]+str[1] if the next token matches STR exactly. + STR must not be NULL. */ + +static int nextarg(const char *str) +{ + if (*G.args == NULL || strcmp(*G.args, str) != 0) + return 0; + return (unsigned char)str[0] + (unsigned char)str[1]; +} + +/* The comparison operator handling functions. */ + +static int cmp_common(VALUE *l, VALUE *r, int op) +{ + arith_t ll, rr; + + ll = l->u.i; + rr = r->u.i; + if (l->type == STRING || r->type == STRING) { + tostring(l); + tostring(r); + ll = strcmp(l->u.s, r->u.s); + rr = 0; + } + /* calculating ll - rr and checking the result is prone to overflows. + * We'll do it differently: */ + if (op == '<') + return ll < rr; + if (op == ('<' + '=')) + return ll <= rr; + if (op == '=' || (op == '=' + '=')) + return ll == rr; + if (op == '!' + '=') + return ll != rr; + if (op == '>') + return ll > rr; + /* >= */ + return ll >= rr; +} + +/* The arithmetic operator handling functions. */ + +static arith_t arithmetic_common(VALUE *l, VALUE *r, int op) +{ + arith_t li, ri; + + if (!toarith(l) || !toarith(r)) + bb_error_msg_and_die("non-numeric argument"); + li = l->u.i; + ri = r->u.i; + if (op == '+') + return li + ri; + if (op == '-') + return li - ri; + if (op == '*') + return li * ri; + if (ri == 0) + bb_error_msg_and_die("division by zero"); + if (op == '/') + return li / ri; + return li % ri; +} + +/* Do the : operator. + SV is the VALUE for the lhs (the string), + PV is the VALUE for the rhs (the pattern). */ + +static VALUE *docolon(VALUE *sv, VALUE *pv) +{ + enum { NMATCH = 2 }; + VALUE *v; + regex_t re_buffer; + regmatch_t re_regs[NMATCH]; + + tostring(sv); + tostring(pv); + + if (pv->u.s[0] == '^') { + bb_error_msg( +"warning: '%s': using '^' as the first character\n" +"of a basic regular expression is not portable; it is ignored", pv->u.s); + } + + memset(&re_buffer, 0, sizeof(re_buffer)); + memset(re_regs, 0, sizeof(re_regs)); + xregcomp(&re_buffer, pv->u.s, 0); + + /* expr uses an anchored pattern match, so check that there was a + * match and that the match starts at offset 0. */ + if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH + && re_regs[0].rm_so == 0 + ) { + /* Were \(...\) used? */ + if (re_buffer.re_nsub > 0 && re_regs[1].rm_so >= 0) { + sv->u.s[re_regs[1].rm_eo] = '\0'; + v = str_value(sv->u.s + re_regs[1].rm_so); + } else { + v = int_value(re_regs[0].rm_eo); + } + } else { + /* Match failed -- return the right kind of null. */ + if (re_buffer.re_nsub > 0) + v = str_value(""); + else + v = int_value(0); + } + regfree(&re_buffer); + return v; +} + +/* Handle bare operands and ( expr ) syntax. */ + +static VALUE *eval7(void) +{ + VALUE *v; + + if (!*G.args) + bb_error_msg_and_die("syntax error"); + + if (nextarg("(")) { + G.args++; + v = eval(); + if (!nextarg(")")) + bb_error_msg_and_die("syntax error"); + G.args++; + return v; + } + + if (nextarg(")")) + bb_error_msg_and_die("syntax error"); + + return str_value(*G.args++); +} + +/* Handle match, substr, index, length, and quote keywords. */ + +static VALUE *eval6(void) +{ + static const char keywords[] ALIGN1 = + "quote\0""length\0""match\0""index\0""substr\0"; + + VALUE *r, *i1, *i2; + VALUE *l = l; /* silence gcc */ + VALUE *v = v; /* silence gcc */ + int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0; + + if (key == 0) /* not a keyword */ + return eval7(); + G.args++; /* We have a valid token, so get the next argument. */ + if (key == 1) { /* quote */ + if (!*G.args) + bb_error_msg_and_die("syntax error"); + return str_value(*G.args++); + } + if (key == 2) { /* length */ + r = eval6(); + tostring(r); + v = int_value(strlen(r->u.s)); + freev(r); + } else + l = eval6(); + + if (key == 3) { /* match */ + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + } + if (key == 4) { /* index */ + r = eval6(); + tostring(l); + tostring(r); + v = int_value(strcspn(l->u.s, r->u.s) + 1); + if (v->u.i == (arith_t) strlen(l->u.s) + 1) + v->u.i = 0; + freev(l); + freev(r); + } + if (key == 5) { /* substr */ + i1 = eval6(); + i2 = eval6(); + tostring(l); + if (!toarith(i1) || !toarith(i2) + || i1->u.i > (arith_t) strlen(l->u.s) + || i1->u.i <= 0 || i2->u.i <= 0) + v = str_value(""); + else { + v = xmalloc(sizeof(VALUE)); + v->type = STRING; + v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i); + } + freev(l); + freev(i1); + freev(i2); + } + return v; +} + +/* Handle : operator (pattern matching). + Calls docolon to do the real work. */ + +static VALUE *eval5(void) +{ + VALUE *l, *r, *v; + + l = eval6(); + while (nextarg(":")) { + G.args++; + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + l = v; + } + return l; +} + +/* Handle *, /, % operators. */ + +static VALUE *eval4(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval5(); + while (1) { + op = nextarg("*"); + if (!op) { op = nextarg("/"); + if (!op) { op = nextarg("%"); + if (!op) return l; + }} + G.args++; + r = eval5(); + val = arithmetic_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval4(); + while (1) { + op = nextarg("+"); + if (!op) { + op = nextarg("-"); + if (!op) return l; + } + G.args++; + r = eval4(); + val = arithmetic_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval3(); + while (1) { + op = nextarg("<"); + if (!op) { op = nextarg("<="); + if (!op) { op = nextarg("="); + if (!op) { op = nextarg("=="); + if (!op) { op = nextarg("!="); + if (!op) { op = nextarg(">="); + if (!op) { op = nextarg(">"); + if (!op) return l; + }}}}}} + G.args++; + r = eval3(); + toarith(l); + toarith(r); + val = cmp_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle &. */ + +static VALUE *eval1(void) +{ + VALUE *l, *r; + + l = eval2(); + while (nextarg("&")) { + G.args++; + r = eval2(); + if (null(l) || null(r)) { + freev(l); + freev(r); + l = int_value(0); + } else + freev(r); + } + return l; +} + +/* Handle |. */ + +static VALUE *eval(void) +{ + VALUE *l, *r; + + l = eval1(); + while (nextarg("|")) { + G.args++; + r = eval1(); + if (null(l)) { + freev(l); + l = r; + } else + freev(r); + } + return l; +} + +int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int expr_main(int argc UNUSED_PARAM, char **argv) +{ + VALUE *v; + + xfunc_error_retval = 2; /* coreutils compat */ + G.args = argv + 1; + if (*G.args == NULL) { + bb_error_msg_and_die("too few arguments"); + } + v = eval(); + if (*G.args) + bb_error_msg_and_die("syntax error"); + if (v->type == INTEGER) + printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i); + else + puts(v->u.s); + fflush_stdout_and_exit(null(v)); +} diff --git a/coreutils/false.c b/coreutils/false.c new file mode 100644 index 000000000..59c2f321a --- /dev/null +++ b/coreutils/false.c @@ -0,0 +1,31 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini false implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */ + +//usage:#define false_trivial_usage +//usage: "" +//usage:#define false_full_usage "\n\n" +//usage: "Return an exit code of FALSE (1)" +//usage: +//usage:#define false_example_usage +//usage: "$ false\n" +//usage: "$ echo $?\n" +//usage: "1\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int false_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int false_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + return EXIT_FAILURE; +} diff --git a/coreutils/fold.c b/coreutils/fold.c new file mode 100644 index 000000000..0e7306377 --- /dev/null +++ b/coreutils/fold.c @@ -0,0 +1,176 @@ +/* vi: set sw=4 ts=4: */ +/* fold -- wrap each input line to fit in specified width. + + Written by David MacKenzie, djm@gnu.ai.mit.edu. + Copyright (C) 91, 1995-2002 Free Software Foundation, Inc. + + Modified for busybox based on coreutils v 5.0 + Copyright (C) 2003 Glenn McGrath + + Licensed under GPLv2 or later, see file LICENSE in this source tree. +*/ + +//usage:#define fold_trivial_usage +//usage: "[-bs] [-w WIDTH] [FILE]..." +//usage:#define fold_full_usage "\n\n" +//usage: "Wrap input lines in each FILE (or stdin), writing to stdout\n" +//usage: "\n -b Count bytes rather than columns" +//usage: "\n -s Break at spaces" +//usage: "\n -w Use WIDTH columns instead of 80" + +#include "libbb.h" +#include "unicode.h" + +/* This is a NOEXEC applet. Be very careful! */ + +/* Must match getopt32 call */ +#define FLAG_COUNT_BYTES 1 +#define FLAG_BREAK_SPACES 2 +#define FLAG_WIDTH 4 + +/* Assuming the current column is COLUMN, return the column that + printing C will move the cursor to. + The first column is 0. */ +static int adjust_column(unsigned column, char c) +{ + if (option_mask32 & FLAG_COUNT_BYTES) + return ++column; + + if (c == '\t') + return column + 8 - column % 8; + + if (c == '\b') { + if ((int)--column < 0) + column = 0; + } + else if (c == '\r') + column = 0; + else { /* just a printable char */ + if (unicode_status != UNICODE_ON /* every byte is a new char */ + || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */ + ) { + column++; + } + } + return column; +} + +/* Note that this function can write NULs, unlike fputs etc. */ +static void write2stdout(const void *buf, unsigned size) +{ + fwrite(buf, 1, size, stdout); +} + +int fold_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fold_main(int argc UNUSED_PARAM, char **argv) +{ + char *line_out = NULL; + const char *w_opt = "80"; + unsigned width; + smallint exitcode = EXIT_SUCCESS; + + init_unicode(); + + if (ENABLE_INCLUDE_SUSv2) { + /* Turn any numeric options into -w options. */ + int i; + for (i = 1; argv[i]; i++) { + const char *a = argv[i]; + if (*a == '-') { + a++; + if (*a == '-' && !a[1]) /* "--" */ + break; + if (isdigit(*a)) + argv[i] = xasprintf("-w%s", a); + } + } + } + + getopt32(argv, "bsw:", &w_opt); + width = xatou_range(w_opt, 1, 10000); + + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + do { + FILE *istream = fopen_or_warn_stdin(*argv); + int c; + unsigned column = 0; /* Screen column where next char will go */ + unsigned offset_out = 0; /* Index in 'line_out' for next char */ + + if (istream == NULL) { + exitcode = EXIT_FAILURE; + continue; + } + + while ((c = getc(istream)) != EOF) { + /* We grow line_out in chunks of 0x1000 bytes */ + if ((offset_out & 0xfff) == 0) { + line_out = xrealloc(line_out, offset_out + 0x1000); + } + rescan: + line_out[offset_out] = c; + if (c == '\n') { + write2stdout(line_out, offset_out + 1); + column = offset_out = 0; + continue; + } + column = adjust_column(column, c); + if (column <= width || offset_out == 0) { + /* offset_out == 0 case happens + * with small width (say, 1) and tabs. + * The very first tab already goes to column 8, + * but we must not wrap it */ + offset_out++; + continue; + } + + /* This character would make the line too long. + * Print the line plus a newline, and make this character + * start the next line */ + if (option_mask32 & FLAG_BREAK_SPACES) { + unsigned i; + unsigned logical_end; + + /* Look for the last blank. */ + for (logical_end = offset_out - 1; (int)logical_end >= 0; logical_end--) { + if (!isblank(line_out[logical_end])) + continue; + + /* Found a space or tab. + * Output up to and including it, and start a new line */ + logical_end++; + /*line_out[logical_end] = '\n'; - NO! this nukes one buffered character */ + write2stdout(line_out, logical_end); + putchar('\n'); + /* Move the remainder to the beginning of the next line. + * The areas being copied here might overlap. */ + memmove(line_out, line_out + logical_end, offset_out - logical_end); + offset_out -= logical_end; + for (column = i = 0; i < offset_out; i++) { + column = adjust_column(column, line_out[i]); + } + goto rescan; + } + /* No blank found, wrap will split the overlong word */ + } + /* Output what we accumulated up to now, and start a new line */ + line_out[offset_out] = '\n'; + write2stdout(line_out, offset_out + 1); + column = offset_out = 0; + goto rescan; + } /* while (not EOF) */ + + if (offset_out) { + write2stdout(line_out, offset_out); + } + + if (fclose_if_not_stdin(istream)) { + bb_simple_perror_msg(*argv); + exitcode = EXIT_FAILURE; + } + } while (*++argv); + + fflush_stdout_and_exit(exitcode); +} diff --git a/coreutils/fsync.c b/coreutils/fsync.c new file mode 100644 index 000000000..652a41c33 --- /dev/null +++ b/coreutils/fsync.c @@ -0,0 +1,51 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fsync implementation for busybox + * + * Copyright (C) 2008 Nokia Corporation. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define fsync_trivial_usage +//usage: "[-d] FILE..." +//usage:#define fsync_full_usage "\n\n" +//usage: "Write files' buffered blocks to disk\n" +//usage: "\n -d Avoid syncing metadata" + +#include "libbb.h" +#ifndef O_NOATIME +# define O_NOATIME 0 +#endif + +/* This is a NOFORK applet. Be very careful! */ + +int fsync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fsync_main(int argc UNUSED_PARAM, char **argv) +{ + int status; + int opts; + + opts = getopt32(argv, "d"); /* fdatasync */ + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + status = EXIT_SUCCESS; + do { + int fd = open_or_warn(*argv, O_NOATIME | O_NOCTTY | O_RDONLY); + + if (fd == -1) { + status = EXIT_FAILURE; + continue; + } + if ((opts ? fdatasync(fd) : fsync(fd))) { + //status = EXIT_FAILURE; - do we want this? + bb_simple_perror_msg(*argv); + } + close(fd); + } while (*++argv); + + return status; +} diff --git a/coreutils/head.c b/coreutils/head.c new file mode 100644 index 000000000..ec4512765 --- /dev/null +++ b/coreutils/head.c @@ -0,0 +1,155 @@ +/* vi: set sw=4 ts=4: */ +/* + * head implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */ + +//usage:#define head_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define head_full_usage "\n\n" +//usage: "Print first 10 lines of each FILE (or stdin) to stdout.\n" +//usage: "With more than one FILE, precede each with a filename header.\n" +//usage: "\n -n N[kbm] Print first N lines" +//usage: IF_FEATURE_FANCY_HEAD( +//usage: "\n -c N[kbm] Print first N bytes" +//usage: "\n -q Never print headers" +//usage: "\n -v Always print headers" +//usage: ) +//usage: "\n" +//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." +//usage: +//usage:#define head_example_usage +//usage: "$ head -n 2 /etc/passwd\n" +//usage: "root:x:0:0:root:/root:/bin/bash\n" +//usage: "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +static const char head_opts[] ALIGN1 = + "n:" +#if ENABLE_FEATURE_FANCY_HEAD + "c:qv" +#endif + ; + +static const struct suffix_mult head_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { "", 0 } +}; + +#define header_fmt_str "\n==> %s <==\n" + +int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int head_main(int argc, char **argv) +{ + unsigned long count = 10; + unsigned long i; +#if ENABLE_FEATURE_FANCY_HEAD + int count_bytes = 0; + int header_threshhold = 1; +#endif + FILE *fp; + const char *fmt; + char *p; + int opt; + int c; + int retval = EXIT_SUCCESS; + +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argv[1] && argv[1][0] == '-' + && isdigit(argv[1][1]) + ) { + --argc; + ++argv; + p = (*argv) + 1; + goto GET_COUNT; + } +#endif + + /* No size benefit in converting this to getopt32 */ + while ((opt = getopt(argc, argv, head_opts)) > 0) { + switch (opt) { +#if ENABLE_FEATURE_FANCY_HEAD + case 'q': + header_threshhold = INT_MAX; + break; + case 'v': + header_threshhold = -1; + break; + case 'c': + count_bytes = 1; + /* fall through */ +#endif + case 'n': + p = optarg; +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD + GET_COUNT: +#endif + count = xatoul_sfx(p, head_suffixes); + break; + default: + bb_show_usage(); + } + } + + argc -= optind; + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + fmt = header_fmt_str + 1; +#if ENABLE_FEATURE_FANCY_HEAD + if (argc <= header_threshhold) { + header_threshhold = 0; + } +#else + if (argc <= 1) { + fmt += 11; /* "" */ + } + /* Now define some things here to avoid #ifdefs in the code below. + * These should optimize out of the if conditions below. */ +#define header_threshhold 1 +#define count_bytes 0 +#endif + + do { + fp = fopen_or_warn_stdin(*argv); + if (fp) { + if (fp == stdin) { + *argv = (char *) bb_msg_standard_input; + } + if (header_threshhold) { + printf(fmt, *argv); + } + i = count; + while (i && ((c = getc(fp)) != EOF)) { + if (count_bytes || (c == '\n')) { + --i; + } + putchar(c); + } + if (fclose_if_not_stdin(fp)) { + bb_simple_perror_msg(*argv); + retval = EXIT_FAILURE; + } + die_if_ferror_stdout(); + } else { + retval = EXIT_FAILURE; + } + fmt = header_fmt_str; + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/hostid.c b/coreutils/hostid.c new file mode 100644 index 000000000..5c1a4e086 --- /dev/null +++ b/coreutils/hostid.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hostid implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +//config:config HOSTID +//config: bool "hostid" +//config: default y +//config: help +//config: hostid prints the numeric identifier (in hexadecimal) for +//config: the current host. + +//applet:IF_HOSTID(APPLET_NOFORK(hostid, hostid, BB_DIR_USR_BIN, BB_SUID_DROP, hostid)) + +//kbuild:lib-$(CONFIG_HOSTID) += hostid.o + +//usage:#define hostid_trivial_usage +//usage: "" +//usage:#define hostid_full_usage "\n\n" +//usage: "Print out a unique 32-bit identifier for the machine" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int hostid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int hostid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + if (argv[1]) { + bb_show_usage(); + } + + printf("%08lx\n", gethostid()); + + return fflush_all(); +} diff --git a/coreutils/id.c b/coreutils/id.c new file mode 100644 index 000000000..399d25e34 --- /dev/null +++ b/coreutils/id.c @@ -0,0 +1,269 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini id implementation for busybox + * + * Copyright (C) 2000 by Randolph Chung + * Copyright (C) 2008 by Tito Ragusa + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant. */ +/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever + * length and to be more similar to GNU id. + * -Z option support: by Yuichi Nakamura + * Added -G option Tito Ragusa (C) 2008 for SUSv3. + */ + +//config:config ID +//config: bool "id" +//config: default y +//config: help +//config: id displays the current user and group ID names. + +//config:config GROUPS +//config: bool "groups" +//config: default y +//config: help +//config: Print the group names associated with current user id. + +//kbuild:lib-$(CONFIG_GROUPS) += id.o +//kbuild:lib-$(CONFIG_ID) += id.o + +//applet:IF_GROUPS(APPLET_NOEXEC(groups, id, BB_DIR_USR_BIN, BB_SUID_DROP, groups)) +//applet:IF_ID( APPLET_NOEXEC(id, id, BB_DIR_USR_BIN, BB_SUID_DROP, id )) + +//usage:#define id_trivial_usage +//usage: "[OPTIONS] [USER]" +//usage:#define id_full_usage "\n\n" +//usage: "Print information about USER or the current user\n" +//usage: IF_SELINUX( +//usage: "\n -Z Security context" +//usage: ) +//usage: "\n -u User ID" +//usage: "\n -g Group ID" +//usage: "\n -G Supplementary group IDs" +//usage: "\n -n Print names instead of numbers" +//usage: "\n -r Print real ID instead of effective ID" +//usage: +//usage:#define id_example_usage +//usage: "$ id\n" +//usage: "uid=1000(andersen) gid=1000(andersen)\n" + +//usage:#define groups_trivial_usage +//usage: "[USER]" +//usage:#define groups_full_usage "\n\n" +//usage: "Print the group memberships of USER or for the current process" +//usage: +//usage:#define groups_example_usage +//usage: "$ groups\n" +//usage: "andersen lp dialout cdrom floppy\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +#if !ENABLE_USE_BB_PWD_GRP +#if defined(__UCLIBC_MAJOR__) && (__UCLIBC_MAJOR__ == 0) +#if (__UCLIBC_MINOR__ < 9) || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 30) +#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build" +#endif +#endif +#endif + +enum { + PRINT_REAL = (1 << 0), + NAME_NOT_NUMBER = (1 << 1), + JUST_USER = (1 << 2), + JUST_GROUP = (1 << 3), + JUST_ALL_GROUPS = (1 << 4), +#if ENABLE_SELINUX + JUST_CONTEXT = (1 << 5), +#endif +}; + +static int print_common(unsigned id, const char *name, const char *prefix) +{ + if (prefix) { + printf("%s", prefix); + } + if (!(option_mask32 & NAME_NOT_NUMBER) || !name) { + printf("%u", id); + } + if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) { + if (name) { + printf(option_mask32 ? "%s" : "(%s)", name); + } else { + /* Don't set error status flag in default mode */ + if (option_mask32) { + if (ENABLE_DESKTOP) + bb_error_msg("unknown ID %u", id); + return EXIT_FAILURE; + } + } + } + return EXIT_SUCCESS; +} + +static int print_group(gid_t id, const char *prefix) +{ + return print_common(id, gid2group(id), prefix); +} + +static int print_user(uid_t id, const char *prefix) +{ + return print_common(id, uid2uname(id), prefix); +} + +/* On error set *n < 0 and return >= 0 + * If *n is too small, update it and return < 0 + * (ok to trash groups[] in both cases) + * Otherwise fill in groups[] and return >= 0 + */ +static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n) +{ + int m; + + if (username) { + /* If the user is a member of more than + * *n groups, then -1 is returned. Otherwise >= 0. + * (and no defined way of detecting errors?!) */ + m = getgrouplist(username, rgid, groups, n); + /* I guess *n < 0 might indicate error. Anyway, + * malloc'ing -1 bytes won't be good, so: */ + if (*n < 0) + return 0; + return m; + } + + *n = getgroups(*n, groups); + if (*n >= 0) + return *n; + /* Error */ + if (errno == EINVAL) /* *n is too small? */ + *n = getgroups(0, groups); /* get needed *n */ + /* if *n >= 0, return -1 (got new *n), else return 0 (error): */ + return -(*n >= 0); +} + +int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int id_main(int argc UNUSED_PARAM, char **argv) +{ + uid_t ruid; + gid_t rgid; + uid_t euid; + gid_t egid; + unsigned opt; + int i; + int status = EXIT_SUCCESS; + const char *prefix; + const char *username; +#if ENABLE_SELINUX + security_context_t scontext = NULL; +#endif + + if (ENABLE_GROUPS && (!ENABLE_ID || applet_name[0] == 'g')) { + /* TODO: coreutils groups prepend "USER : " prefix, + * and accept many usernames. Example: + * # groups root root + * root : root + * root : root + */ + opt = option_mask32 = getopt32(argv, "") | JUST_ALL_GROUPS | NAME_NOT_NUMBER; + } else { + /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ + /* Don't allow more than one username */ + opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" + IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G"); + opt = getopt32(argv, "rnugG" IF_SELINUX("Z")); + } + + username = argv[optind]; + if (username) { + struct passwd *p = xgetpwnam(username); + euid = ruid = p->pw_uid; + egid = rgid = p->pw_gid; + } else { + egid = getegid(); + rgid = getgid(); + euid = geteuid(); + ruid = getuid(); + } + /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */ + /* id says: print the real ID instead of the effective ID, with -ugG */ + /* in fact in this case egid is always printed if egid != rgid */ + if (!opt || (opt & JUST_ALL_GROUPS)) { + gid_t *groups; + int n; + + if (!opt) { + /* Default Mode */ + status |= print_user(ruid, "uid="); + status |= print_group(rgid, " gid="); + if (euid != ruid) + status |= print_user(euid, " euid="); + if (egid != rgid) + status |= print_group(egid, " egid="); + } else { + /* JUST_ALL_GROUPS */ + status |= print_group(rgid, NULL); + if (egid != rgid) + status |= print_group(egid, " "); + } + /* We are supplying largish buffer, trying + * to not run get_groups() twice. That might be slow + * ("user database in remote SQL server" case) */ + groups = xmalloc(64 * sizeof(groups[0])); + n = 64; + if (get_groups(username, rgid, groups, &n) < 0) { + /* Need bigger buffer after all */ + groups = xrealloc(groups, n * sizeof(groups[0])); + get_groups(username, rgid, groups, &n); + } + if (n > 0) { + /* Print the list */ + prefix = " groups="; + for (i = 0; i < n; i++) { + if (opt && (groups[i] == rgid || groups[i] == egid)) + continue; + status |= print_group(groups[i], opt ? " " : prefix); + prefix = ","; + } + } else if (n < 0) { /* error in get_groups() */ + if (ENABLE_DESKTOP) + bb_error_msg_and_die("can't get groups"); + return EXIT_FAILURE; + } + if (ENABLE_FEATURE_CLEAN_UP) + free(groups); +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + if (getcon(&scontext) == 0) + printf(" context=%s", scontext); + } +#endif + } else if (opt & PRINT_REAL) { + euid = ruid; + egid = rgid; + } + + if (opt & JUST_USER) + status |= print_user(euid, NULL); + else if (opt & JUST_GROUP) + status |= print_group(egid, NULL); +#if ENABLE_SELINUX + else if (opt & JUST_CONTEXT) { + selinux_or_die(); + if (username || getcon(&scontext)) { + bb_error_msg_and_die("can't get process context%s", + username ? " for a different user" : ""); + } + fputs(scontext, stdout); + } + /* freecon(NULL) seems to be harmless */ + if (ENABLE_FEATURE_CLEAN_UP) + freecon(scontext); +#endif + bb_putchar('\n'); + fflush_stdout_and_exit(status); +} diff --git a/coreutils/id_test.sh b/coreutils/id_test.sh new file mode 100755 index 000000000..0d65f2ae3 --- /dev/null +++ b/coreutils/id_test.sh @@ -0,0 +1,244 @@ +#!/bin/bash +# Test script for busybox id vs. coreutils id. +# Needs root privileges for some tests. + +cp /usr/bin/id . +BUSYBOX=./busybox +ID=./id +LIST=`awk -F: '{ printf "%s\n", $1 }' /etc/passwd` +FLAG_USER_EXISTS="no" +TEST_USER="f583ca884c1d93458fb61ed137ff44f6" + +echo "test 1: id [options] nousername" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar +done + +echo "test 2: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + if test "$i" = "$TEST_USER"; then + FLAG_USER_EXISTS="yes" + fi + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +if test $FLAG_USER_EXISTS = "yes"; then + echo "test 3,4,5,6,7,8,9,10,11,12 skipped because test user $TEST_USER already exists" + rm -f foo bar + exit 1 +fi + +adduser -s /bin/true -g "" -H -D "$TEST_USER" || exit 1 + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod u+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod u+s $ID 2>&1 /dev/null + +echo "test 3 setuid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 4 setuid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod g+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod g+s $ID 2>&1 /dev/null + +echo "test 5 setgid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 6 setgid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod u+s,g+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod u+s,g+s $ID 2>&1 /dev/null + +echo "test 7 setuid, setgid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 8 setuid, setgid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +deluser $TEST_USER || exit 1 + +echo "test 9 setuid, setgid, not existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar +done + +echo "test 10 setuid, setgid, not existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown .root $BUSYBOX 2>&1 /dev/null +chown .root $ID 2>&1 /dev/null +chmod g+s $BUSYBOX 2>&1 /dev/null +chmod g+s $ID 2>&1 /dev/null + +echo "test 11 setgid, not existing group: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 12 setgid, not existing group: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown root.root $BUSYBOX 2>&1 /dev/null +chown root.root $ID 2>&1 /dev/null +rm -f $ID +rm -f foo bar diff --git a/coreutils/install.c b/coreutils/install.c new file mode 100644 index 000000000..445497f9a --- /dev/null +++ b/coreutils/install.c @@ -0,0 +1,229 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 by Glenn McGrath + * SELinux support: by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* -v, -b, -c are ignored */ +//usage:#define install_trivial_usage +//usage: "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [SOURCE]... DEST" +//usage:#define install_full_usage "\n\n" +//usage: "Copy files and set attributes\n" +//usage: "\n -c Just copy (default)" +//usage: "\n -d Create directories" +//usage: "\n -D Create leading target directories" +//usage: "\n -s Strip symbol table" +//usage: "\n -p Preserve date" +//usage: "\n -o USER Set ownership" +//usage: "\n -g GRP Set group ownership" +//usage: "\n -m MODE Set permissions" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS +static const char install_longopts[] ALIGN1 = + "directory\0" No_argument "d" + "preserve-timestamps\0" No_argument "p" + "strip\0" No_argument "s" + "group\0" Required_argument "g" + "mode\0" Required_argument "m" + "owner\0" Required_argument "o" +/* autofs build insists of using -b --suffix=.orig */ +/* TODO? (short option for --suffix is -S) */ +#if ENABLE_SELINUX + "context\0" Required_argument "Z" + "preserve_context\0" No_argument "\xff" + "preserve-context\0" No_argument "\xff" +#endif + ; +#endif + + +#if ENABLE_SELINUX +static void setdefaultfilecon(const char *path) +{ + struct stat s; + security_context_t scontext = NULL; + + if (!is_selinux_enabled()) { + return; + } + if (lstat(path, &s) != 0) { + return; + } + + if (matchpathcon(path, s.st_mode, &scontext) < 0) { + goto out; + } + if (strcmp(scontext, "<>") == 0) { + goto out; + } + + if (lsetfilecon(path, scontext) < 0) { + if (errno != ENOTSUP) { + bb_perror_msg("warning: can't change context" + " of %s to %s", path, scontext); + } + } + + out: + freecon(scontext); +} + +#endif + +int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int install_main(int argc, char **argv) +{ + struct stat statbuf; + mode_t mode; + uid_t uid; + gid_t gid; + char *arg, *last; + const char *gid_str; + const char *uid_str; + const char *mode_str; + int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; + int opts; + int min_args = 1; + int ret = EXIT_SUCCESS; + int isdir = 0; +#if ENABLE_SELINUX + security_context_t scontext; + bool use_default_selinux_context = 1; +#endif + enum { + OPT_c = 1 << 0, + OPT_v = 1 << 1, + OPT_b = 1 << 2, + OPT_MKDIR_LEADING = 1 << 3, + OPT_DIRECTORY = 1 << 4, + OPT_PRESERVE_TIME = 1 << 5, + OPT_STRIP = 1 << 6, + OPT_GROUP = 1 << 7, + OPT_MODE = 1 << 8, + OPT_OWNER = 1 << 9, +#if ENABLE_SELINUX + OPT_SET_SECURITY_CONTEXT = 1 << 10, + OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11, +#endif + }; + +#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS + applet_long_options = install_longopts; +#endif + opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")); + /* -c exists for backwards compatibility, it's needed */ + /* -v is ignored ("print name of each created directory") */ + /* -b is ignored ("make a backup of each existing destination file") */ + opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"), + &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext)); + argc -= optind; + argv += optind; + +#if ENABLE_SELINUX + if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) { + selinux_or_die(); + use_default_selinux_context = 0; + if (opts & OPT_PRESERVE_SECURITY_CONTEXT) { + copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; + } + if (opts & OPT_SET_SECURITY_CONTEXT) { + setfscreatecon_or_die(scontext); + copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT; + } + } +#endif + + /* preserve access and modification time, this is GNU behaviour, + * BSD only preserves modification time */ + if (opts & OPT_PRESERVE_TIME) { + copy_flags |= FILEUTILS_PRESERVE_STATUS; + } + mode = 0755; /* GNU coreutils 6.10 compat */ + if (opts & OPT_MODE) + bb_parse_mode(mode_str, &mode); + uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid(); + gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid(); + + last = argv[argc - 1]; + if (!(opts & OPT_DIRECTORY)) { + argv[argc - 1] = NULL; + min_args++; + + /* coreutils install resolves link in this case, don't use lstat */ + isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode); + } + + if (argc < min_args) + bb_show_usage(); + + while ((arg = *argv++) != NULL) { + char *dest = last; + if (opts & OPT_DIRECTORY) { + dest = arg; + /* GNU coreutils 6.9 does not set uid:gid + * on intermediate created directories + * (only on last one) */ + if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) { + ret = EXIT_FAILURE; + goto next; + } + } else { + if (opts & OPT_MKDIR_LEADING) { + char *ddir = xstrdup(dest); + bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR); + /* errors are not checked. copy_file + * will fail if dir is not created. */ + free(ddir); + } + if (isdir) + dest = concat_path_file(last, bb_basename(arg)); + if (copy_file(arg, dest, copy_flags) != 0) { + /* copy is not made */ + ret = EXIT_FAILURE; + goto next; + } + if (opts & OPT_STRIP) { + char *args[4]; + args[0] = (char*)"strip"; + args[1] = (char*)"-p"; /* -p --preserve-dates */ + args[2] = dest; + args[3] = NULL; + if (spawn_and_wait(args)) { + bb_perror_msg("strip"); + ret = EXIT_FAILURE; + } + } + } + + /* Set the file mode (always, not only with -m). + * GNU coreutils 6.10 is not affected by umask. */ + if (chmod(dest, mode) == -1) { + bb_perror_msg("can't change %s of %s", "permissions", dest); + ret = EXIT_FAILURE; + } +#if ENABLE_SELINUX + if (use_default_selinux_context) + setdefaultfilecon(dest); +#endif + /* Set the user and group id */ + if ((opts & (OPT_OWNER|OPT_GROUP)) + && lchown(dest, uid, gid) == -1 + ) { + bb_perror_msg("can't change %s of %s", "ownership", dest); + ret = EXIT_FAILURE; + } + next: + if (ENABLE_FEATURE_CLEAN_UP && isdir) + free(dest); + } + + return ret; +} diff --git a/coreutils/length.c.disabled b/coreutils/length.c.disabled new file mode 100644 index 000000000..aee898d22 --- /dev/null +++ b/coreutils/length.c.disabled @@ -0,0 +1,31 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */ + +//usage:#define length_trivial_usage +//usage: "STRING" +//usage:#define length_full_usage "\n\n" +//usage: "Print STRING's length" +//usage: +//usage:#define length_example_usage +//usage: "$ length Hello\n" +//usage: "5\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int length_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int length_main(int argc, char **argv) +{ + if ((argc != 2) || (**(++argv) == '-')) { + bb_show_usage(); + } + + printf("%u\n", (unsigned)strlen(*argv)); + + return fflush_all(); +} diff --git a/coreutils/libcoreutils/Kbuild.src b/coreutils/libcoreutils/Kbuild.src new file mode 100644 index 000000000..2042d5f84 --- /dev/null +++ b/coreutils/libcoreutils/Kbuild.src @@ -0,0 +1,14 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 by Erik Andersen +# +# Licensed under GPLv2 or later, see file LICENSE in this source tree. + +lib-y:= + +INSERT +lib-$(CONFIG_MKFIFO) += getopt_mk_fifo_nod.o +lib-$(CONFIG_MKNOD) += getopt_mk_fifo_nod.o +lib-$(CONFIG_INSTALL) += cp_mv_stat.o +lib-$(CONFIG_CP) += cp_mv_stat.o +lib-$(CONFIG_MV) += cp_mv_stat.o diff --git a/coreutils/libcoreutils/coreutils.h b/coreutils/libcoreutils/coreutils.h new file mode 100644 index 000000000..307d0330a --- /dev/null +++ b/coreutils/libcoreutils/coreutils.h @@ -0,0 +1,20 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +#ifndef COREUTILS_H +#define COREUTILS_H 1 + +PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN + +typedef int (*stat_func)(const char *fn, struct stat *ps); + +int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) FAST_FUNC; +int cp_mv_stat(const char *fn, struct stat *fn_stat) FAST_FUNC; + +mode_t getopt_mk_fifo_nod(char **argv) FAST_FUNC; + +POP_SAVED_FUNCTION_VISIBILITY + +#endif diff --git a/coreutils/libcoreutils/cp_mv_stat.c b/coreutils/libcoreutils/cp_mv_stat.c new file mode 100644 index 000000000..5ba07ecc3 --- /dev/null +++ b/coreutils/libcoreutils/cp_mv_stat.c @@ -0,0 +1,50 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "libbb.h" +#include "coreutils.h" + +int FAST_FUNC cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) +{ + if (sf(fn, fn_stat) < 0) { + if (errno != ENOENT) { +#if ENABLE_FEATURE_VERBOSE_CP_MESSAGE + if (errno == ENOTDIR) { + bb_error_msg("can't stat '%s': Path has non-directory component", fn); + return -1; + } +#endif + bb_perror_msg("can't stat '%s'", fn); + return -1; + } + return 0; + } + if (S_ISDIR(fn_stat->st_mode)) { + return 3; + } + return 1; +} + +int FAST_FUNC cp_mv_stat(const char *fn, struct stat *fn_stat) +{ + return cp_mv_stat2(fn, fn_stat, stat); +} diff --git a/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/coreutils/libcoreutils/getopt_mk_fifo_nod.c new file mode 100644 index 000000000..222717149 --- /dev/null +++ b/coreutils/libcoreutils/getopt_mk_fifo_nod.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "libbb.h" +#include "coreutils.h" + +mode_t FAST_FUNC getopt_mk_fifo_nod(char **argv) +{ + mode_t mode = 0666; + char *smode = NULL; +#if ENABLE_SELINUX + security_context_t scontext; +#endif + int opt; + opt = getopt32(argv, "m:" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext)); + if (opt & 1) { + if (bb_parse_mode(smode, &mode)) + umask(0); + } + +#if ENABLE_SELINUX + if (opt & 2) { + selinux_or_die(); + setfscreatecon_or_die(scontext); + } +#endif + + return mode; +} diff --git a/coreutils/ln.c b/coreutils/ln.c new file mode 100644 index 000000000..88a9a8f91 --- /dev/null +++ b/coreutils/ln.c @@ -0,0 +1,125 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */ + +//usage:#define ln_trivial_usage +//usage: "[OPTIONS] TARGET... LINK|DIR" +//usage:#define ln_full_usage "\n\n" +//usage: "Create a link LINK or DIR/TARGET to the specified TARGET(s)\n" +//usage: "\n -s Make symlinks instead of hardlinks" +//usage: "\n -f Remove existing destinations" +//usage: "\n -n Don't dereference symlinks - treat like normal file" +//usage: "\n -b Make a backup of the target (if exists) before link operation" +//usage: "\n -S suf Use suffix instead of ~ when making backup files" +//usage: +//usage:#define ln_example_usage +//usage: "$ ln -s BusyBox /tmp/ls\n" +//usage: "$ ls -l /tmp/ls\n" +//usage: "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define LN_SYMLINK 1 +#define LN_FORCE 2 +#define LN_NODEREFERENCE 4 +#define LN_BACKUP 8 +#define LN_SUFFIX 16 + +int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ln_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int opts; + char *last; + char *src_name; + char *src; + char *suffix = (char*)"~"; + struct stat statbuf; + int (*link_func)(const char *, const char *); + + opt_complementary = "-1"; /* min one arg */ + opts = getopt32(argv, "sfnbS:", &suffix); + + last = argv[argc - 1]; + argv += optind; + + if (!argv[1]) { + /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */ + *--argv = last; + /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to + * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE" + */ + last = bb_get_last_path_component_strip(xstrdup(last)); + } + + do { + src_name = NULL; + src = last; + + if (is_directory(src, + (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE, + NULL) + ) { + src_name = xstrdup(*argv); + src = concat_path_file(src, bb_get_last_path_component_strip(src_name)); + free(src_name); + src_name = src; + } + if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) { + // coreutils: "ln dangling_symlink new_hardlink" works + if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) { + bb_simple_perror_msg(*argv); + status = EXIT_FAILURE; + free(src_name); + continue; + } + } + + if (opts & LN_BACKUP) { + char *backup; + backup = xasprintf("%s%s", src, suffix); + if (rename(src, backup) < 0 && errno != ENOENT) { + bb_simple_perror_msg(src); + status = EXIT_FAILURE; + free(backup); + continue; + } + free(backup); + /* + * When the source and dest are both hard links to the same + * inode, a rename may succeed even though nothing happened. + * Therefore, always unlink(). + */ + unlink(src); + } else if (opts & LN_FORCE) { + unlink(src); + } + + link_func = link; + if (opts & LN_SYMLINK) { + link_func = symlink; + } + + if (link_func(*argv, src) != 0) { + bb_simple_perror_msg(src); + status = EXIT_FAILURE; + } + + free(src_name); + + } while ((++argv)[1]); + + return status; +} diff --git a/coreutils/logname.c b/coreutils/logname.c new file mode 100644 index 000000000..10b9615a1 --- /dev/null +++ b/coreutils/logname.c @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logname implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * SUSv3 specifies the string used is that returned from getlogin(). + * The previous implementation used getpwuid() for geteuid(), which + * is _not_ the same. Erik apparently made this change almost 3 years + * ago to avoid failing when no utmp was available. However, the + * correct course of action wrt SUSv3 for a failing getlogin() is + * a diagnostic message and an error return. + */ + +//usage:#define logname_trivial_usage +//usage: "" +//usage:#define logname_full_usage "\n\n" +//usage: "Print the name of the current user" +//usage: +//usage:#define logname_example_usage +//usage: "$ logname\n" +//usage: "root\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int logname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int logname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char buf[64]; + + if (argv[1]) { + bb_show_usage(); + } + + /* Using _r function - avoid pulling in static buffer from libc */ + if (getlogin_r(buf, sizeof(buf)) == 0) { + puts(buf); + return fflush_all(); + } + + bb_perror_msg_and_die("getlogin"); +} diff --git a/coreutils/ls.c b/coreutils/ls.c new file mode 100644 index 000000000..d5b25ee70 --- /dev/null +++ b/coreutils/ls.c @@ -0,0 +1,1255 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 1996 Brian Candler + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* [date unknown. Perhaps before year 2000] + * To achieve a small memory footprint, this version of 'ls' doesn't do any + * file sorting, and only has the most essential command line switches + * (i.e., the ones I couldn't live without :-) All features which involve + * linking in substantial chunks of libc can be disabled. + * + * Although I don't really want to add new features to this program to + * keep it small, I *am* interested to receive bug fixes and ways to make + * it more portable. + * + * KNOWN BUGS: + * 1. hidden files can make column width too large + * + * NON-OPTIMAL BEHAVIOUR: + * 1. autowidth reads directories twice + * 2. if you do a short directory listing without filetype characters + * appended, there's no need to stat each one + * PORTABILITY: + * 1. requires lstat (BSD) - how do you do it without? + * + * [2009-03] + * ls sorts listing now, and supports almost all options. + */ + +//usage:#define ls_trivial_usage +//usage: "[-1AaCxd" +//usage: IF_FEATURE_LS_FOLLOWLINKS("LH") +//usage: IF_FEATURE_LS_RECURSIVE("R") +//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" +//usage: IF_FEATURE_LS_TIMESTAMPS("e") +//usage: IF_FEATURE_HUMAN_READABLE("h") +//usage: IF_FEATURE_LS_SORTFILES("rSXv") +//usage: IF_FEATURE_LS_TIMESTAMPS("ctu") +//usage: IF_SELINUX("kKZ") "]" +//usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..." +//usage:#define ls_full_usage "\n\n" +//usage: "List directory contents\n" +//usage: "\n -1 One column output" +//usage: "\n -a Include entries which start with ." +//usage: "\n -A Like -a, but exclude . and .." +//usage: "\n -C List by columns" +//usage: "\n -x List by lines" +//usage: "\n -d List directory entries instead of contents" +//usage: IF_FEATURE_LS_FOLLOWLINKS( +//usage: "\n -L Follow symlinks" +//usage: "\n -H Follow symlinks on command line" +//usage: ) +//usage: IF_FEATURE_LS_RECURSIVE( +//usage: "\n -R Recurse" +//usage: ) +//usage: IF_FEATURE_LS_FILETYPES( +//usage: "\n -p Append / to dir entries" +//usage: "\n -F Append indicator (one of */=@|) to entries" +//usage: ) +//usage: "\n -l Long listing format" +//usage: "\n -i List inode numbers" +//usage: "\n -n List numeric UIDs and GIDs instead of names" +//usage: "\n -s List allocated blocks" +//usage: IF_FEATURE_LS_TIMESTAMPS( +//usage: "\n -e List full date and time" +//usage: ) +//usage: IF_FEATURE_HUMAN_READABLE( +//usage: "\n -h List sizes in human readable format (1K 243M 2G)" +//usage: ) +//usage: IF_FEATURE_LS_SORTFILES( +//usage: "\n -r Sort in reverse order" +//usage: "\n -S Sort by size" +//usage: "\n -X Sort by extension" +//usage: "\n -v Sort by version" +//usage: ) +//usage: IF_FEATURE_LS_TIMESTAMPS( +//usage: "\n -c With -l: sort by ctime" +//usage: "\n -t With -l: sort by mtime" +//usage: "\n -u With -l: sort by atime" +//usage: ) +//usage: IF_SELINUX( +//usage: "\n -k List security context" +//usage: "\n -K List security context in long format" +//usage: "\n -Z List security context and permission" +//usage: ) +//usage: IF_FEATURE_AUTOWIDTH( +//usage: "\n -w N Assume the terminal is N columns wide" +//usage: ) +//usage: IF_FEATURE_LS_COLOR( +//usage: "\n --color[={always,never,auto}] Control coloring" +//usage: ) + +#include "libbb.h" +#include "unicode.h" + + +/* This is a NOEXEC applet. Be very careful! */ + + +#if ENABLE_FTPD +/* ftpd uses ls, and without timestamps Mozilla won't understand + * ftpd's LIST output. + */ +# undef CONFIG_FEATURE_LS_TIMESTAMPS +# undef ENABLE_FEATURE_LS_TIMESTAMPS +# undef IF_FEATURE_LS_TIMESTAMPS +# undef IF_NOT_FEATURE_LS_TIMESTAMPS +# define CONFIG_FEATURE_LS_TIMESTAMPS 1 +# define ENABLE_FEATURE_LS_TIMESTAMPS 1 +# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__ +# define IF_NOT_FEATURE_LS_TIMESTAMPS(...) +#endif + + +enum { +TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ + +SPLIT_FILE = 0, +SPLIT_DIR = 1, +SPLIT_SUBDIR = 2, + +/* Bits in G.all_fmt: */ + +/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ +/* what file information will be listed */ +LIST_INO = 1 << 0, +LIST_BLOCKS = 1 << 1, +LIST_MODEBITS = 1 << 2, +LIST_NLINKS = 1 << 3, +LIST_ID_NAME = 1 << 4, +LIST_ID_NUMERIC = 1 << 5, +LIST_CONTEXT = 1 << 6, +LIST_SIZE = 1 << 7, +LIST_DATE_TIME = 1 << 8, +LIST_FULLTIME = 1 << 9, +LIST_SYMLINK = 1 << 10, +LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */ +LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */ +LIST_MASK = (LIST_CLASSIFY << 1) - 1, + +/* what files will be displayed */ +DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ +DISP_HIDDEN = 1 << 14, /* show filenames starting with . */ +DISP_DOT = 1 << 15, /* show . and .. */ +DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */ +DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */ +DISP_ROWS = 1 << 18, /* print across rows */ +DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1), + +/* what is the overall style of the listing */ +STYLE_COLUMNAR = 1 << 19, /* many records per line */ +STYLE_LONG = 2 << 19, /* one record per line, extended info */ +STYLE_SINGLE = 3 << 19, /* one record per line */ +STYLE_MASK = STYLE_SINGLE, + +/* which of the three times will be used */ +TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, +TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, +TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS, + +/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */ +SORT_REVERSE = 1 << 23, + +SORT_NAME = 0, /* sort by file name */ +SORT_SIZE = 1 << 24, /* sort by file size */ +SORT_ATIME = 2 << 24, /* sort by last access time */ +SORT_CTIME = 3 << 24, /* sort by last change time */ +SORT_MTIME = 4 << 24, /* sort by last modification time */ +SORT_VERSION = 5 << 24, /* sort by version */ +SORT_EXT = 6 << 24, /* sort by file name extension */ +SORT_DIR = 7 << 24, /* sort by file or directory */ +SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES, + +LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ + LIST_DATE_TIME | LIST_SYMLINK, +}; + +/* -Cadil1 Std options, busybox always supports */ +/* -gnsxA Std options, busybox always supports */ +/* -Q GNU option, busybox always supports */ +/* -k SELinux option, busybox always supports (ignores if !SELinux) */ +/* Std has -k which means "show sizes in kbytes" */ +/* -LHRctur Std options, busybox optionally supports */ +/* -Fp Std options, busybox optionally supports */ +/* -SXvhTw GNU options, busybox optionally supports */ +/* -T WIDTH Ignored (we don't use tabs on output) */ +/* -KZ SELinux mandated options, busybox optionally supports */ +/* (coreutils 8.4 has no -K, remove it?) */ +/* -e I think we made this one up (looks similar to GNU --full-time) */ +/* We already used up all 32 bits, if we need to add more, candidates for removal: */ +/* -K, -T, -e (add --full-time instead) */ +static const char ls_options[] ALIGN1 = + "Cadil1gnsxQAk" /* 13 opts, total 13 */ + IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */ + IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */ + IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */ + IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */ + IF_SELINUX("KZ") /* 2, 26 */ + IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */ + IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */ + IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */ + /* with --color, we use all 32 bits */; +enum { + //OPT_C = (1 << 0), + //OPT_a = (1 << 1), + //OPT_d = (1 << 2), + //OPT_i = (1 << 3), + //OPT_l = (1 << 4), + //OPT_1 = (1 << 5), + OPT_g = (1 << 6), + //OPT_n = (1 << 7), + //OPT_s = (1 << 8), + //OPT_x = (1 << 9), + OPT_Q = (1 << 10), + //OPT_A = (1 << 11), + //OPT_k = (1 << 12), + + OPTBIT_c = 13, + OPTBIT_e, + OPTBIT_t, + OPTBIT_u, + OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS, + OPTBIT_X, /* 18 */ + OPTBIT_r, + OPTBIT_v, + OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, + OPTBIT_p, /* 22 */ + OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, + OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, + OPTBIT_Z, /* 25 */ + OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX, + OPTBIT_H, /* 27 */ + OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, + OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, + OPTBIT_w, /* 30 */ + OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH, + + OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES, + OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES, + OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES, + OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES, + OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, + OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, + OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE, + OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX, + OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX, + OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS, + OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS, + OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE, + OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH, + OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH, + OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR, +}; + +/* TODO: simple toggles may be stored as OPT_xxx bits instead */ +static const uint32_t opt_flags[] = { + STYLE_COLUMNAR, /* C */ + DISP_HIDDEN | DISP_DOT, /* a */ + DISP_NOLIST, /* d */ + LIST_INO, /* i */ + LIST_LONG | STYLE_LONG, /* l */ + STYLE_SINGLE, /* 1 */ + LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */ + LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */ + LIST_BLOCKS, /* s */ + DISP_ROWS | STYLE_COLUMNAR, /* x */ + 0, /* Q (quote filename) - handled via OPT_Q */ + DISP_HIDDEN, /* A */ + ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */ +#if ENABLE_FEATURE_LS_TIMESTAMPS + TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */ + LIST_FULLTIME, /* e */ + ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */ + TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */ +#endif +#if ENABLE_FEATURE_LS_SORTFILES + SORT_SIZE, /* S */ + SORT_EXT, /* X */ + SORT_REVERSE, /* r */ + SORT_VERSION, /* v */ +#endif +#if ENABLE_FEATURE_LS_FILETYPES + LIST_FILETYPE | LIST_CLASSIFY, /* F */ + LIST_FILETYPE, /* p */ +#endif +#if ENABLE_FEATURE_LS_RECURSIVE + DISP_RECURSIVE, /* R */ +#endif +#if ENABLE_SELINUX + LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */ + LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */ +#endif + (1U << 31) + /* options after Z are not processed through opt_flags */ +}; + + +/* + * a directory entry and its stat info + */ +struct dnode { + const char *name; /* usually basename, but think "ls -l dir/file" */ + const char *fullname; /* full name (usable for stat etc) */ + struct dnode *dn_next; /* for linked list */ + IF_SELINUX(security_context_t sid;) + smallint fname_allocated; + + /* Used to avoid re-doing [l]stat at printout stage + * if we already collected needed data in scan stage: + */ + mode_t dn_mode_lstat; /* obtained with lstat, or 0 */ + mode_t dn_mode_stat; /* obtained with stat, or 0 */ + +// struct stat dstat; +// struct stat is huge. We don't need it in full. +// At least we don't need st_dev and st_blksize, +// but there are invisible fields as well +// (such as nanosecond-resolution timespamps) +// and padding, which we also don't want to store. +// We also can pre-parse dev_t dn_rdev (in glibc, it's huge). +// On 32-bit uclibc: dnode size went from 112 to 84 bytes. +// + /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ + mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ + off_t dn_size; +#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES + time_t dn_atime; + time_t dn_mtime; + time_t dn_ctime; +#endif + ino_t dn_ino; + blkcnt_t dn_blocks; + nlink_t dn_nlink; + uid_t dn_uid; + gid_t dn_gid; + int dn_rdev_maj; + int dn_rdev_min; +// dev_t dn_dev; +// blksize_t dn_blksize; +}; + +struct globals { +#if ENABLE_FEATURE_LS_COLOR + smallint show_color; +# define G_show_color (G.show_color) +#else +# define G_show_color 0 +#endif + smallint exit_code; + unsigned all_fmt; +#if ENABLE_FEATURE_AUTOWIDTH + unsigned terminal_width; +# define G_terminal_width (G.terminal_width) +#else +# define G_terminal_width TERMINAL_WIDTH +#endif +#if ENABLE_FEATURE_LS_TIMESTAMPS + /* Do time() just once. Saves one syscall per file for "ls -l" */ + time_t current_time_t; +#endif +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { \ + /* we have to zero it out because of NOEXEC */ \ + memset(&G, 0, sizeof(G)); \ + IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \ + IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ +} while (0) + + +/*** Output code ***/ + + +/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket + * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file + * 3/7:multiplexed char/block device) + * and we use 0 for unknown and 15 for executables (see below) */ +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +/* un fi chr - dir - blk - file - link - sock - - exe */ +#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)]) +/* 036 black foreground 050 black background + 037 red foreground 051 red background + 040 green foreground 052 green background + 041 brown foreground 053 brown background + 042 blue foreground 054 blue background + 043 magenta (purple) foreground 055 magenta background + 044 cyan (light blue) foreground 056 cyan background + 045 gray foreground 057 white background +*/ +#define COLOR(mode) ( \ + /*un fi chr - dir - blk - file - link - sock - - exe */ \ + "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ + [TYPEINDEX(mode)]) +/* Select normal (0) [actually "reset all"] or bold (1) + * (other attributes are 2:dim 4:underline 5:blink 7:reverse, + * let's use 7 for "impossible" types, just for fun) + * Note: coreutils 6.9 uses inverted red for setuid binaries. + */ +#define ATTR(mode) ( \ + /*un fi chr - dir - blk - file- link- sock- - exe */ \ + "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ + [TYPEINDEX(mode)]) + +#if ENABLE_FEATURE_LS_COLOR +/* mode of zero is interpreted as "unknown" (stat failed) */ +static char fgcolor(mode_t mode) +{ + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return COLOR(0xF000); /* File is executable ... */ + return COLOR(mode); +} +static char bold(mode_t mode) +{ + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return ATTR(0xF000); /* File is executable ... */ + return ATTR(mode); +} +#endif + +#if ENABLE_FEATURE_LS_FILETYPES +static char append_char(mode_t mode) +{ + if (!(G.all_fmt & LIST_FILETYPE)) + return '\0'; + if (S_ISDIR(mode)) + return '/'; + if (!(G.all_fmt & LIST_CLASSIFY)) + return '\0'; + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return '*'; + return APPCHAR(mode); +} +#endif + +static unsigned calc_name_len(const char *name) +{ + unsigned len; + uni_stat_t uni_stat; + + // TODO: quote tab as \t, etc, if -Q + name = printable_string(&uni_stat, name); + + if (!(option_mask32 & OPT_Q)) { + return uni_stat.unicode_width; + } + + len = 2 + uni_stat.unicode_width; + while (*name) { + if (*name == '"' || *name == '\\') { + len++; + } + name++; + } + return len; +} + +/* Return the number of used columns. + * Note that only STYLE_COLUMNAR uses return value. + * STYLE_SINGLE and STYLE_LONG don't care. + * coreutils 7.2 also supports: + * ls -b (--escape) = octal escapes (although it doesn't look like working) + * ls -N (--literal) = not escape at all + */ +static unsigned print_name(const char *name) +{ + unsigned len; + uni_stat_t uni_stat; + + // TODO: quote tab as \t, etc, if -Q + name = printable_string(&uni_stat, name); + + if (!(option_mask32 & OPT_Q)) { + fputs(name, stdout); + return uni_stat.unicode_width; + } + + len = 2 + uni_stat.unicode_width; + putchar('"'); + while (*name) { + if (*name == '"' || *name == '\\') { + putchar('\\'); + len++; + } + putchar(*name); + name++; + } + putchar('"'); + return len; +} + +/* Return the number of used columns. + * Note that only STYLE_COLUMNAR uses return value, + * STYLE_SINGLE and STYLE_LONG don't care. + */ +static NOINLINE unsigned display_single(const struct dnode *dn) +{ + unsigned column = 0; + char *lpath; +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR + struct stat statbuf; + char append; +#endif + +#if ENABLE_FEATURE_LS_FILETYPES + append = append_char(dn->dn_mode); +#endif + + /* Do readlink early, so that if it fails, error message + * does not appear *inside* the "ls -l" line */ + lpath = NULL; + if (G.all_fmt & LIST_SYMLINK) + if (S_ISLNK(dn->dn_mode)) + lpath = xmalloc_readlink_or_warn(dn->fullname); + + if (G.all_fmt & LIST_INO) + column += printf("%7llu ", (long long) dn->dn_ino); +//TODO: -h should affect -s too: + if (G.all_fmt & LIST_BLOCKS) + column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); + if (G.all_fmt & LIST_MODEBITS) + column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); + if (G.all_fmt & LIST_NLINKS) + column += printf("%4lu ", (long) dn->dn_nlink); + if (G.all_fmt & LIST_ID_NUMERIC) { + if (option_mask32 & OPT_g) + column += printf("%-8u ", (int) dn->dn_gid); + else + column += printf("%-8u %-8u ", + (int) dn->dn_uid, + (int) dn->dn_gid); + } +#if ENABLE_FEATURE_LS_USERNAME + else if (G.all_fmt & LIST_ID_NAME) { + if (option_mask32 & OPT_g) { + column += printf("%-8.8s ", + get_cached_groupname(dn->dn_gid)); + } else { + column += printf("%-8.8s %-8.8s ", + get_cached_username(dn->dn_uid), + get_cached_groupname(dn->dn_gid)); + } + } +#endif + if (G.all_fmt & LIST_SIZE) { + if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) { + column += printf("%4u, %3u ", + dn->dn_rdev_maj, + dn->dn_rdev_min); + } else { + if (option_mask32 & OPT_h) { + column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", + /* print size, show one fractional, use suffixes */ + make_human_readable_str(dn->dn_size, 1, 0) + ); + } else { + column += printf("%9"OFF_FMT"u ", dn->dn_size); + } + } + } +#if ENABLE_FEATURE_LS_TIMESTAMPS + if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { + char *filetime; + time_t ttime = dn->dn_mtime; + if (G.all_fmt & TIME_ACCESS) + ttime = dn->dn_atime; + if (G.all_fmt & TIME_CHANGE) + ttime = dn->dn_ctime; + filetime = ctime(&ttime); + /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ + if (G.all_fmt & LIST_FULLTIME) { /* -e */ + /* Note: coreutils 8.4 ls --full-time prints: + * 2009-07-13 17:49:27.000000000 +0200 + */ + column += printf("%.24s ", filetime); + } else { /* LIST_DATE_TIME */ + /* G.current_time_t ~== time(NULL) */ + time_t age = G.current_time_t - ttime; + printf("%.6s ", filetime + 4); /* "Jun 30" */ + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { + /* hh:mm if less than 6 months old */ + printf("%.5s ", filetime + 11); + } else { /* year. buggy if year > 9999 ;) */ + printf(" %.4s ", filetime + 20); + } + column += 13; + } + } +#endif +#if ENABLE_SELINUX + if (G.all_fmt & LIST_CONTEXT) { + column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); + freecon(dn->sid); + } +#endif + +#if ENABLE_FEATURE_LS_COLOR + if (G_show_color) { + mode_t mode = dn->dn_mode_lstat; + if (!mode) + if (lstat(dn->fullname, &statbuf) == 0) + mode = statbuf.st_mode; + printf("\033[%u;%um", bold(mode), fgcolor(mode)); + } +#endif + column += print_name(dn->name); + if (G_show_color) { + printf("\033[0m"); + } + + if (lpath) { + printf(" -> "); +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR + if ((G.all_fmt & LIST_FILETYPE) || G_show_color) { + mode_t mode = dn->dn_mode_stat; + if (!mode) + if (stat(dn->fullname, &statbuf) == 0) + mode = statbuf.st_mode; +# if ENABLE_FEATURE_LS_FILETYPES + append = append_char(mode); +# endif +# if ENABLE_FEATURE_LS_COLOR + if (G_show_color) { + printf("\033[%u;%um", bold(mode), fgcolor(mode)); + } +# endif + } +#endif + column += print_name(lpath) + 4; + free(lpath); + if (G_show_color) { + printf("\033[0m"); + } + } +#if ENABLE_FEATURE_LS_FILETYPES + if (G.all_fmt & LIST_FILETYPE) { + if (append) { + putchar(append); + column++; + } + } +#endif + + return column; +} + +static void display_files(struct dnode **dn, unsigned nfiles) +{ + unsigned i, ncols, nrows, row, nc; + unsigned column; + unsigned nexttab; + unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ + + if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ + ncols = 1; + } else { + /* find the longest file name, use that as the column width */ + for (i = 0; dn[i]; i++) { + int len = calc_name_len(dn[i]->name); + if (column_width < len) + column_width = len; + } + column_width += 1 + + IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + ) + ((G.all_fmt & LIST_INO) ? 8 : 0) + + ((G.all_fmt & LIST_BLOCKS) ? 5 : 0); + ncols = (unsigned)G_terminal_width / column_width; + } + + if (ncols > 1) { + nrows = nfiles / ncols; + if (nrows * ncols < nfiles) + nrows++; /* round up fractionals */ + } else { + nrows = nfiles; + ncols = 1; + } + + column = 0; + nexttab = 0; + for (row = 0; row < nrows; row++) { + for (nc = 0; nc < ncols; nc++) { + /* reach into the array based on the column and row */ + if (G.all_fmt & DISP_ROWS) + i = (row * ncols) + nc; /* display across row */ + else + i = (nc * nrows) + row; /* display by column */ + if (i < nfiles) { + if (column > 0) { + nexttab -= column; + printf("%*s ", nexttab, ""); + column += nexttab + 1; + } + nexttab = column + column_width; + column += display_single(dn[i]); + } + } + putchar('\n'); + column = 0; + } +} + + +/*** Dir scanning code ***/ + +static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) +{ + struct stat statbuf; + struct dnode *cur; + + cur = xzalloc(sizeof(*cur)); + cur->fullname = fullname; + cur->name = name; + + if ((option_mask32 & OPT_L) || force_follow) { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + getfilecon(fullname, &cur->sid); + } +#endif + if (stat(fullname, &statbuf)) { + bb_simple_perror_msg(fullname); + G.exit_code = EXIT_FAILURE; + free(cur); + return NULL; + } + cur->dn_mode_stat = statbuf.st_mode; + } else { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + lgetfilecon(fullname, &cur->sid); + } +#endif + if (lstat(fullname, &statbuf)) { + bb_simple_perror_msg(fullname); + G.exit_code = EXIT_FAILURE; + free(cur); + return NULL; + } + cur->dn_mode_lstat = statbuf.st_mode; + } + + /* cur->dstat = statbuf: */ + cur->dn_mode = statbuf.st_mode ; + cur->dn_size = statbuf.st_size ; +#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES + cur->dn_atime = statbuf.st_atime ; + cur->dn_mtime = statbuf.st_mtime ; + cur->dn_ctime = statbuf.st_ctime ; +#endif + cur->dn_ino = statbuf.st_ino ; + cur->dn_blocks = statbuf.st_blocks; + cur->dn_nlink = statbuf.st_nlink ; + cur->dn_uid = statbuf.st_uid ; + cur->dn_gid = statbuf.st_gid ; + cur->dn_rdev_maj = major(statbuf.st_rdev); + cur->dn_rdev_min = minor(statbuf.st_rdev); + + return cur; +} + +static unsigned count_dirs(struct dnode **dn, int which) +{ + unsigned dirs, all; + + if (!dn) + return 0; + + dirs = all = 0; + for (; *dn; dn++) { + const char *name; + + all++; + if (!S_ISDIR((*dn)->dn_mode)) + continue; + + name = (*dn)->name; + if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ + /* or if it's not . or .. */ + || name[0] != '.' + || (name[1] && (name[1] != '.' || name[2])) + ) { + dirs++; + } + } + return which != SPLIT_FILE ? dirs : all - dirs; +} + +/* get memory to hold an array of pointers */ +static struct dnode **dnalloc(unsigned num) +{ + if (num < 1) + return NULL; + + num++; /* so that we have terminating NULL */ + return xzalloc(num * sizeof(struct dnode *)); +} + +#if ENABLE_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + unsigned i; + + if (dnp == NULL) + return; + + for (i = 0; dnp[i]; i++) { + struct dnode *cur = dnp[i]; + if (cur->fname_allocated) + free((char*)cur->fullname); + free(cur); + } + free(dnp); +} +#else +#define dfree(...) ((void)0) +#endif + +/* Returns NULL-terminated malloced vector of pointers (or NULL) */ +static struct dnode **splitdnarray(struct dnode **dn, int which) +{ + unsigned dncnt, d; + struct dnode **dnp; + + if (dn == NULL) + return NULL; + + /* count how many dirs or files there are */ + dncnt = count_dirs(dn, which); + + /* allocate a file array and a dir array */ + dnp = dnalloc(dncnt); + + /* copy the entrys into the file or dir array */ + for (d = 0; *dn; dn++) { + if (S_ISDIR((*dn)->dn_mode)) { + const char *name; + + if (which == SPLIT_FILE) + continue; + + name = (*dn)->name; + if ((which & SPLIT_DIR) /* any dir... */ + /* ... or not . or .. */ + || name[0] != '.' + || (name[1] && (name[1] != '.' || name[2])) + ) { + dnp[d++] = *dn; + } + } else + if (which == SPLIT_FILE) { + dnp[d++] = *dn; + } + } + return dnp; +} + +#if ENABLE_FEATURE_LS_SORTFILES +static int sortcmp(const void *a, const void *b) +{ + struct dnode *d1 = *(struct dnode **)a; + struct dnode *d2 = *(struct dnode **)b; + unsigned sort_opts = G.all_fmt & SORT_MASK; + off_t dif; + + dif = 0; /* assume SORT_NAME */ + // TODO: use pre-initialized function pointer + // instead of branch forest + if (sort_opts == SORT_SIZE) { + dif = (d2->dn_size - d1->dn_size); + } else + if (sort_opts == SORT_ATIME) { + dif = (d2->dn_atime - d1->dn_atime); + } else + if (sort_opts == SORT_CTIME) { + dif = (d2->dn_ctime - d1->dn_ctime); + } else + if (sort_opts == SORT_MTIME) { + dif = (d2->dn_mtime - d1->dn_mtime); + } else + if (sort_opts == SORT_DIR) { + dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); + } else +#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1 + if (sort_opts == SORT_VERSION) { + dif = strverscmp(d1->name, d2->name); + } else +#endif + if (sort_opts == SORT_EXT) { + dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.')); + } + if (dif == 0) { + /* sort by name, use as tie breaker for other sorts */ + if (ENABLE_LOCALE_SUPPORT) + dif = strcoll(d1->name, d2->name); + else + dif = strcmp(d1->name, d2->name); + } + + /* Make dif fit into an int */ + if (sizeof(dif) > sizeof(int)) { + enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; + /* shift leaving only "int" worth of bits */ + if (dif != 0) { + dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); + } + } + + return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif; +} + +static void dnsort(struct dnode **dn, int size) +{ + qsort(dn, size, sizeof(*dn), sortcmp); +} + +static void sort_and_display_files(struct dnode **dn, unsigned nfiles) +{ + dnsort(dn, nfiles); + display_files(dn, nfiles); +} +#else +# define dnsort(dn, size) ((void)0) +# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) +#endif + +/* Returns NULL-terminated malloced vector of pointers (or NULL) */ +static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) +{ + struct dnode *dn, *cur, **dnp; + struct dirent *entry; + DIR *dir; + unsigned i, nfiles; + + *nfiles_p = 0; + dir = warn_opendir(path); + if (dir == NULL) { + G.exit_code = EXIT_FAILURE; + return NULL; /* could not open the dir */ + } + dn = NULL; + nfiles = 0; + while ((entry = readdir(dir)) != NULL) { + char *fullname; + + /* are we going to list the file- it may be . or .. or a hidden file */ + if (entry->d_name[0] == '.') { + if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) + && !(G.all_fmt & DISP_DOT) + ) { + continue; + } + if (!(G.all_fmt & DISP_HIDDEN)) + continue; + } + fullname = concat_path_file(path, entry->d_name); + cur = my_stat(fullname, bb_basename(fullname), 0); + if (!cur) { + free(fullname); + continue; + } + cur->fname_allocated = 1; + cur->dn_next = dn; + dn = cur; + nfiles++; + } + closedir(dir); + + if (dn == NULL) + return NULL; + + /* now that we know how many files there are + * allocate memory for an array to hold dnode pointers + */ + *nfiles_p = nfiles; + dnp = dnalloc(nfiles); + for (i = 0; /* i < nfiles - detected via !dn below */; i++) { + dnp[i] = dn; /* save pointer to node in array */ + dn = dn->dn_next; + if (!dn) + break; + } + + return dnp; +} + +#if ENABLE_DESKTOP +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html + * If any of the -l, -n, -s options is specified, each list + * of files within the directory shall be preceded by a + * status line indicating the number of file system blocks + * occupied by files in the directory in 512-byte units if + * the -k option is not specified, or 1024-byte units if the + * -k option is specified, rounded up to the next integral + * number of units. + */ +/* by Jorgen Overgaard (jorgen AT antistaten.se) */ +static off_t calculate_blocks(struct dnode **dn) +{ + uoff_t blocks = 1; + if (dn) { + while (*dn) { + /* st_blocks is in 512 byte blocks */ + blocks += (*dn)->dn_blocks; + dn++; + } + } + + /* Even though standard says use 512 byte blocks, coreutils use 1k */ + /* Actually, we round up by calculating (blocks + 1) / 2, + * "+ 1" was done when we initialized blocks to 1 */ + return blocks >> 1; +} +#endif + +static void scan_and_display_dirs_recur(struct dnode **dn, int first) +{ + unsigned nfiles; + struct dnode **subdnp; + + for (; *dn; dn++) { + if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { + if (!first) + bb_putchar('\n'); + first = 0; + printf("%s:\n", (*dn)->fullname); + } + subdnp = scan_one_dir((*dn)->fullname, &nfiles); +#if ENABLE_DESKTOP + if ((G.all_fmt & STYLE_MASK) == STYLE_LONG) + printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); +#endif + if (nfiles > 0) { + /* list all files at this level */ + sort_and_display_files(subdnp, nfiles); + + if (ENABLE_FEATURE_LS_RECURSIVE + && (G.all_fmt & DISP_RECURSIVE) + ) { + struct dnode **dnd; + unsigned dndirs; + /* recursive - list the sub-dirs */ + dnd = splitdnarray(subdnp, SPLIT_SUBDIR); + dndirs = count_dirs(subdnp, SPLIT_SUBDIR); + if (dndirs > 0) { + dnsort(dnd, dndirs); + scan_and_display_dirs_recur(dnd, 0); + /* free the array of dnode pointers to the dirs */ + free(dnd); + } + } + /* free the dnodes and the fullname mem */ + dfree(subdnp); + } + } +} + + +int ls_main(int argc UNUSED_PARAM, char **argv) +{ + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + unsigned opt; + unsigned nfiles; + unsigned dnfiles; + unsigned dndirs; + unsigned i; +#if ENABLE_FEATURE_LS_COLOR + /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ + /* coreutils 6.10: + * # ls --color=BOGUS + * ls: invalid argument 'BOGUS' for '--color' + * Valid arguments are: + * 'always', 'yes', 'force' + * 'never', 'no', 'none' + * 'auto', 'tty', 'if-tty' + * (and substrings: "--color=alwa" work too) + */ + static const char ls_longopts[] ALIGN1 = + "color\0" Optional_argument "\xff"; /* no short equivalent */ + static const char color_str[] ALIGN1 = + "always\0""yes\0""force\0" + "auto\0""tty\0""if-tty\0"; + /* need to initialize since --color has _an optional_ argument */ + const char *color_opt = color_str; /* "always" */ +#endif + + INIT_G(); + + init_unicode(); + + if (ENABLE_FEATURE_LS_SORTFILES) + G.all_fmt = SORT_NAME; + +#if ENABLE_FEATURE_AUTOWIDTH + /* obtain the terminal width */ + get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL); + /* go one less... */ + G_terminal_width--; +#endif + + /* process options */ + IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;) + opt_complementary = + /* -e implies -l */ + IF_FEATURE_LS_TIMESTAMPS("el") + /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: + * in some pairs of opts, only last one takes effect: + */ + IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */ + // ":m-l:l-m" - we don't have -m + IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H") + ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */ + ":C-1:1-C" /* bycols/oneline */ + ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */ + IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */ + /* -w NUM: */ + IF_FEATURE_AUTOWIDTH(":w+"); + opt = getopt32(argv, ls_options + IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width) + IF_FEATURE_LS_COLOR(, &color_opt) + ); + for (i = 0; opt_flags[i] != (1U << 31); i++) { + if (opt & (1 << i)) { + uint32_t flags = opt_flags[i]; + + if (flags & STYLE_MASK) + G.all_fmt &= ~STYLE_MASK; + if (flags & SORT_MASK) + G.all_fmt &= ~SORT_MASK; + if (flags & TIME_MASK) + G.all_fmt &= ~TIME_MASK; + + G.all_fmt |= flags; + } + } + +#if ENABLE_FEATURE_LS_COLOR + /* set G_show_color = 1/0 */ + if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { + char *p = getenv("LS_COLORS"); + /* LS_COLORS is unset, or (not empty && not "none") ? */ + if (!p || (p[0] && strcmp(p, "none") != 0)) + G_show_color = 1; + } + if (opt & OPT_color) { + if (color_opt[0] == 'n') + G_show_color = 0; + else switch (index_in_substrings(color_str, color_opt)) { + case 3: + case 4: + case 5: + if (isatty(STDOUT_FILENO)) { + case 0: + case 1: + case 2: + G_show_color = 1; + } + } + } +#endif + + /* sort out which command line options take precedence */ + if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST)) + G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ + if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { + if (G.all_fmt & TIME_CHANGE) + G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME; + if (G.all_fmt & TIME_ACCESS) + G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME; + } + if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ + G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); + + /* choose a display format if one was not already specified by an option */ + if (!(G.all_fmt & STYLE_MASK)) + G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); + + argv += optind; + if (!argv[0]) + *--argv = (char*)"."; + + if (argv[1]) + G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ + + /* stuff the command line file names into a dnode array */ + dn = NULL; + nfiles = 0; + do { + cur = my_stat(*argv, *argv, + /* follow links on command line unless -l, -s or -F: */ + !((G.all_fmt & STYLE_MASK) == STYLE_LONG + || (G.all_fmt & LIST_BLOCKS) + || (option_mask32 & OPT_F) + ) + /* ... or if -H: */ + || (option_mask32 & OPT_H) + /* ... or if -L, but my_stat always follows links if -L */ + ); + argv++; + if (!cur) + continue; + /*cur->fname_allocated = 0; - already is */ + cur->dn_next = dn; + dn = cur; + nfiles++; + } while (*argv); + + /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ + if (nfiles == 0) + return G.exit_code; + + /* now that we know how many files there are + * allocate memory for an array to hold dnode pointers + */ + dnp = dnalloc(nfiles); + for (i = 0; /* i < nfiles - detected via !dn below */; i++) { + dnp[i] = dn; /* save pointer to node in array */ + dn = dn->dn_next; + if (!dn) + break; + } + + if (G.all_fmt & DISP_NOLIST) { + sort_and_display_files(dnp, nfiles); + } else { + dnd = splitdnarray(dnp, SPLIT_DIR); + dnf = splitdnarray(dnp, SPLIT_FILE); + dndirs = count_dirs(dnp, SPLIT_DIR); + dnfiles = nfiles - dndirs; + if (dnfiles > 0) { + sort_and_display_files(dnf, dnfiles); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnf); + } + if (dndirs > 0) { + dnsort(dnd, dndirs); + scan_and_display_dirs_recur(dnd, dnfiles == 0); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnd); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) + dfree(dnp); + return G.exit_code; +} diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c new file mode 100644 index 000000000..2cb6dd43c --- /dev/null +++ b/coreutils/md5_sha1_sum.c @@ -0,0 +1,241 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003-2004 Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define md5sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define md5sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums" +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" +//usage: "\n -c Check sums against list in FILEs" +//usage: "\n -s Don't output anything, status code shows success" +//usage: "\n -w Warn about improperly formatted checksum lines" +//usage: ) +//usage: +//usage:#define md5sum_example_usage +//usage: "$ md5sum < busybox\n" +//usage: "6fd11e98b98a58f64ff3398d7b324003\n" +//usage: "$ md5sum busybox\n" +//usage: "6fd11e98b98a58f64ff3398d7b324003 busybox\n" +//usage: "$ md5sum -c -\n" +//usage: "6fd11e98b98a58f64ff3398d7b324003 busybox\n" +//usage: "busybox: OK\n" +//usage: "^D\n" +//usage: +//usage:#define sha1sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define sha1sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums" +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" +//usage: "\n -c Check sums against list in FILEs" +//usage: "\n -s Don't output anything, status code shows success" +//usage: "\n -w Warn about improperly formatted checksum lines" +//usage: ) +//usage: +//usage:#define sha256sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define sha256sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA256 checksums" +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" +//usage: "\n -c Check sums against list in FILEs" +//usage: "\n -s Don't output anything, status code shows success" +//usage: "\n -w Warn about improperly formatted checksum lines" +//usage: ) +//usage: +//usage:#define sha512sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define sha512sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA512 checksums" +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" +//usage: "\n -c Check sums against list in FILEs" +//usage: "\n -s Don't output anything, status code shows success" +//usage: "\n -w Warn about improperly formatted checksum lines" +//usage: ) + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +enum { + /* 4th letter of applet_name is... */ + HASH_MD5 = 's', /* "md5>s 0) { + update(&context, in_buf, count); + } + hash_value = NULL; + if (count == 0) { + final(&context, in_buf); + hash_value = hash_bin_to_hex(in_buf, hash_len); + } + RELEASE_CONFIG_BUFFER(in_buf); + } + + if (src_fd != STDIN_FILENO) { + close(src_fd); + } + + return hash_value; +} + +int md5_sha1_sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) +{ + int return_value = EXIT_SUCCESS; + unsigned flags; + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) { + /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ + flags = getopt32(argv, "scwbt"); + argv += optind; + //argc -= optind; + } else { + argv += 1; + //argc -= 1; + } + if (!*argv) + *--argv = (char*)"-"; + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) { + if (flags & FLAG_SILENT) { + bb_error_msg_and_die("-%c is meaningful only with -c", 's'); + } + if (flags & FLAG_WARN) { + bb_error_msg_and_die("-%c is meaningful only with -c", 'w'); + } + } + + do { + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { + FILE *pre_computed_stream; + char *line; + int count_total = 0; + int count_failed = 0; + + pre_computed_stream = xfopen_stdin(*argv); + + while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) { + uint8_t *hash_value; + char *filename_ptr; + + count_total++; + filename_ptr = strstr(line, " "); + /* handle format for binary checksums */ + if (filename_ptr == NULL) { + filename_ptr = strstr(line, " *"); + } + if (filename_ptr == NULL) { + if (flags & FLAG_WARN) { + bb_error_msg("invalid format"); + } + count_failed++; + return_value = EXIT_FAILURE; + free(line); + continue; + } + *filename_ptr = '\0'; + filename_ptr += 2; + + hash_value = hash_file(filename_ptr); + + if (hash_value && (strcmp((char*)hash_value, line) == 0)) { + if (!(flags & FLAG_SILENT)) + printf("%s: OK\n", filename_ptr); + } else { + if (!(flags & FLAG_SILENT)) + printf("%s: FAILED\n", filename_ptr); + count_failed++; + return_value = EXIT_FAILURE; + } + /* possible free(NULL) */ + free(hash_value); + free(line); + } + if (count_failed && !(flags & FLAG_SILENT)) { + bb_error_msg("WARNING: %d of %d computed checksums did NOT match", + count_failed, count_total); + } + fclose_if_not_stdin(pre_computed_stream); + } else { + uint8_t *hash_value = hash_file(*argv); + if (hash_value == NULL) { + return_value = EXIT_FAILURE; + } else { + printf("%s %s\n", hash_value, *argv); + free(hash_value); + } + } + } while (*++argv); + + return return_value; +} diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c new file mode 100644 index 000000000..a4429b1cb --- /dev/null +++ b/coreutils/mkdir.c @@ -0,0 +1,96 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkdir implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed broken permission setting when -p was used; especially in + * conjunction with -m. + */ + +/* Nov 28, 2006 Yoshinori Sato : Add SELinux Support. + */ + +//usage:#define mkdir_trivial_usage +//usage: "[OPTIONS] DIRECTORY..." +//usage:#define mkdir_full_usage "\n\n" +//usage: "Create DIRECTORY\n" +//usage: "\n -m MODE Mode" +//usage: "\n -p No error if exists; make parent directories as needed" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) +//usage: +//usage:#define mkdir_example_usage +//usage: "$ mkdir /tmp/foo\n" +//usage: "$ mkdir /tmp/foo\n" +//usage: "/tmp/foo: File exists\n" +//usage: "$ mkdir /tmp/foo/bar/baz\n" +//usage: "/tmp/foo/bar/baz: No such file or directory\n" +//usage: "$ mkdir -p /tmp/foo/bar/baz\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS +static const char mkdir_longopts[] ALIGN1 = + "mode\0" Required_argument "m" + "parents\0" No_argument "p" +#if ENABLE_SELINUX + "context\0" Required_argument "Z" +#endif + ; +#endif + +int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mkdir_main(int argc UNUSED_PARAM, char **argv) +{ + mode_t mode = (mode_t)(-1); + int status = EXIT_SUCCESS; + int flags = 0; + unsigned opt; + char *smode; +#if ENABLE_SELINUX + security_context_t scontext; +#endif + +#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS + applet_long_options = mkdir_longopts; +#endif + opt = getopt32(argv, "m:p" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext)); + if (opt & 1) { + mode = 0777; + if (!bb_parse_mode(smode, &mode)) { + bb_error_msg_and_die("invalid mode '%s'", smode); + } + } + if (opt & 2) + flags |= FILEUTILS_RECUR; +#if ENABLE_SELINUX + if (opt & 4) { + selinux_or_die(); + setfscreatecon_or_die(scontext); + } +#endif + + argv += optind; + if (!argv[0]) + bb_show_usage(); + + do { + if (bb_make_directory(*argv, mode, flags)) { + status = EXIT_FAILURE; + } + } while (*++argv); + + return status; +} diff --git a/coreutils/mkfifo.c b/coreutils/mkfifo.c new file mode 100644 index 000000000..ef58325b3 --- /dev/null +++ b/coreutils/mkfifo.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfifo implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */ + +//usage:#define mkfifo_trivial_usage +//usage: "[-m MODE] " IF_SELINUX("[-Z] ") "NAME" +//usage:#define mkfifo_full_usage "\n\n" +//usage: "Create named pipe\n" +//usage: "\n -m MODE Mode (default a=rw)" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int mkfifo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mkfifo_main(int argc UNUSED_PARAM, char **argv) +{ + mode_t mode; + int retval = EXIT_SUCCESS; + + mode = getopt_mk_fifo_nod(argv); + + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + do { + if (mkfifo(*argv, mode) < 0) { + bb_simple_perror_msg(*argv); /* Avoid multibyte problems. */ + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} diff --git a/coreutils/mknod.c b/coreutils/mknod.c new file mode 100644 index 000000000..32d3659ac --- /dev/null +++ b/coreutils/mknod.c @@ -0,0 +1,76 @@ +/* vi: set sw=4 ts=4: */ +/* + * mknod implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +//usage:#define mknod_trivial_usage +//usage: "[-m MODE] " IF_SELINUX("[-Z] ") "NAME TYPE MAJOR MINOR" +//usage:#define mknod_full_usage "\n\n" +//usage: "Create a special file (block, character, or pipe)\n" +//usage: "\n -m MODE Creation mode (default a=rw)" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) +//usage: "\nTYPE:" +//usage: "\n b Block device" +//usage: "\n c or u Character device" +//usage: "\n p Named pipe (MAJOR and MINOR are ignored)" +//usage: +//usage:#define mknod_example_usage +//usage: "$ mknod /dev/fd0 b 2 0\n" +//usage: "$ mknod -m 644 /tmp/pipe p\n" + +#include // For makedev + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + +static const char modes_chars[] ALIGN1 = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 }; +static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK }; + +int mknod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mknod_main(int argc, char **argv) +{ + mode_t mode; + dev_t dev; + const char *name; + + mode = getopt_mk_fifo_nod(argv); + argv += optind; + argc -= optind; + + if (argc >= 2) { + name = strchr(modes_chars, argv[1][0]); + if (name != NULL) { + mode |= modes_cubp[(int)(name[4])]; + + dev = 0; + if (*name != 'p') { + argc -= 2; + if (argc == 2) { + /* Autodetect what the system supports; these macros should + * optimize out to two constants. */ + dev = makedev(xatoul_range(argv[2], 0, major(UINT_MAX)), + xatoul_range(argv[3], 0, minor(UINT_MAX))); + } + } + + if (argc == 2) { + name = *argv; + if (mknod(name, mode, dev) == 0) { + return EXIT_SUCCESS; + } + bb_simple_perror_msg_and_die(name); + } + } + } + bb_show_usage(); +} diff --git a/coreutils/mv.c b/coreutils/mv.c new file mode 100644 index 000000000..87f4cd5a5 --- /dev/null +++ b/coreutils/mv.c @@ -0,0 +1,154 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mv implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction and improved error checking. + */ + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +//usage:#define mv_trivial_usage +//usage: "[-fin] SOURCE DEST\n" +//usage: "or: mv [-fin] SOURCE... DIRECTORY" +//usage:#define mv_full_usage "\n\n" +//usage: "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY\n" +//usage: "\n -f Don't prompt before overwriting" +//usage: "\n -i Interactive, prompt before overwrite" +//usage: "\n -n Don't overwrite an existing file" +//usage: +//usage:#define mv_example_usage +//usage: "$ mv /tmp/foo /bin/bar\n" + +#if ENABLE_FEATURE_MV_LONG_OPTIONS +static const char mv_longopts[] ALIGN1 = + "interactive\0" No_argument "i" + "force\0" No_argument "f" + "no-clobber\0" No_argument "n" + ; +#endif + +#define OPT_FILEUTILS_FORCE 1 +#define OPT_FILEUTILS_INTERACTIVE 2 +#define OPT_FILEUTILS_NOCLOBBER 4 + +int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mv_main(int argc, char **argv) +{ + struct stat dest_stat; + const char *last; + const char *dest; + unsigned flags; + int dest_exists; + int status = 0; + int copy_flag = 0; + +#if ENABLE_FEATURE_MV_LONG_OPTIONS + applet_long_options = mv_longopts; +#endif + /* Need at least two arguments. + * If more than one of -f, -i, -n is specified , only the final one + * takes effect (it unsets previous options). */ + opt_complementary = "-2:f-in:i-fn:n-fi"; + flags = getopt32(argv, "fin"); + argc -= optind; + argv += optind; + last = argv[argc - 1]; + + if (argc == 2) { + dest_exists = cp_mv_stat(last, &dest_stat); + if (dest_exists < 0) { + return EXIT_FAILURE; + } + + if (!(dest_exists & 2)) { /* last is not a directory */ + dest = last; + goto DO_MOVE; + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); + dest_exists = cp_mv_stat(dest, &dest_stat); + if (dest_exists < 0) { + goto RET_1; + } + + DO_MOVE: + if (dest_exists) { + if (flags & OPT_FILEUTILS_NOCLOBBER) + goto RET_0; + if (!(flags & OPT_FILEUTILS_FORCE) + && ((access(dest, W_OK) < 0 && isatty(0)) + || (flags & OPT_FILEUTILS_INTERACTIVE)) + ) { + if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { + goto RET_1; /* Ouch! fprintf failed! */ + } + if (!bb_ask_confirmation()) { + goto RET_0; + } + } + } + + if (rename(*argv, dest) < 0) { + struct stat source_stat; + int source_exists; + + if (errno != EXDEV + || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 + ) { + bb_perror_msg("can't rename '%s'", *argv); + } else { + static const char fmt[] ALIGN1 = + "can't overwrite %sdirectory with %sdirectory"; + + if (dest_exists) { + if (dest_exists == 3) { + if (source_exists != 3) { + bb_error_msg(fmt, "", "non-"); + goto RET_1; + } + } else { + if (source_exists == 3) { + bb_error_msg(fmt, "non-", ""); + goto RET_1; + } + } + if (unlink(dest) < 0) { + bb_perror_msg("can't remove '%s'", dest); + goto RET_1; + } + } + /* FILEUTILS_RECUR also prevents nasties like + * "read from device and write contents to dst" + * instead of "create same device node" */ + copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; +#if ENABLE_SELINUX + copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; +#endif + if ((copy_file(*argv, dest, copy_flag) >= 0) + && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) + ) { + goto RET_0; + } + } + RET_1: + status = 1; + } + RET_0: + if (dest != last) { + free((void *) dest); + } + } while (*++argv != last); + + return status; +} diff --git a/coreutils/nice.c b/coreutils/nice.c new file mode 100644 index 000000000..ce759916f --- /dev/null +++ b/coreutils/nice.c @@ -0,0 +1,57 @@ +/* vi: set sw=4 ts=4: */ +/* + * nice implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define nice_trivial_usage +//usage: "[-n ADJUST] [PROG ARGS]" +//usage:#define nice_full_usage "\n\n" +//usage: "Change scheduling priority, run PROG\n" +//usage: "\n -n ADJUST Adjust priority by ADJUST" + +#include +#include "libbb.h" + +int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nice_main(int argc, char **argv) +{ + int old_priority, adjustment; + + old_priority = getpriority(PRIO_PROCESS, 0); + + if (!*++argv) { /* No args, so (GNU) output current nice value. */ + printf("%d\n", old_priority); + fflush_stdout_and_exit(EXIT_SUCCESS); + } + + adjustment = 10; /* Set default adjustment. */ + + if (argv[0][0] == '-') { + if (argv[0][1] == 'n') { /* -n */ + if (argv[0][2]) { /* -nNNNN (w/o space) */ + argv[0] += 2; argv--; argc++; + } + } else { /* -NNN (NNN may be negative) == -n NNN */ + argv[0] += 1; argv--; argc++; + } + if (argc < 4) { /* Missing priority and/or utility! */ + bb_show_usage(); + } + adjustment = xatoi_range(argv[1], INT_MIN/2, INT_MAX/2); + argv += 2; + } + + { /* Set our priority. */ + int prio = old_priority + adjustment; + + if (setpriority(PRIO_PROCESS, 0, prio) < 0) { + bb_perror_msg_and_die("setpriority(%d)", prio); + } + } + + BB_EXECVP_or_die(argv); +} diff --git a/coreutils/nohup.c b/coreutils/nohup.c new file mode 100644 index 000000000..63853fd55 --- /dev/null +++ b/coreutils/nohup.c @@ -0,0 +1,88 @@ +/* vi: set sw=4 ts=4: */ +/* nohup - invoke a utility immune to hangups. + * + * Busybox version based on nohup specification at + * http://www.opengroup.org/onlinepubs/007904975/utilities/nohup.html + * + * Copyright 2006 Rob Landley + * Copyright 2006 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define nohup_trivial_usage +//usage: "PROG ARGS" +//usage:#define nohup_full_usage "\n\n" +//usage: "Run PROG immune to hangups, with output to a non-tty" +//usage: +//usage:#define nohup_example_usage +//usage: "$ nohup make &" + +#include "libbb.h" + +/* Compat info: nohup (GNU coreutils 6.8) does this: +# nohup true +nohup: ignoring input and appending output to `nohup.out' +# nohup true 1>/dev/null +nohup: ignoring input and redirecting stderr to stdout +# nohup true 2>zz +# cat zz +nohup: ignoring input and appending output to `nohup.out' +# nohup true 2>zz 1>/dev/null +# cat zz +nohup: ignoring input +# nohup true /dev/null +nohup: redirecting stderr to stdout +# nohup true zz 1>/dev/null +# cat zz + (nothing) +# +*/ + +int nohup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nohup_main(int argc UNUSED_PARAM, char **argv) +{ + const char *nohupout; + char *home; + + xfunc_error_retval = 127; + + if (!argv[1]) { + bb_show_usage(); + } + + /* If stdin is a tty, detach from it. */ + if (isatty(STDIN_FILENO)) { + /* bb_error_msg("ignoring input"); */ + close(STDIN_FILENO); + xopen(bb_dev_null, O_RDONLY); /* will be fd 0 (STDIN_FILENO) */ + } + + nohupout = "nohup.out"; + /* Redirect stdout to nohup.out, either in "." or in "$HOME". */ + if (isatty(STDOUT_FILENO)) { + close(STDOUT_FILENO); + if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) { + home = getenv("HOME"); + if (home) { + nohupout = concat_path_file(home, nohupout); + xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR); + } else { + xopen(bb_dev_null, O_RDONLY); /* will be fd 1 */ + } + } + bb_error_msg("appending output to %s", nohupout); + } + + /* If we have a tty on stderr, redirect to stdout. */ + if (isatty(STDERR_FILENO)) { + /* if (stdout_wasnt_a_tty) + bb_error_msg("redirecting stderr to stdout"); */ + dup2(STDOUT_FILENO, STDERR_FILENO); + } + + signal(SIGHUP, SIG_IGN); + + argv++; + BB_EXECVP_or_die(argv); +} diff --git a/coreutils/od.c b/coreutils/od.c new file mode 100644 index 000000000..fb11fcfe3 --- /dev/null +++ b/coreutils/od.c @@ -0,0 +1,228 @@ +/* vi: set sw=4 ts=4: */ +/* + * od implementation for busybox + * Based on code from util-linux v 2.11l + * + * Copyright (c) 1990 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice is retained at the end of this file. + */ + +//usage:#if !ENABLE_DESKTOP +//usage:#define od_trivial_usage +//usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]" +//usage:#define od_full_usage "\n\n" +//usage: "Print FILE (or stdin) unambiguously, as octal bytes by default" +//usage:#endif + +#include "libbb.h" +#if ENABLE_DESKTOP +/* This one provides -t (busybox's own build script needs it) */ +#include "od_bloaty.c" +#else + +#include "dump.h" + +static void +odoffset(dumper_t *dumper, int argc, char ***argvp) +{ + char *num, *p; + int base; + char *end; + + /* + * The offset syntax of od(1) was genuinely bizarre. First, if + * it started with a plus it had to be an offset. Otherwise, if + * there were at least two arguments, a number or lower-case 'x' + * followed by a number makes it an offset. By default it was + * octal; if it started with 'x' or '0x' it was hex. If it ended + * in a '.', it was decimal. If a 'b' or 'B' was appended, it + * multiplied the number by 512 or 1024 byte units. There was + * no way to assign a block count to a hex offset. + * + * We assumes it's a file if the offset is bad. + */ + p = **argvp; + + if (!p) { + /* hey someone is probably piping to us ... */ + return; + } + + if ((*p != '+') + && (argc < 2 + || (!isdigit(p[0]) + && ((p[0] != 'x') || !isxdigit(p[1]))))) + return; + + base = 0; + /* + * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and + * set base. + */ + if (p[0] == '+') + ++p; + if (p[0] == 'x' && isxdigit(p[1])) { + ++p; + base = 16; + } else if (p[0] == '0' && p[1] == 'x') { + p += 2; + base = 16; + } + + /* skip over the number */ + if (base == 16) + for (num = p; isxdigit(*p); ++p) + continue; + else + for (num = p; isdigit(*p); ++p) + continue; + + /* check for no number */ + if (num == p) + return; + + /* if terminates with a '.', base is decimal */ + if (*p == '.') { + if (base) + return; + base = 10; + } + + dumper->dump_skip = strtol(num, &end, base ? base : 8); + + /* if end isn't the same as p, we got a non-octal digit */ + if (end != p) + dumper->dump_skip = 0; + else { + if (*p) { + if (*p == 'b') { + dumper->dump_skip *= 512; + ++p; + } else if (*p == 'B') { + dumper->dump_skip *= 1024; + ++p; + } + } + if (*p) + dumper->dump_skip = 0; + else { + ++*argvp; + /* + * If the offset uses a non-octal base, the base of + * the offset is changed as well. This isn't pretty, + * but it's easy. + */ +#define TYPE_OFFSET 7 + { + char x_or_d; + if (base == 16) { + x_or_d = 'x'; + goto DO_X_OR_D; + } + if (base == 10) { + x_or_d = 'd'; + DO_X_OR_D: + dumper->fshead->nextfu->fmt[TYPE_OFFSET] + = dumper->fshead->nextfs->nextfu->fmt[TYPE_OFFSET] + = x_or_d; + } + } + } + } +} + +static const char *const add_strings[] = { + "16/1 \"%3_u \" \"\\n\"", /* a */ + "8/2 \" %06o \" \"\\n\"", /* B, o */ + "16/1 \"%03o \" \"\\n\"", /* b */ + "16/1 \"%3_c \" \"\\n\"", /* c */ + "8/2 \" %05u \" \"\\n\"", /* d */ + "4/4 \" %010u \" \"\\n\"", /* D */ + "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */ + "4/4 \" %14.7e \" \"\\n\"", /* f */ + "4/4 \" %08x \" \"\\n\"", /* H, X */ + "8/2 \" %04x \" \"\\n\"", /* h, x */ + "4/4 \" %11d \" \"\\n\"", /* I, L, l */ + "8/2 \" %6d \" \"\\n\"", /* i */ + "4/4 \" %011o \" \"\\n\"", /* O */ +}; + +static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv"; + +static const char od_o2si[] ALIGN1 = { + 0, 1, 2, 3, 5, + 4, 6, 6, 7, 8, + 9, 0xa, 0xb, 0xa, 0xa, + 0xb, 1, 8, 9, +}; + +int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int od_main(int argc, char **argv) +{ + int ch; + int first = 1; + char *p; + dumper_t *dumper = alloc_dumper(); + + while ((ch = getopt(argc, argv, od_opts)) > 0) { + if (ch == 'v') { + dumper->dump_vflag = ALL; + } else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) { + if (first) { + first = 0; + bb_dump_add(dumper, "\"%07.7_Ao\n\""); + bb_dump_add(dumper, "\"%07.7_ao \""); + } else { + bb_dump_add(dumper, "\" \""); + } + bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]); + } else { /* P, p, s, w, or other unhandled */ + bb_show_usage(); + } + } + if (!dumper->fshead) { + bb_dump_add(dumper, "\"%07.7_Ao\n\""); + bb_dump_add(dumper, "\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); + } + + argc -= optind; + argv += optind; + + odoffset(dumper, argc, &argv); + + return bb_dump_dump(dumper, argv); +} +#endif /* ENABLE_DESKTOP */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c new file mode 100644 index 000000000..347f879d7 --- /dev/null +++ b/coreutils/od_bloaty.c @@ -0,0 +1,1384 @@ +/* od -- dump files in octal and other formats + Copyright (C) 92, 1995-2004 Free Software Foundation, Inc. + + 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, 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. + */ +/* Written by Jim Meyering. */ +/* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */ + + +/* #include "libbb.h" - done in od.c */ +#define assert(a) ((void)0) + + +//usage:#if ENABLE_DESKTOP +//usage:#define od_trivial_usage +//usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE...]" +// We don't support: +// ... [FILE] [[+]OFFSET[.][b]] +// Support is buggy for: +// od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]] + +//usage:#define od_full_usage "\n\n" +//usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default" +//usage:#endif + +enum { + OPT_A = 1 << 0, + OPT_N = 1 << 1, + OPT_a = 1 << 2, + OPT_b = 1 << 3, + OPT_c = 1 << 4, + OPT_d = 1 << 5, + OPT_f = 1 << 6, + OPT_h = 1 << 7, + OPT_i = 1 << 8, + OPT_j = 1 << 9, + OPT_l = 1 << 10, + OPT_o = 1 << 11, + OPT_t = 1 << 12, + /* When zero and two or more consecutive blocks are equal, format + only the first block and output an asterisk alone on the following + line to indicate that identical blocks have been elided: */ + OPT_v = 1 << 13, + OPT_x = 1 << 14, + OPT_s = 1 << 15, + OPT_S = 1 << 16, + OPT_w = 1 << 17, + OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, +}; + +#define OD_GETOPT32() getopt32(argv, \ + "A:N:abcdfhij:lot:vxsS:w::", \ + /* -w with optional param */ \ + /* -S was -s and also had optional parameter */ \ + /* but in coreutils 6.3 it was renamed and now has */ \ + /* _mandatory_ parameter */ \ + &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block) + + +/* Check for 0x7f is a coreutils 6.3 addition */ +#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f) + +typedef long double longdouble_t; +typedef unsigned long long ulonglong_t; +typedef long long llong; + +#if ENABLE_LFS +# define xstrtooff_sfx xstrtoull_sfx +#else +# define xstrtooff_sfx xstrtoul_sfx +#endif + +/* The default number of input bytes per output line. */ +#define DEFAULT_BYTES_PER_BLOCK 16 + +/* The number of decimal digits of precision in a float. */ +#ifndef FLT_DIG +# define FLT_DIG 7 +#endif + +/* The number of decimal digits of precision in a double. */ +#ifndef DBL_DIG +# define DBL_DIG 15 +#endif + +/* The number of decimal digits of precision in a long double. */ +#ifndef LDBL_DIG +# define LDBL_DIG DBL_DIG +#endif + +enum size_spec { + NO_SIZE, + CHAR, + SHORT, + INT, + LONG, + LONG_LONG, + FLOAT_SINGLE, + FLOAT_DOUBLE, + FLOAT_LONG_DOUBLE, + N_SIZE_SPECS +}; + +enum output_format { + SIGNED_DECIMAL, + UNSIGNED_DECIMAL, + OCTAL, + HEXADECIMAL, + FLOATING_POINT, + NAMED_CHARACTER, + CHARACTER +}; + +/* Each output format specification (from '-t spec' or from + old-style options) is represented by one of these structures. */ +struct tspec { + enum output_format fmt; + enum size_spec size; + void (*print_function) (size_t, const char *, const char *); + char *fmt_string; + int hexl_mode_trailer; + int field_width; +}; + +/* Convert the number of 8-bit bytes of a binary representation to + the number of characters (digits + sign if the type is signed) + required to represent the same quantity in the specified base/type. + For example, a 32-bit (4-byte) quantity may require a field width + as wide as the following for these types: + 11 unsigned octal + 11 signed decimal + 10 unsigned decimal + 8 unsigned hexadecimal */ + +static const uint8_t bytes_to_oct_digits[] ALIGN1 = +{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43}; + +static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 = +{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40}; + +static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 = +{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39}; + +static const uint8_t bytes_to_hex_digits[] ALIGN1 = +{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}; + +/* Convert enum size_spec to the size of the named type. */ +static const signed char width_bytes[] ALIGN1 = { + -1, + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + sizeof(ulonglong_t), + sizeof(float), + sizeof(double), + sizeof(longdouble_t) +}; +/* Ensure that for each member of 'enum size_spec' there is an + initializer in the width_bytes array. */ +struct ERR_width_bytes_has_bad_size { + char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; +}; + +static smallint exit_code; + +static unsigned string_min; + +/* An array of specs describing how to format each input block. */ +static size_t n_specs; +static struct tspec *spec; + +/* Function that accepts an address and an optional following char, + and prints the address and char to stdout. */ +static void (*format_address)(off_t, char); +/* The difference between the old-style pseudo starting address and + the number of bytes to skip. */ +#if ENABLE_LONG_OPTS +static off_t pseudo_offset; +#else +enum { pseudo_offset = 0 }; +#endif +/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all + input is formatted. */ + +/* The number of input bytes formatted per output line. It must be + a multiple of the least common multiple of the sizes associated with + the specified output types. It should be as large as possible, but + no larger than 16 -- unless specified with the -w option. */ +static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */ + +/* A NULL-terminated list of the file-arguments from the command line. */ +static const char *const *file_list; + +/* The input stream associated with the current file. */ +static FILE *in_stream; + +#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) +static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = { + [sizeof(char)] = CHAR, +#if USHRT_MAX != UCHAR_MAX + [sizeof(short)] = SHORT, +#endif +#if UINT_MAX != USHRT_MAX + [sizeof(int)] = INT, +#endif +#if ULONG_MAX != UINT_MAX + [sizeof(long)] = LONG, +#endif +#if ULLONG_MAX != ULONG_MAX + [sizeof(ulonglong_t)] = LONG_LONG, +#endif +}; + +#define MAX_FP_TYPE_SIZE sizeof(longdouble_t) +static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { + /* gcc seems to allow repeated indexes. Last one wins */ + [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, + [sizeof(double)] = FLOAT_DOUBLE, + [sizeof(float)] = FLOAT_SINGLE +}; + + +static unsigned +gcd(unsigned u, unsigned v) +{ + unsigned t; + while (v != 0) { + t = u % v; + u = v; + v = t; + } + return u; +} + +/* Compute the least common multiple of U and V. */ +static unsigned +lcm(unsigned u, unsigned v) { + unsigned t = gcd(u, v); + if (t == 0) + return 0; + return u * v / t; +} + +static void +print_s_char(size_t n_bytes, const char *block, const char *fmt_string) +{ + while (n_bytes--) { + int tmp = *(signed char *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned char); + } +} + +static void +print_char(size_t n_bytes, const char *block, const char *fmt_string) +{ + while (n_bytes--) { + unsigned tmp = *(unsigned char *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned char); + } +} + +static void +print_s_short(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(signed short); + while (n_bytes--) { + int tmp = *(signed short *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned short); + } +} + +static void +print_short(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned short); + while (n_bytes--) { + unsigned tmp = *(unsigned short *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned short); + } +} + +static void +print_int(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned); + while (n_bytes--) { + unsigned tmp = *(unsigned *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned); + } +} + +#if UINT_MAX == ULONG_MAX +# define print_long print_int +#else +static void +print_long(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned long); + while (n_bytes--) { + unsigned long tmp = *(unsigned long *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned long); + } +} +#endif + +#if ULONG_MAX == ULLONG_MAX +# define print_long_long print_long +#else +static void +print_long_long(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(ulonglong_t); + while (n_bytes--) { + ulonglong_t tmp = *(ulonglong_t *) block; + printf(fmt_string, tmp); + block += sizeof(ulonglong_t); + } +} +#endif + +static void +print_float(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(float); + while (n_bytes--) { + float tmp = *(float *) block; + printf(fmt_string, tmp); + block += sizeof(float); + } +} + +static void +print_double(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(double); + while (n_bytes--) { + double tmp = *(double *) block; + printf(fmt_string, tmp); + block += sizeof(double); + } +} + +static void +print_long_double(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(longdouble_t); + while (n_bytes--) { + longdouble_t tmp = *(longdouble_t *) block; + printf(fmt_string, tmp); + block += sizeof(longdouble_t); + } +} + +/* print_[named]_ascii are optimized for speed. + * Remember, someday you may want to pump gigabytes through this thing. + * Saving a dozen of .text bytes here is counter-productive */ + +static void +print_named_ascii(size_t n_bytes, const char *block, + const char *unused_fmt_string UNUSED_PARAM) +{ + /* Names for some non-printing characters. */ + static const char charname[33][3] ALIGN1 = { + "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", + " bs", " ht", " nl", " vt", " ff", " cr", " so", " si", + "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", + "can", " em", "sub", "esc", " fs", " gs", " rs", " us", + " sp" + }; + // buf[N] pos: 01234 56789 + char buf[12] = " x\0 0xx\0"; + // actually " x\0 xxx\0", but want to share string with print_ascii. + // [12] because we take three 32bit stack slots anyway, and + // gcc is too dumb to initialize with constant stores, + // it copies initializer from rodata. Oh well. + + while (n_bytes--) { + unsigned masked_c = *(unsigned char *) block++; + + masked_c &= 0x7f; + if (masked_c == 0x7f) { + fputs(" del", stdout); + continue; + } + if (masked_c > ' ') { + buf[3] = masked_c; + fputs(buf, stdout); + continue; + } + /* Why? Because printf(" %3.3s") is much slower... */ + buf[6] = charname[masked_c][0]; + buf[7] = charname[masked_c][1]; + buf[8] = charname[masked_c][2]; + fputs(buf+5, stdout); + } +} + +static void +print_ascii(size_t n_bytes, const char *block, + const char *unused_fmt_string UNUSED_PARAM) +{ + // buf[N] pos: 01234 56789 + char buf[12] = " x\0 0xx\0"; + + while (n_bytes--) { + const char *s; + unsigned c = *(unsigned char *) block++; + + if (ISPRINT(c)) { + buf[3] = c; + fputs(buf, stdout); + continue; + } + switch (c) { + case '\0': + s = " \\0"; + break; + case '\007': + s = " \\a"; + break; + case '\b': + s = " \\b"; + break; + case '\f': + s = " \\f"; + break; + case '\n': + s = " \\n"; + break; + case '\r': + s = " \\r"; + break; + case '\t': + s = " \\t"; + break; + case '\v': + s = " \\v"; + break; + case '\x7f': + s = " 177"; + break; + default: /* c is never larger than 040 */ + buf[7] = (c >> 3) + '0'; + buf[8] = (c & 7) + '0'; + s = buf + 5; + } + fputs(s, stdout); + } +} + +/* Given a list of one or more input filenames FILE_LIST, set the global + file pointer IN_STREAM and the global string INPUT_FILENAME to the + first one that can be successfully opened. Modify FILE_LIST to + reference the next filename in the list. A file name of "-" is + interpreted as standard input. If any file open fails, give an error + message and return nonzero. */ + +static void +open_next_file(void) +{ + while (1) { + if (!*file_list) + return; + in_stream = fopen_or_warn_stdin(*file_list++); + if (in_stream) { + break; + } + exit_code = 1; + } + + if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N) + setbuf(in_stream, NULL); +} + +/* Test whether there have been errors on in_stream, and close it if + it is not standard input. Return nonzero if there has been an error + on in_stream or stdout; return zero otherwise. This function will + report more than one error only if both a read and a write error + have occurred. IN_ERRNO, if nonzero, is the error number + corresponding to the most recent action for IN_STREAM. */ + +static void +check_and_close(void) +{ + if (in_stream) { + if (ferror(in_stream)) { + bb_error_msg("%s: read error", (in_stream == stdin) + ? bb_msg_standard_input + : file_list[-1] + ); + exit_code = 1; + } + fclose_if_not_stdin(in_stream); + in_stream = NULL; + } + + if (ferror(stdout)) { + bb_error_msg_and_die(bb_msg_write_error); + } +} + +/* If S points to a single valid modern od format string, put + a description of that format in *TSPEC, return pointer to + character following the just-decoded format. + For example, if S were "d4afL", we will return a rtp to "afL" + and *TSPEC would be + { + fmt = SIGNED_DECIMAL; + size = INT or LONG; (whichever integral_type_size[4] resolves to) + print_function = print_int; (assuming size == INT) + fmt_string = "%011d%c"; + } + S_ORIG is solely for reporting errors. It should be the full format + string argument. */ + +static NOINLINE const char * +decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) +{ + enum size_spec size_spec; + unsigned size; + enum output_format fmt; + const char *p; + char *end; + char *fmt_string = NULL; + void (*print_function) (size_t, const char *, const char *); + unsigned c; + unsigned field_width = 0; + int pos; + + switch (*s) { + case 'd': + case 'o': + case 'u': + case 'x': { + static const char CSIL[] ALIGN1 = "CSIL"; + + c = *s++; + p = strchr(CSIL, *s); + /* if *s == NUL, p != NULL! Testcase: "od -tx" */ + if (!p || *p == '\0') { + size = sizeof(int); + if (isdigit(s[0])) { + size = bb_strtou(s, &end, 0); + if (errno == ERANGE + || MAX_INTEGRAL_TYPE_SIZE < size + || integral_type_size[size] == NO_SIZE + ) { + bb_error_msg_and_die("invalid type string '%s'; " + "%u-byte %s type is not supported", + s_orig, size, "integral"); + } + s = end; + } + } else { + static const uint8_t CSIL_sizeof[4] = { + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + }; + size = CSIL_sizeof[p - CSIL]; + s++; /* skip C/S/I/L */ + } + +#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \ + ((Spec) == LONG_LONG ? (Max_format) \ + : ((Spec) == LONG ? (Long_format) : (Min_format))) + +#define FMT_BYTES_ALLOCATED 9 + size_spec = integral_type_size[size]; + + { + static const char doux[] ALIGN1 = "doux"; + static const char doux_fmt_letter[][4] = { + "lld", "llo", "llu", "llx" + }; + static const enum output_format doux_fmt[] = { + SIGNED_DECIMAL, + OCTAL, + UNSIGNED_DECIMAL, + HEXADECIMAL, + }; + static const uint8_t *const doux_bytes_to_XXX[] = { + bytes_to_signed_dec_digits, + bytes_to_oct_digits, + bytes_to_unsigned_dec_digits, + bytes_to_hex_digits, + }; + static const char doux_fmtstring[][sizeof(" %%0%u%s")] = { + " %%%u%s", + " %%0%u%s", + " %%%u%s", + " %%0%u%s", + }; + + pos = strchr(doux, c) - doux; + fmt = doux_fmt[pos]; + field_width = doux_bytes_to_XXX[pos][size]; + p = doux_fmt_letter[pos] + 2; + if (size_spec == LONG) p--; + if (size_spec == LONG_LONG) p -= 2; + fmt_string = xasprintf(doux_fmtstring[pos], field_width, p); + } + + switch (size_spec) { + case CHAR: + print_function = (fmt == SIGNED_DECIMAL + ? print_s_char + : print_char); + break; + case SHORT: + print_function = (fmt == SIGNED_DECIMAL + ? print_s_short + : print_short); + break; + case INT: + print_function = print_int; + break; + case LONG: + print_function = print_long; + break; + default: /* case LONG_LONG: */ + print_function = print_long_long; + break; + } + break; + } + + case 'f': { + static const char FDL[] ALIGN1 = "FDL"; + + fmt = FLOATING_POINT; + ++s; + p = strchr(FDL, *s); + if (!p) { + size = sizeof(double); + if (isdigit(s[0])) { + size = bb_strtou(s, &end, 0); + if (errno == ERANGE || size > MAX_FP_TYPE_SIZE + || fp_type_size[size] == NO_SIZE + ) { + bb_error_msg_and_die("invalid type string '%s'; " + "%u-byte %s type is not supported", + s_orig, size, "floating point"); + } + s = end; + } + } else { + static const uint8_t FDL_sizeof[] = { + sizeof(float), + sizeof(double), + sizeof(longdouble_t), + }; + + size = FDL_sizeof[p - FDL]; + } + + size_spec = fp_type_size[size]; + + switch (size_spec) { + case FLOAT_SINGLE: + print_function = print_float; + field_width = FLT_DIG + 8; + /* Don't use %#e; not all systems support it. */ + fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG); + break; + case FLOAT_DOUBLE: + print_function = print_double; + field_width = DBL_DIG + 8; + fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG); + break; + default: /* case FLOAT_LONG_DOUBLE: */ + print_function = print_long_double; + field_width = LDBL_DIG + 8; + fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG); + break; + } + break; + } + + case 'a': + ++s; + fmt = NAMED_CHARACTER; + size_spec = CHAR; + print_function = print_named_ascii; + field_width = 3; + break; + case 'c': + ++s; + fmt = CHARACTER; + size_spec = CHAR; + print_function = print_ascii; + field_width = 3; + break; + default: + bb_error_msg_and_die("invalid character '%c' " + "in type string '%s'", *s, s_orig); + } + + tspec->size = size_spec; + tspec->fmt = fmt; + tspec->print_function = print_function; + tspec->fmt_string = fmt_string; + + tspec->field_width = field_width; + tspec->hexl_mode_trailer = (*s == 'z'); + if (tspec->hexl_mode_trailer) + s++; + + return s; +} + +/* Decode the modern od format string S. Append the decoded + representation to the global array SPEC, reallocating SPEC if + necessary. */ + +static void +decode_format_string(const char *s) +{ + const char *s_orig = s; + + while (*s != '\0') { + struct tspec tspec; + const char *next; + + next = decode_one_format(s_orig, s, &tspec); + + assert(s != next); + s = next; + spec = xrealloc_vector(spec, 4, n_specs); + memcpy(&spec[n_specs], &tspec, sizeof(spec[0])); + n_specs++; + } +} + +/* Given a list of one or more input filenames FILE_LIST, set the global + file pointer IN_STREAM to position N_SKIP in the concatenation of + those files. If any file operation fails or if there are fewer than + N_SKIP bytes in the combined input, give an error message and return + nonzero. When possible, use seek rather than read operations to + advance IN_STREAM. */ + +static void +skip(off_t n_skip) +{ + if (n_skip == 0) + return; + + while (in_stream) { /* !EOF */ + struct stat file_stats; + + /* First try seeking. For large offsets, this extra work is + worthwhile. If the offset is below some threshold it may be + more efficient to move the pointer by reading. There are two + issues when trying to seek: + - the file must be seekable. + - before seeking to the specified position, make sure + that the new position is in the current file. + Try to do that by getting file's size using fstat. + But that will work only for regular files. */ + + /* The st_size field is valid only for regular files + (and for symbolic links, which cannot occur here). + If the number of bytes left to skip is at least + as large as the size of the current file, we can + decrement n_skip and go on to the next file. */ + if (fstat(fileno(in_stream), &file_stats) == 0 + && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0 + ) { + if (file_stats.st_size < n_skip) { + n_skip -= file_stats.st_size; + /* take "check & close / open_next" route */ + } else { + if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) + exit_code = 1; + return; + } + } else { + /* If it's not a regular file with positive size, + position the file pointer by reading. */ + char buf[1024]; + size_t n_bytes_to_read = 1024; + size_t n_bytes_read; + + while (n_skip > 0) { + if (n_skip < n_bytes_to_read) + n_bytes_to_read = n_skip; + n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream); + n_skip -= n_bytes_read; + if (n_bytes_read != n_bytes_to_read) + break; /* EOF on this file or error */ + } + } + if (n_skip == 0) + return; + + check_and_close(); + open_next_file(); + } + + if (n_skip) + bb_error_msg_and_die("can't skip past end of combined input"); +} + + +typedef void FN_format_address(off_t address, char c); + +static void +format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM) +{ +} + +static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc"; +/* Corresponds to 'x' above */ +#define address_base_char address_fmt[sizeof(address_fmt)-3] +/* Corresponds to 'n' above */ +#define address_pad_len_char address_fmt[2] + +static void +format_address_std(off_t address, char c) +{ + /* Corresponds to 'c' */ + address_fmt[sizeof(address_fmt)-2] = c; + printf(address_fmt, address); +} + +#if ENABLE_LONG_OPTS +/* only used with --traditional */ +static void +format_address_paren(off_t address, char c) +{ + putchar('('); + format_address_std(address, ')'); + if (c) putchar(c); +} + +static void +format_address_label(off_t address, char c) +{ + format_address_std(address, ' '); + format_address_paren(address + pseudo_offset, c); +} +#endif + +static void +dump_hexl_mode_trailer(size_t n_bytes, const char *block) +{ + fputs(" >", stdout); + while (n_bytes--) { + unsigned c = *(unsigned char *) block++; + c = (ISPRINT(c) ? c : '.'); + putchar(c); + } + putchar('<'); +} + +/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each + of the N_SPEC format specs. CURRENT_OFFSET is the byte address of + CURR_BLOCK in the concatenation of input files, and it is printed + (optionally) only before the output line associated with the first + format spec. When duplicate blocks are being abbreviated, the output + for a sequence of identical input blocks is the output for the first + block followed by an asterisk alone on a line. It is valid to compare + the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK. + That condition may be false only for the last input block -- and then + only when it has not been padded to length BYTES_PER_BLOCK. */ + +static void +write_block(off_t current_offset, size_t n_bytes, + const char *prev_block, const char *curr_block) +{ + static char first = 1; + static char prev_pair_equal = 0; + size_t i; + + if (!(option_mask32 & OPT_v) + && !first + && n_bytes == bytes_per_block + && memcmp(prev_block, curr_block, bytes_per_block) == 0 + ) { + if (prev_pair_equal) { + /* The two preceding blocks were equal, and the current + block is the same as the last one, so print nothing. */ + } else { + puts("*"); + prev_pair_equal = 1; + } + } else { + first = 0; + prev_pair_equal = 0; + for (i = 0; i < n_specs; i++) { + if (i == 0) + format_address(current_offset, '\0'); + else + printf("%*s", address_pad_len_char - '0', ""); + (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); + if (spec[i].hexl_mode_trailer) { + /* space-pad out to full line width, then dump the trailer */ + unsigned datum_width = width_bytes[spec[i].size]; + unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width; + unsigned field_width = spec[i].field_width + 1; + printf("%*s", blank_fields * field_width, ""); + dump_hexl_mode_trailer(n_bytes, curr_block); + } + putchar('\n'); + } + } +} + +static void +read_block(size_t n, char *block, size_t *n_bytes_in_buffer) +{ + assert(0 < n && n <= bytes_per_block); + + *n_bytes_in_buffer = 0; + + if (n == 0) + return; + + while (in_stream != NULL) { /* EOF. */ + size_t n_needed; + size_t n_read; + + n_needed = n - *n_bytes_in_buffer; + n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream); + *n_bytes_in_buffer += n_read; + if (n_read == n_needed) + break; + /* error check is done in check_and_close */ + check_and_close(); + open_next_file(); + } +} + +/* Return the least common multiple of the sizes associated + with the format specs. */ + +static int +get_lcm(void) +{ + size_t i; + int l_c_m = 1; + + for (i = 0; i < n_specs; i++) + l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]); + return l_c_m; +} + +/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the + formatted block to standard output, and repeat until the specified + maximum number of bytes has been read or until all input has been + processed. If the last block read is smaller than BYTES_PER_BLOCK + and its size is not a multiple of the size associated with a format + spec, extend the input block with zero bytes until its length is a + multiple of all format spec sizes. Write the final block. Finally, + write on a line by itself the offset of the byte after the last byte + read. */ + +static void +dump(off_t current_offset, off_t end_offset) +{ + char *block[2]; + int idx; + size_t n_bytes_read; + + block[0] = xmalloc(2 * bytes_per_block); + block[1] = block[0] + bytes_per_block; + + idx = 0; + if (option_mask32 & OPT_N) { + while (1) { + size_t n_needed; + if (current_offset >= end_offset) { + n_bytes_read = 0; + break; + } + n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block); + read_block(n_needed, block[idx], &n_bytes_read); + if (n_bytes_read < bytes_per_block) + break; + assert(n_bytes_read == bytes_per_block); + write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); + current_offset += n_bytes_read; + idx ^= 1; + } + } else { + while (1) { + read_block(bytes_per_block, block[idx], &n_bytes_read); + if (n_bytes_read < bytes_per_block) + break; + assert(n_bytes_read == bytes_per_block); + write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]); + current_offset += n_bytes_read; + idx ^= 1; + } + } + + if (n_bytes_read > 0) { + int l_c_m; + size_t bytes_to_write; + + l_c_m = get_lcm(); + + /* Make bytes_to_write the smallest multiple of l_c_m that + is at least as large as n_bytes_read. */ + bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m); + + memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); + write_block(current_offset, bytes_to_write, + block[idx ^ 1], block[idx]); + current_offset += n_bytes_read; + } + + format_address(current_offset, '\n'); + + if ((option_mask32 & OPT_N) && current_offset >= end_offset) + check_and_close(); + + free(block[0]); +} + +/* Read N bytes into BLOCK from the concatenation of the input files + named in the global array FILE_LIST. On the first call to this + function, the global variable IN_STREAM is expected to be an open + stream associated with the input file INPUT_FILENAME. If all N + bytes cannot be read from IN_STREAM, close IN_STREAM and update + the global variables IN_STREAM and INPUT_FILENAME. Then try to + read the remaining bytes from the newly opened file. Repeat if + necessary until EOF is reached for the last file in FILE_LIST. + On subsequent calls, don't modify BLOCK and return zero. Set + *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs, + it will be detected through ferror when the stream is about to be + closed. If there is an error, give a message but continue reading + as usual and return nonzero. Otherwise return zero. */ + +/* STRINGS mode. Find each "string constant" in the input. + A string constant is a run of at least 'string_min' ASCII + graphic (or formatting) characters terminated by a null. + Based on a function written by Richard Stallman for a + traditional version of od. */ + +static void +dump_strings(off_t address, off_t end_offset) +{ + unsigned bufsize = MAX(100, string_min); + unsigned char *buf = xmalloc(bufsize); + + while (1) { + size_t i; + int c; + + /* See if the next 'string_min' chars are all printing chars. */ + tryline: + if ((option_mask32 & OPT_N) && (end_offset - string_min <= address)) + break; + i = 0; + while (!(option_mask32 & OPT_N) || address < end_offset) { + if (i == bufsize) { + bufsize += bufsize/8; + buf = xrealloc(buf, bufsize); + } + + while (in_stream) { /* !EOF */ + c = fgetc(in_stream); + if (c != EOF) + goto got_char; + check_and_close(); + open_next_file(); + } + /* EOF */ + goto ret; + got_char: + address++; + if (!c) + break; + if (!ISPRINT(c)) + goto tryline; /* It isn't; give up on this string. */ + buf[i++] = c; /* String continues; store it all. */ + } + + if (i < string_min) /* Too short! */ + goto tryline; + + /* If we get here, the string is all printable and NUL-terminated */ + buf[i] = 0; + format_address(address - i - 1, ' '); + + for (i = 0; (c = buf[i]); i++) { + switch (c) { + case '\007': fputs("\\a", stdout); break; + case '\b': fputs("\\b", stdout); break; + case '\f': fputs("\\f", stdout); break; + case '\n': fputs("\\n", stdout); break; + case '\r': fputs("\\r", stdout); break; + case '\t': fputs("\\t", stdout); break; + case '\v': fputs("\\v", stdout); break; + default: putchar(c); + } + } + putchar('\n'); + } + + /* We reach this point only if we search through + (max_bytes_to_format - string_min) bytes before reaching EOF. */ + check_and_close(); + ret: + free(buf); +} + +#if ENABLE_LONG_OPTS +/* If S is a valid traditional offset specification with an optional + leading '+' return nonzero and set *OFFSET to the offset it denotes. */ + +static int +parse_old_offset(const char *s, off_t *offset) +{ + static const struct suffix_mult Bb[] = { + { "B", 1024 }, + { "b", 512 }, + { "", 0 } + }; + char *p; + int radix; + + /* Skip over any leading '+'. */ + if (s[0] == '+') ++s; + if (!isdigit(s[0])) return 0; /* not a number */ + + /* Determine the radix we'll use to interpret S. If there is a '.', + * it's decimal, otherwise, if the string begins with '0X'or '0x', + * it's hexadecimal, else octal. */ + p = strchr(s, '.'); + radix = 8; + if (p) { + p[0] = '\0'; /* cheating */ + radix = 10; + } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + radix = 16; + + *offset = xstrtooff_sfx(s, radix, Bb); + if (p) p[0] = '.'; + + return (*offset >= 0); +} +#endif + +int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int od_main(int argc UNUSED_PARAM, char **argv) +{ + static const struct suffix_mult bkm[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { "", 0 } + }; +#if ENABLE_LONG_OPTS + static const char od_longopts[] ALIGN1 = + "skip-bytes\0" Required_argument "j" + "address-radix\0" Required_argument "A" + "read-bytes\0" Required_argument "N" + "format\0" Required_argument "t" + "output-duplicates\0" No_argument "v" + /* Yes, it's true: -S NUM, but --strings[=NUM]! + * that is, NUM is mandatory for -S but optional for --strings! + */ + "strings\0" Optional_argument "S" + "width\0" Optional_argument "w" + "traditional\0" No_argument "\xff" + ; +#endif + const char *str_A, *str_N, *str_j, *str_S = "3"; + llist_t *lst_t = NULL; + unsigned opt; + int l_c_m; + /* The number of input bytes to skip before formatting and writing. */ + off_t n_bytes_to_skip = 0; + /* The offset of the first byte after the last byte to be formatted. */ + off_t end_offset = 0; + /* The maximum number of bytes that will be formatted. */ + off_t max_bytes_to_format = 0; + + spec = NULL; + format_address = format_address_std; + address_base_char = 'o'; + address_pad_len_char = '7'; + + /* Parse command line */ + opt_complementary = "w+:t::"; /* -w N, -t is a list */ +#if ENABLE_LONG_OPTS + applet_long_options = od_longopts; +#endif + opt = OD_GETOPT32(); + argv += optind; + if (opt & OPT_A) { + static const char doxn[] ALIGN1 = "doxn"; + static const char doxn_address_base_char[] ALIGN1 = { + 'u', 'o', 'x', /* '?' fourth one is not important */ + }; + static const uint8_t doxn_address_pad_len_char[] ALIGN1 = { + '7', '7', '6', /* '?' */ + }; + char *p; + int pos; + p = strchr(doxn, str_A[0]); + if (!p) + bb_error_msg_and_die("bad output address radix " + "'%c' (must be [doxn])", str_A[0]); + pos = p - doxn; + if (pos == 3) format_address = format_address_none; + address_base_char = doxn_address_base_char[pos]; + address_pad_len_char = doxn_address_pad_len_char[pos]; + } + if (opt & OPT_N) { + max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); + } + if (opt & OPT_a) decode_format_string("a"); + if (opt & OPT_b) decode_format_string("oC"); + if (opt & OPT_c) decode_format_string("c"); + if (opt & OPT_d) decode_format_string("u2"); + if (opt & OPT_f) decode_format_string("fF"); + if (opt & OPT_h) decode_format_string("x2"); + if (opt & OPT_i) decode_format_string("d2"); + if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); + if (opt & OPT_l) decode_format_string("d4"); + if (opt & OPT_o) decode_format_string("o2"); + while (lst_t) { + decode_format_string(llist_pop(&lst_t)); + } + if (opt & OPT_x) decode_format_string("x2"); + if (opt & OPT_s) decode_format_string("d2"); + if (opt & OPT_S) { + string_min = xstrtou_sfx(str_S, 0, bkm); + } + + // Bloat: + //if ((option_mask32 & OPT_S) && n_specs > 0) + // bb_error_msg_and_die("no type may be specified when dumping strings"); + + /* If the --traditional option is used, there may be from + * 0 to 3 remaining command line arguments; handle each case + * separately. + * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]] + * The offset and pseudo_start have the same syntax. + * + * FIXME: POSIX 1003.1-2001 with XSI requires support for the + * traditional syntax even if --traditional is not given. */ + +#if ENABLE_LONG_OPTS + if (opt & OPT_traditional) { + if (argv[0]) { + off_t pseudo_start = -1; + off_t o1, o2; + + if (!argv[1]) { /* one arg */ + if (parse_old_offset(argv[0], &o1)) { + /* od --traditional OFFSET */ + n_bytes_to_skip = o1; + argv++; + } + /* od --traditional FILE */ + } else if (!argv[2]) { /* two args */ + if (parse_old_offset(argv[0], &o1) + && parse_old_offset(argv[1], &o2) + ) { + /* od --traditional OFFSET LABEL */ + n_bytes_to_skip = o1; + pseudo_start = o2; + argv += 2; + } else if (parse_old_offset(argv[1], &o2)) { + /* od --traditional FILE OFFSET */ + n_bytes_to_skip = o2; + argv[1] = NULL; + } else { + bb_error_msg_and_die("invalid second argument '%s'", argv[1]); + } + } else if (!argv[3]) { /* three args */ + if (parse_old_offset(argv[1], &o1) + && parse_old_offset(argv[2], &o2) + ) { + /* od --traditional FILE OFFSET LABEL */ + n_bytes_to_skip = o1; + pseudo_start = o2; + argv[1] = NULL; + } else { + bb_error_msg_and_die("the last two arguments must be offsets"); + } + } else { /* >3 args */ + bb_error_msg_and_die("too many arguments"); + } + + if (pseudo_start >= 0) { + if (format_address == format_address_none) { + address_base_char = 'o'; + address_pad_len_char = '7'; + format_address = format_address_paren; + } else { + format_address = format_address_label; + } + pseudo_offset = pseudo_start - n_bytes_to_skip; + } + } + /* else: od --traditional (without args) */ + } +#endif + + if (option_mask32 & OPT_N) { + end_offset = n_bytes_to_skip + max_bytes_to_format; + if (end_offset < n_bytes_to_skip) + bb_error_msg_and_die("SKIP + SIZE is too large"); + } + + if (n_specs == 0) { + decode_format_string("o2"); + /*n_specs = 1; - done by decode_format_string */ + } + + /* If no files were listed on the command line, + set the global pointer FILE_LIST so that it + references the null-terminated list of one name: "-". */ + file_list = bb_argv_dash; + if (argv[0]) { + /* Set the global pointer FILE_LIST so that it + references the first file-argument on the command-line. */ + file_list = (char const *const *) argv; + } + + /* Open the first input file */ + open_next_file(); + /* Skip over any unwanted header bytes */ + skip(n_bytes_to_skip); + if (!in_stream) + return EXIT_FAILURE; + + /* Compute output block length */ + l_c_m = get_lcm(); + + if (opt & OPT_w) { /* -w: width */ + if (!bytes_per_block || bytes_per_block % l_c_m != 0) { + bb_error_msg("warning: invalid width %u; using %d instead", + (unsigned)bytes_per_block, l_c_m); + bytes_per_block = l_c_m; + } + } else { + bytes_per_block = l_c_m; + if (l_c_m < DEFAULT_BYTES_PER_BLOCK) + bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m; + } + +#ifdef DEBUG + for (i = 0; i < n_specs; i++) { + printf("%d: fmt=\"%s\" width=%d\n", + i, spec[i].fmt_string, width_bytes[spec[i].size]); + } +#endif + + if (option_mask32 & OPT_S) + dump_strings(n_bytes_to_skip, end_offset); + else + dump(n_bytes_to_skip, end_offset); + + if (fclose(stdin)) + bb_perror_msg_and_die(bb_msg_standard_input); + + return exit_code; +} diff --git a/coreutils/printenv.c b/coreutils/printenv.c new file mode 100644 index 000000000..bd5db7073 --- /dev/null +++ b/coreutils/printenv.c @@ -0,0 +1,50 @@ +/* vi: set sw=4 ts=4: */ +/* + * printenv implementation for busybox + * + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define printenv_trivial_usage +//usage: "[VARIABLE]..." +//usage:#define printenv_full_usage "\n\n" +//usage: "Print environment VARIABLEs.\n" +//usage: "If no VARIABLE specified, print all." + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int printenv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int printenv_main(int argc UNUSED_PARAM, char **argv) +{ + int exit_code = EXIT_SUCCESS; + + /* no variables specified, show whole env */ + if (!argv[1]) { + char **e = environ; + + /* environ can be NULL! (for example, after clearenv()) + * Check for that: + */ + if (e) + while (*e) + puts(*e++); + } else { + /* search for specified variables and print them out if found */ + char *arg, *env; + + while ((arg = *++argv) != NULL) { + env = getenv(arg); + if (env) + puts(env); + else + exit_code = EXIT_FAILURE; + } + } + + fflush_stdout_and_exit(exit_code); +} diff --git a/coreutils/printf.c b/coreutils/printf.c new file mode 100644 index 000000000..f53aa4787 --- /dev/null +++ b/coreutils/printf.c @@ -0,0 +1,414 @@ +/* vi: set sw=4 ts=4: */ +/* printf - format and print data + + Copyright 1999 Dave Cinege + Portions copyright (C) 1990-1996 Free Software Foundation, Inc. + + Licensed under GPLv2 or later, see file LICENSE in this source tree. +*/ + +/* Usage: printf format [argument...] + + A front end to the printf function that lets it be used from the shell. + + Backslash escapes: + + \" = double quote + \\ = backslash + \a = alert (bell) + \b = backspace + \c = produce no further output + \f = form feed + \n = new line + \r = carriage return + \t = horizontal tab + \v = vertical tab + \0ooo = octal number (ooo is 0 to 3 digits) + \xhhh = hexadecimal number (hhh is 1 to 3 digits) + + Additional directive: + + %b = print an argument string, interpreting backslash escapes + + The 'format' argument is re-used as many times as necessary + to convert all of the given arguments. + + David MacKenzie +*/ + +// 19990508 Busy Boxed! Dave Cinege + +//usage:#define printf_trivial_usage +//usage: "FORMAT [ARGUMENT]..." +//usage:#define printf_full_usage "\n\n" +//usage: "Format and print ARGUMENT(s) according to FORMAT,\n" +//usage: "where FORMAT controls the output exactly as in C printf" +//usage: +//usage:#define printf_example_usage +//usage: "$ printf \"Val=%d\\n\" 5\n" +//usage: "Val=5\n" + +#include "libbb.h" + +/* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. + * They report it: + * bash: printf: XXX: invalid number + * printf: XXX: expected a numeric value + * bash: printf: 123XXX: invalid number + * printf: 123XXX: value not completely converted + * but then they use 0 (or partially converted numeric prefix) as a value + * and continue. They exit with 1 in this case. + * Both accept insane field width/precision (e.g. %9999999999.9999999999d). + * Both print error message and assume 0 if %*.*f width/precision is "bad" + * (but negative numbers are not "bad"). + * Both accept negative numbers for %u specifier. + * + * We try to be compatible. + */ + +typedef void FAST_FUNC (*converter)(const char *arg, void *result); + +static int multiconvert(const char *arg, void *result, converter convert) +{ + if (*arg == '"' || *arg == '\'') { + arg = utoa((unsigned char)arg[1]); + } + errno = 0; + convert(arg, result); + if (errno) { + bb_error_msg("invalid number '%s'", arg); + return 1; + } + return 0; +} + +static void FAST_FUNC conv_strtoull(const char *arg, void *result) +{ + *(unsigned long long*)result = bb_strtoull(arg, NULL, 0); + /* both coreutils 6.10 and bash 3.2: + * $ printf '%x\n' -2 + * fffffffffffffffe + * Mimic that: + */ + if (errno) { + *(unsigned long long*)result = bb_strtoll(arg, NULL, 0); + } +} +static void FAST_FUNC conv_strtoll(const char *arg, void *result) +{ + *(long long*)result = bb_strtoll(arg, NULL, 0); +} +static void FAST_FUNC conv_strtod(const char *arg, void *result) +{ + char *end; + /* Well, this one allows leading whitespace... so what? */ + /* What I like much less is that "-" accepted too! :( */ + *(double*)result = strtod(arg, &end); + if (end[0]) { + errno = ERANGE; + *(double*)result = 0; + } +} + +/* Callers should check errno to detect errors */ +static unsigned long long my_xstrtoull(const char *arg) +{ + unsigned long long result; + if (multiconvert(arg, &result, conv_strtoull)) + result = 0; + return result; +} +static long long my_xstrtoll(const char *arg) +{ + long long result; + if (multiconvert(arg, &result, conv_strtoll)) + result = 0; + return result; +} +static double my_xstrtod(const char *arg) +{ + double result; + multiconvert(arg, &result, conv_strtod); + return result; +} + +static void print_esc_string(const char *str) +{ + char c; + while ((c = *str) != '\0') { + str++; + if (c == '\\') + c = bb_process_escape_sequence(&str); + putchar(c); + } +} + +static void print_direc(char *format, unsigned fmt_length, + int field_width, int precision, + const char *argument) +{ + long long llv; + double dv; + char saved; + char *have_prec, *have_width; + + saved = format[fmt_length]; + format[fmt_length] = '\0'; + + have_prec = strstr(format, ".*"); + have_width = strchr(format, '*'); + if (have_width - 1 == have_prec) + have_width = NULL; + + errno = 0; + + switch (format[fmt_length - 1]) { + case 'c': + printf(format, *argument); + break; + case 'd': + case 'i': + llv = my_xstrtoll(argument); + print_long: + if (!have_width) { + if (!have_prec) + printf(format, llv); + else + printf(format, precision, llv); + } else { + if (!have_prec) + printf(format, field_width, llv); + else + printf(format, field_width, precision, llv); + } + break; + case 'o': + case 'u': + case 'x': + case 'X': + llv = my_xstrtoull(argument); + /* cheat: unsigned long and long have same width, so... */ + goto print_long; + case 's': + /* Are char* and long long the same? */ + if (sizeof(argument) == sizeof(llv)) { + llv = (long long)(ptrdiff_t)argument; + goto print_long; + } else { + /* Hope compiler will optimize it out by moving call + * instruction after the ifs... */ + if (!have_width) { + if (!have_prec) + printf(format, argument, /*unused:*/ argument, argument); + else + printf(format, precision, argument, /*unused:*/ argument); + } else { + if (!have_prec) + printf(format, field_width, argument, /*unused:*/ argument); + else + printf(format, field_width, precision, argument); + } + break; + } + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + dv = my_xstrtod(argument); + if (!have_width) { + if (!have_prec) + printf(format, dv); + else + printf(format, precision, dv); + } else { + if (!have_prec) + printf(format, field_width, dv); + else + printf(format, field_width, precision, dv); + } + break; + } /* switch */ + + format[fmt_length] = saved; +} + +/* Handle params for "%*.*f". Negative numbers are ok (compat). */ +static int get_width_prec(const char *str) +{ + int v = bb_strtoi(str, NULL, 10); + if (errno) { + bb_error_msg("invalid number '%s'", str); + v = 0; + } + return v; +} + +/* Print the text in FORMAT, using ARGV for arguments to any '%' directives. + Return advanced ARGV. */ +static char **print_formatted(char *f, char **argv, int *conv_err) +{ + char *direc_start; /* Start of % directive. */ + unsigned direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*' */ + int precision; /* Arg to second '*' */ + char **saved_argv = argv; + + for (; *f; ++f) { + switch (*f) { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = 0; + if (*f == '%') { + bb_putchar('%'); + break; + } + if (*f == 'b') { + if (*argv) { + print_esc_string(*argv); + ++argv; + } + break; + } + if (strchr("-+ #", *f)) { + ++f; + ++direc_length; + } + if (*f == '*') { + ++f; + ++direc_length; + if (*argv) + field_width = get_width_prec(*argv++); + } else { + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + if (*f == '.') { + ++f; + ++direc_length; + if (*f == '*') { + ++f; + ++direc_length; + if (*argv) + precision = get_width_prec(*argv++); + } else { + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + } + + /* Remove "lLhz" size modifiers, repeatedly. + * bash does not like "%lld", but coreutils + * happily takes even "%Llllhhzhhzd"! + * We are permissive like coreutils */ + while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { + overlapping_strcpy(f, f + 1); + } + /* Add "ll" if integer modifier, then print */ + { + static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; + char *p = strchr(format_chars, *f); + /* needed - try "printf %" without it */ + if (p == NULL) { + bb_error_msg("%s: invalid format", direc_start); + /* causes main() to exit with error */ + return saved_argv - 1; + } + ++direc_length; + if (p - format_chars <= 5) { + /* it is one of "diouxX" */ + p = xmalloc(direc_length + 3); + memcpy(p, direc_start, direc_length); + p[direc_length + 1] = p[direc_length - 1]; + p[direc_length - 1] = 'l'; + p[direc_length] = 'l'; + //bb_error_msg("<%s>", p); + direc_length += 2; + direc_start = p; + } else { + p = NULL; + } + if (*argv) { + print_direc(direc_start, direc_length, field_width, + precision, *argv++); + } else { + print_direc(direc_start, direc_length, field_width, + precision, ""); + } + *conv_err |= errno; + free(p); + } + break; + case '\\': + if (*++f == 'c') { + return saved_argv; /* causes main() to exit */ + } + bb_putchar(bb_process_escape_sequence((const char **)&f)); + f--; + break; + default: + putchar(*f); + } + } + + return argv; +} + +int printf_main(int argc UNUSED_PARAM, char **argv) +{ + int conv_err; + char *format; + char **argv2; + + /* We must check that stdout is not closed. + * The reason for this is highly non-obvious. + * printf_main is used from shell. + * Shell must correctly handle 'printf "%s" foo' + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. */ +// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? + if (fcntl(1, F_GETFL) == -1) + return 1; /* match coreutils 6.10 (sans error msg to stderr) */ + //if (dup2(1, 1) != 1) - old way + // return 1; + + /* bash builtin errors out on "printf '-%s-\n' foo", + * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". + * We will mimic coreutils. */ + if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) + argv++; + if (!argv[1]) { + if (ENABLE_ASH_BUILTIN_PRINTF + && applet_name[0] != 'p' + ) { + bb_error_msg("usage: printf FORMAT [ARGUMENT...]"); + return 2; /* bash compat */ + } + bb_show_usage(); + } + + format = argv[1]; + argv2 = argv + 2; + + conv_err = 0; + do { + argv = argv2; + argv2 = print_formatted(format, argv, &conv_err); + } while (argv2 > argv && *argv2); + + /* coreutils compat (bash doesn't do this): + if (*argv) + fprintf(stderr, "excess args ignored"); + */ + + return (argv2 < argv) /* if true, print_formatted errored out */ + || conv_err; /* print_formatted saw invalid number */ +} diff --git a/coreutils/pwd.c b/coreutils/pwd.c new file mode 100644 index 000000000..739b835b5 --- /dev/null +++ b/coreutils/pwd.c @@ -0,0 +1,36 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pwd implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define pwd_trivial_usage +//usage: "" +//usage:#define pwd_full_usage "\n\n" +//usage: "Print the full filename of the current working directory" +//usage: +//usage:#define pwd_example_usage +//usage: "$ pwd\n" +//usage: "/root\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int pwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pwd_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char *buf; + + buf = xrealloc_getcwd_or_warn(NULL); + if (buf != NULL) { + puts(buf); + free(buf); + return fflush_all(); + } + + return EXIT_FAILURE; +} diff --git a/coreutils/readlink.c b/coreutils/readlink.c new file mode 100644 index 000000000..f7ad791ec --- /dev/null +++ b/coreutils/readlink.c @@ -0,0 +1,82 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini readlink implementation for busybox + * + * Copyright (C) 2000,2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define readlink_trivial_usage +//usage: IF_FEATURE_READLINK_FOLLOW("[-fnv] ") "FILE" +//usage:#define readlink_full_usage "\n\n" +//usage: "Display the value of a symlink" +//usage: IF_FEATURE_READLINK_FOLLOW( "\n" +//usage: "\n -f Canonicalize by following all symlinks" +//usage: "\n -n Don't add newline" +//usage: "\n -v Verbose" +//usage: ) + +#include "libbb.h" + +/* + * # readlink --version + * readlink (GNU coreutils) 6.10 + * # readlink --help + * -f, --canonicalize + * canonicalize by following every symlink in + * every component of the given name recursively; + * all but the last component must exist + * -e, --canonicalize-existing + * canonicalize by following every symlink in + * every component of the given name recursively, + * all components must exist + * -m, --canonicalize-missing + * canonicalize by following every symlink in + * every component of the given name recursively, + * without requirements on components existence + * -n, --no-newline do not output the trailing newline + * -q, --quiet, -s, --silent suppress most error messages + * -v, --verbose report error messages + * + * bbox supports: -f -n -v (fully), -q -s (accepts but ignores) + */ + +int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int readlink_main(int argc UNUSED_PARAM, char **argv) +{ + char *buf; + char *fname; + + IF_FEATURE_READLINK_FOLLOW( + unsigned opt; + /* We need exactly one non-option argument. */ + opt_complementary = "=1"; + opt = getopt32(argv, "fnvsq"); + fname = argv[optind]; + ) + IF_NOT_FEATURE_READLINK_FOLLOW( + const unsigned opt = 0; + if (argc != 2) bb_show_usage(); + fname = argv[1]; + ) + + /* compat: coreutils readlink reports errors silently via exit code */ + if (!(opt & 4)) /* not -v */ + logmode = LOGMODE_NONE; + + if (opt & 1) { /* -f */ + buf = xmalloc_realpath(fname); + } else { + buf = xmalloc_readlink_or_warn(fname); + } + + if (!buf) + return EXIT_FAILURE; + printf((opt & 2) ? "%s" : "%s\n", buf); + + if (ENABLE_FEATURE_CLEAN_UP) + free(buf); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/realpath.c b/coreutils/realpath.c new file mode 100644 index 000000000..c513b5549 --- /dev/null +++ b/coreutils/realpath.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on output and returns a failure exit code + * if one or more paths cannot be resolved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define realpath_trivial_usage +//usage: "FILE..." +//usage:#define realpath_full_usage "\n\n" +//usage: "Return the absolute pathnames of given FILE" + +#include "libbb.h" + +int realpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int realpath_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + + if (!*++argv) { + bb_show_usage(); + } + + do { + char *resolved_path = xmalloc_realpath(*argv); + if (resolved_path != NULL) { + puts(resolved_path); + free(resolved_path); + } else { + retval = EXIT_FAILURE; + bb_simple_perror_msg(*argv); + } + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/rm.c b/coreutils/rm.c new file mode 100644 index 000000000..042fba162 --- /dev/null +++ b/coreutils/rm.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rm implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ + +//usage:#define rm_trivial_usage +//usage: "[-irf] FILE..." +//usage:#define rm_full_usage "\n\n" +//usage: "Remove (unlink) FILEs\n" +//usage: "\n -i Always prompt before removing" +//usage: "\n -f Never prompt" +//usage: "\n -R,-r Recurse" +//usage: +//usage:#define rm_example_usage +//usage: "$ rm -rf /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rm_main(int argc UNUSED_PARAM, char **argv) +{ + int status = 0; + int flags = 0; + unsigned opt; + + opt_complementary = "f-i:i-f"; + /* -v (verbose) is ignored */ + opt = getopt32(argv, "fiRrv"); + argv += optind; + if (opt & 1) + flags |= FILEUTILS_FORCE; + if (opt & 2) + flags |= FILEUTILS_INTERACTIVE; + if (opt & (8|4)) + flags |= FILEUTILS_RECUR; + + if (*argv != NULL) { + do { + const char *base = bb_get_last_path_component_strip(*argv); + + if (DOT_OR_DOTDOT(base)) { + bb_error_msg("can't remove '.' or '..'"); + } else if (remove_file(*argv, flags) >= 0) { + continue; + } + status = 1; + } while (*++argv); + } else if (!(flags & FILEUTILS_FORCE)) { + bb_show_usage(); + } + + return status; +} diff --git a/coreutils/rmdir.c b/coreutils/rmdir.c new file mode 100644 index 000000000..2840d1cfa --- /dev/null +++ b/coreutils/rmdir.c @@ -0,0 +1,85 @@ +/* vi: set sw=4 ts=4: */ +/* + * rmdir implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */ + +//usage:#define rmdir_trivial_usage +//usage: "[OPTIONS] DIRECTORY..." +//usage:#define rmdir_full_usage "\n\n" +//usage: "Remove DIRECTORY if it is empty\n" +//usage: IF_FEATURE_RMDIR_LONG_OPTIONS( +//usage: "\n -p|--parents Include parents" +//usage: "\n --ignore-fail-on-non-empty" +//usage: ) +//usage: IF_NOT_FEATURE_RMDIR_LONG_OPTIONS( +//usage: "\n -p Include parents" +//usage: ) +//usage: +//usage:#define rmdir_example_usage +//usage: "# rmdir /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +#define PARENTS 0x01 +#define IGNORE_NON_EMPTY 0x02 + +int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rmdir_main(int argc UNUSED_PARAM, char **argv) +{ + int status = EXIT_SUCCESS; + int flags; + char *path; + +#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS + static const char rmdir_longopts[] ALIGN1 = + "parents\0" No_argument "p" + /* Debian etch: many packages fail to be purged or installed + * because they desperately want this option: */ + "ignore-fail-on-non-empty\0" No_argument "\xff" + ; + applet_long_options = rmdir_longopts; +#endif + flags = getopt32(argv, "p"); + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + path = *argv; + + while (1) { + if (rmdir(path) < 0) { +#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS + if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) + break; +#endif + bb_perror_msg("'%s'", path); /* Match gnu rmdir msg. */ + status = EXIT_FAILURE; + } else if (flags & PARENTS) { + /* Note: path was not "" since rmdir succeeded. */ + path = dirname(path); + /* Path is now just the parent component. Dirname + * returns "." if there are no parents. + */ + if (NOT_LONE_CHAR(path, '.')) { + continue; + } + } + break; + } + } while (*++argv); + + return status; +} diff --git a/coreutils/seq.c b/coreutils/seq.c new file mode 100644 index 000000000..898619293 --- /dev/null +++ b/coreutils/seq.c @@ -0,0 +1,109 @@ +/* vi: set sw=4 ts=4: */ +/* + * seq implementation for busybox + * + * Copyright (C) 2004, Glenn McGrath + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +//usage:#define seq_trivial_usage +//usage: "[-w] [-s SEP] [FIRST [INC]] LAST" +//usage:#define seq_full_usage "\n\n" +//usage: "Print numbers from FIRST to LAST, in steps of INC.\n" +//usage: "FIRST, INC default to 1.\n" +//usage: "\n -w Pad to last with leading zeros" +//usage: "\n -s SEP String separator" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int seq_main(int argc, char **argv) +{ + enum { + OPT_w = (1 << 0), + OPT_s = (1 << 1), + }; + double first, last, increment, v; + unsigned n; + unsigned width; + unsigned frac_part; + const char *sep, *opt_s = "\n"; + unsigned opt; + +#if ENABLE_LOCALE_SUPPORT + /* Undo busybox.c: on input, we want to use dot + * as fractional separator, regardless of current locale */ + setlocale(LC_NUMERIC, "C"); +#endif + + opt = getopt32(argv, "+ws:", &opt_s); + argc -= optind; + argv += optind; + first = increment = 1; + errno = 0; + switch (argc) { + char *pp; + case 3: + increment = strtod(argv[1], &pp); + errno |= *pp; + case 2: + first = strtod(argv[0], &pp); + errno |= *pp; + case 1: + last = strtod(argv[argc-1], &pp); + if (!errno && *pp == '\0') + break; + default: + bb_show_usage(); + } + +#if ENABLE_LOCALE_SUPPORT + setlocale(LC_NUMERIC, ""); +#endif + + /* Last checked to be compatible with: coreutils-6.10 */ + width = 0; + frac_part = 0; + while (1) { + char *dot = strchrnul(*argv, '.'); + int w = (dot - *argv); + int f = strlen(dot); + if (width < w) + width = w; + argv++; + if (!*argv) + break; + /* Why do the above _before_ frac check below? + * Try "seq 1 2.0" and "seq 1.0 2.0": + * coreutils never pay attention to the number + * of fractional digits in last arg. */ + if (frac_part < f) + frac_part = f; + } + if (frac_part) { + frac_part--; + if (frac_part) + width += frac_part + 1; + } + if (!(opt & OPT_w)) + width = 0; + + sep = ""; + v = first; + n = 0; + while (increment >= 0 ? v <= last : v >= last) { + if (printf("%s%0*.*f", sep, width, frac_part, v) < 0) + break; /* I/O error, bail out (yes, this really happens) */ + sep = opt_s; + /* v += increment; - would accumulate floating point errors */ + n++; + v = first + n * increment; + } + if (n) /* if while loop executed at least once */ + bb_putchar('\n'); + + return fflush_all(); +} diff --git a/coreutils/sleep.c b/coreutils/sleep.c new file mode 100644 index 000000000..0ffbd16eb --- /dev/null +++ b/coreutils/sleep.c @@ -0,0 +1,126 @@ +/* vi: set sw=4 ts=4: */ +/* + * sleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to do proper arg and error checking. + * Also, added a 'fancy' configuration to accept multiple args with + * time suffixes for seconds, minutes, hours, and days. + */ + +//usage:#define sleep_trivial_usage +//usage: IF_FEATURE_FANCY_SLEEP("[") "N" IF_FEATURE_FANCY_SLEEP("]...") +//usage:#define sleep_full_usage "\n\n" +//usage: IF_NOT_FEATURE_FANCY_SLEEP("Pause for N seconds") +//usage: IF_FEATURE_FANCY_SLEEP( +//usage: "Pause for a time equal to the total of the args given, where each arg can\n" +//usage: "have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays") +//usage: +//usage:#define sleep_example_usage +//usage: "$ sleep 2\n" +//usage: "[2 second delay results]\n" +//usage: IF_FEATURE_FANCY_SLEEP( +//usage: "$ sleep 1d 3h 22m 8s\n" +//usage: "[98528 second delay results]\n") + +#include "libbb.h" + +/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */ + + +#if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP +static const struct suffix_mult sfx[] = { + { "s", 1 }, + { "m", 60 }, + { "h", 60*60 }, + { "d", 24*60*60 }, + { "", 0 } +}; +#endif + +int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sleep_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_FEATURE_FLOAT_SLEEP + double duration; + struct timespec ts; +#else + unsigned duration; +#endif + + ++argv; + if (!*argv) + bb_show_usage(); + +#if ENABLE_FEATURE_FLOAT_SLEEP + +# if ENABLE_LOCALE_SUPPORT + /* undo busybox.c setlocale */ + setlocale(LC_NUMERIC, "C"); +# endif + duration = 0; + do { + char *arg = *argv; + if (strchr(arg, '.')) { + double d; + char *pp; + int len = strspn(arg, "0123456789."); + char sv = arg[len]; + arg[len] = '\0'; + errno = 0; + d = strtod(arg, &pp); + if (errno || *pp) + bb_show_usage(); + arg += len; + *arg-- = sv; + sv = *arg; + *arg = '1'; + duration += d * xatoul_sfx(arg, sfx); + *arg = sv; + } else { + duration += xatoul_sfx(arg, sfx); + } + } while (*++argv); + + ts.tv_sec = MAXINT(typeof(ts.tv_sec)); + ts.tv_nsec = 0; + if (duration >= 0 && duration < ts.tv_sec) { + ts.tv_sec = duration; + ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; + } + do { + errno = 0; + nanosleep(&ts, &ts); + } while (errno == EINTR); + +#elif ENABLE_FEATURE_FANCY_SLEEP + + duration = 0; + do { + duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx); + } while (*++argv); + sleep(duration); + +#else /* simple */ + + duration = xatou(*argv); + sleep(duration); + // Off. If it's really needed, provide example why + //if (sleep(duration)) { + // bb_perror_nomsg_and_die(); + //} + +#endif + + return EXIT_SUCCESS; +} diff --git a/coreutils/sort.c b/coreutils/sort.c new file mode 100644 index 000000000..1df07285c --- /dev/null +++ b/coreutils/sort.c @@ -0,0 +1,469 @@ +/* vi: set sw=4 ts=4: */ +/* + * SuS3 compliant sort implementation for busybox + * + * Copyright (C) 2004 by Rob Landley + * + * MAINTAINER: Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * See SuS3 sort standard at: + * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html + */ + +//usage:#define sort_trivial_usage +//usage: "[-nru" +//usage: IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR") +//usage: "] [FILE]..." +//usage:#define sort_full_usage "\n\n" +//usage: "Sort lines of text\n" +//usage: IF_FEATURE_SORT_BIG( +//usage: "\n -b Ignore leading blanks" +//usage: "\n -c Check whether input is sorted" +//usage: "\n -d Dictionary order (blank or alphanumeric only)" +//usage: "\n -f Ignore case" +//usage: "\n -g General numerical sort" +//usage: "\n -i Ignore unprintable characters" +//usage: "\n -k Sort key" +//usage: "\n -M Sort month" +//usage: ) +//usage: "\n -n Sort numbers" +//usage: IF_FEATURE_SORT_BIG( +//usage: "\n -o Output to file" +//usage: "\n -k Sort by key" +//usage: "\n -t CHAR Key separator" +//usage: ) +//usage: "\n -r Reverse sort order" +//usage: IF_FEATURE_SORT_BIG( +//usage: "\n -s Stable (don't sort ties alphabetically)" +//usage: ) +//usage: "\n -u Suppress duplicate lines" +//usage: IF_FEATURE_SORT_BIG( +//usage: "\n -z Lines are terminated by NUL, not newline" +//usage: "\n -mST Ignored for GNU compatibility") +//usage: +//usage:#define sort_example_usage +//usage: "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" +//usage: "a\n" +//usage: "b\n" +//usage: "c\n" +//usage: "d\n" +//usage: "e\n" +//usage: "f\n" +//usage: IF_FEATURE_SORT_BIG( +//usage: "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n" +//usage: "d 2\n" +//usage: "b 2\n" +//usage: "c 3\n" +//usage: ) +//usage: "" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +/* + sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] + sort -c [-bdfinru][-t char][-k keydef][file] +*/ + +/* These are sort types */ +static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:t:"; +enum { + FLAG_n = 1, /* Numeric sort */ + FLAG_g = 2, /* Sort using strtod() */ + FLAG_M = 4, /* Sort date */ +/* ucsz apply to root level only, not keys. b at root level implies bb */ + FLAG_u = 8, /* Unique */ + FLAG_c = 0x10, /* Check: no output, exit(!ordered) */ + FLAG_s = 0x20, /* Stable sort, no ascii fallback at end */ + FLAG_z = 0x40, /* Input and output is NUL terminated, not \n */ +/* These can be applied to search keys, the previous four can't */ + FLAG_b = 0x80, /* Ignore leading blanks */ + FLAG_r = 0x100, /* Reverse */ + FLAG_d = 0x200, /* Ignore !(isalnum()|isspace()) */ + FLAG_f = 0x400, /* Force uppercase */ + FLAG_i = 0x800, /* Ignore !isprint() */ + FLAG_m = 0x1000, /* ignored: merge already sorted files; do not sort */ + FLAG_S = 0x2000, /* ignored: -S, --buffer-size=SIZE */ + FLAG_T = 0x4000, /* ignored: -T, --temporary-directory=DIR */ + FLAG_o = 0x8000, + FLAG_k = 0x10000, + FLAG_t = 0x20000, + FLAG_bb = 0x80000000, /* Ignore trailing blanks */ +}; + +#if ENABLE_FEATURE_SORT_BIG +static char key_separator; + +static struct sort_key { + struct sort_key *next_key; /* linked list */ + unsigned range[4]; /* start word, start char, end word, end char */ + unsigned flags; +} *key_list; + +static char *get_key(char *str, struct sort_key *key, int flags) +{ + int start = 0, end = 0, len, j; + unsigned i; + + /* Special case whole string, so we don't have to make a copy */ + if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3] + && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb)) + ) { + return str; + } + + /* Find start of key on first pass, end on second pass */ + len = strlen(str); + for (j = 0; j < 2; j++) { + if (!key->range[2*j]) + end = len; + /* Loop through fields */ + else { + end = 0; + for (i = 1; i < key->range[2*j] + j; i++) { + if (key_separator) { + /* Skip body of key and separator */ + while (str[end]) { + if (str[end++] == key_separator) + break; + } + } else { + /* Skip leading blanks */ + while (isspace(str[end])) + end++; + /* Skip body of key */ + while (str[end]) { + if (isspace(str[end])) + break; + end++; + } + } + } + } + if (!j) start = end; + } + /* Strip leading whitespace if necessary */ +//XXX: skip_whitespace() + if (flags & FLAG_b) + while (isspace(str[start])) start++; + /* Strip trailing whitespace if necessary */ + if (flags & FLAG_bb) + while (end > start && isspace(str[end-1])) end--; + /* Handle offsets on start and end */ + if (key->range[3]) { + end += key->range[3] - 1; + if (end > len) end = len; + } + if (key->range[1]) { + start += key->range[1] - 1; + if (start > len) start = len; + } + /* Make the copy */ + if (end < start) end = start; + str = xstrndup(str+start, end-start); + /* Handle -d */ + if (flags & FLAG_d) { + for (start = end = 0; str[end]; end++) + if (isspace(str[end]) || isalnum(str[end])) + str[start++] = str[end]; + str[start] = '\0'; + } + /* Handle -i */ + if (flags & FLAG_i) { + for (start = end = 0; str[end]; end++) + if (isprint_asciionly(str[end])) + str[start++] = str[end]; + str[start] = '\0'; + } + /* Handle -f */ + if (flags & FLAG_f) + for (i = 0; str[i]; i++) + str[i] = toupper(str[i]); + + return str; +} + +static struct sort_key *add_key(void) +{ + struct sort_key **pkey = &key_list; + while (*pkey) + pkey = &((*pkey)->next_key); + return *pkey = xzalloc(sizeof(struct sort_key)); +} + +#define GET_LINE(fp) \ + ((option_mask32 & FLAG_z) \ + ? bb_get_chunk_from_file(fp, NULL) \ + : xmalloc_fgetline(fp)) +#else +#define GET_LINE(fp) xmalloc_fgetline(fp) +#endif + +/* Iterate through keys list and perform comparisons */ +static int compare_keys(const void *xarg, const void *yarg) +{ + int flags = option_mask32, retval = 0; + char *x, *y; + +#if ENABLE_FEATURE_SORT_BIG + struct sort_key *key; + + for (key = key_list; !retval && key; key = key->next_key) { + flags = key->flags ? key->flags : option_mask32; + /* Chop out and modify key chunks, handling -dfib */ + x = get_key(*(char **)xarg, key, flags); + y = get_key(*(char **)yarg, key, flags); +#else + /* This curly bracket serves no purpose but to match the nesting + level of the for () loop we're not using */ + { + x = *(char **)xarg; + y = *(char **)yarg; +#endif + /* Perform actual comparison */ + switch (flags & 7) { + default: + bb_error_msg_and_die("unknown sort type"); + break; + /* Ascii sort */ + case 0: +#if ENABLE_LOCALE_SUPPORT + retval = strcoll(x, y); +#else + retval = strcmp(x, y); +#endif + break; +#if ENABLE_FEATURE_SORT_BIG + case FLAG_g: { + char *xx, *yy; + double dx = strtod(x, &xx); + double dy = strtod(y, &yy); + /* not numbers < NaN < -infinity < numbers < +infinity) */ + if (x == xx) + retval = (y == yy ? 0 : -1); + else if (y == yy) + retval = 1; + /* Check for isnan */ + else if (dx != dx) + retval = (dy != dy) ? 0 : -1; + else if (dy != dy) + retval = 1; + /* Check for infinity. Could underflow, but it avoids libm. */ + else if (1.0 / dx == 0.0) { + if (dx < 0) + retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; + else + retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; + } else if (1.0 / dy == 0.0) + retval = (dy < 0) ? 1 : -1; + else + retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); + break; + } + case FLAG_M: { + struct tm thyme; + int dx; + char *xx, *yy; + + xx = strptime(x, "%b", &thyme); + dx = thyme.tm_mon; + yy = strptime(y, "%b", &thyme); + if (!xx) + retval = (!yy) ? 0 : -1; + else if (!yy) + retval = 1; + else + retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon; + break; + } + /* Full floating point version of -n */ + case FLAG_n: { + double dx = atof(x); + double dy = atof(y); + retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); + break; + } + } /* switch */ + /* Free key copies. */ + if (x != *(char **)xarg) free(x); + if (y != *(char **)yarg) free(y); + /* if (retval) break; - done by for () anyway */ +#else + /* Integer version of -n for tiny systems */ + case FLAG_n: + retval = atoi(x) - atoi(y); + break; + } /* switch */ +#endif + } /* for */ + + /* Perform fallback sort if necessary */ + if (!retval && !(option_mask32 & FLAG_s)) + retval = strcmp(*(char **)xarg, *(char **)yarg); + + if (flags & FLAG_r) return -retval; + return retval; +} + +#if ENABLE_FEATURE_SORT_BIG +static unsigned str2u(char **str) +{ + unsigned long lu; + if (!isdigit((*str)[0])) + bb_error_msg_and_die("bad field specification"); + lu = strtoul(*str, str, 10); + if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu) + bb_error_msg_and_die("bad field specification"); + return lu; +} +#endif + +int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sort_main(int argc UNUSED_PARAM, char **argv) +{ + char *line, **lines; + char *str_ignored, *str_o, *str_t; + llist_t *lst_k = NULL; + int i, flag; + int linecount; + unsigned opts; + + xfunc_error_retval = 2; + + /* Parse command line options */ + /* -o and -t can be given at most once */ + opt_complementary = "o--o:t--t:" /* -t, -o: at most one of each */ + "k::"; /* -k takes list */ + opts = getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); + /* global b strips leading and trailing spaces */ + if (opts & FLAG_b) + option_mask32 |= FLAG_bb; +#if ENABLE_FEATURE_SORT_BIG + if (opts & FLAG_t) { + if (!str_t[0] || str_t[1]) + bb_error_msg_and_die("bad -t parameter"); + key_separator = str_t[0]; + } + /* note: below this point we use option_mask32, not opts, + * since that reduces register pressure and makes code smaller */ + + /* parse sort key */ + while (lst_k) { + enum { + FLAG_allowed_for_k = + FLAG_n | /* Numeric sort */ + FLAG_g | /* Sort using strtod() */ + FLAG_M | /* Sort date */ + FLAG_b | /* Ignore leading blanks */ + FLAG_r | /* Reverse */ + FLAG_d | /* Ignore !(isalnum()|isspace()) */ + FLAG_f | /* Force uppercase */ + FLAG_i | /* Ignore !isprint() */ + 0 + }; + struct sort_key *key = add_key(); + char *str_k = llist_pop(&lst_k); + + i = 0; /* i==0 before comma, 1 after (-k3,6) */ + while (*str_k) { + /* Start of range */ + /* Cannot use bb_strtou - suffix can be a letter */ + key->range[2*i] = str2u(&str_k); + if (*str_k == '.') { + str_k++; + key->range[2*i+1] = str2u(&str_k); + } + while (*str_k) { + const char *temp2; + + if (*str_k == ',' && !i++) { + str_k++; + break; + } /* no else needed: fall through to syntax error + because comma isn't in OPT_STR */ + temp2 = strchr(OPT_STR, *str_k); + if (!temp2) + bb_error_msg_and_die("unknown key option"); + flag = 1 << (temp2 - OPT_STR); + if (flag & ~FLAG_allowed_for_k) + bb_error_msg_and_die("unknown sort type"); + /* b after ',' means strip _trailing_ space */ + if (i && flag == FLAG_b) + flag = FLAG_bb; + key->flags |= flag; + str_k++; + } + } + } +#endif + + /* Open input files and read data */ + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + linecount = 0; + lines = NULL; + do { + /* coreutils 6.9 compat: abort on first open error, + * do not continue to next file: */ + FILE *fp = xfopen_stdin(*argv); + for (;;) { + line = GET_LINE(fp); + if (!line) + break; + lines = xrealloc_vector(lines, 6, linecount); + lines[linecount++] = line; + } + fclose_if_not_stdin(fp); + } while (*++argv); + +#if ENABLE_FEATURE_SORT_BIG + /* if no key, perform alphabetic sort */ + if (!key_list) + add_key()->range[0] = 1; + /* handle -c */ + if (option_mask32 & FLAG_c) { + int j = (option_mask32 & FLAG_u) ? -1 : 0; + for (i = 1; i < linecount; i++) { + if (compare_keys(&lines[i-1], &lines[i]) > j) { + fprintf(stderr, "Check line %u\n", i); + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; + } +#endif + /* Perform the actual sort */ + qsort(lines, linecount, sizeof(lines[0]), compare_keys); + /* handle -u */ + if (option_mask32 & FLAG_u) { + flag = 0; + /* coreutils 6.3 drop lines for which only key is the same */ + /* -- disabling last-resort compare... */ + option_mask32 |= FLAG_s; + for (i = 1; i < linecount; i++) { + if (compare_keys(&lines[flag], &lines[i]) == 0) + free(lines[i]); + else + lines[++flag] = lines[i]; + } + if (linecount) + linecount = flag+1; + } + + /* Print it */ +#if ENABLE_FEATURE_SORT_BIG + /* Open output file _after_ we read all input ones */ + if (option_mask32 & FLAG_o) + xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO); +#endif + flag = (option_mask32 & FLAG_z) ? '\0' : '\n'; + for (i = 0; i < linecount; i++) + printf("%s%c", lines[i], flag); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/split.c b/coreutils/split.c new file mode 100644 index 000000000..11e640442 --- /dev/null +++ b/coreutils/split.c @@ -0,0 +1,153 @@ +/* vi: set sw=4 ts=4: */ +/* + * split - split a file into pieces + * Copyright (c) 2007 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* BB_AUDIT: SUSv3 compliant + * SUSv3 requirements: + * http://www.opengroup.org/onlinepubs/009695399/utilities/split.html + */ + +//usage:#define split_trivial_usage +//usage: "[OPTIONS] [INPUT [PREFIX]]" +//usage:#define split_full_usage "\n\n" +//usage: " -b N[k|m] Split by N (kilo|mega)bytes" +//usage: "\n -l N Split by N lines" +//usage: "\n -a N Use N letters as suffix" +//usage: +//usage:#define split_example_usage +//usage: "$ split TODO foo\n" +//usage: "$ cat TODO | split -a 2 -l 2 TODO_\n" + +#include "libbb.h" + +static const struct suffix_mult split_suffices[] = { +#if ENABLE_FEATURE_SPLIT_FANCY + { "b", 512 }, +#endif + { "k", 1024 }, + { "m", 1024*1024 }, +#if ENABLE_FEATURE_SPLIT_FANCY + { "g", 1024*1024*1024 }, +#endif + { "", 0 } +}; + +/* Increment the suffix part of the filename. + * Returns NULL if we are out of filenames. + */ +static char *next_file(char *old, unsigned suffix_len) +{ + size_t end = strlen(old); + unsigned i = 1; + char *curr; + + while (1) { + curr = old + end - i; + if (*curr < 'z') { + *curr += 1; + break; + } + i++; + if (i > suffix_len) { + return NULL; + } + *curr = 'a'; + } + + return old; +} + +#define read_buffer bb_common_bufsiz1 +enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 }; + +#define SPLIT_OPT_l (1<<0) +#define SPLIT_OPT_b (1<<1) +#define SPLIT_OPT_a (1<<2) + +int split_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int split_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned suffix_len = 2; + char *pfx; + char *count_p; + const char *sfx; + off_t cnt = 1000; + off_t remaining = 0; + unsigned opt; + ssize_t bytes_read, to_write; + char *src; + + opt_complementary = "?2:a+"; /* max 2 args; -a N */ + opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &suffix_len); + + if (opt & SPLIT_OPT_l) + cnt = XATOOFF(count_p); + if (opt & SPLIT_OPT_b) // FIXME: also needs XATOOFF + cnt = xatoull_sfx(count_p, split_suffices); + sfx = "x"; + + argv += optind; + if (argv[0]) { + int fd; + if (argv[1]) + sfx = argv[1]; + fd = xopen_stdin(argv[0]); + xmove_fd(fd, STDIN_FILENO); + } else { + argv[0] = (char *) bb_msg_standard_input; + } + + if (NAME_MAX < strlen(sfx) + suffix_len) + bb_error_msg_and_die("suffix too long"); + + { + char *char_p = xzalloc(suffix_len + 1); + memset(char_p, 'a', suffix_len); + pfx = xasprintf("%s%s", sfx, char_p); + if (ENABLE_FEATURE_CLEAN_UP) + free(char_p); + } + + while (1) { + bytes_read = safe_read(STDIN_FILENO, read_buffer, READ_BUFFER_SIZE); + if (!bytes_read) + break; + if (bytes_read < 0) + bb_simple_perror_msg_and_die(argv[0]); + src = read_buffer; + do { + if (!remaining) { + if (!pfx) + bb_error_msg_and_die("suffixes exhausted"); + xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); + pfx = next_file(pfx, suffix_len); + remaining = cnt; + } + + if (opt & SPLIT_OPT_b) { + /* split by bytes */ + to_write = (bytes_read < remaining) ? bytes_read : remaining; + remaining -= to_write; + } else { + /* split by lines */ + /* can be sped up by using _memrchr_ + * and writing many lines at once... */ + char *end = memchr(src, '\n', bytes_read); + if (end) { + --remaining; + to_write = end - src + 1; + } else { + to_write = bytes_read; + } + } + + xwrite(STDOUT_FILENO, src, to_write); + bytes_read -= to_write; + src += to_write; + } while (bytes_read); + } + return EXIT_SUCCESS; +} diff --git a/coreutils/stat.c b/coreutils/stat.c new file mode 100644 index 000000000..2797719dd --- /dev/null +++ b/coreutils/stat.c @@ -0,0 +1,728 @@ +/* vi: set sw=4 ts=4: */ +/* + * stat -- display file or file system status + * + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * Copyright (C) 2006 by Yoshinori Sato + * + * Written by Michael Meskes + * Taken from coreutils and turned into a busybox applet by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define stat_trivial_usage +//usage: "[OPTIONS] FILE..." +//usage:#define stat_full_usage "\n\n" +//usage: "Display file (default) or filesystem status\n" +//usage: IF_FEATURE_STAT_FORMAT( +//usage: "\n -c fmt Use the specified format" +//usage: ) +//usage: "\n -f Display filesystem status" +//usage: "\n -L Follow links" +//usage: "\n -t Display info in terse form" +//usage: IF_SELINUX( +//usage: "\n -Z Print security context" +//usage: ) +//usage: IF_FEATURE_STAT_FORMAT( +//usage: "\n\nValid format sequences for files:\n" +//usage: " %a Access rights in octal\n" +//usage: " %A Access rights in human readable form\n" +//usage: " %b Number of blocks allocated (see %B)\n" +//usage: " %B The size in bytes of each block reported by %b\n" +//usage: " %d Device number in decimal\n" +//usage: " %D Device number in hex\n" +//usage: " %f Raw mode in hex\n" +//usage: " %F File type\n" +//usage: " %g Group ID of owner\n" +//usage: " %G Group name of owner\n" +//usage: " %h Number of hard links\n" +//usage: " %i Inode number\n" +//usage: " %n File name\n" +//usage: " %N File name, with -> TARGET if symlink\n" +//usage: " %o I/O block size\n" +//usage: " %s Total size, in bytes\n" +//usage: " %t Major device type in hex\n" +//usage: " %T Minor device type in hex\n" +//usage: " %u User ID of owner\n" +//usage: " %U User name of owner\n" +//usage: " %x Time of last access\n" +//usage: " %X Time of last access as seconds since Epoch\n" +//usage: " %y Time of last modification\n" +//usage: " %Y Time of last modification as seconds since Epoch\n" +//usage: " %z Time of last change\n" +//usage: " %Z Time of last change as seconds since Epoch\n" +//usage: "\nValid format sequences for file systems:\n" +//usage: " %a Free blocks available to non-superuser\n" +//usage: " %b Total data blocks in file system\n" +//usage: " %c Total file nodes in file system\n" +//usage: " %d Free file nodes in file system\n" +//usage: " %f Free blocks in file system\n" +//usage: IF_SELINUX( +//usage: " %C Security context in selinux\n" +//usage: ) +//usage: " %i File System ID in hex\n" +//usage: " %l Maximum length of filenames\n" +//usage: " %n File name\n" +//usage: " %s Block size (for faster transfer)\n" +//usage: " %S Fundamental block size (for block counts)\n" +//usage: " %t Type in hex\n" +//usage: " %T Type in human readable form" +//usage: ) + +#include "libbb.h" + +#define OPT_FILESYS (1 << 0) +#define OPT_TERSE (1 << 1) +#define OPT_DEREFERENCE (1 << 2) +#define OPT_SELINUX (1 << 3) + +#if ENABLE_FEATURE_STAT_FORMAT +typedef bool (*statfunc_ptr)(const char *, const char *); +#else +typedef bool (*statfunc_ptr)(const char *); +#endif + +static const char *file_type(const struct stat *st) +{ + /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 + * for some of these formats. + * To keep diagnostics grammatical in English, the + * returned string must start with a consonant. + */ + if (S_ISREG(st->st_mode)) return st->st_size == 0 ? "regular empty file" : "regular file"; + if (S_ISDIR(st->st_mode)) return "directory"; + if (S_ISBLK(st->st_mode)) return "block special file"; + if (S_ISCHR(st->st_mode)) return "character special file"; + if (S_ISFIFO(st->st_mode)) return "fifo"; + if (S_ISLNK(st->st_mode)) return "symbolic link"; + if (S_ISSOCK(st->st_mode)) return "socket"; + if (S_TYPEISMQ(st)) return "message queue"; + if (S_TYPEISSEM(st)) return "semaphore"; + if (S_TYPEISSHM(st)) return "shared memory object"; +#ifdef S_TYPEISTMO + if (S_TYPEISTMO(st)) return "typed memory object"; +#endif + return "weird file"; +} + +static const char *human_time(time_t t) +{ + /* Old + static char *str; + str = ctime(&t); + str[strlen(str)-1] = '\0'; + return str; + */ + /* coreutils 6.3 compat: */ + + /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/ +#define buf bb_common_bufsiz1 + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t)); + return buf; +#undef buf +} + +/* Return the type of the specified file system. + * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris) + * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) + * Still others have neither and have to get by with f_type (Linux). + */ +static const char *human_fstype(uint32_t f_type) +{ + static const struct types { + uint32_t type; + const char *const fs; + } humantypes[] = { + { 0xADFF, "affs" }, + { 0x1Cd1, "devpts" }, + { 0x137D, "ext" }, + { 0xEF51, "ext2" }, + { 0xEF53, "ext2/ext3" }, + { 0x3153464a, "jfs" }, + { 0x58465342, "xfs" }, + { 0xF995E849, "hpfs" }, + { 0x9660, "isofs" }, + { 0x4000, "isofs" }, + { 0x4004, "isofs" }, + { 0x137F, "minix" }, + { 0x138F, "minix (30 char.)" }, + { 0x2468, "minix v2" }, + { 0x2478, "minix v2 (30 char.)" }, + { 0x4d44, "msdos" }, + { 0x4006, "fat" }, + { 0x564c, "novell" }, + { 0x6969, "nfs" }, + { 0x9fa0, "proc" }, + { 0x517B, "smb" }, + { 0x012FF7B4, "xenix" }, + { 0x012FF7B5, "sysv4" }, + { 0x012FF7B6, "sysv2" }, + { 0x012FF7B7, "coh" }, + { 0x00011954, "ufs" }, + { 0x012FD16D, "xia" }, + { 0x5346544e, "ntfs" }, + { 0x1021994, "tmpfs" }, + { 0x52654973, "reiserfs" }, + { 0x28cd3d45, "cramfs" }, + { 0x7275, "romfs" }, + { 0x858458f6, "romfs" }, + { 0x73717368, "squashfs" }, + { 0x62656572, "sysfs" }, + { 0, "UNKNOWN" } + }; + + int i; + + for (i = 0; humantypes[i].type; ++i) + if (humantypes[i].type == f_type) + break; + return humantypes[i].fs; +} + +/* "man statfs" says that statfsbuf->f_fsid is a mess */ +/* coreutils treats it as an array of ints, most significant first */ +static unsigned long long get_f_fsid(const struct statfs *statfsbuf) +{ + const unsigned *p = (const void*) &statfsbuf->f_fsid; + unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned); + unsigned long long r = 0; + + do + r = (r << (sizeof(unsigned)*8)) | *p++; + while (--sz > 0); + return r; +} + +#if ENABLE_FEATURE_STAT_FORMAT +static void strcatc(char *str, char c) +{ + int len = strlen(str); + str[len++] = c; + str[len] = '\0'; +} + +static void printfs(char *pformat, const char *msg) +{ + strcatc(pformat, 's'); + printf(pformat, msg); +} + +/* print statfs info */ +static void FAST_FUNC print_statfs(char *pformat, const char m, + const char *const filename, const void *data + IF_SELINUX(, security_context_t scontext)) +{ + const struct statfs *statfsbuf = data; + if (m == 'n') { + printfs(pformat, filename); + } else if (m == 'i') { + strcat(pformat, "llx"); + printf(pformat, get_f_fsid(statfsbuf)); + } else if (m == 'l') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statfsbuf->f_namelen); + } else if (m == 't') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) statfsbuf->f_type); /* no equiv */ + } else if (m == 'T') { + printfs(pformat, human_fstype(statfsbuf->f_type)); + } else if (m == 'b') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_blocks); + } else if (m == 'f') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_bfree); + } else if (m == 'a') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_bavail); + } else if (m == 's' || m == 'S') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statfsbuf->f_bsize); + } else if (m == 'c') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_files); + } else if (m == 'd') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_ffree); +# if ENABLE_SELINUX + } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { + printfs(pformat, scontext); +# endif + } else { + strcatc(pformat, 'c'); + printf(pformat, m); + } +} + +/* print stat info */ +static void FAST_FUNC print_stat(char *pformat, const char m, + const char *const filename, const void *data + IF_SELINUX(, security_context_t scontext)) +{ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + struct stat *statbuf = (struct stat *) data; + struct passwd *pw_ent; + struct group *gw_ent; + + if (m == 'n') { + printfs(pformat, filename); + } else if (m == 'N') { + strcatc(pformat, 's'); + if (S_ISLNK(statbuf->st_mode)) { + char *linkname = xmalloc_readlink_or_warn(filename); + if (linkname == NULL) + return; + printf("'%s' -> '%s'", filename, linkname); + free(linkname); + } else { + printf(pformat, filename); + } + } else if (m == 'd') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_dev); + } else if (m == 'D') { + strcat(pformat, "llx"); + printf(pformat, (unsigned long long) statbuf->st_dev); + } else if (m == 'i') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_ino); + } else if (m == 'a') { + strcat(pformat, "lo"); + printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); + } else if (m == 'A') { + printfs(pformat, bb_mode_string(statbuf->st_mode)); + } else if (m == 'f') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) statbuf->st_mode); + } else if (m == 'F') { + printfs(pformat, file_type(statbuf)); + } else if (m == 'h') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_nlink); + } else if (m == 'u') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_uid); + } else if (m == 'U') { + pw_ent = getpwuid(statbuf->st_uid); + printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN"); + } else if (m == 'g') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_gid); + } else if (m == 'G') { + gw_ent = getgrgid(statbuf->st_gid); + printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); + } else if (m == 't') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) major(statbuf->st_rdev)); + } else if (m == 'T') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) minor(statbuf->st_rdev)); + } else if (m == 's') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_size); + } else if (m == 'B') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE + } else if (m == 'b') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_blocks); + } else if (m == 'o') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_blksize); + } else if (m == 'x') { + printfs(pformat, human_time(statbuf->st_atime)); + } else if (m == 'X') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + /* note: (unsigned long) would be wrong: + * imagine (unsigned long64)int32 */ + printf(pformat, (long) statbuf->st_atime); + } else if (m == 'y') { + printfs(pformat, human_time(statbuf->st_mtime)); + } else if (m == 'Y') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (long) statbuf->st_mtime); + } else if (m == 'z') { + printfs(pformat, human_time(statbuf->st_ctime)); + } else if (m == 'Z') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (long) statbuf->st_ctime); +# if ENABLE_SELINUX + } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { + printfs(pformat, scontext); +# endif + } else { + strcatc(pformat, 'c'); + printf(pformat, m); + } +} + +static void print_it(const char *masterformat, + const char *filename, + void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)), + const void *data + IF_SELINUX(, security_context_t scontext)) +{ + /* Create a working copy of the format string */ + char *format = xstrdup(masterformat); + /* Add 2 to accomodate our conversion of the stat '%s' format string + * to the printf '%llu' one. */ + char *dest = xmalloc(strlen(format) + 2 + 1); + char *b; + + b = format; + while (b) { + /* Each iteration finds next %spec, + * prints preceding string and handles found %spec + */ + size_t len; + char *p = strchr(b, '%'); + if (!p) { + /* coreutils 6.3 always prints newline at the end */ + /*fputs(b, stdout);*/ + puts(b); + break; + } + + /* dest = "%" */ + len = 1 + strspn(p + 1, "#-+.I 0123456789"); + memcpy(dest, p, len); + dest[len] = '\0'; + + /* print preceding string */ + *p = '\0'; + fputs(b, stdout); + + p += len; + b = p + 1; + switch (*p) { + case '\0': + b = NULL; + /* fall through */ + case '%': + bb_putchar('%'); + break; + default: + /* Completes "%" with specifier and printfs */ + print_func(dest, *p, filename, data IF_SELINUX(,scontext)); + break; + } + } + + free(format); + free(dest); +} +#endif /* FEATURE_STAT_FORMAT */ + +/* Stat the file system and print what we find. */ +#if !ENABLE_FEATURE_STAT_FORMAT +#define do_statfs(filename, format) do_statfs(filename) +#endif +static bool do_statfs(const char *filename, const char *format) +{ + struct statfs statfsbuf; +#if !ENABLE_FEATURE_STAT_FORMAT + const char *format; +#endif +#if ENABLE_SELINUX + security_context_t scontext = NULL; + + if (option_mask32 & OPT_SELINUX) { + if ((option_mask32 & OPT_DEREFERENCE + ? lgetfilecon(filename, &scontext) + : getfilecon(filename, &scontext) + ) < 0 + ) { + bb_perror_msg(filename); + return 0; + } + } +#endif + if (statfs(filename, &statfsbuf) != 0) { + bb_perror_msg("can't read file system information for '%s'", filename); + return 0; + } + +#if ENABLE_FEATURE_STAT_FORMAT + if (format == NULL) { +# if !ENABLE_SELINUX + format = (option_mask32 & OPT_TERSE + ? "%n %i %l %t %s %b %f %a %c %d\n" + : " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d"); +# else + format = (option_mask32 & OPT_TERSE + ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n": + "%n %i %l %t %s %b %f %a %c %d\n") + : (option_mask32 & OPT_SELINUX ? + " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d" + " S_context: %C\n": + " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d\n") + ); +# endif /* SELINUX */ + } + print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext)); +#else /* FEATURE_STAT_FORMAT */ + format = (option_mask32 & OPT_TERSE + ? "%s %llx %lu " + : " File: \"%s\"\n" + " ID: %-8llx Namelen: %-7lu "); + printf(format, + filename, + get_f_fsid(&statfsbuf), + statfsbuf.f_namelen); + + if (option_mask32 & OPT_TERSE) + printf("%lx ", (unsigned long) statfsbuf.f_type); + else + printf("Type: %s\n", human_fstype(statfsbuf.f_type)); + +# if !ENABLE_SELINUX + format = (option_mask32 & OPT_TERSE + ? "%lu %llu %llu %llu %llu %llu\n" + : "Block size: %-10lu\n" + "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" + "Inodes: Total: %-10llu Free: %llu\n"); + printf(format, + (unsigned long) statfsbuf.f_bsize, + (unsigned long long) statfsbuf.f_blocks, + (unsigned long long) statfsbuf.f_bfree, + (unsigned long long) statfsbuf.f_bavail, + (unsigned long long) statfsbuf.f_files, + (unsigned long long) statfsbuf.f_ffree); +# else + format = (option_mask32 & OPT_TERSE + ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n") + : (option_mask32 & OPT_SELINUX + ? "Block size: %-10lu\n" + "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" + "Inodes: Total: %-10llu Free: %llu" + "S_context: %C\n" + : "Block size: %-10lu\n" + "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" + "Inodes: Total: %-10llu Free: %llu\n" + ) + ); + printf(format, + (unsigned long) statfsbuf.f_bsize, + (unsigned long long) statfsbuf.f_blocks, + (unsigned long long) statfsbuf.f_bfree, + (unsigned long long) statfsbuf.f_bavail, + (unsigned long long) statfsbuf.f_files, + (unsigned long long) statfsbuf.f_ffree, + scontext); + + if (scontext) + freecon(scontext); +# endif +#endif /* FEATURE_STAT_FORMAT */ + return 1; +} + +/* stat the file and print what we find */ +#if !ENABLE_FEATURE_STAT_FORMAT +#define do_stat(filename, format) do_stat(filename) +#endif +static bool do_stat(const char *filename, const char *format) +{ + struct stat statbuf; +#if ENABLE_SELINUX + security_context_t scontext = NULL; + + if (option_mask32 & OPT_SELINUX) { + if ((option_mask32 & OPT_DEREFERENCE + ? lgetfilecon(filename, &scontext) + : getfilecon(filename, &scontext) + ) < 0 + ) { + bb_perror_msg(filename); + return 0; + } + } +#endif + if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) { + bb_perror_msg("can't stat '%s'", filename); + return 0; + } + +#if ENABLE_FEATURE_STAT_FORMAT + if (format == NULL) { +# if !ENABLE_SELINUX + if (option_mask32 & OPT_TERSE) { + format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; + } else { + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } else { + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } + } +# else + if (option_mask32 & OPT_TERSE) { + format = (option_mask32 & OPT_SELINUX ? + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n": + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"); + } else { + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { + format = (option_mask32 & OPT_SELINUX ? + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + " S_Context: %C\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n": + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"); + } else { + format = (option_mask32 & OPT_SELINUX ? + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "S_Context: %C\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n": + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"); + } + } +# endif + } + print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext)); +#else /* FEATURE_STAT_FORMAT */ + if (option_mask32 & OPT_TERSE) { + printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu" + IF_NOT_SELINUX("\n"), + filename, + (unsigned long long) statbuf.st_size, + (unsigned long long) statbuf.st_blocks, + (unsigned long) statbuf.st_mode, + (unsigned long) statbuf.st_uid, + (unsigned long) statbuf.st_gid, + (unsigned long long) statbuf.st_dev, + (unsigned long long) statbuf.st_ino, + (unsigned long) statbuf.st_nlink, + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev), + (unsigned long) statbuf.st_atime, + (unsigned long) statbuf.st_mtime, + (unsigned long) statbuf.st_ctime, + (unsigned long) statbuf.st_blksize + ); +# if ENABLE_SELINUX + if (option_mask32 & OPT_SELINUX) + printf(" %lc\n", *scontext); + else + bb_putchar('\n'); +# endif + } else { + char *linkname = NULL; + struct passwd *pw_ent; + struct group *gw_ent; + + gw_ent = getgrgid(statbuf.st_gid); + pw_ent = getpwuid(statbuf.st_uid); + + if (S_ISLNK(statbuf.st_mode)) + linkname = xmalloc_readlink_or_warn(filename); + if (linkname) { + printf(" File: '%s' -> '%s'\n", filename, linkname); + free(linkname); + } else { + printf(" File: '%s'\n", filename); + } + + printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n" + "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu", + (unsigned long long) statbuf.st_size, + (unsigned long long) statbuf.st_blocks, + (unsigned long) statbuf.st_blksize, + file_type(&statbuf), + (unsigned long long) statbuf.st_dev, + (unsigned long long) statbuf.st_dev, + (unsigned long long) statbuf.st_ino, + (unsigned long) statbuf.st_nlink); + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) + printf(" Device type: %lx,%lx\n", + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev)); + else + bb_putchar('\n'); + printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n", + (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), + bb_mode_string(statbuf.st_mode), + (unsigned long) statbuf.st_uid, + (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN", + (unsigned long) statbuf.st_gid, + (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); +# if ENABLE_SELINUX + printf(" S_Context: %lc\n", *scontext); +# endif + printf("Access: %s\n", human_time(statbuf.st_atime)); + printf("Modify: %s\n", human_time(statbuf.st_mtime)); + printf("Change: %s\n", human_time(statbuf.st_ctime)); + } +#endif /* FEATURE_STAT_FORMAT */ + return 1; +} + +int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stat_main(int argc UNUSED_PARAM, char **argv) +{ + IF_FEATURE_STAT_FORMAT(char *format = NULL;) + int i; + int ok; + unsigned opts; + statfunc_ptr statfunc = do_stat; + + opt_complementary = "-1"; /* min one arg */ + opts = getopt32(argv, "ftL" + IF_SELINUX("Z") + IF_FEATURE_STAT_FORMAT("c:", &format) + ); + if (opts & OPT_FILESYS) /* -f */ + statfunc = do_statfs; +#if ENABLE_SELINUX + if (opts & OPT_SELINUX) { + selinux_or_die(); + } +#endif + ok = 1; + argv += optind; + for (i = 0; argv[i]; ++i) + ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format)); + + return (ok ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/coreutils/stty.c b/coreutils/stty.c new file mode 100644 index 000000000..7f057ead2 --- /dev/null +++ b/coreutils/stty.c @@ -0,0 +1,1554 @@ +/* vi: set sw=4 ts=4: */ +/* stty -- change and print terminal line settings + Copyright (C) 1990-1999 Free Software Foundation, Inc. + + Licensed under GPLv2 or later, see file LICENSE in this source tree. +*/ +/* Usage: stty [-ag] [-F device] [setting...] + + Options: + -a Write all current settings to stdout in human-readable form. + -g Write all current settings to stdout in stty-readable form. + -F Open and use the specified device instead of stdin + + If no args are given, write to stdout the baud rate and settings that + have been changed from their defaults. Mode reading and changes + are done on the specified device, or stdin if none was specified. + + David MacKenzie + + Special for busybox ported by Vladimir Oleynik 2001 + + */ + +//usage:#define stty_trivial_usage +//usage: "[-a|g] [-F DEVICE] [SETTING]..." +//usage:#define stty_full_usage "\n\n" +//usage: "Without arguments, prints baud rate, line discipline,\n" +//usage: "and deviations from stty sane\n" +//usage: "\n -F DEVICE Open device instead of stdin" +//usage: "\n -a Print all current settings in human-readable form" +//usage: "\n -g Print in stty-readable form" +//usage: "\n [SETTING] See manpage" + +#include "libbb.h" + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE ((unsigned char) 0) +#endif + +#define Control(c) ((c) & 0x1f) +/* Canonical values for control characters */ +#ifndef CINTR +# define CINTR Control('c') +#endif +#ifndef CQUIT +# define CQUIT 28 +#endif +#ifndef CERASE +# define CERASE 127 +#endif +#ifndef CKILL +# define CKILL Control('u') +#endif +#ifndef CEOF +# define CEOF Control('d') +#endif +#ifndef CEOL +# define CEOL _POSIX_VDISABLE +#endif +#ifndef CSTART +# define CSTART Control('q') +#endif +#ifndef CSTOP +# define CSTOP Control('s') +#endif +#ifndef CSUSP +# define CSUSP Control('z') +#endif +#if defined(VEOL2) && !defined(CEOL2) +# define CEOL2 _POSIX_VDISABLE +#endif +/* glibc-2.12.1 uses only VSWTC name */ +#if defined(VSWTC) && !defined(VSWTCH) +# define VSWTCH VSWTC +#endif +/* ISC renamed swtch to susp for termios, but we'll accept either name */ +#if defined(VSUSP) && !defined(VSWTCH) +# define VSWTCH VSUSP +# define CSWTCH CSUSP +#endif +#if defined(VSWTCH) && !defined(CSWTCH) +# define CSWTCH _POSIX_VDISABLE +#endif + +/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'. + So the default is to disable 'swtch.' */ +#if defined(__sparc__) && defined(__svr4__) +# undef CSWTCH +# define CSWTCH _POSIX_VDISABLE +#endif + +#if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */ +# define VWERASE VWERSE +#endif +#if defined(VDSUSP) && !defined(CDSUSP) +# define CDSUSP Control('y') +#endif +#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */ +# define VREPRINT VRPRNT +#endif +#if defined(VREPRINT) && !defined(CRPRNT) +# define CRPRNT Control('r') +#endif +#if defined(VWERASE) && !defined(CWERASE) +# define CWERASE Control('w') +#endif +#if defined(VLNEXT) && !defined(CLNEXT) +# define CLNEXT Control('v') +#endif +#if defined(VDISCARD) && !defined(VFLUSHO) +# define VFLUSHO VDISCARD +#endif +#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */ +# define VFLUSHO VFLUSH +#endif +#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */ +# define ECHOCTL CTLECH +#endif +#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */ +# define ECHOCTL TCTLECH +#endif +#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */ +# define ECHOKE CRTKIL +#endif +#if defined(VFLUSHO) && !defined(CFLUSHO) +# define CFLUSHO Control('o') +#endif +#if defined(VSTATUS) && !defined(CSTATUS) +# define CSTATUS Control('t') +#endif + +/* Save us from #ifdef forest plague */ +#ifndef BSDLY +# define BSDLY 0 +#endif +#ifndef CIBAUD +# define CIBAUD 0 +#endif +#ifndef CRDLY +# define CRDLY 0 +#endif +#ifndef CRTSCTS +# define CRTSCTS 0 +#endif +#ifndef ECHOCTL +# define ECHOCTL 0 +#endif +#ifndef ECHOKE +# define ECHOKE 0 +#endif +#ifndef ECHOPRT +# define ECHOPRT 0 +#endif +#ifndef FFDLY +# define FFDLY 0 +#endif +#ifndef IEXTEN +# define IEXTEN 0 +#endif +#ifndef IMAXBEL +# define IMAXBEL 0 +#endif +#ifndef IUCLC +# define IUCLC 0 +#endif +#ifndef IXANY +# define IXANY 0 +#endif +#ifndef NLDLY +# define NLDLY 0 +#endif +#ifndef OCRNL +# define OCRNL 0 +#endif +#ifndef OFDEL +# define OFDEL 0 +#endif +#ifndef OFILL +# define OFILL 0 +#endif +#ifndef OLCUC +# define OLCUC 0 +#endif +#ifndef ONLCR +# define ONLCR 0 +#endif +#ifndef ONLRET +# define ONLRET 0 +#endif +#ifndef ONOCR +# define ONOCR 0 +#endif +#ifndef OXTABS +# define OXTABS 0 +#endif +#ifndef TABDLY +# define TABDLY 0 +#endif +#ifndef TAB1 +# define TAB1 0 +#endif +#ifndef TAB2 +# define TAB2 0 +#endif +#ifndef TOSTOP +# define TOSTOP 0 +#endif +#ifndef VDSUSP +# define VDSUSP 0 +#endif +#ifndef VEOL2 +# define VEOL2 0 +#endif +#ifndef VFLUSHO +# define VFLUSHO 0 +#endif +#ifndef VLNEXT +# define VLNEXT 0 +#endif +#ifndef VREPRINT +# define VREPRINT 0 +#endif +#ifndef VSTATUS +# define VSTATUS 0 +#endif +#ifndef VSWTCH +# define VSWTCH 0 +#endif +#ifndef VTDLY +# define VTDLY 0 +#endif +#ifndef VWERASE +# define VWERASE 0 +#endif +#ifndef XCASE +# define XCASE 0 +#endif +#ifndef IUTF8 +# define IUTF8 0 +#endif + +/* Which speeds to set */ +enum speed_setting { + input_speed, output_speed, both_speeds +}; + +/* Which member(s) of 'struct termios' a mode uses */ +enum { + /* Do NOT change the order or values, as mode_type_flag() + * depends on them */ + control, input, output, local, combination +}; + +/* Flags for 'struct mode_info' */ +#define SANE_SET 1 /* Set in 'sane' mode */ +#define SANE_UNSET 2 /* Unset in 'sane' mode */ +#define REV 4 /* Can be turned off by prepending '-' */ +#define OMIT 8 /* Don't display value */ + + +/* Each mode. + * This structure should be kept as small as humanly possible. + */ +struct mode_info { + const uint8_t type; /* Which structure element to change */ + const uint8_t flags; /* Setting and display options */ + /* only these values are ever used, so... */ +#if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 + const uint8_t mask; +#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 + const uint16_t mask; +#else + const tcflag_t mask; /* Other bits to turn off for this mode */ +#endif + /* was using short here, but ppc32 was unhappy */ + const tcflag_t bits; /* Bits to set for this mode */ +}; + +enum { + /* Must match mode_name[] and mode_info[] order! */ + IDX_evenp = 0, + IDX_parity, + IDX_oddp, + IDX_nl, + IDX_ek, + IDX_sane, + IDX_cooked, + IDX_raw, + IDX_pass8, + IDX_litout, + IDX_cbreak, + IDX_crt, + IDX_dec, +#if IXANY + IDX_decctlq, +#endif +#if TABDLY || OXTABS + IDX_tabs, +#endif +#if XCASE && IUCLC && OLCUC + IDX_lcase, + IDX_LCASE, +#endif +}; + +#define MI_ENTRY(N,T,F,B,M) N "\0" + +/* Mode names given on command line */ +static const char mode_name[] = + MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("ek", combination, OMIT, 0, 0 ) + MI_ENTRY("sane", combination, OMIT, 0, 0 ) + MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("crt", combination, OMIT, 0, 0 ) + MI_ENTRY("dec", combination, OMIT, 0, 0 ) +#if IXANY + MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) +#endif +#if TABDLY || OXTABS + MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) +#endif +#if XCASE && IUCLC && OLCUC + MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) +#endif + MI_ENTRY("parenb", control, REV, PARENB, 0 ) + MI_ENTRY("parodd", control, REV, PARODD, 0 ) + MI_ENTRY("cs5", control, 0, CS5, CSIZE) + MI_ENTRY("cs6", control, 0, CS6, CSIZE) + MI_ENTRY("cs7", control, 0, CS7, CSIZE) + MI_ENTRY("cs8", control, 0, CS8, CSIZE) + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) +#if CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) + MI_ENTRY("inpck", input, REV, INPCK, 0 ) + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) + MI_ENTRY("ixon", input, REV, IXON, 0 ) + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) + MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) +#if IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) +#endif +#if IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) +#endif +#if IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) +#endif +#if IUTF8 + MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) +#if OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) +#endif +#if OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) +#endif +#if ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) +#endif +#if ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) +#endif +#if ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) +#endif +#if OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) +#endif +#if OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) +#endif +#if NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) +#endif +#if CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) +#endif + +#if TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) +# if TAB2 + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) +# endif +# if TAB1 + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) +# endif + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) +#else +# if OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) +# endif +#endif + +#if BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) +#endif +#if VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) +#endif +#if FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) +#if IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) + MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) +#if XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) +#endif +#if TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) +#endif +#if ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) + MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) +#endif +#if ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) + MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) +#endif +#if ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) + MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) +#endif + ; + +#undef MI_ENTRY +#define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, + +static const struct mode_info mode_info[] = { + /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ + MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("ek", combination, OMIT, 0, 0 ) + MI_ENTRY("sane", combination, OMIT, 0, 0 ) + MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("crt", combination, OMIT, 0, 0 ) + MI_ENTRY("dec", combination, OMIT, 0, 0 ) +#if IXANY + MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) +#endif +#if TABDLY || OXTABS + MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) +#endif +#if XCASE && IUCLC && OLCUC + MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) +#endif + MI_ENTRY("parenb", control, REV, PARENB, 0 ) + MI_ENTRY("parodd", control, REV, PARODD, 0 ) + MI_ENTRY("cs5", control, 0, CS5, CSIZE) + MI_ENTRY("cs6", control, 0, CS6, CSIZE) + MI_ENTRY("cs7", control, 0, CS7, CSIZE) + MI_ENTRY("cs8", control, 0, CS8, CSIZE) + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) +#if CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) + MI_ENTRY("inpck", input, REV, INPCK, 0 ) + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) + MI_ENTRY("ixon", input, REV, IXON, 0 ) + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) + MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) +#if IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) +#endif +#if IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) +#endif +#if IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) +#endif +#if IUTF8 + MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) +#if OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) +#endif +#if OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) +#endif +#if ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) +#endif +#if ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) +#endif +#if ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) +#endif +#if OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) +#endif +#if OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) +#endif +#if NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) +#endif +#if CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) +#endif + +#if TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) +# if TAB2 + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) +# endif +# if TAB1 + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) +# endif + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) +#else +# if OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) +# endif +#endif + +#if BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) +#endif +#if VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) +#endif +#if FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) +#if IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) + MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) +#if XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) +#endif +#if TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) +#endif +#if ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) + MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) +#endif +#if ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) + MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) +#endif +#if ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) + MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) +#endif +}; + +enum { + NUM_mode_info = ARRAY_SIZE(mode_info) +}; + + +/* Control characters */ +struct control_info { + const uint8_t saneval; /* Value to set for 'stty sane' */ + const uint8_t offset; /* Offset in c_cc */ +}; + +enum { + /* Must match control_name[] and control_info[] order! */ + CIDX_intr = 0, + CIDX_quit, + CIDX_erase, + CIDX_kill, + CIDX_eof, + CIDX_eol, +#if VEOL2 + CIDX_eol2, +#endif +#if VSWTCH + CIDX_swtch, +#endif + CIDX_start, + CIDX_stop, + CIDX_susp, +#if VDSUSP + CIDX_dsusp, +#endif +#if VREPRINT + CIDX_rprnt, +#endif +#if VWERASE + CIDX_werase, +#endif +#if VLNEXT + CIDX_lnext, +#endif +#if VFLUSHO + CIDX_flush, +#endif +#if VSTATUS + CIDX_status, +#endif + CIDX_min, + CIDX_time, +}; + +#define CI_ENTRY(n,s,o) n "\0" + +/* Name given on command line */ +static const char control_name[] = + CI_ENTRY("intr", CINTR, VINTR ) + CI_ENTRY("quit", CQUIT, VQUIT ) + CI_ENTRY("erase", CERASE, VERASE ) + CI_ENTRY("kill", CKILL, VKILL ) + CI_ENTRY("eof", CEOF, VEOF ) + CI_ENTRY("eol", CEOL, VEOL ) +#if VEOL2 + CI_ENTRY("eol2", CEOL2, VEOL2 ) +#endif +#if VSWTCH + CI_ENTRY("swtch", CSWTCH, VSWTCH ) +#endif + CI_ENTRY("start", CSTART, VSTART ) + CI_ENTRY("stop", CSTOP, VSTOP ) + CI_ENTRY("susp", CSUSP, VSUSP ) +#if VDSUSP + CI_ENTRY("dsusp", CDSUSP, VDSUSP ) +#endif +#if VREPRINT + CI_ENTRY("rprnt", CRPRNT, VREPRINT) +#endif +#if VWERASE + CI_ENTRY("werase", CWERASE, VWERASE ) +#endif +#if VLNEXT + CI_ENTRY("lnext", CLNEXT, VLNEXT ) +#endif +#if VFLUSHO + CI_ENTRY("flush", CFLUSHO, VFLUSHO ) +#endif +#if VSTATUS + CI_ENTRY("status", CSTATUS, VSTATUS ) +#endif + /* These must be last because of the display routines */ + CI_ENTRY("min", 1, VMIN ) + CI_ENTRY("time", 0, VTIME ) + ; + +#undef CI_ENTRY +#define CI_ENTRY(n,s,o) { s, o }, + +static const struct control_info control_info[] = { + /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */ + CI_ENTRY("intr", CINTR, VINTR ) + CI_ENTRY("quit", CQUIT, VQUIT ) + CI_ENTRY("erase", CERASE, VERASE ) + CI_ENTRY("kill", CKILL, VKILL ) + CI_ENTRY("eof", CEOF, VEOF ) + CI_ENTRY("eol", CEOL, VEOL ) +#if VEOL2 + CI_ENTRY("eol2", CEOL2, VEOL2 ) +#endif +#if VSWTCH + CI_ENTRY("swtch", CSWTCH, VSWTCH ) +#endif + CI_ENTRY("start", CSTART, VSTART ) + CI_ENTRY("stop", CSTOP, VSTOP ) + CI_ENTRY("susp", CSUSP, VSUSP ) +#if VDSUSP + CI_ENTRY("dsusp", CDSUSP, VDSUSP ) +#endif +#if VREPRINT + CI_ENTRY("rprnt", CRPRNT, VREPRINT) +#endif +#if VWERASE + CI_ENTRY("werase", CWERASE, VWERASE ) +#endif +#if VLNEXT + CI_ENTRY("lnext", CLNEXT, VLNEXT ) +#endif +#if VFLUSHO + CI_ENTRY("flush", CFLUSHO, VFLUSHO ) +#endif +#if VSTATUS + CI_ENTRY("status", CSTATUS, VSTATUS ) +#endif + /* These must be last because of the display routines */ + CI_ENTRY("min", 1, VMIN ) + CI_ENTRY("time", 0, VTIME ) +}; + +enum { + NUM_control_info = ARRAY_SIZE(control_info) +}; + + +struct globals { + const char *device_name; + /* The width of the screen, for output wrapping */ + unsigned max_col; + /* Current position, to know when to wrap */ + unsigned current_col; + char buf[10]; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { \ + G.device_name = bb_msg_standard_input; \ + G.max_col = 80; \ +} while (0) + + +/* Return a string that is the printable representation of character CH */ +/* Adapted from 'cat' by Torbjorn Granlund */ +static const char *visible(unsigned ch) +{ + char *bpout = G.buf; + + if (ch == _POSIX_VDISABLE) + return ""; + + if (ch >= 128) { + ch -= 128; + *bpout++ = 'M'; + *bpout++ = '-'; + } + + if (ch < 32) { + *bpout++ = '^'; + *bpout++ = ch + 64; + } else if (ch < 127) { + *bpout++ = ch; + } else { + *bpout++ = '^'; + *bpout++ = '?'; + } + + *bpout = '\0'; + return G.buf; +} + +static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode) +{ + static const uint8_t tcflag_offsets[] ALIGN1 = { + offsetof(struct termios, c_cflag), /* control */ + offsetof(struct termios, c_iflag), /* input */ + offsetof(struct termios, c_oflag), /* output */ + offsetof(struct termios, c_lflag) /* local */ + }; + + if (type <= local) { + return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); + } + return NULL; +} + +static void set_speed_or_die(enum speed_setting type, const char *arg, + struct termios *mode) +{ + speed_t baud; + + baud = tty_value_to_baud(xatou(arg)); + + if (type != output_speed) { /* either input or both */ + cfsetispeed(mode, baud); + } + if (type != input_speed) { /* either output or both */ + cfsetospeed(mode, baud); + } +} + +static NORETURN void perror_on_device_and_die(const char *fmt) +{ + bb_perror_msg_and_die(fmt, G.device_name); +} + +static void perror_on_device(const char *fmt) +{ + bb_perror_msg(fmt, G.device_name); +} + +/* Print format string MESSAGE and optional args. + Wrap to next line first if it won't fit. + Print a space first unless MESSAGE will start a new line */ +static void wrapf(const char *message, ...) +{ + char buf[128]; + va_list args; + unsigned buflen; + + va_start(args, message); + buflen = vsnprintf(buf, sizeof(buf), message, args); + va_end(args); + /* We seem to be called only with suitable lengths, but check if + somebody failed to adhere to this assumption just to be sure. */ + if (!buflen || buflen >= sizeof(buf)) return; + + if (G.current_col > 0) { + G.current_col++; + if (buf[0] != '\n') { + if (G.current_col + buflen >= G.max_col) { + bb_putchar('\n'); + G.current_col = 0; + } else + bb_putchar(' '); + } + } + fputs(buf, stdout); + G.current_col += buflen; + if (buf[buflen-1] == '\n') + G.current_col = 0; +} + +static void newline(void) +{ + if (G.current_col != 0) + wrapf("\n"); +} + +#ifdef TIOCGWINSZ +static void set_window_size(int rows, int cols) +{ + struct winsize win = { 0, 0, 0, 0 }; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) { + if (errno != EINVAL) { + goto bail; + } + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + + if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win)) +bail: + perror_on_device("%s"); +} +#endif + +static void display_window_size(int fancy) +{ + const char *fmt_str = "%s\0%s: no size information for this device"; + unsigned width, height; + + if (get_terminal_width_height(STDIN_FILENO, &width, &height)) { + if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { + perror_on_device(fmt_str); + } + } else { + wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n", + height, width); + } +} + +static const struct suffix_mult stty_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "B", 1024 }, + { "", 0 } +}; + +static const struct mode_info *find_mode(const char *name) +{ + int i = index_in_strings(mode_name, name); + return i >= 0 ? &mode_info[i] : NULL; +} + +static const struct control_info *find_control(const char *name) +{ + int i = index_in_strings(control_name, name); + return i >= 0 ? &control_info[i] : NULL; +} + +enum { + param_need_arg = 0x80, + param_line = 1 | 0x80, + param_rows = 2 | 0x80, + param_cols = 3 | 0x80, + param_columns = 4 | 0x80, + param_size = 5, + param_speed = 6, + param_ispeed = 7 | 0x80, + param_ospeed = 8 | 0x80, +}; + +static int find_param(const char *name) +{ + static const char params[] ALIGN1 = + "line\0" /* 1 */ + "rows\0" /* 2 */ + "cols\0" /* 3 */ + "columns\0" /* 4 */ + "size\0" /* 5 */ + "speed\0" /* 6 */ + "ispeed\0" + "ospeed\0"; + int i = index_in_strings(params, name) + 1; + if (i == 0) + return 0; + if (i != 5 && i != 6) + i |= 0x80; + return i; +} + +static int recover_mode(const char *arg, struct termios *mode) +{ + int i, n; + unsigned chr; + unsigned long iflag, oflag, cflag, lflag; + + /* Scan into temporaries since it is too much trouble to figure out + the right format for 'tcflag_t' */ + if (sscanf(arg, "%lx:%lx:%lx:%lx%n", + &iflag, &oflag, &cflag, &lflag, &n) != 4) + return 0; + mode->c_iflag = iflag; + mode->c_oflag = oflag; + mode->c_cflag = cflag; + mode->c_lflag = lflag; + arg += n; + for (i = 0; i < NCCS; ++i) { + if (sscanf(arg, ":%x%n", &chr, &n) != 1) + return 0; + mode->c_cc[i] = chr; + arg += n; + } + + /* Fail if there are too many fields */ + if (*arg != '\0') + return 0; + + return 1; +} + +static void display_recoverable(const struct termios *mode, + int UNUSED_PARAM dummy) +{ + int i; + printf("%lx:%lx:%lx:%lx", + (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, + (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); + for (i = 0; i < NCCS; ++i) + printf(":%x", (unsigned int) mode->c_cc[i]); + bb_putchar('\n'); +} + +static void display_speed(const struct termios *mode, int fancy) +{ + //____________________ 01234567 8 9 + const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;"; + unsigned long ispeed, ospeed; + + ispeed = cfgetispeed(mode); + ospeed = cfgetospeed(mode); + if (ispeed == 0 || ispeed == ospeed) { + ispeed = ospeed; /* in case ispeed was 0 */ + //________ 0123 4 5 6 7 8 9 + fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;"; + } + if (fancy) fmt_str += 9; + wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); +} + +static void do_display(const struct termios *mode, int all) +{ + int i; + tcflag_t *bitsp; + unsigned long mask; + int prev_type = control; + + display_speed(mode, 1); + if (all) + display_window_size(1); +#ifdef __linux__ + wrapf("line = %u;\n", mode->c_line); +#else + newline(); +#endif + + for (i = 0; i != CIDX_min; ++i) { + /* If swtch is the same as susp, don't print both */ +#if VSWTCH == VSUSP + if (i == CIDX_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies */ +#if VEOF == VMIN + if (!(mode->c_lflag & ICANON) + && (i == CIDX_eof || i == CIDX_eol) + ) { + continue; + } +#endif + wrapf("%s = %s;", nth_string(control_name, i), + visible(mode->c_cc[control_info[i].offset])); + } +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0) +#endif + wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); + newline(); + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & OMIT) + continue; + if (mode_info[i].type != prev_type) { + newline(); + prev_type = mode_info[i].type; + } + + bitsp = mode_type_flag(mode_info[i].type, mode); + mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; + if ((*bitsp & mask) == mode_info[i].bits) { + if (all || (mode_info[i].flags & SANE_UNSET)) + wrapf("-%s"+1, nth_string(mode_name, i)); + } else { + if ((all && mode_info[i].flags & REV) + || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) + ) { + wrapf("-%s", nth_string(mode_name, i)); + } + } + } + newline(); +} + +static void sane_mode(struct termios *mode) +{ + int i; + tcflag_t *bitsp; + + for (i = 0; i < NUM_control_info; ++i) { +#if VMIN == VEOF + if (i == CIDX_min) + break; +#endif + mode->c_cc[control_info[i].offset] = control_info[i].saneval; + } + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & SANE_SET) { + bitsp = mode_type_flag(mode_info[i].type, mode); + *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask)) + | mode_info[i].bits; + } else if (mode_info[i].flags & SANE_UNSET) { + bitsp = mode_type_flag(mode_info[i].type, mode); + *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask) + & ~mode_info[i].bits; + } + } +} + +static void set_mode(const struct mode_info *info, int reversed, + struct termios *mode) +{ + tcflag_t *bitsp; + + bitsp = mode_type_flag(info->type, mode); + + if (bitsp) { + if (reversed) + *bitsp = *bitsp & ~info->mask & ~info->bits; + else + *bitsp = (*bitsp & ~info->mask) | info->bits; + return; + } + + /* Combination mode */ + if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; + } else if (info == &mode_info[IDX_oddp]) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; + } else if (info == &mode_info[IDX_nl]) { + if (reversed) { + mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; + mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET; + } else { + mode->c_iflag = mode->c_iflag & ~ICRNL; + if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR; + } + } else if (info == &mode_info[IDX_ek]) { + mode->c_cc[VERASE] = CERASE; + mode->c_cc[VKILL] = CKILL; + } else if (info == &mode_info[IDX_sane]) { + sane_mode(mode); + } else if (info == &mode_info[IDX_cbreak]) { + if (reversed) + mode->c_lflag |= ICANON; + else + mode->c_lflag &= ~ICANON; + } else if (info == &mode_info[IDX_pass8]) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + } + } else if (info == &mode_info[IDX_litout]) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + mode->c_oflag |= OPOST; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + mode->c_oflag &= ~OPOST; + } + } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { + if ((info == &mode_info[IDX_raw] && reversed) + || (info == &mode_info[IDX_cooked] && !reversed) + ) { + /* Cooked mode */ + mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; + mode->c_oflag |= OPOST; + mode->c_lflag |= ISIG | ICANON; +#if VMIN == VEOF + mode->c_cc[VEOF] = CEOF; +#endif +#if VTIME == VEOL + mode->c_cc[VEOL] = CEOL; +#endif + } else { + /* Raw mode */ + mode->c_iflag = 0; + mode->c_oflag &= ~OPOST; + mode->c_lflag &= ~(ISIG | ICANON | XCASE); + mode->c_cc[VMIN] = 1; + mode->c_cc[VTIME] = 0; + } + } +#if IXANY + else if (info == &mode_info[IDX_decctlq]) { + if (reversed) + mode->c_iflag |= IXANY; + else + mode->c_iflag &= ~IXANY; + } +#endif +#if TABDLY + else if (info == &mode_info[IDX_tabs]) { + if (reversed) + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; + else + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; + } +#endif +#if OXTABS + else if (info == &mode_info[IDX_tabs]) { + if (reversed) + mode->c_oflag |= OXTABS; + else + mode->c_oflag &= ~OXTABS; + } +#endif +#if XCASE && IUCLC && OLCUC + else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) { + if (reversed) { + mode->c_lflag &= ~XCASE; + mode->c_iflag &= ~IUCLC; + mode->c_oflag &= ~OLCUC; + } else { + mode->c_lflag |= XCASE; + mode->c_iflag |= IUCLC; + mode->c_oflag |= OLCUC; + } + } +#endif + else if (info == &mode_info[IDX_crt]) { + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + } else if (info == &mode_info[IDX_dec]) { + mode->c_cc[VINTR] = 3; /* ^C */ + mode->c_cc[VERASE] = 127; /* DEL */ + mode->c_cc[VKILL] = 21; /* ^U */ + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + if (IXANY) mode->c_iflag &= ~IXANY; + } +} + +static void set_control_char_or_die(const struct control_info *info, + const char *arg, struct termios *mode) +{ + unsigned char value; + + if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time]) + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + else if (arg[0] == '\0' || arg[1] == '\0') + value = arg[0]; + else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0) + value = _POSIX_VDISABLE; + else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */ + value = arg[1] & 0x1f; /* Non-letters get weird results */ + if (arg[1] == '?') + value = 127; + } else + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + mode->c_cc[info->offset] = value; +} + +#define STTY_require_set_attr (1 << 0) +#define STTY_speed_was_set (1 << 1) +#define STTY_verbose_output (1 << 2) +#define STTY_recoverable_output (1 << 3) +#define STTY_noargs (1 << 4) + +int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stty_main(int argc UNUSED_PARAM, char **argv) +{ + struct termios mode; + void (*output_func)(const struct termios *, int); + const char *file_name = NULL; + int display_all = 0; + int stty_state; + int k; + + INIT_G(); + + stty_state = STTY_noargs; + output_func = do_display; + + /* First pass: only parse/verify command line params */ + k = 0; + while (argv[++k]) { + const struct mode_info *mp; + const struct control_info *cp; + const char *arg = argv[k]; + const char *argnext = argv[k+1]; + int param; + + if (arg[0] == '-') { + int i; + mp = find_mode(arg+1); + if (mp) { + if (!(mp->flags & REV)) + goto invalid_argument; + stty_state &= ~STTY_noargs; + continue; + } + /* It is an option - parse it */ + i = 0; + while (arg[++i]) { + switch (arg[i]) { + case 'a': + stty_state |= STTY_verbose_output; + output_func = do_display; + display_all = 1; + break; + case 'g': + stty_state |= STTY_recoverable_output; + output_func = display_recoverable; + break; + case 'F': + if (file_name) + bb_error_msg_and_die("only one device may be specified"); + file_name = &arg[i+1]; /* "-Fdevice" ? */ + if (!file_name[0]) { /* nope, "-F device" */ + int p = k+1; /* argv[p] is argnext */ + file_name = argnext; + if (!file_name) + bb_error_msg_and_die(bb_msg_requires_arg, "-F"); + /* remove -F param from arg[vc] */ + while (argv[p]) { + argv[p] = argv[p+1]; + ++p; + } + } + goto end_option; + default: + goto invalid_argument; + } + } + end_option: + continue; + } + + mp = find_mode(arg); + if (mp) { + stty_state &= ~STTY_noargs; + continue; + } + + cp = find_control(arg); + if (cp) { + if (!argnext) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + /* called for the side effect of xfunc death only */ + set_control_char_or_die(cp, argnext, &mode); + stty_state &= ~STTY_noargs; + ++k; + continue; + } + + param = find_param(arg); + if (param & param_need_arg) { + if (!argnext) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ++k; + } + + switch (param) { +#ifdef __linux__ + case param_line: +# ifndef TIOCGWINSZ + xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); + break; +# endif /* else fall-through */ +#endif +#ifdef TIOCGWINSZ + case param_rows: + case param_cols: + case param_columns: + xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); + break; + case param_size: +#endif + case param_speed: + break; + case param_ispeed: + /* called for the side effect of xfunc death only */ + set_speed_or_die(input_speed, argnext, &mode); + break; + case param_ospeed: + /* called for the side effect of xfunc death only */ + set_speed_or_die(output_speed, argnext, &mode); + break; + default: + if (recover_mode(arg, &mode) == 1) break; + if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; + invalid_argument: + bb_error_msg_and_die("invalid argument '%s'", arg); + } + stty_state &= ~STTY_noargs; + } + + /* Specifying both -a and -g is an error */ + if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == + (STTY_verbose_output | STTY_recoverable_output)) + bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive"); + /* Specifying -a or -g with non-options is an error */ + if (!(stty_state & STTY_noargs) + && (stty_state & (STTY_verbose_output | STTY_recoverable_output)) + ) { + bb_error_msg_and_die("modes may not be set when specifying an output style"); + } + + /* Now it is safe to start doing things */ + if (file_name) { + G.device_name = file_name; + xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); + ndelay_off(STDIN_FILENO); + } + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure */ + memset(&mode, 0, sizeof(mode)); + if (tcgetattr(STDIN_FILENO, &mode)) + perror_on_device_and_die("%s"); + + if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { + get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL); + output_func(&mode, display_all); + return EXIT_SUCCESS; + } + + /* Second pass: perform actions */ + k = 0; + while (argv[++k]) { + const struct mode_info *mp; + const struct control_info *cp; + const char *arg = argv[k]; + const char *argnext = argv[k+1]; + int param; + + if (arg[0] == '-') { + mp = find_mode(arg+1); + if (mp) { + set_mode(mp, 1 /* reversed */, &mode); + stty_state |= STTY_require_set_attr; + } + /* It is an option - already parsed. Skip it */ + continue; + } + + mp = find_mode(arg); + if (mp) { + set_mode(mp, 0 /* non-reversed */, &mode); + stty_state |= STTY_require_set_attr; + continue; + } + + cp = find_control(arg); + if (cp) { + ++k; + set_control_char_or_die(cp, argnext, &mode); + stty_state |= STTY_require_set_attr; + continue; + } + + param = find_param(arg); + if (param & param_need_arg) { + ++k; + } + + switch (param) { +#ifdef __linux__ + case param_line: + mode.c_line = xatoul_sfx(argnext, stty_suffixes); + stty_state |= STTY_require_set_attr; + break; +#endif +#ifdef TIOCGWINSZ + case param_cols: + case param_columns: + set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); + break; + case param_size: + display_window_size(0); + break; + case param_rows: + set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); + break; +#endif + case param_speed: + display_speed(&mode, 0); + break; + case param_ispeed: + set_speed_or_die(input_speed, argnext, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + break; + case param_ospeed: + set_speed_or_die(output_speed, argnext, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + break; + default: + if (recover_mode(arg, &mode) == 1) + stty_state |= STTY_require_set_attr; + else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ + set_speed_or_die(both_speeds, arg, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + } /* else - impossible (caught in the first pass): + bb_error_msg_and_die("invalid argument '%s'", arg); */ + } + } + + if (stty_state & STTY_require_set_attr) { + struct termios new_mode; + + if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) + perror_on_device_and_die("%s"); + + /* POSIX (according to Zlotnick's book) tcsetattr returns zero if + it performs *any* of the requested operations. This means it + can report 'success' when it has actually failed to perform + some proper subset of the requested operations. To detect + this partial failure, get the current terminal attributes and + compare them to the requested ones */ + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure */ + memset(&new_mode, 0, sizeof(new_mode)); + if (tcgetattr(STDIN_FILENO, &new_mode)) + perror_on_device_and_die("%s"); + + if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { +#if CIBAUD + /* SunOS 4.1.3 (at least) has the problem that after this sequence, + tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); + sometimes (m1 != m2). The only difference is in the four bits + of the c_cflag field corresponding to the baud rate. To save + Sun users a little confusion, don't report an error if this + happens. But suppress the error only if we haven't tried to + set the baud rate explicitly -- otherwise we'd never give an + error for a true failure to set the baud rate */ + + new_mode.c_cflag &= (~CIBAUD); + if ((stty_state & STTY_speed_was_set) + || memcmp(&mode, &new_mode, sizeof(mode)) != 0) +#endif + perror_on_device_and_die("%s: cannot perform all requested operations"); + } + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/sum.c b/coreutils/sum.c new file mode 100644 index 000000000..95110a6da --- /dev/null +++ b/coreutils/sum.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * sum -- checksum and count the blocks in a file + * Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. + * + * Copyright (C) 86, 89, 91, 1995-2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * + * Written by Kayvan Aghaiepour and David MacKenzie + * Taken from coreutils and turned into a busybox applet by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define sum_trivial_usage +//usage: "[-rs] [FILE]..." +//usage:#define sum_full_usage "\n\n" +//usage: "Checksum and count the blocks in a file\n" +//usage: "\n -r Use BSD sum algorithm (1K blocks)" +//usage: "\n -s Use System V sum algorithm (512byte blocks)" + +#include "libbb.h" + +enum { SUM_BSD, PRINT_NAME, SUM_SYSV }; + +/* BSD: calculate and print the rotated checksum and the size in 1K blocks + The checksum varies depending on sizeof (int). */ +/* SYSV: calculate and print the checksum and the size in 512-byte blocks */ +/* Return 1 if successful. */ +static unsigned sum_file(const char *file, unsigned type) +{ +#define buf bb_common_bufsiz1 + unsigned long long total_bytes = 0; + int fd, r; + /* The sum of all the input bytes, modulo (UINT_MAX + 1). */ + unsigned s = 0; + + fd = open_or_warn_stdin(file); + if (fd == -1) + return 0; + + while (1) { + size_t bytes_read = safe_read(fd, buf, BUFSIZ); + + if ((ssize_t)bytes_read <= 0) { + r = (fd && close(fd) != 0); + if (!bytes_read && !r) + /* no error */ + break; + bb_simple_perror_msg(file); + return 0; + } + + total_bytes += bytes_read; + if (type >= SUM_SYSV) { + do s += buf[--bytes_read]; while (bytes_read); + } else { + r = 0; + do { + s = (s >> 1) + ((s & 1) << 15); + s += buf[r++]; + s &= 0xffff; /* Keep it within bounds. */ + } while (--bytes_read); + } + } + + if (type < PRINT_NAME) + file = ""; + if (type >= SUM_SYSV) { + r = (s & 0xffff) + ((s & 0xffffffff) >> 16); + s = (r & 0xffff) + (r >> 16); + printf("%d %llu %s\n", s, (total_bytes + 511) / 512, file); + } else + printf("%05d %5llu %s\n", s, (total_bytes + 1023) / 1024, file); + return 1; +#undef buf +} + +int sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sum_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned n; + unsigned type = SUM_BSD; + + n = getopt32(argv, "sr"); + argv += optind; + if (n & 1) type = SUM_SYSV; + /* give the bsd priority over sysv func */ + if (n & 2) type = SUM_BSD; + + if (!argv[0]) { + /* Do not print the name */ + n = sum_file("-", type); + } else { + /* Need to print the name if either + - more than one file given + - doing sysv */ + type += (argv[1] || type == SUM_SYSV); + n = 1; + do { + n &= sum_file(*argv, type); + } while (*++argv); + } + return !n; +} diff --git a/coreutils/sync.c b/coreutils/sync.c new file mode 100644 index 000000000..7d98a1e30 --- /dev/null +++ b/coreutils/sync.c @@ -0,0 +1,30 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sync implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +//usage:#define sync_trivial_usage +//usage: "" +//usage:#define sync_full_usage "\n\n" +//usage: "Write all buffered blocks to disk" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int sync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sync_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) +{ + /* coreutils-6.9 compat */ + bb_warn_ignoring_args(argv[1]); + + sync(); + + return EXIT_SUCCESS; +} diff --git a/coreutils/tac.c b/coreutils/tac.c new file mode 100644 index 000000000..94d669de1 --- /dev/null +++ b/coreutils/tac.c @@ -0,0 +1,111 @@ +/* vi: set sw=4 ts=4: */ +/* + * tac implementation for busybox + * + * Copyright (C) 2003 Yang Xiaopeng + * Copyright (C) 2007 Natanael Copa + * Copyright (C) 2007 Tito Ragusa + * + * Licensed under GPLv2, see file LICENSE in this source tree. + * + */ + +/* tac - concatenate and print files in reverse */ + +/* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch + * http://www.uclibc.org/lists/busybox/2003-July/008813.html + */ + +//usage:#define tac_trivial_usage +//usage: "[FILE]..." +//usage:#define tac_full_usage "\n\n" +//usage: "Concatenate FILEs and print them in reverse" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +struct lstring { + int size; + char buf[1]; +}; + +int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tac_main(int argc UNUSED_PARAM, char **argv) +{ + char **name; + FILE *f; + struct lstring *line = NULL; + llist_t *list = NULL; + int retval = EXIT_SUCCESS; + +#if ENABLE_DESKTOP +/* tac from coreutils 6.9 supports: + -b, --before + attach the separator before instead of after + -r, --regex + interpret the separator as a regular expression + -s, --separator=STRING + use STRING as the separator instead of newline +We support none, but at least we will complain or handle "--": +*/ + getopt32(argv, ""); + argv += optind; +#else + argv++; +#endif + if (!*argv) + *--argv = (char *)"-"; + /* We will read from last file to first */ + name = argv; + while (*name) + name++; + + do { + int ch, i; + + name--; + f = fopen_or_warn_stdin(*name); + if (f == NULL) { + /* error message is printed by fopen_or_warn_stdin */ + retval = EXIT_FAILURE; + continue; + } + + errno = i = 0; + do { + ch = fgetc(f); + if (ch != EOF) { + if (!(i & 0x7f)) + /* Grow on every 128th char */ + line = xrealloc(line, i + 0x7f + sizeof(int) + 1); + line->buf[i++] = ch; + } + if (ch == '\n' || (ch == EOF && i != 0)) { + line = xrealloc(line, i + sizeof(int)); + line->size = i; + llist_add_to(&list, line); + line = NULL; + i = 0; + } + } while (ch != EOF); + /* fgetc sets errno to ENOENT on EOF, we don't want + * to warn on this non-error! */ + if (errno && errno != ENOENT) { + bb_simple_perror_msg(*name); + retval = EXIT_FAILURE; + } + } while (name != argv); + + while (list) { + line = (struct lstring *)list->data; + xwrite(STDOUT_FILENO, line->buf, line->size); + if (ENABLE_FEATURE_CLEAN_UP) { + free(llist_pop(&list)); + } else { + list = list->link; + } + } + + return retval; +} diff --git a/coreutils/tail.c b/coreutils/tail.c new file mode 100644 index 000000000..454c25936 --- /dev/null +++ b/coreutils/tail.c @@ -0,0 +1,382 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tail implementation for busybox + * + * Copyright (C) 2001 by Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant (need fancy for -c) */ +/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Pretty much rewritten to fix numerous bugs and reduce realloc() calls. + * Bugs fixed (although I may have forgotten one or two... it was pretty bad) + * 1) mixing printf/write without fflush()ing stdout + * 2) no check that any open files are present + * 3) optstring had -q taking an arg + * 4) no error checking on write in some cases, and a warning even then + * 5) q and s interaction bug + * 6) no check for lseek error + * 7) lseek attempted when count==0 even if arg was +0 (from top) + */ + +//usage:#define tail_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define tail_full_usage "\n\n" +//usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n" +//usage: "With more than one FILE, precede each with a filename header.\n" +//usage: "\n -f Print data as file grows" +//usage: IF_FEATURE_FANCY_TAIL( +//usage: "\n -s SECONDS Wait SECONDS between reads with -f" +//usage: ) +//usage: "\n -n N[kbm] Print last N lines" +//usage: IF_FEATURE_FANCY_TAIL( +//usage: "\n -c N[kbm] Print last N bytes" +//usage: "\n -q Never print headers" +//usage: "\n -v Always print headers" +//usage: "\n" +//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." +//usage: "\nIf N starts with a '+', output begins with the Nth item from the start" +//usage: "\nof each file, not from the end." +//usage: ) +//usage: +//usage:#define tail_example_usage +//usage: "$ tail -n 1 /etc/resolv.conf\n" +//usage: "nameserver 10.0.0.1\n" + +#include "libbb.h" + +static const struct suffix_mult tail_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { "", 0 } +}; + +struct globals { + bool from_top; + bool exitcode; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) + +static void tail_xprint_header(const char *fmt, const char *filename) +{ + if (fdprintf(STDOUT_FILENO, fmt, filename) < 0) + bb_perror_nomsg_and_die(); +} + +static ssize_t tail_read(int fd, char *buf, size_t count) +{ + ssize_t r; + off_t current; + struct stat sbuf; + + /* /proc files report zero st_size, don't lseek them. */ + if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) { + current = lseek(fd, 0, SEEK_CUR); + if (sbuf.st_size < current) + xlseek(fd, 0, SEEK_SET); + } + + r = full_read(fd, buf, count); + if (r < 0) { + bb_perror_msg(bb_msg_read_error); + G.exitcode = EXIT_FAILURE; + } + + return r; +} + +#define header_fmt_str "\n==> %s <==\n" + +static unsigned eat_num(const char *p) +{ + if (*p == '-') + p++; + else if (*p == '+') { + p++; + G.from_top = 1; + } + return xatou_sfx(p, tail_suffixes); +} + +int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tail_main(int argc, char **argv) +{ + unsigned count = 10; + unsigned sleep_period = 1; + const char *str_c, *str_n; + + char *tailbuf; + size_t tailbufsize; + unsigned header_threshhold = 1; + unsigned nfiles; + int i, opt; + + int *fds; + const char *fmt; + +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-') + && isdigit(argv[1][1]) + ) { + count = eat_num(argv[1]); + argv++; + argc--; + } +#endif + + /* -s NUM, -F imlies -f */ + IF_FEATURE_FANCY_TAIL(opt_complementary = "s+:Ff";) + opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"), + &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period)); +#define FOLLOW (opt & 0x1) +#define COUNT_BYTES (opt & 0x2) + //if (opt & 0x1) // -f + if (opt & 0x2) count = eat_num(str_c); // -c + if (opt & 0x4) count = eat_num(str_n); // -n +#if ENABLE_FEATURE_FANCY_TAIL + /* q: make it impossible for nfiles to be > header_threshhold */ + if (opt & 0x8) header_threshhold = UINT_MAX; // -q + //if (opt & 0x10) // -s + if (opt & 0x20) header_threshhold = 0; // -v +# define FOLLOW_RETRY (opt & 0x40) +#else +# define FOLLOW_RETRY 0 +#endif + argc -= optind; + argv += optind; + + /* open all the files */ + fds = xmalloc(sizeof(fds[0]) * (argc + 1)); + if (!argv[0]) { + struct stat statbuf; + + if (fstat(STDIN_FILENO, &statbuf) == 0 + && S_ISFIFO(statbuf.st_mode) + ) { + opt &= ~1; /* clear FOLLOW */ + } + argv[0] = (char *) bb_msg_standard_input; + } + nfiles = i = 0; + do { + int fd = open_or_warn_stdin(argv[i]); + if (fd < 0 && !FOLLOW_RETRY) { + G.exitcode = EXIT_FAILURE; + continue; + } + fds[nfiles] = fd; + argv[nfiles++] = argv[i]; + } while (++i < argc); + + if (!nfiles) + bb_error_msg_and_die("no files"); + + /* prepare the buffer */ + tailbufsize = BUFSIZ; + if (!G.from_top && COUNT_BYTES) { + if (tailbufsize < count + BUFSIZ) { + tailbufsize = count + BUFSIZ; + } + } + /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block. + * (In fact, it doesn't need ANY memory). So delay allocation. + */ + tailbuf = NULL; + + /* tail the files */ + + fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */ + i = 0; + do { + char *buf; + int taillen; + int newlines_seen; + unsigned seen; + int nread; + int fd = fds[i]; + + if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) + continue; /* may happen with -E */ + + if (nfiles > header_threshhold) { + tail_xprint_header(fmt, argv[i]); + fmt = header_fmt_str; + } + + if (!G.from_top) { + off_t current = lseek(fd, 0, SEEK_END); + if (current > 0) { + unsigned off; + if (COUNT_BYTES) { + /* Optimizing count-bytes case if the file is seekable. + * Beware of backing up too far. + * Also we exclude files with size 0 (because of /proc/xxx) */ + if (count == 0) + continue; /* showing zero bytes is easy :) */ + current -= count; + if (current < 0) + current = 0; + xlseek(fd, current, SEEK_SET); + bb_copyfd_size(fd, STDOUT_FILENO, count); + continue; + } +#if 1 /* This is technically incorrect for *LONG* strings, but very useful */ + /* Optimizing count-lines case if the file is seekable. + * We assume the lines are <64k. + * (Users complain that tail takes too long + * on multi-gigabyte files) */ + off = (count | 0xf); /* for small counts, be more paranoid */ + if (off > (INT_MAX / (64*1024))) + off = (INT_MAX / (64*1024)); + current -= off * (64*1024); + if (current < 0) + current = 0; + xlseek(fd, current, SEEK_SET); +#endif + } + } + + if (!tailbuf) + tailbuf = xmalloc(tailbufsize); + + buf = tailbuf; + taillen = 0; + /* "We saw 1st line/byte". + * Used only by +N code ("start from Nth", 1-based): */ + seen = 1; + newlines_seen = 0; + while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) { + if (G.from_top) { + int nwrite = nread; + if (seen < count) { + /* We need to skip a few more bytes/lines */ + if (COUNT_BYTES) { + nwrite -= (count - seen); + seen = count; + } else { + char *s = buf; + do { + --nwrite; + if (*s++ == '\n' && ++seen == count) { + break; + } + } while (nwrite); + } + } + if (nwrite > 0) + xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite); + } else if (count) { + if (COUNT_BYTES) { + taillen += nread; + if (taillen > (int)count) { + memmove(tailbuf, tailbuf + taillen - count, count); + taillen = count; + } + } else { + int k = nread; + int newlines_in_buf = 0; + + do { /* count '\n' in last read */ + k--; + if (buf[k] == '\n') { + newlines_in_buf++; + } + } while (k); + + if (newlines_seen + newlines_in_buf < (int)count) { + newlines_seen += newlines_in_buf; + taillen += nread; + } else { + int extra = (buf[nread-1] != '\n'); + char *s; + + k = newlines_seen + newlines_in_buf + extra - count; + s = tailbuf; + while (k) { + if (*s == '\n') { + k--; + } + s++; + } + taillen += nread - (s - tailbuf); + memmove(tailbuf, s, taillen); + newlines_seen = count - extra; + } + if (tailbufsize < (size_t)taillen + BUFSIZ) { + tailbufsize = taillen + BUFSIZ; + tailbuf = xrealloc(tailbuf, tailbufsize); + } + } + buf = tailbuf + taillen; + } + } /* while (tail_read() > 0) */ + if (!G.from_top) { + xwrite(STDOUT_FILENO, tailbuf, taillen); + } + } while (++i < nfiles); + + tailbuf = xrealloc(tailbuf, BUFSIZ); + + fmt = NULL; + + if (FOLLOW) while (1) { + sleep(sleep_period); + + i = 0; + do { + int nread; + const char *filename = argv[i]; + int fd = fds[i]; + + if (FOLLOW_RETRY) { + struct stat sbuf, fsbuf; + + if (fd < 0 + || fstat(fd, &fsbuf) < 0 + || stat(filename, &sbuf) < 0 + || fsbuf.st_dev != sbuf.st_dev + || fsbuf.st_ino != sbuf.st_ino + ) { + int new_fd; + + if (fd >= 0) + close(fd); + new_fd = open(filename, O_RDONLY); + if (new_fd >= 0) { + bb_error_msg("%s has %s; following end of new file", + filename, (fd < 0) ? "appeared" : "been replaced" + ); + } else if (fd >= 0) { + bb_perror_msg("%s has become inaccessible", filename); + } + fds[i] = fd = new_fd; + } + } + if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) + continue; + if (nfiles > header_threshhold) { + fmt = header_fmt_str; + } + while ((nread = tail_read(fd, tailbuf, BUFSIZ)) > 0) { + if (fmt) { + tail_xprint_header(fmt, filename); + fmt = NULL; + } + xwrite(STDOUT_FILENO, tailbuf, nread); + } + } while (++i < nfiles); + } /* while (1) */ + + if (ENABLE_FEATURE_CLEAN_UP) { + free(fds); + free(tailbuf); + } + return G.exitcode; +} diff --git a/coreutils/tee.c b/coreutils/tee.c new file mode 100644 index 000000000..48cc0508f --- /dev/null +++ b/coreutils/tee.c @@ -0,0 +1,118 @@ +/* vi: set sw=4 ts=4: */ +/* + * tee implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */ + +//usage:#define tee_trivial_usage +//usage: "[-ai] [FILE]..." +//usage:#define tee_full_usage "\n\n" +//usage: "Copy stdin to each FILE, and also to stdout\n" +//usage: "\n -a Append to the given FILEs, don't overwrite" +//usage: "\n -i Ignore interrupt signals (SIGINT)" +//usage: +//usage:#define tee_example_usage +//usage: "$ echo \"Hello\" | tee /tmp/foo\n" +//usage: "$ cat /tmp/foo\n" +//usage: "Hello\n" + +#include "libbb.h" + +int tee_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tee_main(int argc, char **argv) +{ + const char *mode = "w\0a"; + FILE **files; + FILE **fp; + char **names; + char **np; + char retval; +//TODO: make unconditional +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + ssize_t c; +# define buf bb_common_bufsiz1 +#else + int c; +#endif + retval = getopt32(argv, "ia"); /* 'a' must be 2nd */ + argc -= optind; + argv += optind; + + mode += (retval & 2); /* Since 'a' is the 2nd option... */ + + if (retval & 1) { + signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */ + } + retval = EXIT_SUCCESS; + /* gnu tee ignores SIGPIPE in case one of the output files is a pipe + * that doesn't consume all its input. Good idea... */ + signal(SIGPIPE, SIG_IGN); + + /* Allocate an array of FILE *'s, with one extra for a sentinel. */ + fp = files = xzalloc(sizeof(FILE *) * (argc + 2)); + np = names = argv - 1; + + files[0] = stdout; + goto GOT_NEW_FILE; + do { + *fp = stdout; + if (NOT_LONE_DASH(*argv)) { + *fp = fopen_or_warn(*argv, mode); + if (*fp == NULL) { + retval = EXIT_FAILURE; + argv++; + continue; + } + } + *np = *argv++; + GOT_NEW_FILE: + setbuf(*fp, NULL); /* tee must not buffer output. */ + fp++; + np++; + } while (*argv); + /* names[0] will be filled later */ + +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) { + fp = files; + do + fwrite(buf, 1, c, *fp); + while (*++fp); + } + if (c < 0) { /* Make sure read errors are signaled. */ + retval = EXIT_FAILURE; + } +#else + setvbuf(stdout, NULL, _IONBF, 0); + while ((c = getchar()) != EOF) { + fp = files; + do + putc(c, *fp); + while (*++fp); + } +#endif + + /* Now we need to check for i/o errors on stdin and the various + * output files. Since we know that the first entry in the output + * file table is stdout, we can save one "if ferror" test by + * setting the first entry to stdin and checking stdout error + * status with fflush_stdout_and_exit()... although fflush()ing + * is unnecessary here. */ + np = names; + fp = files; + names[0] = (char *) bb_msg_standard_input; + files[0] = stdin; + do { /* Now check for input and output errors. */ + /* Checking ferror should be sufficient, but we may want to fclose. + * If we do, remember not to close stdin! */ + die_if_ferror(*fp++, *np++); + } while (*fp); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/test.c b/coreutils/test.c new file mode 100644 index 000000000..1f5398ad8 --- /dev/null +++ b/coreutils/test.c @@ -0,0 +1,915 @@ +/* vi: set sw=4 ts=4: */ +/* + * test implementation for busybox + * + * Copyright (c) by a whole pile of folks: + * + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * modified by Herbert Xu to be used as built-in in ash. + * modified by Erik Andersen to be used + * in busybox. + * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty). + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice states: + * "This program is in the Public Domain." + */ + +//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o + +//config:config TEST +//config: bool "test" +//config: default y +//config: help +//config: test is used to check file types and compare values, +//config: returning an appropriate exit code. The bash shell +//config: has test built in, ash can build it in optionally. +//config: +//config:config FEATURE_TEST_64 +//config: bool "Extend test to 64 bit" +//config: default y +//config: depends on TEST || ASH_BUILTIN_TEST || HUSH +//config: help +//config: Enable 64-bit support in test. + +/* "test --help" does not print help (POSIX compat), only "[ --help" does. + * We display " EXPRESSION ]" here (not " EXPRESSION") + * Unfortunately, it screws up generated BusyBox.html. TODO. */ +//usage:#define test_trivial_usage +//usage: "EXPRESSION ]" +//usage:#define test_full_usage "\n\n" +//usage: "Check file types, compare values etc. Return a 0/1 exit code\n" +//usage: "depending on logical value of EXPRESSION" +//usage: +//usage:#define test_example_usage +//usage: "$ test 1 -eq 2\n" +//usage: "$ echo $?\n" +//usage: "1\n" +//usage: "$ test 1 -eq 1\n" +//usage: "$ echo $?\n" +//usage: "0\n" +//usage: "$ [ -d /etc ]\n" +//usage: "$ echo $?\n" +//usage: "0\n" +//usage: "$ [ -d /junk ]\n" +//usage: "$ echo $?\n" +//usage: "1\n" + +#include "libbb.h" +#include + +/* This is a NOFORK applet. Be very careful! */ + +/* test_main() is called from shells, and we need to be extra careful here. + * This is true regardless of PREFER_APPLETS and SH_STANDALONE + * state. */ + +/* test(1) accepts the following grammar: + oexpr ::= aexpr | aexpr "-o" oexpr ; + aexpr ::= nexpr | nexpr "-a" aexpr ; + nexpr ::= primary | "!" primary + primary ::= unary-operator operand + | operand binary-operator operand + | operand + | "(" oexpr ")" + ; + unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| + "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; + + binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + "-nt"|"-ot"|"-ef"; + operand ::= +*/ + +/* TODO: handle [[ expr ]] bashism bash-compatibly. + * [[ ]] is meant to be a "better [ ]", with less weird syntax + * and without the risk of variables and quoted strings misinterpreted + * as operators. + * This will require support from shells - we need to know quote status + * of each parameter (see below). + * + * Word splitting and pathname expansion should NOT be performed: + * # a="a b"; [[ $a = "a b" ]] && echo YES + * YES + * # [[ /bin/m* ]] && echo YES + * YES + * + * =~ should do regexp match + * = and == should do pattern match against right side: + * # [[ *a* == bab ]] && echo YES + * # [[ bab == *a* ]] && echo YES + * YES + * != does the negated == (i.e., also with pattern matching). + * Pattern matching is quotation-sensitive: + * # [[ bab == "b"a* ]] && echo YES + * YES + * # [[ bab == b"a*" ]] && echo YES + * + * Conditional operators such as -f must be unquoted literals to be recognized: + * # [[ -e /bin ]] && echo YES + * YES + * # [[ '-e' /bin ]] && echo YES + * bash: conditional binary operator expected... + * # A='-e'; [[ $A /bin ]] && echo YES + * bash: conditional binary operator expected... + * + * || and && should work as -o and -a work in [ ] + * -a and -o aren't recognized (&& and || are to be used instead) + * ( and ) do not need to be quoted unlike in [ ]: + * # [[ ( abc ) && '' ]] && echo YES + * # [[ ( abc ) || '' ]] && echo YES + * YES + * # [[ ( abc ) -o '' ]] && echo YES + * bash: syntax error in conditional expression... + * + * Apart from the above, [[ expr ]] should work as [ expr ] + */ + +#define TEST_DEBUG 0 + +enum token { + EOI, + + FILRD, /* file access */ + FILWR, + FILEX, + + FILEXIST, + + FILREG, /* file type */ + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + + FILSYM, + FILGZ, + FILTT, + + FILSUID, /* file bit */ + FILSGID, + FILSTCK, + + FILNT, /* file ops */ + FILOT, + FILEQ, + + FILUID, + FILGID, + + STREZ, /* str ops */ + STRNZ, + STREQ, + STRNE, + STRLT, + STRGT, + + INTEQ, /* int ops */ + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + + UNOT, + BAND, + BOR, + LPAREN, + RPAREN, + OPERAND +}; +#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) +#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) +#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) +#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) +#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) +#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) + +#if TEST_DEBUG +int depth; +#define nest_msg(...) do { \ + depth++; \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) +#define unnest_msg(...) do { \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ + depth--; \ +} while (0) +#define dbg_msg(...) do { \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) +#define unnest_msg_and_return(expr, ...) do { \ + number_t __res = (expr); \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__, res); \ + depth--; \ + return __res; \ +} while (0) +static const char *const TOKSTR[] = { + "EOI", + "FILRD", + "FILWR", + "FILEX", + "FILEXIST", + "FILREG", + "FILDIR", + "FILCDEV", + "FILBDEV", + "FILFIFO", + "FILSOCK", + "FILSYM", + "FILGZ", + "FILTT", + "FILSUID", + "FILSGID", + "FILSTCK", + "FILNT", + "FILOT", + "FILEQ", + "FILUID", + "FILGID", + "STREZ", + "STRNZ", + "STREQ", + "STRNE", + "STRLT", + "STRGT", + "INTEQ", + "INTNE", + "INTGE", + "INTGT", + "INTLE", + "INTLT", + "UNOT", + "BAND", + "BOR", + "LPAREN", + "RPAREN", + "OPERAND" +}; +#else +#define nest_msg(...) ((void)0) +#define unnest_msg(...) ((void)0) +#define dbg_msg(...) ((void)0) +#define unnest_msg_and_return(expr, ...) return expr +#endif + +enum { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +struct operator_t { + unsigned char op_num, op_type; +}; + +static const struct operator_t ops_table[] = { + { /* "-r" */ FILRD , UNOP }, + { /* "-w" */ FILWR , UNOP }, + { /* "-x" */ FILEX , UNOP }, + { /* "-e" */ FILEXIST, UNOP }, + { /* "-f" */ FILREG , UNOP }, + { /* "-d" */ FILDIR , UNOP }, + { /* "-c" */ FILCDEV , UNOP }, + { /* "-b" */ FILBDEV , UNOP }, + { /* "-p" */ FILFIFO , UNOP }, + { /* "-u" */ FILSUID , UNOP }, + { /* "-g" */ FILSGID , UNOP }, + { /* "-k" */ FILSTCK , UNOP }, + { /* "-s" */ FILGZ , UNOP }, + { /* "-t" */ FILTT , UNOP }, + { /* "-z" */ STREZ , UNOP }, + { /* "-n" */ STRNZ , UNOP }, + { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */ + + { /* "-O" */ FILUID , UNOP }, + { /* "-G" */ FILGID , UNOP }, + { /* "-L" */ FILSYM , UNOP }, + { /* "-S" */ FILSOCK , UNOP }, + { /* "=" */ STREQ , BINOP }, + { /* "==" */ STREQ , BINOP }, + { /* "!=" */ STRNE , BINOP }, + { /* "<" */ STRLT , BINOP }, + { /* ">" */ STRGT , BINOP }, + { /* "-eq"*/ INTEQ , BINOP }, + { /* "-ne"*/ INTNE , BINOP }, + { /* "-ge"*/ INTGE , BINOP }, + { /* "-gt"*/ INTGT , BINOP }, + { /* "-le"*/ INTLE , BINOP }, + { /* "-lt"*/ INTLT , BINOP }, + { /* "-nt"*/ FILNT , BINOP }, + { /* "-ot"*/ FILOT , BINOP }, + { /* "-ef"*/ FILEQ , BINOP }, + { /* "!" */ UNOT , BUNOP }, + { /* "-a" */ BAND , BBINOP }, + { /* "-o" */ BOR , BBINOP }, + { /* "(" */ LPAREN , PAREN }, + { /* ")" */ RPAREN , PAREN }, +}; +/* Please keep these two tables in sync */ +static const char ops_texts[] ALIGN1 = + "-r" "\0" + "-w" "\0" + "-x" "\0" + "-e" "\0" + "-f" "\0" + "-d" "\0" + "-c" "\0" + "-b" "\0" + "-p" "\0" + "-u" "\0" + "-g" "\0" + "-k" "\0" + "-s" "\0" + "-t" "\0" + "-z" "\0" + "-n" "\0" + "-h" "\0" + + "-O" "\0" + "-G" "\0" + "-L" "\0" + "-S" "\0" + "=" "\0" + "==" "\0" + "!=" "\0" + "<" "\0" + ">" "\0" + "-eq" "\0" + "-ne" "\0" + "-ge" "\0" + "-gt" "\0" + "-le" "\0" + "-lt" "\0" + "-nt" "\0" + "-ot" "\0" + "-ef" "\0" + "!" "\0" + "-a" "\0" + "-o" "\0" + "(" "\0" + ")" "\0" +; + + +#if ENABLE_FEATURE_TEST_64 +typedef int64_t number_t; +#else +typedef int number_t; +#endif + + +/* We try to minimize both static and stack usage. */ +struct test_statics { + char **args; + /* set only by check_operator(), either to bogus struct + * or points to matching operator_t struct. Never NULL. */ + const struct operator_t *last_operator; + gid_t *group_array; + int ngroups; + jmp_buf leaving; +}; + +/* See test_ptr_hack.c */ +extern struct test_statics *const test_ptr_to_statics; + +#define S (*test_ptr_to_statics) +#define args (S.args ) +#define last_operator (S.last_operator) +#define group_array (S.group_array ) +#define ngroups (S.ngroups ) +#define leaving (S.leaving ) + +#define INIT_S() do { \ + (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \ + barrier(); \ +} while (0) +#define DEINIT_S() do { \ + free(test_ptr_to_statics); \ +} while (0) + +static number_t primary(enum token n); + +static void syntax(const char *op, const char *msg) NORETURN; +static void syntax(const char *op, const char *msg) +{ + if (op && *op) { + bb_error_msg("%s: %s", op, msg); + } else { + bb_error_msg("%s: %s"+4, msg); + } + longjmp(leaving, 2); +} + +/* atoi with error detection */ +//XXX: FIXME: duplicate of existing libbb function? +static number_t getn(const char *s) +{ + char *p; +#if ENABLE_FEATURE_TEST_64 + long long r; +#else + long r; +#endif + + errno = 0; +#if ENABLE_FEATURE_TEST_64 + r = strtoll(s, &p, 10); +#else + r = strtol(s, &p, 10); +#endif + + if (errno != 0) + syntax(s, "out of range"); + + if (p == s || *(skip_whitespace(p)) != '\0') + syntax(s, "bad number"); + + return r; +} + +/* UNUSED +static int newerf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); +} + +static int olderf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); +} + +static int equalf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && + b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); +} +*/ + + +static enum token check_operator(const char *s) +{ + static const struct operator_t no_op = { + .op_num = -1, + .op_type = -1 + }; + int n; + + last_operator = &no_op; + if (s == NULL) + return EOI; + n = index_in_strings(ops_texts, s); + if (n < 0) + return OPERAND; + last_operator = &ops_table[n]; + return ops_table[n].op_num; +} + + +static int binop(void) +{ + const char *opnd1, *opnd2; + const struct operator_t *op; + number_t val1, val2; + + opnd1 = *args; + check_operator(*++args); + op = last_operator; + + opnd2 = *++args; + if (opnd2 == NULL) + syntax(args[-1], "argument expected"); + + if (is_int_op(op->op_num)) { + val1 = getn(opnd1); + val2 = getn(opnd2); + if (op->op_num == INTEQ) + return val1 == val2; + if (op->op_num == INTNE) + return val1 != val2; + if (op->op_num == INTGE) + return val1 >= val2; + if (op->op_num == INTGT) + return val1 > val2; + if (op->op_num == INTLE) + return val1 <= val2; + /*if (op->op_num == INTLT)*/ + return val1 < val2; + } + if (is_str_op(op->op_num)) { + val1 = strcmp(opnd1, opnd2); + if (op->op_num == STREQ) + return val1 == 0; + if (op->op_num == STRNE) + return val1 != 0; + if (op->op_num == STRLT) + return val1 < 0; + /*if (op->op_num == STRGT)*/ + return val1 > 0; + } + /* We are sure that these three are by now the only binops we didn't check + * yet, so we do not check if the class is correct: + */ +/* if (is_file_op(op->op_num)) */ + { + struct stat b1, b2; + + if (stat(opnd1, &b1) || stat(opnd2, &b2)) + return 0; /* false, since at least one stat failed */ + if (op->op_num == FILNT) + return b1.st_mtime > b2.st_mtime; + if (op->op_num == FILOT) + return b1.st_mtime < b2.st_mtime; + /*if (op->op_num == FILEQ)*/ + return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; + } + /*return 1; - NOTREACHED */ +} + + +static void initialize_group_array(void) +{ + int n; + + /* getgroups may be expensive, try to use it only once */ + ngroups = 32; + do { + /* FIXME: ash tries so hard to not die on OOM, + * and we spoil it with just one xrealloc here */ + /* We realloc, because test_main can be entered repeatedly by shell. + * Testcase (ash): 'while true; do test -x some_file; done' + * and watch top. (some_file must have owner != you) */ + n = ngroups; + group_array = xrealloc(group_array, n * sizeof(gid_t)); + ngroups = getgroups(n, group_array); + } while (ngroups > n); +} + + +/* Return non-zero if GID is one that we have in our groups list. */ +//XXX: FIXME: duplicate of existing libbb function? +// see toplevel TODO file: +// possible code duplication ingroup() and is_a_group_member() +static int is_a_group_member(gid_t gid) +{ + int i; + + /* Short-circuit if possible, maybe saving a call to getgroups(). */ + if (gid == getgid() || gid == getegid()) + return 1; + + if (ngroups == 0) + initialize_group_array(); + + /* Search through the list looking for GID. */ + for (i = 0; i < ngroups; i++) + if (gid == group_array[i]) + return 1; + + return 0; +} + + +/* Do the same thing access(2) does, but use the effective uid and gid, + and don't make the mistake of telling root that any file is + executable. */ +static int test_eaccess(char *path, int mode) +{ + struct stat st; + unsigned int euid = geteuid(); + + if (stat(path, &st) < 0) + return -1; + + if (euid == 0) { + /* Root can read or write any file. */ + if (mode != X_OK) + return 0; + + /* Root can execute any file that has any one of the execute + bits set. */ + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return 0; + } + + if (st.st_uid == euid) /* owner */ + mode <<= 6; + else if (is_a_group_member(st.st_gid)) + mode <<= 3; + + if (st.st_mode & mode) + return 0; + + return -1; +} + + +static int filstat(char *nm, enum token mode) +{ + struct stat s; + unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */ + + if (mode == FILSYM) { +#ifdef S_IFLNK + if (lstat(nm, &s) == 0) { + i = S_IFLNK; + goto filetype; + } +#endif + return 0; + } + + if (stat(nm, &s) != 0) + return 0; + if (mode == FILEXIST) + return 1; + if (is_file_access(mode)) { + if (mode == FILRD) + i = R_OK; + if (mode == FILWR) + i = W_OK; + if (mode == FILEX) + i = X_OK; + return test_eaccess(nm, i) == 0; + } + if (is_file_type(mode)) { + if (mode == FILREG) + i = S_IFREG; + if (mode == FILDIR) + i = S_IFDIR; + if (mode == FILCDEV) + i = S_IFCHR; + if (mode == FILBDEV) + i = S_IFBLK; + if (mode == FILFIFO) { +#ifdef S_IFIFO + i = S_IFIFO; +#else + return 0; +#endif + } + if (mode == FILSOCK) { +#ifdef S_IFSOCK + i = S_IFSOCK; +#else + return 0; +#endif + } + filetype: + return ((s.st_mode & S_IFMT) == i); + } + if (is_file_bit(mode)) { + if (mode == FILSUID) + i = S_ISUID; + if (mode == FILSGID) + i = S_ISGID; + if (mode == FILSTCK) + i = S_ISVTX; + return ((s.st_mode & i) != 0); + } + if (mode == FILGZ) + return s.st_size > 0L; + if (mode == FILUID) + return s.st_uid == geteuid(); + if (mode == FILGID) + return s.st_gid == getegid(); + return 1; /* NOTREACHED */ +} + + +static number_t nexpr(enum token n) +{ + number_t res; + + nest_msg(">nexpr(%s)\n", TOKSTR[n]); + if (n == UNOT) { + n = check_operator(*++args); + if (n == EOI) { + /* special case: [ ! ], [ a -a ! ] are valid */ + /* IOW, "! ARG" may miss ARG */ + unnest_msg("aexpr(%s)\n", TOKSTR[n]); + res = nexpr(n); + dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]); + if (check_operator(*++args) == BAND) { + dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]); + res = aexpr(check_operator(*++args)) && res; + unnest_msg("oexpr(%s)\n", TOKSTR[n]); + res = aexpr(n); + dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]); + if (check_operator(*++args) == BOR) { + dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]); + res = oexpr(check_operator(*++args)) || res; + unnest_msg("primary(%s)\n", TOKSTR[n]); + if (n == EOI) { + syntax(NULL, "argument expected"); + } + if (n == LPAREN) { + res = oexpr(check_operator(*++args)); + if (check_operator(*++args) != RPAREN) + syntax(NULL, "closing paren expected"); + unnest_msg("op_type == BINOP) + unnest_msg_and_return(binop(), "op_type == UNOP) { + /* unary expression */ + if (args[1] == NULL) +// syntax(args0_op->op_text, "argument expected"); + goto check_emptiness; + args++; + if (n == STREZ) + unnest_msg_and_return(args[0][0] == '\0', "op_type == BINOP) { + /* args[2] is known to be NULL, isn't it bound to fail? */ + unnest_msg_and_return(binop(), "op_type == BINOP) { + /* "test [!] arg1 arg2" */ + args = argv; + res = (binop() == 0); + goto ret; + } + } + + /* Some complex expression. Undo '!' removal */ + if (negate) { + negate = 0; + //argc++; + argv--; + } +#endif + args = argv; + res = !oexpr(check_operator(*args)); + + if (*args != NULL && *++args != NULL) { + /* Examples: + * test 3 -lt 5 6 + * test -t 1 2 + */ + bb_error_msg("%s: unknown operand", *args); + res = 2; + } + ret: + DEINIT_S(); +// return negate ? !res : res; + return res; +} diff --git a/coreutils/test_ptr_hack.c b/coreutils/test_ptr_hack.c new file mode 100644 index 000000000..5ba9dcc68 --- /dev/null +++ b/coreutils/test_ptr_hack.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +struct test_statics; + +#ifndef GCC_COMBINE + +/* We cheat here. It is declared as const ptr in libbb.h, + * but here we make it live in R/W memory */ +struct test_statics *test_ptr_to_statics; + +#else + +/* gcc -combine will see through and complain */ +/* Using alternative method which is more likely to break + * on weird architectures, compilers, linkers and so on */ +struct test_statics *const test_ptr_to_statics __attribute__ ((section (".data"))); + +#endif diff --git a/coreutils/touch.c b/coreutils/touch.c new file mode 100644 index 000000000..0f980fd7b --- /dev/null +++ b/coreutils/touch.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Previous version called open() and then utime(). While this will be + * be necessary to implement -r and -t, it currently only makes things bigger. + * Also, exiting on a failure was a bug. All args should be processed. + */ + +#include "libbb.h" + +//config:config TOUCH +//config: bool "touch" +//config: default y +//config: help +//config: touch is used to create or change the access and/or +//config: modification timestamp of specified files. + +//applet:IF_TOUCH(APPLET_NOFORK(touch, touch, BB_DIR_BIN, BB_SUID_DROP, touch)) + +//kbuild:lib-$(CONFIG_TOUCH) += touch.o + +//usage:#define touch_trivial_usage +//usage: "[-c]" IF_DESKTOP(" [-d DATE] [-r FILE]") " FILE [FILE]..." +//usage:#define touch_full_usage "\n\n" +//usage: "Update the last-modified date on the given FILE[s]\n" +//usage: "\n -c Don't create files" +//usage: IF_DESKTOP( +//usage: "\n -d DT Date/time to use" +//usage: "\n -r FILE Use FILE's date/time" +//usage: ) +//usage: +//usage:#define touch_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "/bin/ls: /tmp/foo: No such file or directory\n" +//usage: "$ touch /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n" + +/* This is a NOFORK applet. Be very careful! */ + +/* coreutils implements: + * -a change only the access time + * -c, --no-create + * do not create any files + * -d, --date=STRING + * parse STRING and use it instead of current time + * -f (ignored, BSD compat) + * -m change only the modification time + * -r, --reference=FILE + * use this file's times instead of current time + * -t STAMP + * use [[CC]YY]MMDDhhmm[.ss] instead of current time + * --time=WORD + * change the specified time: WORD is access, atime, or use + */ + +int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int touch_main(int argc UNUSED_PARAM, char **argv) +{ + int fd; + int status = EXIT_SUCCESS; + int opts; +#if ENABLE_DESKTOP +# if ENABLE_LONG_OPTS + static const char touch_longopts[] ALIGN1 = + /* name, has_arg, val */ + "no-create\0" No_argument "c" + "reference\0" Required_argument "r" + "date\0" Required_argument "d" + ; +# endif + char *reference_file = NULL; + char *date_str = NULL; + struct timeval timebuf[2]; + timebuf[1].tv_usec = timebuf[0].tv_usec = 0; +#else +# define reference_file NULL +# define date_str NULL +# define timebuf ((struct timeval*)NULL) +#endif + +#if ENABLE_DESKTOP && ENABLE_LONG_OPTS + applet_long_options = touch_longopts; +#endif + /* -d and -t both set time. In coreutils, + * accepted data format differs a bit between -d and -t. + * We accept the same formats for both */ + opts = getopt32(argv, "c" IF_DESKTOP("r:d:t:") + /*ignored:*/ "fma" + IF_DESKTOP(, &reference_file) + IF_DESKTOP(, &date_str) + IF_DESKTOP(, &date_str) + ); + + opts &= 1; /* only -c bit is left */ + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + if (reference_file) { + struct stat stbuf; + xstat(reference_file, &stbuf); + timebuf[1].tv_sec = timebuf[0].tv_sec = stbuf.st_mtime; + } + + if (date_str) { + struct tm tm_time; + time_t t; + + //memset(&tm_time, 0, sizeof(tm_time)); + /* Better than memset: makes "HH:MM" dates meaningful */ + time(&t); + localtime_r(&t, &tm_time); + parse_datestr(date_str, &tm_time); + + /* Correct any day of week and day of year etc. fields */ + tm_time.tm_isdst = -1; /* Be sure to recheck dst */ + t = validate_tm_time(date_str, &tm_time); + + timebuf[1].tv_sec = timebuf[0].tv_sec = t; + } + + do { + if (utimes(*argv, (reference_file || date_str) ? timebuf : NULL) != 0) { + if (errno == ENOENT) { /* no such file */ + if (opts) { /* creation is disabled, so ignore */ + continue; + } + /* Try to create the file */ + fd = open(*argv, O_RDWR | O_CREAT, 0666); + if (fd >= 0) { + xclose(fd); + if (reference_file || date_str) + utimes(*argv, timebuf); + continue; + } + } + status = EXIT_FAILURE; + bb_simple_perror_msg(*argv); + } + } while (*++argv); + + return status; +} diff --git a/coreutils/tr.c b/coreutils/tr.c new file mode 100644 index 000000000..e67948a36 --- /dev/null +++ b/coreutils/tr.c @@ -0,0 +1,346 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tr implementation for busybox + * + ** Copyright (c) 1987,1997, Prentice Hall All rights reserved. + * + * The name of Prentice Hall may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * Copyright (c) Michiel Huisjes + * + * This version of tr is adapted from Minix tr and was modified + * by Erik Andersen to be used in busybox. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html + * TODO: graph, print + */ + +//kbuild:lib-$(CONFIG_TR) += tr.o + +//config:config TR +//config: bool "tr" +//config: default y +//config: help +//config: tr is used to squeeze, and/or delete characters from standard +//config: input, writing to standard output. +//config: +//config:config FEATURE_TR_CLASSES +//config: bool "Enable character classes (such as [:upper:])" +//config: default y +//config: depends on TR +//config: help +//config: Enable character classes, enabling commands such as: +//config: tr [:upper:] [:lower:] to convert input into lowercase. +//config: +//config:config FEATURE_TR_EQUIV +//config: bool "Enable equivalence classes" +//config: default y +//config: depends on TR +//config: help +//config: Enable equivalence classes, which essentially add the enclosed +//config: character to the current set. For instance, tr [=a=] xyz would +//config: replace all instances of 'a' with 'xyz'. This option is mainly +//config: useful for cases when no other way of expressing a character +//config: is possible. + +//usage:#define tr_trivial_usage +//usage: "[-cds] STRING1 [STRING2]" +//usage:#define tr_full_usage "\n\n" +//usage: "Translate, squeeze, or delete characters from stdin, writing to stdout\n" +//usage: "\n -c Take complement of STRING1" +//usage: "\n -d Delete input characters coded STRING1" +//usage: "\n -s Squeeze multiple output characters of STRING2 into one character" +//usage: +//usage:#define tr_example_usage +//usage: "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n" +//usage: "hello world\n" + +#include "libbb.h" + +enum { + ASCII = 256, + /* string buffer needs to be at least as big as the whole "alphabet". + * BUFSIZ == ASCII is ok, but we will realloc in expand + * even for smallest patterns, let's avoid that by using *2: + */ + TR_BUFSIZ = (BUFSIZ > ASCII*2) ? BUFSIZ : ASCII*2, +}; + +static void map(char *pvector, + char *string1, unsigned string1_len, + char *string2, unsigned string2_len) +{ + char last = '0'; + unsigned i, j; + + for (j = 0, i = 0; i < string1_len; i++) { + if (string2_len <= j) + pvector[(unsigned char)(string1[i])] = last; + else + pvector[(unsigned char)(string1[i])] = last = string2[j++]; + } +} + +/* supported constructs: + * Ranges, e.g., 0-9 ==> 0123456789 + * Escapes, e.g., \a ==> Control-G + * Character classes, e.g. [:upper:] ==> A...Z + * Equiv classess, e.g. [=A=] ==> A (hmmmmmmm?) + * not supported: + * \ooo-\ooo - octal ranges + * [x*N] - repeat char x N times + * [x*] - repeat char x until it fills STRING2: + * # echo qwe123 | /usr/bin/tr 123456789 '[d]' + * qwe[d] + * # echo qwe123 | /usr/bin/tr 123456789 '[d*]' + * qweddd + */ +static unsigned expand(const char *arg, char **buffer_p) +{ + char *buffer = *buffer_p; + unsigned pos = 0; + unsigned size = TR_BUFSIZ; + unsigned i; /* can't be unsigned char: must be able to hold 256 */ + unsigned char ac; + + while (*arg) { + if (pos + ASCII > size) { + size += ASCII; + *buffer_p = buffer = xrealloc(buffer, size); + } + if (*arg == '\\') { + arg++; + buffer[pos++] = bb_process_escape_sequence(&arg); + continue; + } + if (arg[1] == '-') { /* "0-9..." */ + ac = arg[2]; + if (ac == '\0') { /* "0-": copy verbatim */ + buffer[pos++] = *arg++; /* copy '0' */ + continue; /* next iter will copy '-' and stop */ + } + i = (unsigned char) *arg; + while (i <= ac) /* ok: i is unsigned _int_ */ + buffer[pos++] = i++; + arg += 3; /* skip 0-9 */ + continue; + } + if ((ENABLE_FEATURE_TR_CLASSES || ENABLE_FEATURE_TR_EQUIV) + && *arg == '[' + ) { + arg++; + i = (unsigned char) *arg++; + /* "[xyz...". i=x, arg points to y */ + if (ENABLE_FEATURE_TR_CLASSES && i == ':') { /* [:class:] */ +#define CLO ":]\0" + static const char classes[] ALIGN1 = + "alpha"CLO "alnum"CLO "digit"CLO + "lower"CLO "upper"CLO "space"CLO + "blank"CLO "punct"CLO "cntrl"CLO + "xdigit"CLO; + enum { + CLASS_invalid = 0, /* we increment the retval */ + CLASS_alpha = 1, + CLASS_alnum = 2, + CLASS_digit = 3, + CLASS_lower = 4, + CLASS_upper = 5, + CLASS_space = 6, + CLASS_blank = 7, + CLASS_punct = 8, + CLASS_cntrl = 9, + CLASS_xdigit = 10, + //CLASS_graph = 11, + //CLASS_print = 12, + }; + smalluint j; + char *tmp; + + /* xdigit needs 8, not 7 */ + i = 7 + (arg[0] == 'x'); + tmp = xstrndup(arg, i); + j = index_in_strings(classes, tmp) + 1; + free(tmp); + + if (j == CLASS_invalid) + goto skip_bracket; + + arg += i; + if (j == CLASS_alnum || j == CLASS_digit || j == CLASS_xdigit) { + for (i = '0'; i <= '9'; i++) + buffer[pos++] = i; + } + if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_upper) { + for (i = 'A'; i <= 'Z'; i++) + buffer[pos++] = i; + } + if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_lower) { + for (i = 'a'; i <= 'z'; i++) + buffer[pos++] = i; + } + if (j == CLASS_space || j == CLASS_blank) { + buffer[pos++] = '\t'; + if (j == CLASS_space) { + buffer[pos++] = '\n'; + buffer[pos++] = '\v'; + buffer[pos++] = '\f'; + buffer[pos++] = '\r'; + } + buffer[pos++] = ' '; + } + if (j == CLASS_punct || j == CLASS_cntrl) { + for (i = '\0'; i < ASCII; i++) { + if ((j == CLASS_punct && isprint_asciionly(i) && !isalnum(i) && !isspace(i)) + || (j == CLASS_cntrl && iscntrl(i)) + ) { + buffer[pos++] = i; + } + } + } + if (j == CLASS_xdigit) { + for (i = 'A'; i <= 'F'; i++) { + buffer[pos + 6] = i | 0x20; + buffer[pos++] = i; + } + pos += 6; + } + continue; + } + /* "[xyz...", i=x, arg points to y */ + if (ENABLE_FEATURE_TR_EQUIV && i == '=') { /* [=CHAR=] */ + buffer[pos++] = *arg; /* copy CHAR */ + if (!arg[0] || arg[1] != '=' || arg[2] != ']') + bb_show_usage(); + arg += 3; /* skip CHAR=] */ + continue; + } + /* The rest of "[xyz..." cases is treated as normal + * string, "[" has no special meaning here: + * tr "[a-z]" "[A-Z]" can be written as tr "a-z" "A-Z", + * also try tr "[a-z]" "_A-Z+" and you'll see that + * [] is not special here. + */ + skip_bracket: + arg -= 2; /* points to "[" in "[xyz..." */ + } + buffer[pos++] = *arg++; + } + return pos; +} + +/* NB: buffer is guaranteed to be at least TR_BUFSIZE + * (which is >= ASCII) big. + */ +static int complement(char *buffer, int buffer_len) +{ + int len; + char conv[ASCII]; + unsigned char ch; + + len = 0; + ch = '\0'; + while (1) { + if (memchr(buffer, ch, buffer_len) == NULL) + conv[len++] = ch; + if (++ch == '\0') + break; + } + memcpy(buffer, conv, len); + return len; +} + +int tr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tr_main(int argc UNUSED_PARAM, char **argv) +{ + int i; + smalluint opts; + ssize_t read_chars; + size_t in_index, out_index; + unsigned last = UCHAR_MAX + 1; /* not equal to any char */ + unsigned char coded, c; + char *str1 = xmalloc(TR_BUFSIZ); + char *str2 = xmalloc(TR_BUFSIZ); + int str2_length; + int str1_length; + char *vector = xzalloc(ASCII * 3); + char *invec = vector + ASCII; + char *outvec = vector + ASCII * 2; + +#define TR_OPT_complement (3 << 0) +#define TR_OPT_delete (1 << 2) +#define TR_OPT_squeeze_reps (1 << 3) + + for (i = 0; i < ASCII; i++) { + vector[i] = i; + /*invec[i] = outvec[i] = FALSE; - done by xzalloc */ + } + + /* -C/-c difference is that -C complements "characters", + * and -c complements "values" (binary bytes I guess). + * In POSIX locale, these are the same. + */ + + opt_complementary = "-1"; + opts = getopt32(argv, "+Ccds"); /* '+': stop at first non-option */ + argv += optind; + + str1_length = expand(*argv++, &str1); + str2_length = 0; + if (opts & TR_OPT_complement) + str1_length = complement(str1, str1_length); + if (*argv) { + if (argv[0][0] == '\0') + bb_error_msg_and_die("STRING2 cannot be empty"); + str2_length = expand(*argv, &str2); + map(vector, str1, str1_length, + str2, str2_length); + } + for (i = 0; i < str1_length; i++) + invec[(unsigned char)(str1[i])] = TRUE; + for (i = 0; i < str2_length; i++) + outvec[(unsigned char)(str2[i])] = TRUE; + + goto start_from; + + /* In this loop, str1 space is reused as input buffer, + * str2 - as output one. */ + for (;;) { + /* If we're out of input, flush output and read more input. */ + if ((ssize_t)in_index == read_chars) { + if (out_index) { + xwrite(STDOUT_FILENO, str2, out_index); + start_from: + out_index = 0; + } + read_chars = safe_read(STDIN_FILENO, str1, TR_BUFSIZ); + if (read_chars <= 0) { + if (read_chars < 0) + bb_perror_msg_and_die(bb_msg_read_error); + break; + } + in_index = 0; + } + c = str1[in_index++]; + if ((opts & TR_OPT_delete) && invec[c]) + continue; + coded = vector[c]; + if ((opts & TR_OPT_squeeze_reps) && last == coded + && (invec[c] || outvec[coded]) + ) { + continue; + } + str2[out_index++] = last = coded; + } + + if (ENABLE_FEATURE_CLEAN_UP) { + free(vector); + free(str2); + free(str1); + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/true.c b/coreutils/true.c new file mode 100644 index 000000000..382e476a8 --- /dev/null +++ b/coreutils/true.c @@ -0,0 +1,31 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini true implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ + +//usage:#define true_trivial_usage +//usage: "" +//usage:#define true_full_usage "\n\n" +//usage: "Return an exit code of TRUE (0)" +//usage: +//usage:#define true_example_usage +//usage: "$ true\n" +//usage: "$ echo $?\n" +//usage: "0\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int true_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int true_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + return EXIT_SUCCESS; +} diff --git a/coreutils/tty.c b/coreutils/tty.c new file mode 100644 index 000000000..45175054e --- /dev/null +++ b/coreutils/tty.c @@ -0,0 +1,56 @@ +/* vi: set sw=4 ts=4: */ +/* + * tty implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv4 compliant */ +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */ + +//usage:#define tty_trivial_usage +//usage: "" +//usage:#define tty_full_usage "\n\n" +//usage: "Print file name of stdin's terminal" +//usage: IF_INCLUDE_SUSv2( "\n" +//usage: "\n -s Print nothing, only return exit status" +//usage: ) +//usage: +//usage:#define tty_example_usage +//usage: "$ tty\n" +//usage: "/dev/tty2\n" + +#include "libbb.h" + +int tty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tty_main(int argc UNUSED_PARAM, char **argv) +{ + const char *s; + IF_INCLUDE_SUSv2(int silent;) /* Note: No longer relevant in SUSv3. */ + int retval; + + xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */ + + IF_INCLUDE_SUSv2(silent = getopt32(argv, "s");) + IF_INCLUDE_SUSv2(argv += optind;) + IF_NOT_INCLUDE_SUSv2(argv += 1;) + + /* gnu tty outputs a warning that it is ignoring all args. */ + bb_warn_ignoring_args(argv[0]); + + retval = EXIT_SUCCESS; + + s = xmalloc_ttyname(STDIN_FILENO); + if (s == NULL) { + /* According to SUSv3, ttyname can fail with EBADF or ENOTTY. + * We know the file descriptor is good, so failure means not a tty. */ + s = "not a tty"; + retval = EXIT_FAILURE; + } + IF_INCLUDE_SUSv2(if (!silent) puts(s);) + IF_NOT_INCLUDE_SUSv2(puts(s);) + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/uname.c b/coreutils/uname.c new file mode 100644 index 000000000..b96d76b6c --- /dev/null +++ b/coreutils/uname.c @@ -0,0 +1,171 @@ +/* vi: set sw=4 ts=4: */ +/* uname -- print system information + * Copyright (C) 1989-1999 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */ + +/* Option Example + * -s, --sysname SunOS + * -n, --nodename rocky8 + * -r, --release 4.0 + * -v, --version + * -m, --machine sun + * -a, --all SunOS rocky8 4.0 sun + * + * The default behavior is equivalent to '-s'. + * + * David MacKenzie + * + * GNU coreutils 6.10: + * Option: struct Example(s): + * utsname + * field: + * -s, --kernel-name sysname Linux + * -n, --nodename nodename localhost.localdomain + * -r, --kernel-release release 2.6.29 + * -v, --kernel-version version #1 SMP Sun Jan 11 20:52:37 EST 2009 + * -m, --machine machine x86_64 i686 + * -p, --processor (none) x86_64 i686 + * -i, --hardware-platform (none) x86_64 i386 + * NB: vanilla coreutils reports "unknown" -p and -i, + * x86_64 and i686/i386 shown above are Fedora's inventions. + * -o, --operating-system (none) GNU/Linux + * -a, --all: all of the above, in the order shown. + * If -p or -i is not known, don't show them + */ + +/* Busyboxed by Erik Andersen + * + * Before 2003: Glenn McGrath and Manuel Novoa III + * Further size reductions. + * Mar 16, 2003: Manuel Novoa III (mjn3@codepoet.org) + * Now does proper error checking on i/o. Plus some further space savings. + * Jan 2009: + * Fix handling of -a to not print "unknown", add -o and -i support. + */ + +//usage:#define uname_trivial_usage +//usage: "[-amnrspv]" +//usage:#define uname_full_usage "\n\n" +//usage: "Print system information\n" +//usage: "\n -a Print all" +//usage: "\n -m The machine (hardware) type" +//usage: "\n -n Hostname" +//usage: "\n -r OS release" +//usage: "\n -s OS name (default)" +//usage: "\n -p Processor type" +//usage: "\n -v OS version" +//usage: +//usage:#define uname_example_usage +//usage: "$ uname -a\n" +//usage: "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n" + +#include "libbb.h" +/* After libbb.h, since it needs sys/types.h on some systems */ +#include + +typedef struct { + struct utsname name; + char processor[sizeof(((struct utsname*)NULL)->machine)]; + char platform[sizeof(((struct utsname*)NULL)->machine)]; + char os[sizeof("GNU/Linux")]; +} uname_info_t; + +static const char options[] ALIGN1 = "snrvmpioa"; +static const unsigned short utsname_offset[] = { + offsetof(uname_info_t, name.sysname), /* -s */ + offsetof(uname_info_t, name.nodename), /* -n */ + offsetof(uname_info_t, name.release), /* -r */ + offsetof(uname_info_t, name.version), /* -v */ + offsetof(uname_info_t, name.machine), /* -m */ + offsetof(uname_info_t, processor), /* -p */ + offsetof(uname_info_t, platform), /* -i */ + offsetof(uname_info_t, os), /* -o */ +}; + +int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uname_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_LONG_OPTS + static const char uname_longopts[] ALIGN1 = + /* name, has_arg, val */ + "all\0" No_argument "a" + "kernel-name\0" No_argument "s" + "nodename\0" No_argument "n" + "kernel-release\0" No_argument "r" + "release\0" No_argument "r" + "kernel-version\0" No_argument "v" + "machine\0" No_argument "m" + "processor\0" No_argument "p" + "hardware-platform\0" No_argument "i" + "operating-system\0" No_argument "o" + ; +#endif + uname_info_t uname_info; +#if defined(__sparc__) && defined(__linux__) + char *fake_sparc = getenv("FAKE_SPARC"); +#endif + const char *unknown_str = "unknown"; + const char *fmt; + const unsigned short *delta; + unsigned toprint; + + IF_LONG_OPTS(applet_long_options = uname_longopts); + toprint = getopt32(argv, options); + + if (argv[optind]) { /* coreutils-6.9 compat */ + bb_show_usage(); + } + + if (toprint & (1 << 8)) { /* -a => all opts on */ + toprint = (1 << 8) - 1; + unknown_str = ""; /* -a does not print unknown fields */ + } + + if (toprint == 0) { /* no opts => -s (sysname) */ + toprint = 1; + } + + uname(&uname_info.name); /* never fails */ + +#if defined(__sparc__) && defined(__linux__) + if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') { + strcpy(uname_info.name.machine, "sparc"); + } +#endif + strcpy(uname_info.processor, unknown_str); + strcpy(uname_info.platform, unknown_str); + strcpy(uname_info.os, "GNU/Linux"); +#if 0 + /* Fedora does something like this */ + strcpy(uname_info.processor, uname_info.name.machine); + strcpy(uname_info.platform, uname_info.name.machine); + if (uname_info.platform[0] == 'i' + && uname_info.platform[1] + && uname_info.platform[2] == '8' + && uname_info.platform[3] == '6' + ) { + uname_info.platform[1] = '3'; + } +#endif + + delta = utsname_offset; + fmt = " %s" + 1; + do { + if (toprint & 1) { + const char *p = (char *)(&uname_info) + *delta; + if (p[0]) { + printf(fmt, p); + fmt = " %s"; + } + } + ++delta; + } while (toprint >>= 1); + bb_putchar('\n'); + + fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */ +} diff --git a/coreutils/uniq.c b/coreutils/uniq.c new file mode 100644 index 000000000..9208d34ec --- /dev/null +++ b/coreutils/uniq.c @@ -0,0 +1,124 @@ +/* vi: set sw=4 ts=4: */ +/* + * uniq implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */ + +//usage:#define uniq_trivial_usage +//usage: "[-cdu][-f,s,w N] [INPUT [OUTPUT]]" +//usage:#define uniq_full_usage "\n\n" +//usage: "Discard duplicate lines\n" +//usage: "\n -c Prefix lines by the number of occurrences" +//usage: "\n -d Only print duplicate lines" +//usage: "\n -u Only print unique lines" +//usage: "\n -f N Skip first N fields" +//usage: "\n -s N Skip first N chars (after any skipped fields)" +//usage: "\n -w N Compare N characters in line" +//usage: +//usage:#define uniq_example_usage +//usage: "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" +//usage: "a\n" +//usage: "b\n" +//usage: "c\n" + +#include "libbb.h" + +int uniq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uniq_main(int argc UNUSED_PARAM, char **argv) +{ + const char *input_filename; + unsigned skip_fields, skip_chars, max_chars; + unsigned opt; + char *cur_line; + const char *cur_compare; + + enum { + OPT_c = 0x1, + OPT_d = 0x2, /* print only dups */ + OPT_u = 0x4, /* print only uniq */ + OPT_f = 0x8, + OPT_s = 0x10, + OPT_w = 0x20, + }; + + skip_fields = skip_chars = 0; + max_chars = INT_MAX; + + opt_complementary = "f+:s+:w+"; + opt = getopt32(argv, "cduf:s:w:", &skip_fields, &skip_chars, &max_chars); + argv += optind; + + input_filename = argv[0]; + if (input_filename) { + const char *output; + + if (input_filename[0] != '-' || input_filename[1]) { + close(STDIN_FILENO); /* == 0 */ + xopen(input_filename, O_RDONLY); /* fd will be 0 */ + } + output = argv[1]; + if (output) { + if (argv[2]) + bb_show_usage(); + if (output[0] != '-' || output[1]) { + // Won't work with "uniq - FILE" and closed stdin: + //close(STDOUT_FILENO); + //xopen(output, O_WRONLY | O_CREAT | O_TRUNC); + xmove_fd(xopen(output, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); + } + } + } + + cur_compare = cur_line = NULL; /* prime the pump */ + + do { + unsigned i; + unsigned long dups; + char *old_line; + const char *old_compare; + + old_line = cur_line; + old_compare = cur_compare; + dups = 0; + + /* gnu uniq ignores newlines */ + while ((cur_line = xmalloc_fgetline(stdin)) != NULL) { + cur_compare = cur_line; + for (i = skip_fields; i; i--) { + cur_compare = skip_whitespace(cur_compare); + cur_compare = skip_non_whitespace(cur_compare); + } + for (i = skip_chars; *cur_compare && i; i--) { + ++cur_compare; + } + + if (!old_line || strncmp(old_compare, cur_compare, max_chars)) { + break; + } + + free(cur_line); + ++dups; /* testing for overflow seems excessive */ + } + + if (old_line) { + if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_u) */ + if (opt & OPT_c) { + /* %7lu matches GNU coreutils 6.9 */ + printf("%7lu ", dups + 1); + } + printf("%s\n", old_line); + } + free(old_line); + } + } while (cur_line); + + die_if_ferror(stdin, input_filename); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/usleep.c b/coreutils/usleep.c new file mode 100644 index 000000000..2e4eb5721 --- /dev/null +++ b/coreutils/usleep.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * usleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +//usage:#define usleep_trivial_usage +//usage: "N" +//usage:#define usleep_full_usage "\n\n" +//usage: "Pause for N microseconds" +//usage: +//usage:#define usleep_example_usage +//usage: "$ usleep 1000000\n" +//usage: "[pauses for 1 second]\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int usleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int usleep_main(int argc UNUSED_PARAM, char **argv) +{ + if (!argv[1]) { + bb_show_usage(); + } + + usleep(xatou(argv[1])); + + return EXIT_SUCCESS; +} diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c new file mode 100644 index 000000000..6ecfe6cef --- /dev/null +++ b/coreutils/uudecode.c @@ -0,0 +1,238 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2003, Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Based on specification from + * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html + * + * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the + * "end" line + */ + +//usage:#define uudecode_trivial_usage +//usage: "[-o OUTFILE] [INFILE]" +//usage:#define uudecode_full_usage "\n\n" +//usage: "Uudecode a file\n" +//usage: "Finds outfile name in uuencoded source unless -o is given" +//usage: +//usage:#define uudecode_example_usage +//usage: "$ uudecode -o busybox busybox.uu\n" +//usage: "$ ls -l busybox\n" +//usage: "-rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox\n" + +#include "libbb.h" + +#if ENABLE_UUDECODE +static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags UNUSED_PARAM) +{ + char *line; + + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + int encoded_len, str_len; + char *line_ptr, *dst; + + if (strcmp(line, "end") == 0) { + return; /* the only non-error exit */ + } + + line_ptr = line; + while (*line_ptr) { + *line_ptr = (*line_ptr - 0x20) & 0x3f; + line_ptr++; + } + str_len = line_ptr - line; + + encoded_len = line[0] * 4 / 3; + /* Check that line is not too short. (we tolerate + * overly _long_ line to accomodate possible extra '`'). + * Empty line case is also caught here. */ + if (str_len <= encoded_len) { + break; /* go to bb_error_msg_and_die("short file"); */ + } + if (encoded_len <= 0) { + /* Ignore the "`\n" line, why is it even in the encode file ? */ + free(line); + continue; + } + if (encoded_len > 60) { + bb_error_msg_and_die("line too long"); + } + + dst = line; + line_ptr = line + 1; + do { + /* Merge four 6 bit chars to three 8 bit chars */ + *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4; + encoded_len--; + if (encoded_len == 0) { + break; + } + + *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2; + encoded_len--; + if (encoded_len == 0) { + break; + } + + *dst++ = line_ptr[2] << 6 | line_ptr[3]; + line_ptr += 4; + encoded_len -= 2; + } while (encoded_len > 0); + fwrite(line, 1, dst - line, dst_stream); + free(line); + } + bb_error_msg_and_die("short file"); +} +#endif + +#if ENABLE_UUDECODE +int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uudecode_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *src_stream; + char *outname = NULL; + char *line; + + opt_complementary = "?1"; /* 1 argument max */ + getopt32(argv, "o:", &outname); + argv += optind; + + if (!argv[0]) + *--argv = (char*)"-"; + src_stream = xfopen_stdin(argv[0]); + + /* Search for the start of the encoding */ + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + void FAST_FUNC (*decode_fn_ptr)(FILE *src, FILE *dst, int flags); + char *line_ptr; + FILE *dst_stream; + int mode; + + if (strncmp(line, "begin-base64 ", 13) == 0) { + line_ptr = line + 13; + decode_fn_ptr = read_base64; + } else if (strncmp(line, "begin ", 6) == 0) { + line_ptr = line + 6; + decode_fn_ptr = read_stduu; + } else { + free(line); + continue; + } + + /* begin line found. decode and exit */ + mode = bb_strtou(line_ptr, NULL, 8); + if (outname == NULL) { + outname = strchr(line_ptr, ' '); + if ((outname == NULL) || (*outname == '\0')) { + break; + } + outname++; + } + dst_stream = stdout; + if (NOT_LONE_DASH(outname)) { + dst_stream = xfopen_for_write(outname); + fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + } + free(line); + decode_fn_ptr(src_stream, dst_stream, /*flags:*/ BASE64_FLAG_UU_STOP + BASE64_FLAG_NO_STOP_CHAR); + /* fclose_if_not_stdin(src_stream); - redundant */ + return EXIT_SUCCESS; + } + bb_error_msg_and_die("no 'begin' line"); +} +#endif + +//applet:IF_BASE64(APPLET(base64, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_BASE64) += uudecode.o + +//config:config BASE64 +//config: bool "base64" +//config: default y +//config: help +//config: Base64 encode and decode + +//usage:#define base64_trivial_usage +//usage: "[-d] [FILE]" +//usage:#define base64_full_usage "\n\n" +//usage: "Base64 encode or decode FILE to standard output" +//usage: "\n -d Decode data" +////usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)" +////usage: "\n -i When decoding, ignore non-alphabet characters" + +#if ENABLE_BASE64 +int base64_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int base64_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *src_stream; + unsigned opts; + + opt_complementary = "?1"; /* 1 argument max */ + opts = getopt32(argv, "d"); + argv += optind; + + if (!argv[0]) + *--argv = (char*)"-"; + src_stream = xfopen_stdin(argv[0]); + if (opts) { + read_base64(src_stream, stdout, /*flags:*/ (char)EOF); + } else { + enum { + SRC_BUF_SIZE = 76/4*3, /* This *MUST* be a multiple of 3 */ + DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), + }; + char src_buf[SRC_BUF_SIZE]; + char dst_buf[DST_BUF_SIZE + 1]; + int src_fd = fileno(src_stream); + while (1) { + size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); + if (!size) + break; + if ((ssize_t)size < 0) + bb_perror_msg_and_die(bb_msg_read_error); + /* Encode the buffer we just read in */ + bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); + xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); + bb_putchar('\n'); + fflush(stdout); + } + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} +#endif + +/* Test script. +Put this into an empty dir with busybox binary, an run. + +#!/bin/sh +test -x busybox || { echo "No ./busybox?"; exit; } +ln -sf busybox uudecode +ln -sf busybox uuencode +>A_null +echo -n A >A +echo -n AB >AB +echo -n ABC >ABC +echo -n ABCD >ABCD +echo -n ABCDE >ABCDE +echo -n ABCDEF >ABCDEF +cat busybox >A_bbox +for f in A*; do + echo uuencode $f + ./uuencode $f <$f >u_$f + ./uuencode -m $f <$f >m_$f +done +mkdir unpk_u unpk_m 2>/dev/null +for f in u_*; do + ./uudecode <$f -o unpk_u/${f:2} + diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1 + echo uudecode $f: $? +done +for f in m_*; do + ./uudecode <$f -o unpk_m/${f:2} + diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1 + echo uudecode $f: $? +done +*/ diff --git a/coreutils/uuencode.c b/coreutils/uuencode.c new file mode 100644 index 000000000..84a489a11 --- /dev/null +++ b/coreutils/uuencode.c @@ -0,0 +1,74 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2000 by Glenn McGrath + * + * based on the function base64_encode from http.c in wget v1.6 + * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define uuencode_trivial_usage +//usage: "[-m] [INFILE] STORED_FILENAME" +//usage:#define uuencode_full_usage "\n\n" +//usage: "Uuencode a file to stdout\n" +//usage: "\n -m Use base64 encoding per RFC1521" +//usage: +//usage:#define uuencode_example_usage +//usage: "$ uuencode busybox busybox\n" +//usage: "begin 755 busybox\n" +//usage: "\n" +//usage: "$ uudecode busybox busybox > busybox.uu\n" +//usage: "$\n" + +#include "libbb.h" + +enum { + SRC_BUF_SIZE = 15*3, /* This *MUST* be a multiple of 3 */ + DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), +}; + +int uuencode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uuencode_main(int argc UNUSED_PARAM, char **argv) +{ + struct stat stat_buf; + int src_fd = STDIN_FILENO; + const char *tbl; + mode_t mode; + char src_buf[SRC_BUF_SIZE]; + char dst_buf[DST_BUF_SIZE + 1]; + + tbl = bb_uuenc_tbl_std; + mode = 0666 & ~umask(0666); + opt_complementary = "-1:?2"; /* must have 1 or 2 args */ + if (getopt32(argv, "m")) { + tbl = bb_uuenc_tbl_base64; + } + argv += optind; + if (argv[1]) { + src_fd = xopen(argv[0], O_RDONLY); + fstat(src_fd, &stat_buf); + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + argv++; + } + + printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, *argv); + while (1) { + size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); + if (!size) + break; + if ((ssize_t)size < 0) + bb_perror_msg_and_die(bb_msg_read_error); + /* Encode the buffer we just read in */ + bb_uuencode(dst_buf, src_buf, size, tbl); + bb_putchar('\n'); + if (tbl == bb_uuenc_tbl_std) { + bb_putchar(tbl[size]); + } + fflush(stdout); + xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); + } + printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n"); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/wc.c b/coreutils/wc.c new file mode 100644 index 000000000..a410e407a --- /dev/null +++ b/coreutils/wc.c @@ -0,0 +1,240 @@ +/* vi: set sw=4 ts=4: */ +/* + * wc implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 compliant. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to fix a number of problems and do some size optimizations. + * Problems in the previous busybox implementation (besides bloat) included: + * 1) broken 'wc -c' optimization (read note below) + * 2) broken handling of '-' args + * 3) no checking of ferror on EOF returns + * 4) isprint() wasn't considered when word counting. + * + * NOTES: + * + * The previous busybox wc attempted an optimization using stat for the + * case of counting chars only. I omitted that because it was broken. + * It didn't take into account the possibility of input coming from a + * pipe, or input from a file with file pointer not at the beginning. + * + * To implement such a speed optimization correctly, not only do you + * need the size, but also the file position. Note also that the + * file position may be past the end of file. Consider the example + * (adapted from example in gnu wc.c) + * + * echo hello > /tmp/testfile && + * (dd ibs=1k skip=1 count=0 &> /dev/null; wc -c) < /tmp/testfile + * + * for which 'wc -c' should output '0'. + */ +#include "libbb.h" +#include "unicode.h" + +#if !ENABLE_LOCALE_SUPPORT +# undef isprint +# undef isspace +# define isprint(c) ((unsigned)((c) - 0x20) <= (0x7e - 0x20)) +# define isspace(c) ((c) == ' ') +#endif + +#if ENABLE_FEATURE_WC_LARGE +# define COUNT_T unsigned long long +# define COUNT_FMT "llu" +#else +# define COUNT_T unsigned +# define COUNT_FMT "u" +#endif + +/* We support -m even when UNICODE_SUPPORT is off, + * we just don't advertise it in help text, + * since it is the same as -c in this case. + */ + +//usage:#define wc_trivial_usage +//usage: "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..." +//usage: +//usage:#define wc_full_usage "\n\n" +//usage: "Count lines, words, and bytes for each FILE (or stdin)\n" +//usage: "\n -c Count bytes" +//usage: IF_UNICODE_SUPPORT( +//usage: "\n -m Count characters" +//usage: ) +//usage: "\n -l Count newlines" +//usage: "\n -w Count words" +//usage: "\n -L Print longest line length" +//usage: +//usage:#define wc_example_usage +//usage: "$ wc /etc/passwd\n" +//usage: " 31 46 1365 /etc/passwd\n" + +/* Order is important if we want to be compatible with + * column order in "wc -cmlwL" output: + */ +enum { + WC_LINES = 0, /* -l */ + WC_WORDS = 1, /* -w */ + WC_UNICHARS = 2, /* -m */ + WC_BYTES = 3, /* -c */ + WC_LENGTH = 4, /* -L */ + NUM_WCS = 5, +}; + +int wc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int wc_main(int argc UNUSED_PARAM, char **argv) +{ + const char *arg; + const char *start_fmt = " %9"COUNT_FMT + 1; + const char *fname_fmt = " %s\n"; + COUNT_T *pcounts; + COUNT_T counts[NUM_WCS]; + COUNT_T totals[NUM_WCS]; + int num_files; + smallint status = EXIT_SUCCESS; + unsigned print_type; + + init_unicode(); + + print_type = getopt32(argv, "lwmcL"); + + if (print_type == 0) { + print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_BYTES); + } + + argv += optind; + if (!argv[0]) { + *--argv = (char *) bb_msg_standard_input; + fname_fmt = "\n"; + } + if (!argv[1]) { /* zero or one filename? */ + if (!((print_type-1) & print_type)) /* exactly one option? */ + start_fmt = "%"COUNT_FMT; + } + + memset(totals, 0, sizeof(totals)); + + pcounts = counts; + + num_files = 0; + while ((arg = *argv++) != NULL) { + FILE *fp; + const char *s; + unsigned u; + unsigned linepos; + smallint in_word; + + ++num_files; + fp = fopen_or_warn_stdin(arg); + if (!fp) { + status = EXIT_FAILURE; + continue; + } + + memset(counts, 0, sizeof(counts)); + linepos = 0; + in_word = 0; + + while (1) { + int c; + /* Our -w doesn't match GNU wc exactly... oh well */ + + c = getc(fp); + if (c == EOF) { + if (ferror(fp)) { + bb_simple_perror_msg(arg); + status = EXIT_FAILURE; + } + goto DO_EOF; /* Treat an EOF as '\r'. */ + } + + /* Cater for -c and -m */ + ++counts[WC_BYTES]; + if (unicode_status != UNICODE_ON /* every byte is a new char */ + || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */ + ) { + ++counts[WC_UNICHARS]; + } + + if (isprint_asciionly(c)) { /* FIXME: not unicode-aware */ + ++linepos; + if (!isspace(c)) { + in_word = 1; + continue; + } + } else if ((unsigned)(c - 9) <= 4) { + /* \t 9 + * \n 10 + * \v 11 + * \f 12 + * \r 13 + */ + if (c == '\t') { + linepos = (linepos | 7) + 1; + } else { /* '\n', '\r', '\f', or '\v' */ + DO_EOF: + if (linepos > counts[WC_LENGTH]) { + counts[WC_LENGTH] = linepos; + } + if (c == '\n') { + ++counts[WC_LINES]; + } + if (c != '\v') { + linepos = 0; + } + } + } else { + continue; + } + + counts[WC_WORDS] += in_word; + in_word = 0; + if (c == EOF) { + break; + } + } + + fclose_if_not_stdin(fp); + + if (totals[WC_LENGTH] < counts[WC_LENGTH]) { + totals[WC_LENGTH] = counts[WC_LENGTH]; + } + totals[WC_LENGTH] -= counts[WC_LENGTH]; + + OUTPUT: + /* coreutils wc tries hard to print pretty columns + * (saves results for all files, finds max col len etc...) + * we won't try that hard, it will bloat us too much */ + s = start_fmt; + u = 0; + do { + if (print_type & (1 << u)) { + printf(s, pcounts[u]); + s = " %9"COUNT_FMT; /* Ok... restore the leading space. */ + } + totals[u] += pcounts[u]; + } while (++u < NUM_WCS); + printf(fname_fmt, arg); + } + + /* If more than one file was processed, we want the totals. To save some + * space, we set the pcounts ptr to the totals array. This has the side + * effect of trashing the totals array after outputting it, but that's + * irrelavent since we no longer need it. */ + if (num_files > 1) { + num_files = 0; /* Make sure we don't get here again. */ + arg = "total"; + pcounts = totals; + --argv; + goto OUTPUT; + } + + fflush_stdout_and_exit(status); +} diff --git a/coreutils/who.c b/coreutils/who.c new file mode 100644 index 000000000..c6c925264 --- /dev/null +++ b/coreutils/who.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/*---------------------------------------------------------------------- + * Mini who is used to display user name, login time, + * idle time and host name. + * + * Author: Da Chen + * + * This is a free document; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation: + * http://www.gnu.org/copyleft/gpl.html + * + * Copyright (c) 2002 AYR Networks, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + *---------------------------------------------------------------------- + */ +/* BB_AUDIT SUSv3 _NOT_ compliant -- missing options -b, -d, -l, -m, -p, -q, -r, -s, -t, -T, -u; Missing argument 'file'. */ + +//config:config WHO +//config: bool "who" +//config: default y +//config: depends on FEATURE_UTMP +//config: help +//config: who is used to show who is logged on. + +//config:config USERS +//config: bool "users" +//config: default y +//config: depends on FEATURE_UTMP +//config: help +//config: Print users currently logged on. + +//applet:IF_USERS(APPLET_ODDNAME(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users)) +//applet:IF_WHO( APPLET( who, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_USERS) += who.o +//kbuild:lib-$(CONFIG_WHO) += who.o + +//usage:#define users_trivial_usage +//usage: "" +//usage:#define users_full_usage "\n\n" +//usage: "Print the users currently logged on" + +//usage:#define who_trivial_usage +//usage: "[-a]" +//usage:#define who_full_usage "\n\n" +//usage: "Show who is logged on\n" +//usage: "\n -a Show all" + +#include "libbb.h" + +static void idle_string(char *str6, time_t t) +{ + t = time(NULL) - t; + + /*if (t < 60) { + str6[0] = '.'; + str6[1] = '\0'; + return; + }*/ + if (t >= 0 && t < (24 * 60 * 60)) { + sprintf(str6, "%02d:%02d", + (int) (t / (60 * 60)), + (int) ((t % (60 * 60)) / 60)); + return; + } + strcpy(str6, "old"); +} + +int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int who_main(int argc UNUSED_PARAM, char **argv) +{ + struct utmp *ut; + unsigned opt; + int do_users = (ENABLE_USERS && (!ENABLE_WHO || applet_name[0] == 'u')); + const char *fmt = "%s"; + + opt_complementary = "=0"; + opt = getopt32(argv, do_users ? "" : "aH"); + if (opt & 2) // -H + printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n"); + + setutent(); + while ((ut = getutent()) != NULL) { + if (ut->ut_user[0] + && ((opt & 1) || ut->ut_type == USER_PROCESS) + ) { + if (!do_users) { + char str6[6]; + char name[sizeof("/dev/") + sizeof(ut->ut_line) + 1]; + struct stat st; + time_t seconds; + + str6[0] = '?'; + str6[1] = '\0'; + strcpy(name, "/dev/"); + safe_strncpy(ut->ut_line[0] == '/' ? name : name + sizeof("/dev/")-1, + ut->ut_line, + sizeof(ut->ut_line)+1 + ); + if (stat(name, &st) == 0) + idle_string(str6, st.st_atime); + /* manpages say ut_tv.tv_sec *is* time_t, + * but some systems have it wrong */ + seconds = ut->ut_tv.tv_sec; + /* How wide time field can be? + * "Nov 10 19:33:20": 15 chars + * "2010-11-10 19:33": 16 chars + */ + printf("%-15.*s %-15.*s %-7s %-16.16s %.*s\n", + (int)sizeof(ut->ut_user), ut->ut_user, + (int)sizeof(ut->ut_line), ut->ut_line, + str6, + ctime(&seconds) + 4, + (int)sizeof(ut->ut_host), ut->ut_host + ); + } else { + printf(fmt, ut->ut_user); + fmt = " %s"; + } + } + } + if (do_users) + bb_putchar('\n'); + if (ENABLE_FEATURE_CLEAN_UP) + endutent(); + return EXIT_SUCCESS; +} diff --git a/coreutils/whoami.c b/coreutils/whoami.c new file mode 100644 index 000000000..30b17cab3 --- /dev/null +++ b/coreutils/whoami.c @@ -0,0 +1,31 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini whoami implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +//usage:#define whoami_trivial_usage +//usage: "" +//usage:#define whoami_full_usage "\n\n" +//usage: "Print the user name associated with the current effective user id" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int whoami_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int whoami_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + if (argv[1]) + bb_show_usage(); + + /* Will complain and die if username not found */ + puts(xuid2uname(geteuid())); + + return fflush_all(); +} diff --git a/coreutils/yes.c b/coreutils/yes.c new file mode 100644 index 000000000..5d799f09b --- /dev/null +++ b/coreutils/yes.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * yes implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reductions and removed redundant applet name prefix from error messages. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +//usage:#define yes_trivial_usage +//usage: "[STRING]" +//usage:#define yes_full_usage "\n\n" +//usage: "Repeatedly output a line with STRING, or 'y'" + +int yes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int yes_main(int argc UNUSED_PARAM, char **argv) +{ + char **pp; + + argv[0] = (char*)"y"; + if (argv[1]) + ++argv; + + do { + pp = argv; + while (1) { + fputs(*pp, stdout); + if (!*++pp) + break; + putchar(' '); + } + } while (putchar('\n') != EOF); + + bb_perror_nomsg_and_die(); +} diff --git a/debianutils/Config.src b/debianutils/Config.src new file mode 100644 index 000000000..cbc09b5ce --- /dev/null +++ b/debianutils/Config.src @@ -0,0 +1,85 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Debian Utilities" + +INSERT + +config MKTEMP + bool "mktemp" + default y + help + mktemp is used to create unique temporary files + +config PIPE_PROGRESS + bool "pipe_progress" + default y + help + Display a dot to indicate pipe activity. + +config RUN_PARTS + bool "run-parts" + default y + help + run-parts is a utility designed to run all the scripts in a directory. + + It is useful to set up a directory like cron.daily, where you need to + execute all the scripts in that directory. + + In this implementation of run-parts some features (such as report + mode) are not implemented. + + Unless you know that run-parts is used in some of your scripts + you can safely say N here. + +config FEATURE_RUN_PARTS_LONG_OPTIONS + bool "Enable long options" + default y + depends on RUN_PARTS && LONG_OPTS + help + Support long options for the run-parts applet. + +config FEATURE_RUN_PARTS_FANCY + bool "Support additional arguments" + default y + depends on RUN_PARTS + help + Support additional options: + -l --list print the names of the all matching files (not + limited to executables), but don't actually run them. + +config START_STOP_DAEMON + bool "start-stop-daemon" + default y + help + start-stop-daemon is used to control the creation and + termination of system-level processes, usually the ones + started during the startup of the system. + +config FEATURE_START_STOP_DAEMON_FANCY + bool "Support additional arguments" + default y + depends on START_STOP_DAEMON + help + Support additional arguments. + -o|--oknodo ignored since we exit with 0 anyway + -v|--verbose + -N|--nicelevel N + +config FEATURE_START_STOP_DAEMON_LONG_OPTIONS + bool "Enable long options" + default y + depends on START_STOP_DAEMON && LONG_OPTS + help + Support long options for the start-stop-daemon applet. + +config WHICH + bool "which" + default y + help + which is used to find programs in your PATH and + print out their pathnames. + +endmenu diff --git a/debianutils/Kbuild.src b/debianutils/Kbuild.src new file mode 100644 index 000000000..d41b5c8e4 --- /dev/null +++ b/debianutils/Kbuild.src @@ -0,0 +1,14 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT +lib-$(CONFIG_MKTEMP) += mktemp.o +lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o +lib-$(CONFIG_RUN_PARTS) += run_parts.o +lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o +lib-$(CONFIG_WHICH) += which.o diff --git a/debianutils/mktemp.c b/debianutils/mktemp.c new file mode 100644 index 000000000..007cb1c5b --- /dev/null +++ b/debianutils/mktemp.c @@ -0,0 +1,98 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mktemp implementation for busybox + * + * + * Copyright (C) 2000 by Daniel Jacobowitz + * Written by Daniel Jacobowitz + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* Coreutils 6.12 man page says: + * mktemp [OPTION]... [TEMPLATE] + * Create a temporary file or directory, safely, and print its name. If + * TEMPLATE is not specified, use tmp.XXXXXXXXXX. + * -d, --directory + * create a directory, not a file + * -q, --quiet + * suppress diagnostics about file/dir-creation failure + * -u, --dry-run + * do not create anything; merely print a name (unsafe) + * --tmpdir[=DIR] + * interpret TEMPLATE relative to DIR. If DIR is not specified, + * use $TMPDIR if set, else /tmp. With this option, TEMPLATE must + * not be an absolute name. Unlike with -t, TEMPLATE may contain + * slashes, but even here, mktemp still creates only the final com- + * ponent. + * -p DIR use DIR as a prefix; implies -t [deprecated] + * -t interpret TEMPLATE as a single file name component, relative to + * a directory: $TMPDIR, if set; else the directory specified via + * -p; else /tmp [deprecated] + */ + +//usage:#define mktemp_trivial_usage +//usage: "[-dt] [-p DIR] [TEMPLATE]" +//usage:#define mktemp_full_usage "\n\n" +//usage: "Create a temporary file with name based on TEMPLATE and print its name.\n" +//usage: "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n" +//usage: "Without TEMPLATE, -t tmp.XXXXXX is assumed.\n" +//usage: "\n -d Make directory, not file" +////usage: "\n -q Fail silently on errors" - we ignore this opt +//usage: "\n -t Prepend base directory name to TEMPLATE" +//usage: "\n -p DIR Use DIR as a base directory (implies -t)" +//usage: "\n" +//usage: "\nBase directory is: -p DIR, else $TMPDIR, else /tmp" +//usage: +//usage:#define mktemp_example_usage +//usage: "$ mktemp /tmp/temp.XXXXXX\n" +//usage: "/tmp/temp.mWiLjM\n" +//usage: "$ ls -la /tmp/temp.mWiLjM\n" +//usage: "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n" + +#include "libbb.h" + +int mktemp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mktemp_main(int argc UNUSED_PARAM, char **argv) +{ + const char *path; + char *chp; + unsigned opts; + enum { + OPT_d = 1 << 0, + OPT_q = 1 << 1, + OPT_t = 1 << 2, + OPT_p = 1 << 3, + }; + + path = getenv("TMPDIR"); + if (!path || path[0] == '\0') + path = "/tmp"; + + /* -q is ignored */ + opt_complementary = "?1"; /* 1 argument max */ + opts = getopt32(argv, "dqtp:", &path); + + chp = argv[optind]; + if (!chp) { + /* GNU coreutils 8.4: + * bare "mktemp" -> "mktemp -t tmp.XXXXXX" + */ + chp = xstrdup("tmp.XXXXXX"); + opts |= OPT_t; + } + if (opts & (OPT_t|OPT_p)) + chp = concat_path_file(path, chp); + + if (opts & OPT_d) { + if (mkdtemp(chp) == NULL) + return EXIT_FAILURE; + } else { + if (mkstemp(chp) < 0) + return EXIT_FAILURE; + } + + puts(chp); + + return EXIT_SUCCESS; +} diff --git a/debianutils/pipe_progress.c b/debianutils/pipe_progress.c new file mode 100644 index 000000000..2c7444f31 --- /dev/null +++ b/debianutils/pipe_progress.c @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * Monitor a pipe with a simple progress display. + * + * Copyright (C) 2003 by Rob Landley , Joey Hess + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define pipe_progress_trivial_usage NOUSAGE_STR +//usage:#define pipe_progress_full_usage "" + +#include "libbb.h" + +#define PIPE_PROGRESS_SIZE 4096 + +/* Read a block of data from stdin, write it to stdout. + * Activity is indicated by a '.' to stderr + */ +int pipe_progress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pipe_progress_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char buf[PIPE_PROGRESS_SIZE]; + time_t t = time(NULL); + int len; + + while ((len = safe_read(STDIN_FILENO, buf, PIPE_PROGRESS_SIZE)) > 0) { + time_t new_time = time(NULL); + if (new_time != t) { + t = new_time; + bb_putchar_stderr('.'); + } + full_write(STDOUT_FILENO, buf, len); + } + + bb_putchar_stderr('\n'); + + return 0; +} diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c new file mode 100644 index 000000000..8f08f6dc6 --- /dev/null +++ b/debianutils/run_parts.c @@ -0,0 +1,200 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini run-parts implementation for busybox + * + * Copyright (C) 2007 Bernhard Reutner-Fischer + * + * Based on a older version that was in busybox which was 1k big.. + * Copyright (C) 2001 by Emanuele Aina + * + * Based on the Debian run-parts program, version 1.15 + * Copyright (C) 1996 Jeff Noxon , + * Copyright (C) 1996-1999 Guy Maor + * + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* This is my first attempt to write a program in C (well, this is my first + * attempt to write a program! :-) . */ + +/* This piece of code is heavily based on the original version of run-parts, + * taken from debian-utils. I've only removed the long options and a the + * report mode. As the original run-parts support only long options, I've + * broken compatibility because the BusyBox policy doesn't allow them. + * The supported options are: + * -t test. Print the name of the files to be executed, without + * execute them. + * -a ARG argument. Pass ARG as an argument the program executed. It can + * be repeated to pass multiple arguments. + * -u MASK umask. Set the umask of the program executed to MASK. + */ + +//usage:#define run_parts_trivial_usage +//usage: "[-t] "IF_FEATURE_RUN_PARTS_FANCY("[-l] ")"[-a ARG] [-u MASK] DIRECTORY" +//usage:#define run_parts_full_usage "\n\n" +//usage: "Run a bunch of scripts in DIRECTORY\n" +//usage: "\n -t Print what would be run, but don't actually run anything" +//usage: "\n -a ARG Pass ARG as argument for every program" +//usage: "\n -u MASK Set the umask to MASK before running every program" +//usage: IF_FEATURE_RUN_PARTS_FANCY( +//usage: "\n -l Print names of all matching files even if they are not executable" +//usage: ) +//usage: +//usage:#define run_parts_example_usage +//usage: "$ run-parts -a start /etc/init.d\n" +//usage: "$ run-parts -a stop=now /etc/init.d\n\n" +//usage: "Let's assume you have a script foo/dosomething:\n" +//usage: "#!/bin/sh\n" +//usage: "for i in $*; do eval $i; done; unset i\n" +//usage: "case \"$1\" in\n" +//usage: "start*) echo starting something;;\n" +//usage: "stop*) set -x; shutdown -h $stop;;\n" +//usage: "esac\n\n" +//usage: "Running this yields:\n" +//usage: "$run-parts -a stop=+4m foo/\n" +//usage: "+ shutdown -h +4m" + +#include "libbb.h" + +struct globals { + char **names; + int cur; + char *cmd[1]; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define names (G.names) +#define cur (G.cur ) +#define cmd (G.cmd ) +#define INIT_G() do { } while (0) + +enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 }; + +enum { + OPT_r = (1 << 0), + OPT_a = (1 << 1), + OPT_u = (1 << 2), + OPT_t = (1 << 3), + OPT_l = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_FANCY, +}; + +#if ENABLE_FEATURE_RUN_PARTS_FANCY +#define list_mode (option_mask32 & OPT_l) +#else +#define list_mode 0 +#endif + +/* Is this a valid filename (upper/lower alpha, digits, + * underscores, and hyphens only?) + */ +static bool invalid_name(const char *c) +{ + c = bb_basename(c); + + while (*c && (isalnum(*c) || *c == '_' || *c == '-')) + c++; + + return *c; /* TRUE (!0) if terminating NUL is not reached */ +} + +static int bb_alphasort(const void *p1, const void *p2) +{ + int r = strcmp(*(char **) p1, *(char **) p2); + return (option_mask32 & OPT_r) ? -r : r; +} + +static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth) +{ + if (depth == 1) + return TRUE; + + if (depth == 2 + && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) + || invalid_name(file) + || (!list_mode && access(file, X_OK) != 0)) + ) { + return SKIP; + } + + names = xrealloc_vector(names, 4, cur); + names[cur++] = xstrdup(file); + /*names[cur] = NULL; - xrealloc_vector did it */ + + return TRUE; +} + +#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS +static const char runparts_longopts[] ALIGN1 = + "arg\0" Required_argument "a" + "umask\0" Required_argument "u" + "test\0" No_argument "t" +#if ENABLE_FEATURE_RUN_PARTS_FANCY + "list\0" No_argument "l" + "reverse\0" No_argument "r" +//TODO: "verbose\0" No_argument "v" +#endif + ; +#endif + +int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int run_parts_main(int argc UNUSED_PARAM, char **argv) +{ + const char *umask_p = "22"; + llist_t *arg_list = NULL; + unsigned n; + int ret; + + INIT_G(); + +#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS + applet_long_options = runparts_longopts; +#endif + /* We require exactly one argument: the directory name */ + opt_complementary = "=1:a::"; + getopt32(argv, "ra:u:t"IF_FEATURE_RUN_PARTS_FANCY("l"), &arg_list, &umask_p); + + umask(xstrtou_range(umask_p, 8, 0, 07777)); + + n = 1; + while (arg_list && n < NUM_CMD) { + cmd[n++] = llist_pop(&arg_list); + } + /* cmd[n] = NULL; - is already zeroed out */ + + /* run-parts has to sort executables by name before running them */ + + recursive_action(argv[optind], + ACTION_RECURSE|ACTION_FOLLOWLINKS, + act, /* file action */ + act, /* dir action */ + NULL, /* user data */ + 1 /* depth */ + ); + + if (!names) + return 0; + + qsort(names, cur, sizeof(char *), bb_alphasort); + + n = 0; + while (1) { + char *name = *names++; + if (!name) + break; + if (option_mask32 & (OPT_t | OPT_l)) { + puts(name); + continue; + } + cmd[0] = name; + ret = spawn_and_wait(cmd); + if (ret == 0) + continue; + n = 1; + if (ret < 0) + bb_perror_msg("can't execute '%s'", name); + else /* ret > 0 */ + bb_error_msg("%s exited with code %d", name, ret & 0xff); + } + + return n; +} diff --git a/debianutils/start_stop_daemon.c b/debianutils/start_stop_daemon.c new file mode 100644 index 000000000..02609c04f --- /dev/null +++ b/debianutils/start_stop_daemon.c @@ -0,0 +1,527 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini start-stop-daemon implementation(s) for busybox + * + * Written by Marek Michalkiewicz , + * Adapted for busybox David Kimdon + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* +This is how it is supposed to work: + +start-stop-daemon [OPTIONS] [--start|--stop] [[--] arguments...] + +One (only) of these must be given: + -S,--start Start + -K,--stop Stop + +Search for matching processes. +If --stop is given, stop all matching processes (by sending a signal). +If --start is given, start a new process unless a matching process was found. + +Options controlling process matching +(if multiple conditions are specified, all must match): + -u,--user USERNAME|UID Only consider this user's processes + -n,--name PROCESS_NAME Look for processes by matching PROCESS_NAME + with comm field in /proc/$PID/stat. + Only basename is compared: + "ntpd" == "./ntpd" == "/path/to/ntpd". +[TODO: can PROCESS_NAME be a full pathname? Should we require full match then +with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)] + -x,--exec EXECUTABLE Look for processes that were started with this + command in /proc/$PID/cmdline. + Unlike -n, we match against the full path: + "ntpd" != "./ntpd" != "/path/to/ntpd" + -p,--pidfile PID_FILE Look for processes with PID from this file + +Options which are valid for --start only: + -x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory. + -a,--startas NAME argv[0] (defaults to EXECUTABLE) + -b,--background Put process into background + -N,--nicelevel N Add N to process' nice level + -c,--chuid USER[:[GRP]] Change to specified user [and group] + -m,--make-pidfile Write PID to the pidfile + (both -m and -p must be given!) + +Options which are valid for --stop only: + -s,--signal SIG Signal to send (default:TERM) + -t,--test Exit with status 0 if process is found + (we don't actually start or stop daemons) + +Misc options: + -o,--oknodo Exit with status 0 if nothing is done + -q,--quiet Quiet + -v,--verbose Verbose +*/ + +//usage:#define start_stop_daemon_trivial_usage +//usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]" +//usage:#define start_stop_daemon_full_usage "\n\n" +//usage: "Search for matching processes, and then\n" +//usage: "-K: stop all matching processes.\n" +//usage: "-S: start a process unless a matching process is found.\n" +//usage: IF_FEATURE_START_STOP_DAEMON_LONG_OPTIONS( +//usage: "\nProcess matching:" +//usage: "\n -u,--user USERNAME|UID Match only this user's processes" +//usage: "\n -n,--name NAME Match processes with NAME" +//usage: "\n in comm field in /proc/PID/stat" +//usage: "\n -x,--exec EXECUTABLE Match processes with this command" +//usage: "\n in /proc/PID/cmdline" +//usage: "\n -p,--pidfile FILE Match a process with PID from the file" +//usage: "\n All specified conditions must match" +//usage: "\n-S only:" +//usage: "\n -x,--exec EXECUTABLE Program to run" +//usage: "\n -a,--startas NAME Zeroth argument" +//usage: "\n -b,--background Background" +//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( +//usage: "\n -N,--nicelevel N Change nice level" +//usage: ) +//usage: "\n -c,--chuid USER[:[GRP]] Change to user/group" +//usage: "\n -m,--make-pidfile Write PID to the pidfile specified by -p" +//usage: "\n-K only:" +//usage: "\n -s,--signal SIG Signal to send" +//usage: "\n -t,--test Match only, exit with 0 if a process is found" +//usage: "\nOther:" +//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( +//usage: "\n -o,--oknodo Exit with status 0 if nothing is done" +//usage: "\n -v,--verbose Verbose" +//usage: ) +//usage: "\n -q,--quiet Quiet" +//usage: ) +//usage: IF_NOT_FEATURE_START_STOP_DAEMON_LONG_OPTIONS( +//usage: "\nProcess matching:" +//usage: "\n -u USERNAME|UID Match only this user's processes" +//usage: "\n -n NAME Match processes with NAME" +//usage: "\n in comm field in /proc/PID/stat" +//usage: "\n -x EXECUTABLE Match processes with this command" +//usage: "\n command in /proc/PID/cmdline" +//usage: "\n -p FILE Match a process with PID from the file" +//usage: "\n All specified conditions must match" +//usage: "\n-S only:" +//usage: "\n -x EXECUTABLE Program to run" +//usage: "\n -a NAME Zeroth argument" +//usage: "\n -b Background" +//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( +//usage: "\n -N N Change nice level" +//usage: ) +//usage: "\n -c USER[:[GRP]] Change to user/group" +//usage: "\n -m Write PID to the pidfile specified by -p" +//usage: "\n-K only:" +//usage: "\n -s SIG Signal to send" +//usage: "\n -t Match only, exit with 0 if a process is found" +//usage: "\nOther:" +//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( +//usage: "\n -o Exit with status 0 if nothing is done" +//usage: "\n -v Verbose" +//usage: ) +//usage: "\n -q Quiet" +//usage: ) + +#include + +/* Override ENABLE_FEATURE_PIDFILE */ +#define WANT_PIDFILE 1 +#include "libbb.h" + +struct pid_list { + struct pid_list *next; + pid_t pid; +}; + +enum { + CTX_STOP = (1 << 0), + CTX_START = (1 << 1), + OPT_BACKGROUND = (1 << 2), // -b + OPT_QUIET = (1 << 3), // -q + OPT_TEST = (1 << 4), // -t + OPT_MAKEPID = (1 << 5), // -m + OPT_a = (1 << 6), // -a + OPT_n = (1 << 7), // -n + OPT_s = (1 << 8), // -s + OPT_u = (1 << 9), // -u + OPT_c = (1 << 10), // -c + OPT_x = (1 << 11), // -x + OPT_p = (1 << 12), // -p + OPT_OKNODO = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o + OPT_VERBOSE = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v + OPT_NICELEVEL = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N +}; +#define QUIET (option_mask32 & OPT_QUIET) +#define TEST (option_mask32 & OPT_TEST) + +struct globals { + struct pid_list *found_procs; + char *userspec; + char *cmdname; + char *execname; + char *pidfile; + char *execname_cmpbuf; + unsigned execname_sizeof; + int user_id; + smallint signal_nr; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define userspec (G.userspec ) +#define cmdname (G.cmdname ) +#define execname (G.execname ) +#define pidfile (G.pidfile ) +#define user_id (G.user_id ) +#define signal_nr (G.signal_nr ) +#define INIT_G() do { \ + user_id = -1; \ + signal_nr = 15; \ +} while (0) + +#ifdef OLDER_VERSION_OF_X +/* -x,--exec EXECUTABLE + * Look for processes with matching /proc/$PID/exe. + * Match is performed using device+inode. + */ +static int pid_is_exec(pid_t pid) +{ + struct stat st; + char buf[sizeof("/proc/%u/exe") + sizeof(int)*3]; + + sprintf(buf, "/proc/%u/exe", (unsigned)pid); + if (stat(buf, &st) < 0) + return 0; + if (st.st_dev == execstat.st_dev + && st.st_ino == execstat.st_ino) + return 1; + return 0; +} +#endif + +static int pid_is_exec(pid_t pid) +{ + ssize_t bytes; + char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; + + sprintf(buf, "/proc/%u/cmdline", (unsigned)pid); + bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof); + if (bytes > 0) { + G.execname_cmpbuf[bytes] = '\0'; + return strcmp(execname, G.execname_cmpbuf) == 0; + } + return 0; +} + +static int pid_is_name(pid_t pid) +{ + /* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */ + char buf[32]; /* should be enough */ + char *p, *pe; + + sprintf(buf, "/proc/%u/stat", (unsigned)pid); + if (open_read_close(buf, buf, sizeof(buf) - 1) < 0) + return 0; + buf[sizeof(buf) - 1] = '\0'; /* paranoia */ + p = strchr(buf, '('); + if (!p) + return 0; + pe = strrchr(++p, ')'); + if (!pe) + return 0; + *pe = '\0'; + /* we require comm to match and to not be truncated */ + /* in Linux, if comm is 15 chars, it may be a truncated + * name, so we don't allow that to match */ + if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */ + return 0; + return strcmp(p, cmdname) == 0; +} + +static int pid_is_user(int pid) +{ + struct stat sb; + char buf[sizeof("/proc/") + sizeof(int)*3]; + + sprintf(buf, "/proc/%u", (unsigned)pid); + if (stat(buf, &sb) != 0) + return 0; + return (sb.st_uid == (uid_t)user_id); +} + +static void check(int pid) +{ + struct pid_list *p; + + if (execname && !pid_is_exec(pid)) { + return; + } + if (cmdname && !pid_is_name(pid)) { + return; + } + if (userspec && !pid_is_user(pid)) { + return; + } + p = xmalloc(sizeof(*p)); + p->next = G.found_procs; + p->pid = pid; + G.found_procs = p; +} + +static void do_pidfile(void) +{ + FILE *f; + unsigned pid; + + f = fopen_for_read(pidfile); + if (f) { + if (fscanf(f, "%u", &pid) == 1) + check(pid); + fclose(f); + } else if (errno != ENOENT) + bb_perror_msg_and_die("open pidfile %s", pidfile); +} + +static void do_procinit(void) +{ + DIR *procdir; + struct dirent *entry; + int pid; + + if (pidfile) { + do_pidfile(); + return; + } + + procdir = xopendir("/proc"); + + pid = 0; + while (1) { + errno = 0; /* clear any previous error */ + entry = readdir(procdir); +// TODO: this check is too generic, it's better +// to check for exact errno(s) which mean that we got stale entry + if (errno) /* Stale entry, process has died after opendir */ + continue; + if (!entry) /* EOF, no more entries */ + break; + pid = bb_strtou(entry->d_name, NULL, 10); + if (errno) /* NaN */ + continue; + check(pid); + } + closedir(procdir); + if (!pid) + bb_error_msg_and_die("nothing in /proc - not mounted?"); +} + +static int do_stop(void) +{ + char *what; + struct pid_list *p; + int killed = 0; + + if (cmdname) { + if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname); + if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname; + } else if (execname) { + if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname); + if (!ENABLE_FEATURE_CLEAN_UP) what = execname; + } else if (pidfile) { + what = xasprintf("process in pidfile '%s'", pidfile); + } else if (userspec) { + what = xasprintf("process(es) owned by '%s'", userspec); + } else { + bb_error_msg_and_die("internal error, please report"); + } + + if (!G.found_procs) { + if (!QUIET) + printf("no %s found; none killed\n", what); + killed = -1; + goto ret; + } + for (p = G.found_procs; p; p = p->next) { + if (kill(p->pid, TEST ? 0 : signal_nr) == 0) { + killed++; + } else { + bb_perror_msg("warning: killing process %u", (unsigned)p->pid); + p->pid = 0; + if (TEST) { + /* Example: -K --test --pidfile PIDFILE detected + * that PIDFILE's pid doesn't exist */ + killed = -1; + goto ret; + } + } + } + if (!QUIET && killed) { + printf("stopped %s (pid", what); + for (p = G.found_procs; p; p = p->next) + if (p->pid) + printf(" %u", (unsigned)p->pid); + puts(")"); + } + ret: + if (ENABLE_FEATURE_CLEAN_UP) + free(what); + return killed; +} + +#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS +static const char start_stop_daemon_longopts[] ALIGN1 = + "stop\0" No_argument "K" + "start\0" No_argument "S" + "background\0" No_argument "b" + "quiet\0" No_argument "q" + "test\0" No_argument "t" + "make-pidfile\0" No_argument "m" +#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY + "oknodo\0" No_argument "o" + "verbose\0" No_argument "v" + "nicelevel\0" Required_argument "N" +#endif + "startas\0" Required_argument "a" + "name\0" Required_argument "n" + "signal\0" Required_argument "s" + "user\0" Required_argument "u" + "chuid\0" Required_argument "c" + "exec\0" Required_argument "x" + "pidfile\0" Required_argument "p" +#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY + "retry\0" Required_argument "R" +#endif + ; +#endif + +int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opt; + char *signame; + char *startas; + char *chuid; +#ifdef OLDER_VERSION_OF_X + struct stat execstat; +#endif +#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY +// char *retry_arg = NULL; +// int retries = -1; + char *opt_N; +#endif + + INIT_G(); + +#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS + applet_long_options = start_stop_daemon_longopts; +#endif + + /* -K or -S is required; they are mutually exclusive */ + /* -p is required if -m is given */ + /* -xpun (at least one) is required if -K is given */ + /* -xa (at least one) is required if -S is given */ + /* -q turns off -v */ + opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa" + IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"); + opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:" + IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"), + &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile + IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) + /* We accept and ignore -R / --retry */ + IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL) + ); + + if (opt & OPT_s) { + signal_nr = get_signum(signame); + if (signal_nr < 0) bb_show_usage(); + } + + if (!(opt & OPT_a)) + startas = execname; + if (!execname) /* in case -a is given and -x is not */ + execname = startas; + if (execname) { + G.execname_sizeof = strlen(execname) + 1; + G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1); + } + +// IF_FEATURE_START_STOP_DAEMON_FANCY( +// if (retry_arg) +// retries = xatoi_positive(retry_arg); +// ) + //argc -= optind; + argv += optind; + + if (userspec) { + user_id = bb_strtou(userspec, NULL, 10); + if (errno) + user_id = xuname2uid(userspec); + } + /* Both start and stop need to know current processes */ + do_procinit(); + + if (opt & CTX_STOP) { + int i = do_stop(); + return (opt & OPT_OKNODO) ? 0 : (i <= 0); + } + + if (G.found_procs) { + if (!QUIET) + printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid); + return !(opt & OPT_OKNODO); + } + +#ifdef OLDER_VERSION_OF_X + if (execname) + xstat(execname, &execstat); +#endif + + *--argv = startas; + if (opt & OPT_BACKGROUND) { +#if BB_MMU + bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS); + /* DAEMON_DEVNULL_STDIO is superfluous - + * it's always done by bb_daemonize() */ +#else + pid_t pid = xvfork(); + if (pid != 0) { + /* parent */ + /* why _exit? the child may have changed the stack, + * so "return 0" may do bad things */ + _exit(EXIT_SUCCESS); + } + /* Child */ + setsid(); /* detach from controlling tty */ + /* Redirect stdio to /dev/null, close extra FDs. + * We do not actually daemonize because of DAEMON_ONLY_SANITIZE */ + bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO + + DAEMON_CLOSE_EXTRA_FDS + + DAEMON_ONLY_SANITIZE, + NULL /* argv, unused */ ); +#endif + } + if (opt & OPT_MAKEPID) { + /* User wants _us_ to make the pidfile */ + write_pidfile(pidfile); + } + if (opt & OPT_c) { + struct bb_uidgid_t ugid = { -1, -1 }; + parse_chown_usergroup_or_die(&ugid, chuid); + if (ugid.uid != (uid_t) -1) { + struct passwd *pw = xgetpwuid(ugid.uid); + if (ugid.gid != (gid_t) -1) + pw->pw_gid = ugid.gid; + /* initgroups, setgid, setuid: */ + change_identity(pw); + } else if (ugid.gid != (gid_t) -1) { + xsetgid(ugid.gid); + setgroups(1, &ugid.gid); + } + } +#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY + if (opt & OPT_NICELEVEL) { + /* Set process priority */ + int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2); + if (setpriority(PRIO_PROCESS, 0, prio) < 0) { + bb_perror_msg_and_die("setpriority(%d)", prio); + } + } +#endif + execvp(startas, argv); + bb_perror_msg_and_die("can't execute '%s'", startas); +} diff --git a/debianutils/which.c b/debianutils/which.c new file mode 100644 index 000000000..15fd598b7 --- /dev/null +++ b/debianutils/which.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * Which implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2006 Gabriel Somlo + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Based on which from debianutils + */ + +//usage:#define which_trivial_usage +//usage: "[COMMAND]..." +//usage:#define which_full_usage "\n\n" +//usage: "Locate a COMMAND" +//usage: +//usage:#define which_example_usage +//usage: "$ which login\n" +//usage: "/bin/login\n" + +#include "libbb.h" + +int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int which_main(int argc UNUSED_PARAM, char **argv) +{ + IF_DESKTOP(int opt;) + int status = EXIT_SUCCESS; + char *path; + char *p; + + opt_complementary = "-1"; /* at least one argument */ + IF_DESKTOP(opt =) getopt32(argv, "a"); + argv += optind; + + /* This matches what is seen on e.g. ubuntu. + * "which" there is a shell script. */ + path = getenv("PATH"); + if (!path) { + path = (char*)bb_PATH_root_path; + putenv(path); + path += 5; /* skip "PATH=" */ + } + + do { +#if ENABLE_DESKTOP +/* Much bloat just to support -a */ + if (strchr(*argv, '/')) { + if (execable_file(*argv)) { + puts(*argv); + continue; + } + status = EXIT_FAILURE; + } else { + char *path2 = xstrdup(path); + char *tmp = path2; + + p = find_execable(*argv, &tmp); + if (!p) + status = EXIT_FAILURE; + else { + print: + puts(p); + free(p); + if (opt) { + /* -a: show matches in all PATH components */ + if (tmp) { + p = find_execable(*argv, &tmp); + if (p) + goto print; + } + } + } + free(path2); + } +#else +/* Just ignoring -a */ + if (strchr(*argv, '/')) { + if (execable_file(*argv)) { + puts(*argv); + continue; + } + } else { + char *path2 = xstrdup(path); + char *tmp = path2; + p = find_execable(*argv, &tmp); + free(path2); + if (p) { + puts(p); + free(p); + continue; + } + } + status = EXIT_FAILURE; +#endif + } while (*(++argv) != NULL); + + fflush_stdout_and_exit(status); +} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..3d1c5bd8a --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +/busybox.1 +/BusyBox.html +/busybox.net +/BusyBox.txt +/busybox.pod diff --git a/docs/Serial-Programming-HOWTO.txt b/docs/Serial-Programming-HOWTO.txt new file mode 100644 index 000000000..8a3954b09 --- /dev/null +++ b/docs/Serial-Programming-HOWTO.txt @@ -0,0 +1,424 @@ +Downloaded from http://www.lafn.org/~dave/linux/Serial-Programming-HOWTO.txt +Seems to be somewhat old, but contains useful bits for getty.c hacking +============================================================================ + + The Linux Serial Programming HOWTO, Part 1 of 2 + By Vernon C. Hoxie + v2.0 10 September 1999 + + This document describes how to program communications with devices + over a serial port on a Linux box. + ______________________________________________________________________ + + Table of Contents + + 1. Copyright + + 2. Introduction + + 3. Opening + + 4. Commands + + 5. Changing Baud Rates + + 6. Additional Control Calls + + 6.1 Sending a "break". + 6.2 Hardware flow control. + 6.3 Flushing I/O buffers. + + 7. Modem control + + 8. Process Groups + + 8.1 Sessions + 8.2 Process Groups + 8.3 Controlling Terminal + 8.3.1 Get the foreground group process id. + 8.3.2 Set the foreground process group id of a terminal. + 8.3.3 Get process group id. + + 9. Lockfiles + + 10. Additional Information + + 11. Feedback + + ______________________________________________________________________ + + 1. Copyright + + The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Vernon + Hoxie. Linux HOWTO documents may be reproduced and distributed in + whole or in part, in any medium physical or electronic, as long as + this copyright notice is retained on all copies. Commercial + redistribution is allowed and encouraged; however, the author would + like to be notified of any such distributions. + + All translations, derivative works, or aggregate works incorporating + this Linux HOWTO document must be covered under this copyright notice. + That is, you may not produce a derivative work from this HOWTO and + impose additional restrictions on its distribution. + + This version is a complete rewrite of the previous Serial-Programming- + HOWTO by Peter H. Baumann, + + 2. Introduction + + This HOWTO will attempt to give hints about how to write a program + which needs to access a serial port. Its principal focus will be on + the Linux implementation and what the meaning of the various library + functions available. + + Someone asked about which of several sequences of operations was + right. There is no absolute right way to accomplish an outcome. The + options available are too numerous. If your sequences produces the + desired results, then that is the right way for you. Another + programmer may select another set of options and get the same results. + His method is right for him. + + Neither of these methods may operate properly with some other + implementation of UNIX. It is strange that many of the concepts which + were implemented in the SYSV version have been dumped. Because UNIX + was developed by AT&T and much code has been generated on those + concepts, the AT&T version should be the standard to which others + should emulate. + + Now the standard is POSIX. + + It was once stated that the popularity of UNIX and C was that they + were created by programmers for programmers. Not by scholars who + insist on purity of style in deference to results and simplicity of + use. Not by committees with people who have diverse personal or + proprietary agenda. Now ANSI and POSIX have strayed from those + original clear and simply concepts. + + 3. Opening + + The various serial devices are opened just as any other file. + Although, the fopen(3) command may be used, the plain open(2) is + preferred. This call returns the file descriptor which is required + for the various commands that configure the interface. + + Open(2) has the format: + + #include + int open(char *path, int flags, [int mode]); + + In addition to the obvious O_RDWR, O_WRONLY and O_RDONLY, two + additional flags are available. These are O_NONBLOCK and O_NOCTTY. + Other flags listed in the open(2) manual page are not applicable to + serial devices. + + Normally, a serial device opens in "blocking" mode. This means that + the open() will not return until the Carrier Detect line from the port + is active, e.g. modem, is active. When opened with the O_NONBLOCK + flag set, the open() will return immediately regardless of the status + of the DCD line. The "blocking" mode also affects the read() call. + + The fcntl(2) command can be used to change the O_NONBLOCK flag anytime + after the device has been opened. + + The device driver and the data passing through it are controlled + according to settings in the struct termios. This structure is + defined in "/usr/include/termios.h". In the Linux tree, further + reference is made to "/usr/include/asm/termbits.h". + In blocking mode, a read(2) will block until data is available or a + signal is received. It is still subject to state of the ICANON flag. + + When the termios.c_lflag ICANON bit is set, input data is collected + into strings until a NL, EOF or EOL character is received. You can + define these in the termios.c_cc[] array. Also, ERASE and KILL + characters will operate on the incoming data before it is delivered to + the user. + + In non-canonical mode, incoming data is quantified by use of the + c_cc[VMIN and c_cc[VTIME] values in termios.c_cc[]. + + Some programmers use the select() call to detect the completion of a + read(). This is not the best way of checking for incoming data. + Select() is part of the SOCKETS scheme and too complex for most + applications. + + A full explanation of the fields of the termios structure is contained + in termios(7) of the Users Manual. A version is included in Part 2 of + this HOWTO document. + + 4. Commands + + Changes to the struct termios are made by retrieving the current + settings, making the desired changes and transmitting the modified + structure back to the kernel. + + The historic means of communicating with the kernel was by use of the + ioctl(fd, COMMAND, arg) system call. Then the purists in the + computer industry decided that this was not genetically consistent. + Their argument was that the argument changed its stripes. Sometimes + it was an int, sometimes it was a pointer to int and other times it + was a pointer to struct termios. Then there were those times it was + empty or NULL. These variations are dependent upon the COMMAND. + + As a alternative, the tc* series of functions were concocted. + + These are: + + int tcgetattr(int filedes, struct termios *termios_p); + int tcsetattr(int filedes, int optional_actions, + const struct termios *termios_p); + + instead of: + + int ioctl(int filedes, int command, + struct termios *termios_p); + + where command is TCGETS or one of TCSETS, TCSETSW or TCSETSF. + + The TCSETS command is comparable to the TCSANOW optional_action for + the tc* version. These direct the kernel to adopt the changes + immediately. Other pairs are: + + command optional_action Meaning + TCSETSW TCSADRAIN Change after all output has drained. + TCSETSF TCSAFLUSH Change after all output has drained + then discard any input characters + not read. + + Since the return code from either the ioctl(2) or the tcsetattr(2) + commands only indicate that the command was processed by the kernel. + These do not indicate whether or not the changes were actually + accomplished. Either of these commands should be followed by a call + to: + + ioctl(fd, TCGETS, &new_termios); + + or: + + tcgetattr(fd, &new_termios); + + A user function which makes changes to the termios structure should + define two struct termios variables. One of these variables should + contain the desired configuration. The other should contain a copy of + the kernels version. Then after the desired configuration has been + sent to the kernel, another call should be made to retrieve the + kernels version. Then the two compared. + + Here is an example of how to add RTS/CTS flow control: + + struct termios my_termios; + struct termios new_termios; + + tcgetattr(fd, &my_termios); + my_termios.c_flag |= CRTSCTS; + tcsetattr(fd, TCSANOW, &my_termios); + tcgetattr(fd, &new_termios); + if (memcmp(my_termios, new_termios, + sizeof(my_termios)) != 0) { + /* do some error handling */ + } + + 5. Changing Baud Rates + + With Linux, the baud rate can be changed using a technique similar to + add/delete RTS/CTS. + + struct termios my_termios; + struct termios new_termios; + + tcgetattr(fd, &my_termios); + my_termios.c_flag &= ~CBAUD; + my_termios.c_flag |= B19200; + tcsetattr(fd, TCSANOW, &my_termios); + tcgetattr(fd, &new_termios); + if (memcmp(my_termios, new_termios, + sizeof(my_termios)) != 0) { + /* do some error handling */ + } + + POSIX adds another method. They define: + + speed_t cfgetispeed(const struct termios *termios_p); + speed_t cfgetospeed(const struct termios *termios_p); + + library calls to extract the current input or output speed from the + struct termios pointed to with *termio_p. This is a variable defined + in the calling process. In practice, the data contained in this + termios, should be obtained by the tcgetattr() call or an ioctl() call + using the TCGETS command. + + The companion library calls are: + + int cfsetispeed(struct termios *termios_p, speed_t speed); + int cfsetospeed(struct termios *termios_p, speed_t speed); + + which are used to change the value of the baud rate in the locally + defined *termios_p. Following either of these calls, either a call to + tcsetattr() or ioctl() with one of TCSETS, TCSETSW or TCSETSF as the + command to transmit the change to the kernel. + + The cf* commands are preferred for portability. Some weird Unices use + a considerably different format of termios. + + Most implementations of Linux use only the input speed for both input + and output. These functions are defined in the application program by + reference to . In reality, they are in + /usr/include/asm/termbits.h. + + 6. Additional Control Calls + + 6.1. Sending a "break". + + int ioctl(fd, TCSBRK, int arg); + int tcsendbreak(fd, int arg); + + Send a break: Here the action differs between the conventional + ioctl() call and the POSIX call. For the conventional call, an arg of + '0' sets the break control line of the UART for 0.25 seconds. For the + POSIX command, the break line is set for arg times 0.1 seconds. + + 6.2. Hardware flow control. + + int ioctl(fd, TCXONC, int action); + int tcflow(fd, int action); + + The action flags are: + + o TCOOFF 0 suspend output + + o TCOON 1 restart output + + o TCIOFF 2 transmit STOP character to suspend input + + o TCION 3 transmit START character to restart input + + 6.3. Flushing I/O buffers. + + int ioctl(fd, TCFLSH, queue_selector); + int tcflush(fd, queue_selector); + + The queue_selector flags are: + + o TCIFLUSH 0 flush any data not yet read from the input buffer + + o TCOFLUSH 1 flush any data written to the output buffer but not + yet transmitted + + o TCIOFLUSH 2 flush both buffers + + 7. Modem control + + The hardware modem control lines can be monitored or modified by the + ioctl(2) system call. A set of comparable tc* calls apparently do not + exist. The form of this call is: + + int ioctl(fd, COMMAND, (int *)flags); + + The COMMANDS and their action are: + + o TIOCMBIS turn on control lines depending upon which bits are set + in flags. + + o TIOCMBIC turn off control lines depending upon which bits are + unset in flags. + o TIOCMGET the appropriate bits are set in flags according to the + current status + + o TIOCMSET the state of the UART is changed according to which bits + are set/unset in 'flags' + + The bit pattern of flags refer to the following control lines: + + o TIOCM_LE Line enable + + o TIOCM_DTR Data Terminal Ready + + o TIOCM_RTS Request to send + + o TIOCM_ST Secondary transmit + + o TIOCM_SR Secondary receive + + o TIOCM_CTS Clear to send + + o TIOCM_CAR Carrier detect + + o TIOCM_RNG Ring + + o TIOCM_DSR Data set ready + + It should be noted that some of these bits are controlled by the modem + and the UART cannot change them but their status can be sensed by + TIOCMGET. Also, most Personal Computers do not provide hardware for + secondary transmit and receive. + + There are also a pair of ioctl() to monitor these lines. They are + undocumented as far as I have learned. The commands are TIOCMIWAIT + and TCIOGICOUNT. They also differ between versions of the Linux + kernel. + + See the lines.c file in my "serial_suite" for an example of how these + can be used see + + 8. Process Groups + + 8.1. Sessions + + 8.2. Process Groups + + Any newly created process inherits the Process Group of its creator. + The Process Group leader has the same PID as PGID. + + 8.3. Controlling Terminal + + There are a series of ioctl(2) and tc*(2) calls which can be used to + monitor or to change the process group to which the device is + attached. + + 8.3.1. Get the foreground group process id. + + If there is no foreground group, a number not representing an existing + process group is returned. On error, a -1 is returned and errno is + set. + + int ioctl(fd, TIOCGPGRP, (pid_t *)pid); + int tcgetpgrp(fd, (pid_t *)pid); + + 8.3.2. Set the foreground process group id of a terminal. + + The fd must be the controlling terminal and be associated with the + session of the calling process. + + int ioctl(fd, TIOCSPGRP, (pid_t *)pid); + int tcsetpgrp(fd, (pid_t *)pid); + + 8.3.3. Get process group id. + + int ioctl(fd, TIOCGPGRP, &(pid_t)pid); + int tcgetpgrp(fd, &(pid_t)pid); + + 9. Lockfiles + + Any process which accesses a serial device should first check for the + existence of lock file for the desired device. If such a lock lock + file exists, this means that the device may be in use by another + process. + + Check my "libdevlocks-x.x.tgz" at + for an example of how these lock + files should be utilized. + + 10. Additional Information + + Check out my "serial_suite.tgz" for more information about programming + the serial ports at . There some + examples and some blurbs about setting up modems and comments about + some general considerations. + + 11. Feedback + + Please send me any corrections, questions, comments, suggestions, or + additional material. I would like to improve this HOWTO! Tell me + exactly what you don't understand, or what could be clearer. You can + reach me at via email. Please + include the version number of the Serial-Programming-HOWTO when + writing. diff --git a/docs/busybox_footer.pod b/docs/busybox_footer.pod new file mode 100644 index 000000000..c346c736b --- /dev/null +++ b/docs/busybox_footer.pod @@ -0,0 +1,285 @@ +=back + +=head1 LIBC NSS + +GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior +of the C library for the local environment, and to configure how it reads +system data, such as passwords and group information. This is implemented +using an /etc/nsswitch.conf configuration file, and using one or more of the +/lib/libnss_* libraries. BusyBox tries to avoid using any libc calls that make +use of NSS. Some applets however, such as login and su, will use libc functions +that require NSS. + +If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to +directly access the /etc/passwd, /etc/group, and /etc/shadow files without +using NSS. This may allow you to run your system without the need for +installing any of the NSS configuration files and libraries. + +When used with glibc, the BusyBox 'networking' applets will similarly require +that you install at least some of the glibc NSS stuff (in particular, +/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*). + +Shameless Plug: As an alternative, one could use a C library such as uClibc. In +addition to making your system significantly smaller, uClibc does not require the +use of any NSS support files or libraries. + +=head1 MAINTAINER + +Denis Vlasenko + +=head1 AUTHORS + +The following people have contributed code to BusyBox whether they know it or +not. If you have written code included in BusyBox, you should probably be +listed here so you can obtain your bit of eternal glory. If you should be +listed here, or the description of what you have done needs more detail, or is +incorrect, please send in an update. + + +=for html
+ +Emanuele Aina + run-parts + +=for html
+ +Erik Andersen + + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + Lots of tedious effort writing these boring docs that + nobody is going to actually read. + +=for html
+ +Laurence Anderson + + rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm + +=for html
+ +Jeff Angielski + + ftpput, ftpget + +=for html
+ +Edward Betts + + expr, hostid, logname, whoami + +=for html
+ +John Beppu + + du, nslookup, sort + +=for html
+ +Brian Candler + + tiny-ls(ls) + +=for html
+ +Randolph Chung + + fbset, ping, hostname + +=for html
+ +Dave Cinege + + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +=for html
+ +Jordan Crouse + + ipcalc + +=for html
+ +Magnus Damm + + tftp client insmod powerpc support + +=for html
+ +Larry Doolittle + + pristine source directory compilation, lots of patches and fixes. + +=for html
+ +Glenn Engel + + httpd + +=for html
+ +Gennady Feldman + + Sysklogd (single threaded syslogd, IPC Circular buffer support, + logread), various fixes. + +=for html
+ +Karl M. Hegbloom + + cp_mv.c, the test suite, various fixes to utility.c, &c. + +=for html
+ +Daniel Jacobowitz + + mktemp.c + +=for html
+ +Matt Kraai + + documentation, bugfixes, test suite + +=for html
+ +Stephan Linz + + ipcalc, Red Hat equivalence + +=for html
+ +John Lombardo + + tr + +=for html
+ +Glenn McGrath + + Common unarchiving code and unarchiving applets, ifupdown, ftpgetput, + nameif, sed, patch, fold, install, uudecode. + Various bugfixes, review and apply numerous patches. + +=for html
+ +Manuel Novoa III + + cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, + mesg, vconfig, make_directory, parse_mode, dirname, mode_string, + get_last_path_component, simplify_path, and a number trivial libbb routines + + also bug fixes, partial rewrites, and size optimizations in + ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, + mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, + interface, dutmp, ifconfig, route + +=for html
+ +Vladimir Oleynik + + cmdedit; xargs(current), httpd(current); + ports: ash, crond, fdisk, inetd, stty, traceroute, top; + locale, various fixes + and irreconcilable critic of everything not perfect. + +=for html
+ +Bruce Perens + + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + +=for html
+ +Tim Riker + + bug fixes, member of fan club + +=for html
+ +Kent Robotti + + reset, tons and tons of bug reports and patches. + +=for html
+ +Chip Rosenthal , + + wget - Contributed by permission of Covad Communications + +=for html
+ +Pavel Roskin + + Lots of bugs fixes and patches. + +=for html
+ +Gyepi Sam + + Remote logging feature for syslogd + +=for html
+ +Linus Torvalds + + mkswap, fsck.minix, mkfs.minix + +=for html
+ +Mark Whitley + + grep, sed, cut, xargs(previous), + style-guide, new-applet-HOWTO, bug fixes, etc. + +=for html
+ +Charles P. Wright + + gzip, mini-netcat(nc) + +=for html
+ +Enrique Zanardi + + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +=for html
+ +Tito Ragusa + + devfsd and size optimizations in strings, openvt and deallocvt. + +=for html
+ +Paul Fox + + vi editing mode for ash, various other patches/fixes + +=for html
+ +Roberto A. Foglietta + + port: dnsd + +=for html
+ +Bernhard Reutner-Fischer + + misc + +=for html
+ +Mike Frysinger + + initial e2fsprogs, printenv, setarch, sum, misc + +=for html
+ +Jie Zhang + + fixed two bugs in msh and hush (exitcode of killed processes) + +=cut diff --git a/docs/busybox_header.pod b/docs/busybox_header.pod new file mode 100644 index 000000000..85a173e82 --- /dev/null +++ b/docs/busybox_header.pod @@ -0,0 +1,82 @@ +# vi: set sw=4 ts=4: + +=head1 NAME + +BusyBox - The Swiss Army Knife of Embedded Linux + +=head1 SYNTAX + + busybox [arguments...] # or + + [arguments...] # if symlinked + +=head1 DESCRIPTION + +BusyBox combines tiny versions of many common UNIX utilities into a single +small executable. It provides minimalist replacements for most of the utilities +you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox +generally have fewer options than their full-featured GNU cousins; however, the +options that are included provide the expected functionality and behave very +much like their GNU counterparts. + +BusyBox has been written with size-optimization and limited resources in mind. +It is also extremely modular so you can easily include or exclude commands (or +features) at compile time. This makes it easy to customize your embedded +systems. To create a working system, just add /dev, /etc, and a Linux kernel. +BusyBox provides a fairly complete POSIX environment for any small or embedded +system. + +BusyBox is extremely configurable. This allows you to include only the +components you need, thereby reducing binary size. Run 'make config' or 'make +menuconfig' to select the functionality that you wish to enable. Then run +'make' to compile BusyBox using your configuration. + +After the compile has finished, you should use 'make install' to install +BusyBox. This will install the 'bin/busybox' binary, in the target directory +specified by CONFIG_PREFIX. CONFIG_PREFIX can be set when configuring BusyBox, +or you can specify an alternative location at install time (i.e., with a +command line like 'make CONFIG_PREFIX=/tmp/foo install'). If you enabled +any applet installation scheme (either as symlinks or hardlinks), these will +also be installed in the location pointed to by CONFIG_PREFIX. + +=head1 USAGE + +BusyBox is a multi-call binary. A multi-call binary is an executable program +that performs the same job as more than one utility program. That means there +is just a single BusyBox binary, but that single binary acts like a large +number of utilities. This allows BusyBox to be smaller since all the built-in +utility programs (we call them applets) can share code for many common +operations. + +You can also invoke BusyBox by issuing a command as an argument on the +command line. For example, entering + + /bin/busybox ls + +will also cause BusyBox to behave as 'ls'. + +Of course, adding '/bin/busybox' into every command would be painful. So most +people will invoke BusyBox using links to the BusyBox binary. + +For example, entering + + ln -s /bin/busybox ls + ./ls + +will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled +into BusyBox). Generally speaking, you should never need to make all these +links yourself, as the BusyBox build system will do this for you when you run +the 'make install' command. + +If you invoke BusyBox with no arguments, it will provide you with a list of the +applets that have been compiled into your BusyBox binary. + +=head1 COMMON OPTIONS + +Most BusyBox applets support the B<--help> argument to provide a terse runtime +description of their behavior. If the CONFIG_FEATURE_VERBOSE_USAGE option has +been enabled, more detailed usage information will also be available. + +=head1 COMMANDS + +Currently available applets include: diff --git a/docs/cgi/cl.html b/docs/cgi/cl.html new file mode 100644 index 000000000..4f8faae9f --- /dev/null +++ b/docs/cgi/cl.html @@ -0,0 +1,46 @@ +CGI Command line options

CGI Command line options

+

+ +

Specification

+ +The command line is only used in the case of an ISINDEX query. It is +not used in the case of an HTML form or any as yet undefined query +type. The server should search the query information (the QUERY_STRING environment variable) for a non-encoded += character to determine if the command line is to be used, if it +finds one, the command line is not to be used. This trusts the clients +to encode the = sign in ISINDEX queries, a practice which was +considered safe at the time of the design of this specification.

+ +For example, use the finger script and the ISINDEX interface to look up "httpd". You will see that the script will call itself with /cgi-bin/finger?httpd and will actually execute "finger httpd" on the command line and output the results to you. +

+If the server does find a "=" in the QUERY_STRING, +then the command line will not be used, and no decoding will be +performed. The query then remains intact for processing by an +appropriate FORM submission decoder. +Again, as an example, use this hyperlink to submit "httpd=name" to the finger script. Since this QUERY_STRING +contained an unencoded "=", nothing was decoded, the script didn't know +it was being submitted a valid query, and just gave you the default +finger form. +

+If the server finds that it cannot send the string due to internal +limitations (such as exec() or /bin/sh command line restrictions) the +server should include NO command line information and provide the +non-decoded query information in the environment +variable QUERY_STRING.

+


+

Examples

+ +Examples of the command line usage are much better demonstrated than explained. For these +examples, pay close attention to the script output which says what +argc and argv are.

+ +


+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ + + diff --git a/docs/cgi/env.html b/docs/cgi/env.html new file mode 100644 index 000000000..b83c750bf --- /dev/null +++ b/docs/cgi/env.html @@ -0,0 +1,149 @@ +CGI Environment Variables

CGI Environment Variables

+
+ +

+ +In order to pass data about the information request from the server to +the script, the server uses command line arguments as well as +environment variables. These environment variables are set when the +server executes the gateway program.

+ +


+

Specification

+ +

+The following environment variables are not request-specific and are +set for all requests:

+ +

    +
  • SERVER_SOFTWARE

    + + The name and version of the information server software answering + the request (and running the gateway). Format: name/version

    + +

  • SERVER_NAME

    + The server's hostname, DNS alias, or IP address as it would appear + in self-referencing URLs.

    + +

  • GATEWAY_INTERFACE

    + The revision of the CGI specification to which this server + complies. Format: CGI/revision

    + +

+ +
+ +The following environment variables are specific to the request being +fulfilled by the gateway program:

+ +

    +
  • SERVER_PROTOCOL

    + The name and revision of the information protcol this request came + in with. Format: protocol/revision

    + +

  • SERVER_PORT

    + The port number to which the request was sent.

    + +

  • REQUEST_METHOD

    + The method with which the request was made. For HTTP, this is + "GET", "HEAD", "POST", etc.

    + +

  • PATH_INFO

    + The extra path information, as given by the client. In other + words, scripts can be accessed by their virtual pathname, followed + by extra information at the end of this path. The extra + information is sent as PATH_INFO. This information should be + decoded by the server if it comes from a URL before it is passed + to the CGI script.

    + +

  • PATH_TRANSLATED

    + The server provides a translated version of PATH_INFO, which takes + the path and does any virtual-to-physical mapping to it.

    + +

  • SCRIPT_NAME

    + A virtual path to the script being executed, used for + self-referencing URLs.

    + +

  • QUERY_STRING

    + The information which follows the ? in the URL + which referenced this script. This is the query information. It + should not be decoded in any fashion. This variable should always + be set when there is query information, regardless of command line decoding.

    + +

  • REMOTE_HOST

    + The hostname making the request. If the server does not have this + information, it should set REMOTE_ADDR and leave this unset.

    + +

  • REMOTE_ADDR

    + The IP address of the remote host making the request.

    + +

  • AUTH_TYPE

    + If the server supports user authentication, and the script is + protects, this is the protocol-specific authentication method used + to validate the user.

    + +

  • REMOTE_USER

    + If the server supports user authentication, and the script is + protected, this is the username they have authenticated as.

    +

  • REMOTE_IDENT

    + If the HTTP server supports RFC 931 identification, then this + variable will be set to the remote user name retrieved from the + server. Usage of this variable should be limited to logging only. +

    + +

  • CONTENT_TYPE

    + For queries which have attached information, such as HTTP POST and + PUT, this is the content type of the data.

    + +

  • CONTENT_LENGTH

    + The length of the said content as given by the client.

    + +

+ + +
+ +In addition to these, the header lines received from the client, if +any, are placed into the environment with the prefix HTTP_ followed by +the header name. Any - characters in the header name are changed to _ +characters. The server may exclude any headers which it has already +processed, such as Authorization, Content-type, and Content-length. If +necessary, the server may choose to exclude any or all of these +headers if including them would exceed any system environment +limits.

+ +An example of this is the HTTP_ACCEPT variable which was defined in +CGI/1.0. Another example is the header User-Agent.

+ +

    +
  • HTTP_ACCEPT

    + The MIME types which the client will accept, as given by HTTP + headers. Other protocols may need to get this information from + elsewhere. Each item in this list should be separated by commas as + per the HTTP spec.

    + + Format: type/subtype, type/subtype

    + + +

  • HTTP_USER_AGENT

    + + The browser the client is using to send the request. General +format: software/version library/version.

    + +

+ +
+

Examples

+ +Examples of the setting of environment variables are really much better +demonstrated than explained.

+ +


+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ + diff --git a/docs/cgi/in.html b/docs/cgi/in.html new file mode 100644 index 000000000..7ee5fe601 --- /dev/null +++ b/docs/cgi/in.html @@ -0,0 +1,33 @@ +CGI Script input

CGI Script Input

+
+ +

Specification

+ +For requests which have information attached after the header, such as +HTTP POST or PUT, the information will be sent to the script on stdin. +

+ +The server will send CONTENT_LENGTH bytes on +this file descriptor. Remember that it will give the CONTENT_TYPE of the data as well. The server is +in no way obligated to send end-of-file after the script reads +CONTENT_LENGTH bytes.

+


+

Example

+ +Let's take a form with METHOD="POST" as an example. Let's say the form +results are 7 bytes encoded, and look like a=b&b=c. +

+ +In this case, the server will set CONTENT_LENGTH to 7 and CONTENT_TYPE +to application/x-www-form-urlencoded. The first byte on the script's +standard input will be "a", followed by the rest of the encoded string.

+ +


+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ + diff --git a/docs/cgi/interface.html b/docs/cgi/interface.html new file mode 100644 index 000000000..0be016b4c --- /dev/null +++ b/docs/cgi/interface.html @@ -0,0 +1,29 @@ +The Common Gateway Interface Specification +[http://hoohoo.ncsa.uiuc.edu/cgi/interface.html] +

The CGI Specification

+ +
+ +This is the specification for CGI version 1.1, or CGI/1.1. Further +revisions of this protocol are guaranteed to be backward compatible. +

+ +The server and the CGI script communicate in four major ways. Each of +the following is a hotlink to graphic detail.

+ +

+
+ + +[Back]Return to the overview

+ + + +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ diff --git a/docs/cgi/out.html b/docs/cgi/out.html new file mode 100644 index 000000000..5266985b5 --- /dev/null +++ b/docs/cgi/out.html @@ -0,0 +1,126 @@ +CGI Script output

CGI Script Output

+
+ +

Script output

+ +The script sends its output to stdout. This output can either be a +document generated by the script, or instructions to the server for +retrieving the desired output.

+


+ +

Script naming conventions

+ +Normally, scripts produce output which is interpreted and sent back to +the client. An advantage of this is that the scripts do not need to +send a full HTTP/1.0 header for every request.

+ +Some scripts may want to avoid the extra overhead of the server +parsing their output, and talk directly to the client. In order to +distinguish these scripts from the other scripts, CGI requires that +the script name begins with nph- if a script does not want the server +to parse its header. In this case, it is the script's responsibility +to return a valid HTTP/1.0 (or HTTP/0.9) response to the client.

+ +


+

Parsed headers

+ +The output of scripts begins with a small header. This header consists +of text lines, in the same format as an +HTTP header, terminated by a blank line (a line with only a +linefeed or CR/LF).

+ +Any headers which are not server directives are sent directly back to +the client. Currently, this specification defines three server +directives:

+ +

    +
  • Content-type

    + + This is the MIME type of the document you are returning.

    + +

  • Location

    + + This is used to specify to the server that you are returning a + reference to a document rather than an actual document.

    + + If the argument to this is a URL, the server will issue a redirect + to the client.

    + + If the argument to this is a virtual path, the server will + retrieve the document specified as if the client had requested + that document originally. ? directives will work in here, but # + directives must be redirected back to the client.

    + + +

  • Status

    + + This is used to give the server an HTTP/1.0 status +line to send to the client. The format is nnn xxxxx, +where nnn is the 3-digit status code, and +xxxxx is the reason string, such as "Forbidden".

    + +

+ +
+

Examples

+ +Let's say I have a fromgratz to HTML converter. When my converter is +finished with its work, it will output the following on stdout (note +that the lines beginning and ending with --- are just for illustration +and would not be output):

+ +

--- start of output ---
+Content-type: text/html
+
+--- end of output ---
+
+ +Note the blank line after Content-type.

+ +Now, let's say I have a script which, in certain instances, wants to +return the document /path/doc.txt from this server just +as if the user had actually requested +http://server:port/path/doc.txt to begin with. In this +case, the script would output:

+

--- start of output ---
+Location: /path/doc.txt
+
+--- end of output ---
+
+ +The server would then perform the request and send it to the client. +

+ +Let's say that I have a script which wants to reference our gopher +server. In this case, if the script wanted to refer the user to +gopher://gopher.ncsa.uiuc.edu/, it would output:

+ +

--- start of output ---
+Location: gopher://gopher.ncsa.uiuc.edu/
+
+--- end of output ---
+
+ +Finally, I have a script which wants to talk to the client directly. +In this case, if the script is referenced with SERVER_PROTOCOL of HTTP/1.0, +the script would output the following HTTP/1.0 response:

+ +

--- start of output ---
+HTTP/1.0 200 OK
+Server: NCSA/1.0a6
+Content-type: text/plain
+
+This is a plaintext document generated on the fly just for you.
+
+--- end of output ---
+
+ + +
+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ diff --git a/docs/contributing.txt b/docs/contributing.txt new file mode 100644 index 000000000..e3289fd49 --- /dev/null +++ b/docs/contributing.txt @@ -0,0 +1,439 @@ +Contributing To Busybox +======================= + +This document describes what you need to do to contribute to Busybox, where +you can help, guidelines on testing, and how to submit a well-formed patch +that is more likely to be accepted. + +The Busybox home page is at: http://busybox.net/ + + + +Pre-Contribution Checklist +-------------------------- + +So you want to contribute to Busybox, eh? Great, wonderful, glad you want to +help. However, before you dive in, headlong and hotfoot, there are some things +you need to do: + + +Checkout the Latest Code +~~~~~~~~~~~~~~~~~~~~~~~~ + +This is a necessary first step. Please do not try to work with the last +released version, as there is a good chance that somebody has already fixed +the bug you found. Somebody might have even added the feature you had in mind. +Don't make your work obsolete before you start! + +For information on how to check out Busybox development tree, please look at the +following links: + + http://busybox.net/source.html + + +Read the Mailing List +~~~~~~~~~~~~~~~~~~~~~ + +No one is required to read the entire archives of the mailing list, but you +should at least read up on what people have been talking about lately. If +you've recently discovered a problem, chances are somebody else has too. If +you're the first to discover a problem, post a message and let the rest of us +know. + +Archives can be found here: + + http://busybox.net/lists/busybox/ + +If you have a serious interest in Busybox, i.e., you are using it day-to-day or +as part of an embedded project, it would be a good idea to join the mailing +list. + +A web-based sign-up form can be found here: + + http://busybox.net/mailman/listinfo/busybox + + +Coordinate with the Applet Maintainer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some (not all) of the applets in Busybox are "owned" by a maintainer who has +put significant effort into it and is probably more familiar with it than +others. To find the maintainer of an applet, look at the top of the .c file +for a name following the word 'Copyright' or 'Written by' or 'Maintainer'. + +Before plunging ahead, it's a good idea to send a message to the mailing list +that says: "Hey, I was thinking about adding the 'transmogrify' feature to the +'foo' applet. Would this be useful? Is anyone else working on it?" You might +want to CC the maintainer (if any) with your question. + + + +Areas Where You Can Help +------------------------ + +Busybox can always use improvement! If you're looking for ways to help, there +are a variety of areas where you could help. + + +What Busybox Doesn't Need +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before listing the areas where you _can_ help, it's worthwhile to mention the +areas where you shouldn't bother. While Busybox strives to be the "Swiss Army +Knife" of embedded Linux, there are some applets that will not be accepted: + + - Any filesystem manipulation tools: Busybox is filesystem independent and + we do not want to start adding mkfs/fsck tools for every (or any) + filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on + borrowed time.) There are far too many of these tools out there. Use + the upstream version. Rationale: bugs in these tools can destroy + vast amounts of data. Keeping up with filesystem format development + is impractical (especially in the area of keeping fsck tool safe + and up-to-date). + + - Any disk, device, or media-specific tools: Use the -utils or -tools package + that was designed for your device; don't try to shoehorn them into Busybox. + + - Any architecture specific tools: Busybox is (or should be) architecture + independent. Do not send us tools that cannot be used across multiple + platforms / arches. + + +Bug Reporting +~~~~~~~~~~~~~ + +If you find bugs, please submit a detailed bug report to the busybox mailing +list at busybox@busybox.net. A well-written bug report should include a +transcript of a shell session that demonstrates the bad behavior and enables +anyone else to duplicate the bug on their own machine. The following is such +an example: + + To: busybox@busybox.net + From: diligent@testing.linux.org + Subject: /bin/date doesn't work + + Package: busybox + Version: 1.00 + + When I execute Busybox 'date' it produces unexpected results. + With GNU date I get the following output: + + $ date + Wed Mar 21 14:19:41 MST 2001 + + But when I use BusyBox date I get this instead: + + $ date + Illegal instruction + + I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder, + and the latest uClibc from CVS. + + -Diligent + +Note the careful description and use of examples showing not only what BusyBox +does, but also a counter example showing what an equivalent GNU app does. Bug +reports lacking such detail may never be fixed... Thanks for understanding. + + + +Write Documentation +~~~~~~~~~~~~~~~~~~~ + +Chances are, documentation in Busybox is either missing or needs improvement. +Either way, help is welcome. + +Work is being done to automatically generate documentation from sources, +especially from the usage.h file. If you want to correct the documentation, +please make changes to the pre-generation parts, rather than the generated +documentation. [More to come on this later...] + +It is preferred that modifications to documentation be submitted in patch +format (more on this below), but we're a little more lenient when it comes to +docs. You could, for example, just say "after the listing of the mount +options, the following example would be helpful..." + + +Consult Existing Sources +~~~~~~~~~~~~~~~~~~~~~~~~ + +For a quick listing of "needs work" spots in the sources, cd into the Busybox +directory and run the following: + + for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done + +This will show all of the trouble spots or 'questionable' code. Pick a spot, +any spot, these are all invitations for you to contribute. + + +Add a New Applet +~~~~~~~~~~~~~~~~ + +If you want to add a new applet to Busybox, we'd love to see it. However, +before you write any code, please ask beforehand on the mailing list something +like "Do you think applet 'foo' would be useful in Busybox?" or "Would you +guys accept applet 'foo' into Busybox if I were to write it?" If the answer is +"no" by the folks on the mailing list, then you've saved yourself some time. +Conversely, you could get some positive responses from folks who might be +interested in helping you implement it, or can recommend the best approach. +Perhaps most importantly, this is your way of calling "dibs" on something and +avoiding duplication of effort. + +Also, before you write a line of code, please read the 'new-applet-HOWTO.txt' +file in the docs/ directory. + + +Janitorial Work +~~~~~~~~~~~~~~~ + +These are dirty jobs, but somebody's gotta do 'em. + + - Security audits: + http://www.securityfocus.com/popups/forums/secprog/intro.shtml + + - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This + is very Perl-specific, but the advice given in here applies equally well to + C. + + - C library function use audits: Verifying that functions are being used + properly (called with the right args), replacing unsafe library functions + with safer versions, making sure return codes are being checked, etc. + + - Where appropriate, replace preprocessor defined macros and values with + compile-time equivalents. + + - Style guide compliance. See: docs/style-guide.txt + + - Add testcases to tests/testcases. + + - Makefile improvements: + http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html + (I think the recursive problems are pretty much taken care of at this point, non?) + + - "Ten Commandments" compliance: (this is a "maybe", certainly not as + important as any of the previous items.) + http://www.lysator.liu.se/c/ten-commandments.html + +Other useful links: + + - the comp.lang.c FAQ: http://home.datacomm.ch/t_wolf/tw/c/index.html#Sources + + + +Submitting Patches To Busybox +----------------------------- + +Here are some guidelines on how to submit a patch to Busybox. + + +Making A Patch +~~~~~~~~~~~~~~ + +If you've got anonymous Git access set up, making a patch is simple. Just make +sure you're in the busybox/ directory and type: + + git diff -b -w > mychanges.patch + +You can send the resulting .patch file to the mailing list with a description +of what it does. (But not before you test it! See the next section for some +guidelines.) It is preferred that patches be sent as attachments, but it is +not required. + +Also, feel free to help test other people's patches and reply to them with +comments. You can apply a patch by saving it into your busybox/ directory and +typing: + + patch -p1 < mychanges.patch + +Then you can recompile, see if it runs, test if it works as advertised, and +post your findings to the mailing list. + +NOTE: Please do not include extraneous or irrelevant changes in your patches. +Please do not try to "bundle" two patches together into one. Make single, +discreet changes on a per-patch basis. Sometimes you need to make a patch that +touches code in many places, but these kind of patches are rare and should be +coordinated with a maintainer. + + +Testing Guidelines +~~~~~~~~~~~~~~~~~~ + +It's considered good form to test your new feature before you submit a patch +to the mailing list, and especially before you push a change to Git. Here +are some guidelines on how to test your changes. + + - Always test Busybox applets against GNU counterparts and make sure the + behavior / output is identical between the two. + + - Try several different permutations and combinations of the features you're + adding (i.e., different combinations of command-line switches) and make sure + they all work; make sure one feature does not interfere with another. + + - Make sure you test compiling against the source both with the feature + turned on and turned off in Config.h and make sure Busybox compiles cleanly + both ways. + + - Run the multibuild.pl script in the tests directory and make sure + everything checks out OK. (Do this from within the busybox/ directory by + typing: 'tests/multibuild.pl'.) + + +Making Sure Your Patch Doesn't Get Lost +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you don't want your patch to be lost or forgotten, send it to the busybox +mailing list with a subject line something like this: + + [PATCH] - Adds "transmogrify" feature to "foo" + +In the body, you should have a pseudo-header that looks like the following: + + Package: busybox + Version: v1.01pre (or whatever the current version is) + Severity: wishlist + +The remainder of the body should read along these lines: + + This patch adds the "transmogrify" feature to the "foo" applet. I have + tested this on [arch] system(s) and it works. I have tested it against the + GNU counterparts and the outputs are identical. I have run the scripts in + the 'tests' directory and nothing breaks. + + + +Improving Your Chances of Patch Acceptance +------------------------------------------ + +Even after you send a brilliant patch to the mailing list, sometimes it can go +unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an +unfortunate fact of life, but there are steps you can take to help your patch +get noticed and convince a maintainer that it should be added: + + +Be Succinct +~~~~~~~~~~~ + +A patch that includes small, isolated, obvious changes is more likely to be +accepted than a patch that touches code in lots of different places or makes +sweeping, dubious changes. + + +Back It Up +~~~~~~~~~~ + +Hard facts on why your patch is better than the existing code will go a long +way toward convincing maintainers that your patch should be included. +Specifically, patches are more likely to be accepted if they are provably more +correct, smaller, faster, simpler, or more maintainable than the existing +code. + +Conversely, any patch that is supported with nothing more than "I think this +would be cool" or "this patch is good because I say it is and I've got a Phd +in Computer Science" will likely be ignored. + + +Follow The Style Guide +~~~~~~~~~~~~~~~~~~~~~~ + +It's considered good form to abide by the established coding style used in a +project; Busybox is no exception. We have gone so far as to delineate the +"elements of Busybox style" in the file docs/style-guide.txt. Please follow +them. + + +Work With Someone Else +~~~~~~~~~~~~~~~~~~~~~~ + +Working on a patch in isolation is less effective than working with someone +else for a variety of reasons. If another Busybox user is interested in what +you're doing, then it's two (or more) voices instead of one that can petition +for inclusion of the patch. You'll also have more people that can test your +changes, or even offer suggestions on better approaches you could take. + +Getting other folks interested follows as a natural course if you've received +responses from queries to applet maintainer or positive responses from folks +on the mailing list. + +We've made strident efforts to put a useful "collaboration" infrastructure in +place in the form of mailing lists, the bug tracking system, and Git. Please +use these resources. + + +Send Patches to the Bug Tracking System +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost" +section, but it is worth mentioning again. A patch sent to the mailing list +might be unnoticed and forgotten. A patch sent to the bug tracking system will +be stored and closely connected to the bug it fixes. + + +Be Polite +~~~~~~~~~ + +The old saying "You'll catch more flies with honey than you will with vinegar" +applies when submitting patches to the mailing list for approval. The way you +present your patch is sometimes just as important as the actual patch itself +(if not more so). Being rude to the maintainers is not an effective way to +convince them that your patch should be included; it will likely have the +opposite effect. + + + +Pushing Changes to Git +---------------------- + +If you submit several patches that demonstrate that you are a skilled and wise +coder, you may be invited to become a committer, thus enabling you to push +changes directly to Git. This is nice because you don't have to wait for +someone else to push your change for you, you can just do it yourself. + +But note that this is a privilege that comes with some responsibilities. You +should test your changes before you push them. You should also talk to an +applet maintainer before you make any kind of sweeping changes to somebody +else's code. Big changes should still go to the mailing list first. Remember, +being wise, polite, and discreet is more important than being clever. + +For more information on Git push access, see: + + http://busybox.net/developer.html + + +When To Push +~~~~~~~~~~~~ + +Generally, you should feel free to push a change if: + + - Your changes are small and don't touch many files + - You are fixing a bug + - Somebody has told you that it's okay + - It's obviously the Right Thing + +The more of the above are true, the better it is to just push a change +directly to Git. + + +When Not To Push +~~~~~~~~~~~~~~~~ + +Even if you have push access, you should probably still post a patch to the +mailing list if: + + - Your changes are broad and touch many different files + - You are adding a feature + - Your changes are speculative or experimental (i.e., trying a new algorithm) + - You are not the maintainer and your changes make the maintainer cringe + +The more of the above are true, the better it is to post a patch to the +mailing list instead of pushing. + + + +Final Words +----------- + +If all of this seems complicated, don't panic, it's really not that tough. If +you're having difficulty following some of the steps outlined in this +document don't worry, the folks on the Busybox mailing list are a fairly +good-natured bunch and will work with you to help get your patches into shape +or help you make contributions. diff --git a/docs/ctty.htm b/docs/ctty.htm new file mode 100644 index 000000000..8f466cdde --- /dev/null +++ b/docs/ctty.htm @@ -0,0 +1,476 @@ + + + + The Linux kernel: Processes + + +
+

10. Processes

+ +

Before looking at the Linux implementation, first a general Unix +description of threads, processes, process groups and sessions. +

A session contains a number of process groups, and a process group +contains a number of processes, and a process contains a number +of threads. +

A session can have a controlling tty. +At most one process group in a session can be a foreground process group. +An interrupt character typed on a tty ("Teletype", i.e., terminal) +causes a signal to be sent to all members of the foreground process group +in the session (if any) that has that tty as controlling tty. +

All these objects have numbers, and we have thread IDs, process IDs, +process group IDs and session IDs. +

+

10.1 Processes +

+ +

+

Creation

+ +

A new process is traditionally started using the fork() +system call: +

+
pid_t p;
+
+p = fork();
+if (p == (pid_t) -1)
+        /* ERROR */
+else if (p == 0)
+        /* CHILD */
+else
+        /* PARENT */
+
+
+

This creates a child as a duplicate of its parent. +Parent and child are identical in almost all respects. +In the code they are distinguished by the fact that the parent +learns the process ID of its child, while fork() +returns 0 in the child. (It can find the process ID of its +parent using the getppid() system call.) +

+

Termination

+ +

Normal termination is when the process does +

+
exit(n);
+
+
+ +or +
+
return n;
+
+
+ +from its main() procedure. It returns the single byte n +to its parent. +

Abnormal termination is usually caused by a signal. +

+

Collecting the exit code. Zombies

+ +

The parent does +

+
pid_t p;
+int status;
+
+p = wait(&status);
+
+
+ +and collects two bytes: +

+

+ + + +

A process that has terminated but has not yet been waited for +is a zombie. It need only store these two bytes: +exit code and reason for termination. +

On the other hand, if the parent dies first, init (process 1) +inherits the child and becomes its parent. +

+

Signals

+ +

+

Stopping

+ +

Some signals cause a process to stop: +SIGSTOP (stop!), +SIGTSTP (stop from tty: probably ^Z was typed), +SIGTTIN (tty input asked by background process), +SIGTTOU (tty output sent by background process, and this was +disallowed by stty tostop). +

Apart from ^Z there also is ^Y. The former stops the process +when it is typed, the latter stops it when it is read. +

Signals generated by typing the corresponding character on some tty +are sent to all processes that are in the foreground process group +of the session that has that tty as controlling tty. (Details below.) +

If a process is being traced, every signal will stop it. +

+

Continuing

+ +

SIGCONT: continue a stopped process. +

+

Terminating

+ +

SIGKILL (die! now!), +SIGTERM (please, go away), +SIGHUP (modem hangup), +SIGINT (^C), +SIGQUIT (^\), etc. +Many signals have as default action to kill the target. +(Sometimes with an additional core dump, when such is +allowed by rlimit.) +The signals SIGCHLD and SIGWINCH +are ignored by default. +All except SIGKILL and SIGSTOP can be +caught or ignored or blocked. +For details, see signal(7). +

+

10.2 Process groups +

+ +

Every process is member of a unique process group, +identified by its process group ID. +(When the process is created, it becomes a member of the process group +of its parent.) +By convention, the process group ID of a process group +equals the process ID of the first member of the process group, +called the process group leader. +A process finds the ID of its process group using the system call +getpgrp(), or, equivalently, getpgid(0). +One finds the process group ID of process p using +getpgid(p). +

One may use the command ps j to see PPID (parent process ID), +PID (process ID), PGID (process group ID) and SID (session ID) +of processes. With a shell that does not know about job control, +like ash, each of its children will be in the same session +and have the same process group as the shell. With a shell that knows +about job control, like bash, the processes of one pipeline, like +

+
% cat paper | ideal | pic | tbl | eqn | ditroff > out
+
+
+ +form a single process group. +

+

Creation

+ +

A process pid is put into the process group pgid by +

+
setpgid(pid, pgid);
+
+
+ +If pgid == pid or pgid == 0 then this creates +a new process group with process group leader pid. +Otherwise, this puts pid into the already existing +process group pgid. +A zero pid refers to the current process. +The call setpgrp() is equivalent to setpgid(0,0). +

+

Restrictions on setpgid()

+ +

The calling process must be pid itself, or its parent, +and the parent can only do this before pid has done +exec(), and only when both belong to the same session. +It is an error if process pid is a session leader +(and this call would change its pgid). +

+

Typical sequence

+ +

+

+
p = fork();
+if (p == (pid_t) -1) {
+        /* ERROR */
+} else if (p == 0) {    /* CHILD */
+        setpgid(0, pgid);
+        ...
+} else {                /* PARENT */
+        setpgid(p, pgid);
+        ...
+}
+
+
+ +This ensures that regardless of whether parent or child is scheduled +first, the process group setting is as expected by both. +

+

Signalling and waiting

+ +

One can signal all members of a process group: +

+
killpg(pgrp, sig);
+
+
+

One can wait for children in ones own process group: +

+
waitpid(0, &status, ...);
+
+
+ +or in a specified process group: +
+
waitpid(-pgrp, &status, ...);
+
+
+

+

Foreground process group

+ +

Among the process groups in a session at most one can be +the foreground process group of that session. +The tty input and tty signals (signals generated by ^C, ^Z, etc.) +go to processes in this foreground process group. +

A process can determine the foreground process group in its session +using tcgetpgrp(fd), where fd refers to its +controlling tty. If there is none, this returns a random value +larger than 1 that is not a process group ID. +

A process can set the foreground process group in its session +using tcsetpgrp(fd,pgrp), where fd refers to its +controlling tty, and pgrp is a process group in +its session, and this session still is associated to the controlling +tty of the calling process. +

How does one get fd? By definition, /dev/tty +refers to the controlling tty, entirely independent of redirects +of standard input and output. (There is also the function +ctermid() to get the name of the controlling terminal. +On a POSIX standard system it will return /dev/tty.) +Opening the name of the +controlling tty gives a file descriptor fd. +

+

Background process groups

+ +

All process groups in a session that are not foreground +process group are background process groups. +Since the user at the keyboard is interacting with foreground +processes, background processes should stay away from it. +When a background process reads from the terminal it gets +a SIGTTIN signal. Normally, that will stop it, the job control shell +notices and tells the user, who can say fg to continue +this background process as a foreground process, and then this +process can read from the terminal. But if the background process +ignores or blocks the SIGTTIN signal, or if its process group +is orphaned (see below), then the read() returns an EIO error, +and no signal is sent. (Indeed, the idea is to tell the process +that reading from the terminal is not allowed right now. +If it wouldn't see the signal, then it will see the error return.) +

When a background process writes to the terminal, it may get +a SIGTTOU signal. May: namely, when the flag that this must happen +is set (it is off by default). One can set the flag by +

+
% stty tostop
+
+
+ +and clear it again by +
+
% stty -tostop
+
+
+ +and inspect it by +
+
% stty -a
+
+
+ +Again, if TOSTOP is set but the background process ignores or blocks +the SIGTTOU signal, or if its process group is orphaned (see below), +then the write() returns an EIO error, and no signal is sent. +

+

Orphaned process groups

+ +

The process group leader is the first member of the process group. +It may terminate before the others, and then the process group is +without leader. +

A process group is called orphaned when the +parent of every member is either in the process group +or outside the session. +In particular, the process group of the session leader +is always orphaned. +

If termination of a process causes a process group to become +orphaned, and some member is stopped, then all are sent first SIGHUP +and then SIGCONT. +

The idea is that perhaps the parent of the process group leader +is a job control shell. (In the same session but a different +process group.) As long as this parent is alive, it can +handle the stopping and starting of members in the process group. +When it dies, there may be nobody to continue stopped processes. +Therefore, these stopped processes are sent SIGHUP, so that they +die unless they catch or ignore it, and then SIGCONT to continue them. +

Note that the process group of the session leader is already +orphaned, so no signals are sent when the session leader dies. +

Note also that a process group can become orphaned in two ways +by termination of a process: either it was a parent and not itself +in the process group, or it was the last element of the process group +with a parent outside but in the same session. +Furthermore, that a process group can become orphaned +other than by termination of a process, namely when some +member is moved to a different process group. +

+

10.3 Sessions +

+ +

Every process group is in a unique session. +(When the process is created, it becomes a member of the session +of its parent.) +By convention, the session ID of a session +equals the process ID of the first member of the session, +called the session leader. +A process finds the ID of its session using the system call +getsid(). +

Every session may have a controlling tty, +that then also is called the controlling tty of each of +its member processes. +A file descriptor for the controlling tty is obtained by +opening /dev/tty. (And when that fails, there was no +controlling tty.) Given a file descriptor for the controlling tty, +one may obtain the SID using tcgetsid(fd). +

A session is often set up by a login process. The terminal +on which one is logged in then becomes the controlling tty +of the session. All processes that are descendants of the +login process will in general be members of the session. +

+

Creation

+ +

A new session is created by +

+
pid = setsid();
+
+
+ +This is allowed only when the current process is not a process group leader. +In order to be sure of that we fork first: +
+
p = fork();
+if (p) exit(0);
+pid = setsid();
+
+
+ +The result is that the current process (with process ID pid) +becomes session leader of a new session with session ID pid. +Moreover, it becomes process group leader of a new process group. +Both session and process group contain only the single process pid. +Furthermore, this process has no controlling tty. +

The restriction that the current process must not be a process group leader +is needed: otherwise its PID serves as PGID of some existing process group +and cannot be used as the PGID of a new process group. +

+

Getting a controlling tty

+ +

How does one get a controlling terminal? Nobody knows, +this is a great mystery. +

The System V approach is that the first tty opened by the process +becomes its controlling tty. +

The BSD approach is that one has to explicitly call +

+
ioctl(fd, TIOCSCTTY, 0/1);
+
+
+ +to get a controlling tty. +

Linux tries to be compatible with both, as always, and this +results in a very obscure complex of conditions. Roughly: +

The TIOCSCTTY ioctl will give us a controlling tty, +provided that (i) the current process is a session leader, +and (ii) it does not yet have a controlling tty, and +(iii) maybe the tty should not already control some other session; +if it does it is an error if we aren't root, or we steal the tty +if we are all-powerful. +[vda: correction: third parameter controls this: if 1, we steal tty from +any such session, if 0, we don't steal] +

Opening some terminal will give us a controlling tty, +provided that (i) the current process is a session leader, and +(ii) it does not yet have a controlling tty, and +(iii) the tty does not already control some other session, and +(iv) the open did not have the O_NOCTTY flag, and +(v) the tty is not the foreground VT, and +(vi) the tty is not the console, and +(vii) maybe the tty should not be master or slave pty. +

+

Getting rid of a controlling tty

+ +

If a process wants to continue as a daemon, it must detach itself +from its controlling tty. Above we saw that setsid() +will remove the controlling tty. Also the ioctl TIOCNOTTY does this. +Moreover, in order not to get a controlling tty again as soon as it +opens a tty, the process has to fork once more, to assure that it +is not a session leader. Typical code fragment: +

+

        if ((fork()) != 0)
+                exit(0);
+        setsid();
+        if ((fork()) != 0)
+                exit(0);
+
+

See also daemon(3). +

+

Disconnect

+ +

If the terminal goes away by modem hangup, and the line was not local, +then a SIGHUP is sent to the session leader. +Any further reads from the gone terminal return EOF. +(Or possibly -1 with errno set to EIO.) +

If the terminal is the slave side of a pseudotty, and the master side +is closed (for the last time), then a SIGHUP is sent to the foreground +process group of the slave side. +

When the session leader dies, a SIGHUP is sent to all processes +in the foreground process group. Moreover, the terminal stops being +the controlling terminal of this session (so that it can become +the controlling terminal of another session). +

Thus, if the terminal goes away and the session leader is +a job control shell, then it can handle things for its descendants, +e.g. by sending them again a SIGHUP. +If on the other hand the session leader is an innocent process +that does not catch SIGHUP, it will die, and all foreground processes +get a SIGHUP. +

+

10.4 Threads +

+ +

A process can have several threads. New threads (with the same PID +as the parent thread) are started using the clone system +call using the CLONE_THREAD flag. Threads are distinguished +by a thread ID (TID). An ordinary process has a single thread +with TID equal to PID. The system call gettid() returns the +TID. The system call tkill() sends a signal to a single thread. +

Example: a process with two threads. Both only print PID and TID and exit. +(Linux 2.4.19 or later.) +

% cat << EOF > gettid-demo.c
+#include <unistd.h>
+#include <sys/types.h>
+#define CLONE_SIGHAND   0x00000800
+#define CLONE_THREAD    0x00010000
+#include <linux/unistd.h>
+#include <errno.h>
+_syscall0(pid_t,gettid)
+
+int thread(void *p) {
+        printf("thread: %d %d\n", gettid(), getpid());
+}
+
+main() {
+        unsigned char stack[4096];
+        int i;
+
+        i = clone(thread, stack+2048, CLONE_THREAD | CLONE_SIGHAND, NULL);
+        if (i == -1)
+                perror("clone");
+        else
+                printf("clone returns %d\n", i);
+        printf("parent: %d %d\n", gettid(), getpid());
+}
+EOF
+% cc -o gettid-demo gettid-demo.c
+% ./gettid-demo
+clone returns 21826
+parent: 21825 21825
+thread: 21826 21825
+%
+
+

+

+


+ + diff --git a/docs/draft-coar-cgi-v11-03-clean.html b/docs/draft-coar-cgi-v11-03-clean.html new file mode 100644 index 000000000..d52c9b842 --- /dev/null +++ b/docs/draft-coar-cgi-v11-03-clean.html @@ -0,0 +1,2674 @@ + + + + Common Gateway Interface - 1.1 *Draft 03* [http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html] + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ INTERNET-DRAFT                                 + +           Ken A L Coar +
+ draft-coar-cgi-v11-03.{html,txt}              + +         IBM Corporation +
+                                                 + +       D.R.T. Robinson +
+                                                 + +       E*TRADE UK Ltd. +
+                                                 + +         25 June 1999 +
+
+ +

+ The WWW Common Gateway Interface +
+ Version 1.1 +

+ + + +

+ + Abstract + +

+

+ The Common Gateway Interface (CGI) is a simple interface for running + external programs, software or gateways under an information server + in a platform-independent manner. Currently, the supported information + servers are HTTP servers. +

+

+ The interface has been in use by the World-Wide Web since 1993. This + specification defines the + "current practice" parameters of the + 'CGI/1.1' interface developed and documented at the U.S. National + Centre for Supercomputing Applications [NCSA-CGI]. + This document also defines the use of the CGI/1.1 interface + on the Unix and AmigaDOS(tm) systems. +

+

+ Discussion of this draft occurs on the CGI-WG mailing list; see the + project Web page at + <URL:http://CGI-Spec.Golux.Com/> + for details on the mailing list and the status of the project. +

+ + +

+ Revision History +

+

+ The revision history of this draft is being maintained using Web-based + GUI notation, such as struck-through characters and colour-coded + sections. The following legend describes how to determine the origin + of a particular revision according to the colour of the text: +

+
+
Black +
+
Revision 00, released 28 May 1998 +
+
Green +
+
Revision 01, released 28 December 1998 +
+ Major structure change: Section 4, "Request Metadata (Meta-Variables)" + was moved entirely under Section 7, "Data Input to the + CGI Script." + Due to the size of this change, it is noted here and the text in its + former location does not appear as struckthrough. This has + caused major sections 5 and following to decrement + by one. Other + large text movements are likewise not marked up. References to RFC + 1738 were changed to 2396 (1738's replacement). +
+
Red +
+
Revision 02, released 2 April, 1999 +
+ Added text to section 8.3 defining correct handling + of HTTP/1.1 + requests using "chunked" Transfer-Encoding. Labelled metavariable + names in section 8 with the appropriate detail section + numbers. + Clarified allowed usage of Status and + Location response header fields. Included new + Internet-Draft language. +
+
Fuchsia +
+
Revision 03, released 25 June 1999 +
+ Changed references from "HTTP" to "Protocol-Specific" for the listing of + things like HTTP_ACCEPT. Changed 'entity-body' and 'content-body' to + 'message-body.' Added a note that response headers must comply with + requirements of the protocol level in use. Added a lot of stuff about + security (section 11). Clarified a bunch of productions. Pointed out + that zero-length and omitted values are indistinguishable in this + specification. Clarified production describing order of fields in + script response header. Clarified issues surrounding encoding of + data. Acknowledged additional contributors, and changed one of + the authors' addresses. +
+
+ + +

+ + Table of Contents + +

+
+
+  1 Introduction..............................................TBD
+   1.1 Purpose................................................TBD
+   1.2 Requirements...........................................TBD
+   1.3 Specifications.........................................TBD
+   1.4 Terminology............................................TBD
+  2 Notational Conventions and Generic Grammar................TBD
+   2.1 Augmented BNF..........................................TBD
+   2.2 Basic Rules............................................TBD
+  3 Protocol Parameters.......................................TBD
+   3.1 URL Encoding...........................................TBD
+   3.2 The Script-URI.........................................TBD
+  4 Invoking the Script.......................................TBD
+  5 The CGI Script Command Line...............................TBD
+  6 Data Input to the CGI Script..............................TBD
+   6.1 Request Metadata (Metavariables).......................TBD
+    6.1.1 AUTH_TYPE...........................................TBD
+    6.1.2 CONTENT_LENGTH......................................TBD
+    6.1.3 CONTENT_TYPE........................................TBD
+    6.1.4 GATEWAY_INTERFACE...................................TBD
+    6.1.5 Protocol-Specific Metavariables.....................TBD
+    6.1.6 PATH_INFO...........................................TBD
+    6.1.7 PATH_TRANSLATED.....................................TBD
+    6.1.8 QUERY_STRING........................................TBD
+    6.1.9 REMOTE_ADDR.........................................TBD
+    6.1.10 REMOTE_HOST........................................TBD
+    6.1.11 REMOTE_IDENT.......................................TBD
+    6.1.12 REMOTE_USER........................................TBD
+    6.1.13 REQUEST_METHOD.....................................TBD
+    6.1.14 SCRIPT_NAME........................................TBD
+    6.1.15 SERVER_NAME........................................TBD
+    6.1.16 SERVER_PORT........................................TBD
+    6.1.17 SERVER_PROTOCOL....................................TBD
+    6.1.18 SERVER_SOFTWARE....................................TBD
+    6.2 Request Message-Bodies................................TBD
+  7 Data Output from the CGI Script...........................TBD
+   7.1 Non-Parsed Header Output...............................TBD
+   7.2 Parsed Header Output...................................TBD
+    7.2.1 CGI header fields...................................TBD
+     7.2.1.1 Content-Type.....................................TBD
+     7.2.1.2 Location.........................................TBD
+     7.2.1.3 Status...........................................TBD
+     7.2.1.4 Extension header fields..........................TBD
+    7.2.2 HTTP header fields..................................TBD
+  8 Server Implementation.....................................TBD
+   8.1 Requirements for Servers...............................TBD
+    8.1.1 Script-URI..........................................TBD
+    8.1.2 Request Message-body Handling.......................TBD
+    8.1.3 Required Metavariables..............................TBD
+    8.1.4 Response Compliance.................................TBD
+   8.2 Recommendations for Servers............................TBD
+   8.3 Summary of Metavariables...............................TBD
+  9 Script Implementation.....................................TBD
+   9.1 Requirements for Scripts...............................TBD
+   9.2 Recommendations for Scripts............................TBD
+  10 System Specifications....................................TBD
+   10.1 AmigaDOS..............................................TBD
+   10.2 Unix..................................................TBD
+  11 Security Considerations..................................TBD
+   11.1 Safe Methods..........................................TBD
+   11.2 HTTP Header Fields Containing Sensitive Information...TBD
+   11.3 Script Interference with the Server...................TBD
+   11.4 Data Length and Buffering Considerations..............TBD
+   11.5 Stateless Processing..................................TBD
+  12 Acknowledgments..........................................TBD
+  13 References...............................................TBD
+  14 Authors' Addresses.......................................TBD
+     
+
+ +

+ + 1. Introduction + +

+ +

+ + 1.1. Purpose + +

+

+ Together the HTTP [3,8] server + and the CGI script are responsible + for servicing a client + request by sending back responses. The client + request comprises a Universal Resource Identifier (URI) + [1], a + request method, and various ancillary + information about the request + provided by the transport mechanism. +

+

+ The CGI defines the abstract parameters, known as + metavariables, + which describe the client's + request. Together with a + concrete programmer interface this specifies a platform-independent + interface between the script and the HTTP server. +

+ +

+ + 1.2. Requirements + +

+

+ This specification uses the same words as RFC 1123 + [5] to define the + significance of each particular requirement. These are: +

+

+
+
MUST +
+
+

+ This word or the adjective 'required' means that the item is an + absolute requirement of the specification. +

+
+
SHOULD +
+
+

+ This word or the adjective 'recommended' means that there may + exist valid reasons in particular circumstances to ignore this + item, but the full implications should be understood and the case + carefully weighed before choosing a different course. +

+
+
MAY +
+
+

+ This word or the adjective 'optional' means that this item is + truly optional. One vendor may choose to include the item because + a particular marketplace requires it or because it enhances the + product, for example; another vendor may omit the same item. +

+
+
+

+ An implementation is not compliant if it fails to satisfy one or more + of the 'must' requirements for the protocols it implements. An + implementation that satisfies all of the 'must' and all of the + 'should' requirements for its features is said to be 'unconditionally + compliant'; one that satisfies all of the 'must' requirements but not + all of the 'should' requirements for its features is said to be + 'conditionally compliant.' +

+ +

+ + 1.3. Specifications + +

+

+ Not all of the functions and features of the CGI are defined in the + main part of this specification. The following phrases are used to + describe the features which are not specified: +

+
+
system defined +
+
+

+ The feature may differ between systems, but must be the same for + different implementations using the same system. A system will + usually identify a class of operating-systems. Some systems are + defined in + section 10 of this document. + New systems may be defined + by new specifications without revision of this document. +

+
+
implementation defined +
+
+

+ The behaviour of the feature may vary from implementation to + implementation, but a particular implementation must document its + behaviour. +

+
+
+ +

+ + 1.4. Terminology + +

+

+ This specification uses many terms defined in the HTTP/1.1 + specification [8]; however, the following terms are + used here in a + sense which may not accord with their definitions in that document, + or with their common meaning. +

+ +
+
metavariable +
+
+

+ A named parameter that carries information from the server to the + script. It is not necessarily a variable in the operating-system's + environment, although that is the most common implementation. +

+
+ +
script +
+
+

+ The software which is invoked by the server via this + interface. It + need not be a standalone program, but could be a + dynamically-loaded or shared library, or even a subroutine in the + server. It may be a set of statements + interpreted at run-time, as the term 'script' is frequently + understood, but that is not a requirement and within the context + of this specification the term has the broader definition stated. +

+
+
server +
+
+

+ The application program which invokes the script in order to service + requests. +

+
+
+ +

+ + 2. Notational Conventions and Generic Grammar + +

+ +

+ + 2.1. Augmented BNF + +

+

+ All of the mechanisms specified in this document are described in + both prose and an augmented Backus-Naur Form (BNF) similar to that + used by RFC 822 [6]. This augmented BNF contains + the following constructs: +

+
+
name = definition +
+
+

+ The + definition by the equal character ("="). Whitespace is only + significant in that continuation lines of a definition are + indented. +

+
+
"literal" +
+
+

+ Quotation marks (") surround literal text, except for a literal + quotation mark, which is surrounded by angle-brackets ("<" and ">"). + Unless stated otherwise, the text is case-sensitive. +

+
+
rule1 | rule2 +
+
+

+ Alternative rules are separated by a vertical bar ("|"). +

+
+
(rule1 rule2 rule3) +
+
+

+ Elements enclosed in parentheses are treated as a single element. +

+
+
*rule +
+
+

+ A rule preceded by an asterisk ("*") may have zero or more + occurrences. A rule preceded by an integer followed by an asterisk + must occur at least the specified number of times. +

+
+
[rule] +
+
+

+ An element enclosed in square + brackets ("[" and "]") is optional. +

+
+
+ +

+ + 2.2. Basic Rules + +

+

+ The following rules are used throughout this specification to + describe basic parsing constructs. +

+

+
+    alpha         = lowalpha | hialpha
+    alphanum      = alpha | digit
+    lowalpha      = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h"
+                    | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p"
+                    | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
+                    | "y" | "z"
+    hialpha       = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H"
+                    | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P"
+                    | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X"
+                    | "Y" | "Z"
+    digit         = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7"
+                    | "8" | "9"
+    hex           = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a"
+                    | "b" | "c" | "d" | "e" | "f"
+    escaped       = "%" hex hex
+    OCTET         = <any 8-bit sequence of data>
+    CHAR          = <any US-ASCII character (octets 0 - 127)>
+    CTL           = <any US-ASCII control character
+                    (octets 0 - 31) and DEL (127)>
+    CR            = <US-ASCII CR, carriage return (13)>
+    LF            = <US-ASCII LF, linefeed (10)>
+    SP            = <US-ASCII SP, space (32)>
+    HT            = <US-ASCII HT, horizontal tab (9)>
+    NL            = CR | LF
+    LWSP          = SP | HT | NL
+    tspecial      = "(" | ")" | "@" | "," | ";" | ":" | "\" | <">
+                    | "/" | "[" | "]" | "?" | "<" | ">" | "{" | "}"
+                    | SP | HT | NL
+    token         = 1*<any CHAR except CTLs or tspecials>
+    quoted-string = ( <"> *qdtext <"> ) | ( "<" *qatext ">")
+    qdtext        = <any CHAR except <"> and CTLs but including LWSP>
+    qatext        = <any CHAR except "<", ">" and CTLs but
+                    including LWSP>
+    mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+    unreserved    = alphanum | mark
+    reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" |
+                    "$" | ","
+    uric          = reserved | unreserved | escaped
+  
+

+ Note that newline (NL) need not be a single character, but can be a + character sequence. +

+ +

+ + 3. Protocol Parameters + +

+ +

+ + 3.1. URL Encoding + +

+

+ Some variables and constructs used here are described as being + 'URL-encoded'. This encoding is described in section + 2 of RFC + 2396 + [4]. +

+

+ An alternate "shortcut" encoding for representing the space + character exists and is in common use. Scripts MUST be prepared to + recognise both '+' and '%20' as an encoded space in a + URL-encoded value. +

+

+ Note that some unsafe characters may have different semantics if + they are encoded. The definition of which characters are unsafe + depends on the context. + For example, the following two URLs do not + necessarily refer to the same resource: +

+

+
+    http://somehost.com/somedir%2Fvalue
+    http://somehost.com/somedir/value
+  
+

+ See section + 2 of RFC + 2396 [4] + for authoritative treatment of this issue. +

+ +

+ + 3.2. The Script-URI + +

+

+ The 'Script-URI' is defined as the URI of the resource identified + by the metavariables. Often, + this URI will be the same as + the URI requested by the client (the 'Client-URI'); however, it need + not be. Instead, it could be a URI invented by the server, and so it + can only be used in the context of the server and its CGI interface. +

+

+ The Script-URI has the syntax of generic-RL as defined in section 2.1 + of RFC 1808 [7], with the exception that object + parameters and + fragment identifiers are not permitted: +

+

+
+    <scheme>://<host><port>/<path>?<query>
+  
+

+ The various components of the + Script-URI + are defined by some of the + metavariables (see + section 4 + below); +

+

+
+    script-uri = protocol "://" SERVER_NAME ":" SERVER_PORT enc-script
+                 enc-path-info "?" QUERY_STRING
+  
+

+ where 'protocol' is obtained + from SERVER_PROTOCOL, 'enc-script' is a + URL-encoded version of SCRIPT_NAME and 'enc-path-info' is a + URL-encoded version of PATH_INFO. See + section 4.6 for more information about the PATH_INFO + metavariable. +

+

+ Note that the scheme and the protocol are not identical; + for instance, a resource accessed via an SSL mechanism + may have a Client-URI with a scheme of "https" + rather than "http". CGI/1.1 provides no means + for the script to reconstruct this, and therefore + the Script-URI includes the base protocol used. +

+ +

+ + 4. Invoking the Script + +

+

+ The + script is invoked in a system defined manner. Unless specified + otherwise, the file containing the script will be invoked as an + executable program. +

+ +

+ + 5. The CGI Script Command Line + +

+

+ Some systems support a method for supplying an array of strings to + the CGI script. This is only used in the case of an 'indexed' query. + This is identified by a "GET" or "HEAD" HTTP request with a URL + query + string not containing any unencoded "=" characters. For such a + request, + servers SHOULD parse the search string + into words, using the following rules: +

+

+
+    search-string = search-word *( "+" search-word )
+    search-word   = 1*schar
+    schar         = xunreserved | escaped | xreserved
+    xunreserved   = alpha | digit | xsafe | extra
+    xsafe         = "$" | "-" | "_" | "."
+    xreserved     = ";" | "/" | "?" | ":" | "@" | "&"
+  
+

+ After parsing, each word is URL-decoded, optionally encoded in a + system defined manner, + and then the argument list is set to the list + of words. +

+

+ If the server cannot create any part of the argument list, then the + server SHOULD NOT generate any command line information. For example, the + number of arguments may be greater than operating system or server + limitations permit, or one of the words may not be representable as an + argument. +

+

+ Scripts SHOULD check to see if the QUERY_STRING value contains an + unencoded "=" character, and SHOULD NOT use the command line arguments + if it does. +

+ +

+ + 6. Data Input to the CGI Script + +

+

+ Information about a request comes from two different sources: the + request header, and any associated + message-body. + Servers MUST + make portions of this information available to + scripts. +

+ +

+ + 6.1. Request Metadata + (Metavariables) + +

+

+ Each CGI server + implementation MUST define a mechanism + to pass data about the request from + the server to the script. + The metavariables containing these + data + are accessed by the script in a system + defined manner. + The + representation of the characters in the + metavariables is + system defined. +

+

+ This specification does not distinguish between the representation of + null values and missing ones. Whether null or missing values + (such as a query component of "?" or "", respectively) are represented + by undefined metavariables or by metavariables with values of "" is + implementation-defined. +

+

+ Case is not significant in the + metavariable + names, in that there cannot be two + different variables + whose names differ in case only. Here they are + shown using a canonical representation of capitals plus underscore + ("_"). The actual representation of the names is system defined; for + a particular system the representation MAY be defined differently + than this. +

+

+ Metavariable + values MUST be + considered case-sensitive except as noted + otherwise. +

+

+ The canonical + metavariables + defined by this specification are: +

+

+
+    AUTH_TYPE
+    CONTENT_LENGTH
+    CONTENT_TYPE
+    GATEWAY_INTERFACE
+    PATH_INFO
+    PATH_TRANSLATED
+    QUERY_STRING
+    REMOTE_ADDR
+    REMOTE_HOST
+    REMOTE_IDENT
+    REMOTE_USER
+    REQUEST_METHOD
+    SCRIPT_NAME
+    SERVER_NAME
+    SERVER_PORT
+    SERVER_PROTOCOL
+    SERVER_SOFTWARE
+  
+

+ Metavariables with names beginning with the protocol name (e.g., + "HTTP_ACCEPT") are also canonical in their description of request header + fields. The number and meaning of these fields may change independently + of this specification. (See also section 6.1.5.) +

+ +

+ + 6.1.1. AUTH_TYPE + +

+

+ This variable is specific to requests made + via the + "http" + scheme. +

+

+ If the Script-URI + required access authentication for external + access, then the server + MUST set + the value of + this variable + from the 'auth-scheme' token in + the request's "Authorization" header + field. + Otherwise + it is + set to NULL. +

+

+
+    AUTH_TYPE   = "" | auth-scheme
+    auth-scheme = "Basic" | "Digest" | token
+  
+

+ HTTP access authentication schemes are described in section 11 of the + HTTP/1.1 specification [8]. The auth-scheme is + not case-sensitive. +

+

+ Servers + MUST + provide this metavariable + to scripts if the request + header included an "Authorization" field + that was authenticated. +

+ +

+ + 6.1.2. CONTENT_LENGTH + +

+

+ This + metavariable + is set to the + size of the message-body + entity attached to the request, if any, in decimal + number of octets. If no data are attached, then this + metavariable + is either NULL or not + defined. The syntax is + the same as for + the HTTP "Content-Length" header field (section 14.14, HTTP/1.1 + specification [8]). +

+

+
+    CONTENT_LENGTH = "" | 1*digit
+  
+

+ Servers MUST provide this metavariable + to scripts if the request + was accompanied by a + message-body entity. +

+ +

+ + 6.1.3. CONTENT_TYPE + +

+

+ If the request includes a + message-body, + CONTENT_TYPE is set + to + the Internet Media Type + [9] of the attached + entity if the type was provided via + a "Content-type" field in the + request header, or if the server can determine it in the absence + of a supplied "Content-type" field. The syntax is the + same as for the HTTP + "Content-Type" header field. +

+

+
+    CONTENT_TYPE = "" | media-type
+    media-type   = type "/" subtype *( ";" parameter)
+    type         = token
+    subtype      = token
+    parameter    = attribute "=" value
+    attribute    = token
+    value        = token | quoted-string
+  
+

+ The type, subtype, + and parameter attribute names are not + case-sensitive. Parameter values MAY be case sensitive. + Media types and their use in HTTP are described + in section 3.7 of the + HTTP/1.1 specification [8]. +

+

+ Example: +

+

+
+    application/x-www-form-urlencoded
+  
+

+ There is no default value for this variable. If and only if it is + unset, then the script MAY attempt to determine the media type from + the data received. If the type remains unknown, then + the script MAY choose to either assume a + content-type of + application/octet-stream + or reject the request with a 415 ("Unsupported Media Type") + error. See section 7.2.1.3 + for more information about returning error status values. +

+

+ Servers MUST provide this metavariable + to scripts if + a "Content-Type" field was present + in the original request header. If the server receives a request + with an attached entity but no "Content-Type" + header field, it MAY attempt to + determine the correct datatype, or it MAY omit this + metavariable when + communicating the request information to the script. +

+ +

+ + 6.1.4. GATEWAY_INTERFACE + +

+

+ This + metavariable + is set to + the dialect of CGI being used + by the server to communicate with the script. + Syntax: +

+

+
+    GATEWAY_INTERFACE = "CGI" "/" major "." minor
+    major             = 1*digit
+    minor             = 1*digit
+  
+

+ Note that the major and minor numbers are treated as separate + integers and hence each may be + more than a single + digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in turn + is lower than CGI/12.3. Leading zeros in either + the major or the minor number MUST be ignored by scripts and + SHOULD NOT be generated by servers. +

+

+ This document defines the 1.1 version of the CGI interface + ("CGI/1.1"). +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.5. Protocol-Specific Metavariables + +

+

+ These metavariables are specific to + the protocol + via which the request is made. + Interpretation of these variables depends on the value of + the + SERVER_PROTOCOL + metavariable + (see + section 6.1.17). +

+

+ Metavariables + with names beginning with "HTTP_" contain + values from the request header, if the + scheme used was HTTP. + Each + HTTP header field name is converted to upper case, has all occurrences of + "-" replaced with "_", + and has "HTTP_" prepended to form + the metavariable name. + Similar transformations are applied for other + protocols. + The header data MAY be presented as sent + by the client, or MAY be rewritten in ways which do not change its + semantics. If multiple header fields with the same field-name are received + then the server + MUST rewrite them as though they + had been received as a single header field having the same + semantics before being represented in a + metavariable. + Similarly, a header field that is received on more than one line + MUST be merged into a single line. The server MUST, if necessary, + change the representation of the data (for example, the character + set) to be appropriate for a CGI + metavariable. + +

+

+ Servers are + not required to create + metavariables for all + the request + header fields that they + receive. In particular, + they MAY + decline to make available any + header fields carrying authentication information, such as + "Authorization", or + which are available to the script + via other metavariables, + such as "Content-Length" and "Content-Type". +

+ +

+ + 6.1.6. PATH_INFO + +

+

+ The PATH_INFO + metavariable + specifies + a path to be interpreted by the CGI script. It identifies the + resource or sub-resource to be returned + by the CGI + script, and it is derived from the portion + of the URI path following the script name but preceding + any query data. + The syntax + and semantics are similar to a decoded HTTP URL + 'path' token + (defined in + RFC 2396 + [4]), with the exception + that a PATH_INFO of "/" + represents a single void path segment. +

+

+
+    PATH_INFO = "" | ( "/" path )
+    path      = segment *( "/" segment )
+    segment   = *pchar
+    pchar     = <any CHAR except "/">
+  
+

+ The PATH_INFO string is the trailing part of the <path> component of + the Script-URI + (see section 3.2) + that follows the SCRIPT_NAME + portion of the path. +

+

+ Servers MAY impose their own restrictions and + limitations on what values they will accept for PATH_INFO, and MAY + reject or edit any values they + consider objectionable before passing + them to the script. +

+

+ Servers MUST make this URI component available + to CGI scripts. The PATH_INFO + value is case-sensitive, and the + server MUST preserve the case of the PATH_INFO element of the URI + when making it available to scripts. +

+ +

+ + 6.1.7. PATH_TRANSLATED + +

+

+ PATH_TRANSLATED is derived by taking any path-info component of the + request URI (see + section 6.1.6), decoding it + (see section 3.1), parsing it as a URI in its own + right, and performing any virtual-to-physical + translation appropriate to map it onto the + server's document repository structure. + If the request URI includes no path-info + component, the PATH_TRANSLATED metavariable SHOULD NOT be defined. +

+

+
+    PATH_TRANSLATED = *CHAR
+  
+

+ For a request such as the following: +

+

+
+    http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo
+  
+

+ the PATH_INFO component would be decoded, and the result + parsed as though it were a request for the following: +

+

+
+    http://somehost.com/this.is.the.path.info
+  
+

+ This would then be translated to a + location in the server's document repository, + perhaps a filesystem path something + like this: +

+

+
+    /usr/local/www/htdocs/this.is.the.path.info
+  
+

+ The result of the translation is the value of PATH_TRANSLATED. +

+

+ The value of PATH_TRANSLATED may or may not map to a valid + repository + location. + Servers MUST preserve the case of the path-info + segment if and only if the underlying + repository + supports case-sensitive + names. If the + repository + is only case-aware, case-preserving, or case-blind + with regard to + document names, + servers are not required to preserve the + case of the original segment through the translation. +

+

+ The + translation + algorithm the server uses to derive PATH_TRANSLATED is + implementation defined; CGI scripts which use this variable may + suffer limited portability. +

+

+ Servers SHOULD provide this metavariable + to scripts if and only if the request URI includes a + path-info component. +

+ +

+ + 6.1.8. QUERY_STRING + +

+

+ A URL-encoded + string; the <query> part of the + Script-URI. + (See + section 3.2.) +

+

+
+    QUERY_STRING = query-string
+    query-string = *uric
+  
+

+ The URL syntax for a query + string is described in + section 3 of + RFC 2396 + [4]. +

+

+ Servers MUST supply this value to scripts. + The QUERY_STRING value is case-sensitive. + If the Script-URI does not include a query component, + the QUERY_STRING metavariable MUST be defined as an empty string (""). +

+ +

+ + 6.1.9. REMOTE_ADDR + +

+

+ The IP address of the client + sending the request to the server. This + is not necessarily that of the user + agent + (such as if the request came through a proxy). +

+

+
+    REMOTE_ADDR  = hostnumber
+    hostnumber   = ipv4-address | ipv6-address
+  
+

+ The definitions of ipv4-address and ipv6-address + are provided in Appendix B of RFC 2373 [13]. +

+

+ Servers MUST supply this value to scripts. +

+ +

+ + 6.1.10. REMOTE_HOST + +

+

+ The fully qualified domain name of the + client sending the request to + the server, if available, otherwise NULL. + (See section 6.1.9.) + Fully qualified domain names take the form as described in + section 3.5 of RFC 1034 [10] and section 2.1 of + RFC 1123 [5]. Domain names are not case sensitive. +

+

+ Servers SHOULD provide this information to + scripts. +

+ +

+ + 6.1.11. REMOTE_IDENT + +

+

+ The identity information reported about the connection by a + RFC 1413 [11] request to the remote agent, if + available. Servers + MAY choose not + to support this feature, or not to request the data + for efficiency reasons. +

+

+
+    REMOTE_IDENT = *CHAR
+  
+

+ The data returned + may be used for authentication purposes, but the level + of trust reposed in them should be minimal. +

+

+ Servers MAY supply this information to scripts if the + RFC1413 [11] lookup is performed. +

+ +

+ + 6.1.12. REMOTE_USER + +

+

+ If the request required authentication using the "Basic" + mechanism (i.e., the AUTH_TYPE + metavariable is set + to "Basic"), then the value of the REMOTE_USER + metavariable is set to the + user-ID supplied. In all other cases + the value of this metavariable + is undefined. +

+

+
+    REMOTE_USER = *OCTET
+  
+

+ This variable is specific to requests made via the + HTTP protocol. +

+

+ Servers SHOULD provide this metavariable + to scripts. +

+ +

+ + 6.1.13. REQUEST_METHOD + +

+

+ The REQUEST_METHOD + metavariable + is set to the + method with which the request was made, as described in section + 5.1.1 of the HTTP/1.0 specification [3] and + section 5.1.1 of the + HTTP/1.1 specification [8]. +

+

+
+    REQUEST_METHOD   = http-method
+    http-method      = "GET" | "HEAD" | "POST" | "PUT" | "DELETE"
+                       | "OPTIONS" | "TRACE" | extension-method
+    extension-method = token
+  
+

+ The method is case sensitive. + CGI/1.1 servers MAY choose to process some methods + directly rather than passing them to scripts. +

+

+ This variable is specific to requests made with HTTP. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.14. SCRIPT_NAME + +

+

+ The SCRIPT_NAME + metavariable + is + set to a URL path that could identify the CGI script (rather than the + script's + output). The syntax and semantics are identical to a + decoded HTTP URL 'path' token + (see RFC 2396 + [4]). +

+

+
+    SCRIPT_NAME = "" | ( "/" [ path ] )
+  
+

+ The SCRIPT_NAME string is some leading part of the <path> component + of the Script-URI derived in some + implementation defined manner. + No PATH_INFO or QUERY_STRING segments + (see sections 6.1.6 and + 6.1.8) are included + in the SCRIPT_NAME value. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.15. SERVER_NAME + +

+

+ The SERVER_NAME + metavariable + is set to the + name of the + server, as + derived from the <host> part of the + Script-URI + (see section 3.2). +

+

+
+    SERVER_NAME = hostname | hostnumber
+  
+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.16. SERVER_PORT + +

+

+ The SERVER_PORT + metavariable + is set to the + port on which the + request was received, as used in the <port> + part of the Script-URI. +

+

+
+    SERVER_PORT = 1*digit
+  
+

+ If the <port> portion of the script-URI is blank, the actual + port number upon which the request was received MUST be supplied. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.17. SERVER_PROTOCOL + +

+

+ The SERVER_PROTOCOL + metavariable + is set to + the + name and revision of the information protocol with which + the + request + arrived. This is not necessarily the same as the protocol version used by + the server in its response to the client. +

+

+
+    SERVER_PROTOCOL   = HTTP-Version | extension-version
+                        | extension-token
+    HTTP-Version      = "HTTP" "/" 1*digit "." 1*digit
+    extension-version = protocol "/" 1*digit "." 1*digit
+    protocol          = 1*( alpha | digit | "+" | "-" | "." )
+    extension-token   = token
+  
+

+ 'protocol' is a version of the <scheme> part of the + Script-URI, but is + not identical to it. For example, the scheme of a request may be + "https" while the protocol remains "http". + The protocol is not case sensitive, but + by convention, 'protocol' is in + upper case. +

+

+ A well-known extension token value is "INCLUDED", + which signals that the current document is being included as part of + a composite document, rather than being the direct target of the + client request. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.18. SERVER_SOFTWARE + +

+

+ The SERVER_SOFTWARE + metavariable + is set to the + name and version of the information server software answering the + request (and running the gateway). +

+

+
+    SERVER_SOFTWARE = 1*product
+    product         = token [ "/" product-version ]
+    product-version = token
+  
+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.2. Request Message-Bodies + +

+

+ As there may be a data entity attached to the request, there MUST be + a system defined method for the script to read + these data. Unless + defined otherwise, this will be via the 'standard input' file + descriptor. +

+

+ If the CONTENT_LENGTH value (see section 6.1.2) + is non-NULL, the server MUST supply at least that many bytes to + scripts on the standard input stream. + Scripts are + not obliged to read the data. + Servers MAY signal an EOF condition after CONTENT_LENGTH bytes have been + read, but are + not obligated to do so. Therefore, scripts + MUST NOT + attempt to read more than CONTENT_LENGTH bytes, even if more data + are available. +

+

+ For non-parsed header (NPH) scripts (see + section 7.1 + below), + servers SHOULD + attempt to ensure that the data + supplied to the script are precisely + as supplied by the client and unaltered by + the server. +

+

+ Section 8.1.2 describes the requirements of + servers with regard to requests that include + message-bodies. +

+ +

+ + 7. Data Output from the CGI Script + +

+

+ There MUST be a system defined method for the script to send data + back to the server or client; a script MUST always return some data. + Unless defined otherwise, this will be via the 'standard + output' file descriptor. +

+

+ There are two forms of output that scripts can supply to servers: non-parsed + header (NPH) output, and parsed header output. + Servers MUST support parsed header + output and MAY support NPH output. The method of + distinguishing between the two + types of output (or scripts) is implementation defined. +

+

+ Servers MAY implement a timeout period within which data must be + received from scripts. If a server implementation defines such + a timeout and receives no data from a script within the timeout + period, the server MAY terminate the script process and SHOULD + abort the client request with + either a + '504 Gateway Timed Out' or a + '500 Internal Server Error' response. +

+ +

+ + 7.1. Non-Parsed Header Output + +

+

+ Scripts using the NPH output form + MUST return a complete HTTP response message, as described + in Section 6 of the HTTP specifications + [3,8]. + NPH scripts + MUST use the SERVER_PROTOCOL variable to determine the appropriate format + for a response. +

+

+ Servers + SHOULD attempt to ensure that the script output is sent + directly to the client, with minimal + internal and no transport-visible + buffering. +

+ +

+ + 7.2. Parsed Header Output + +

+

+ Scripts using the parsed header output form MUST supply + a CGI response message to the server + as follows: +

+

+
+    CGI-Response   = *optional-field CGI-Field *optional-field NL [ Message-Body ]
+    optional-field = ( CGI-Field | HTTP-Field )
+    CGI-Field      = Content-type
+                   | Location
+                   | Status
+                   | extension-header
+  
+

+ The response comprises a header and a body, separated by a blank line. + The body may be NULL. + The header fields are either CGI header fields to be interpreted by + the server, or HTTP header fields + to be included in the response returned + to the client + if the request method is HTTP. At least one + CGI-Field MUST be + supplied, but no CGI field name may be used more than once + in a response. + If a body is supplied, then a "Content-type" + header field MUST be + supplied by the script, + otherwise the script MUST send a "Location" + or "Status" header field. If a + Location CGI-Field + is returned, then the script MUST NOT supply + any HTTP-Fields. +

+

+ Each header field in a CGI-Response MUST be specified on a single line; + CGI/1.1 does not support continuation lines. +

+ +

+ + 7.2.1. CGI header fields + +

+

+ The CGI header fields have the generic syntax: +

+

+
+    generic-field  = field-name ":" [ field-value ] NL
+    field-name     = token
+    field-value    = *( field-content | LWSP )
+    field-content  = *( token | tspecial | quoted-string )
+  
+

+ The field-name is not case sensitive; a NULL field value is + equivalent to the header field not being sent. +

+ +

+ + 7.2.1.1. Content-Type + +

+

+ The Internet Media Type [9] of the entity + body, which is to be sent unmodified to the client. +

+

+
+    Content-Type = "Content-Type" ":" media-type NL
+  
+

+ This is actually an HTTP-Field + rather than a CGI-Field, but + it is listed here because of its importance in the CGI dialogue as + a member of the "one of these is required" set of header + fields. +

+ +

+ + 7.2.1.2. Location + +

+

+ This is used to specify to the server that the script is returning a + reference to a document rather than an actual document. +

+

+
+    Location         = "Location" ":"
+                       ( fragment-URI | rel-URL-abs-path ) NL
+    fragment-URI     = URI [ # fragmentid ]
+    URI              = scheme ":" *qchar
+    fragmentid       = *qchar
+    rel-URL-abs-path = "/" [ hpath ] [ "?" query-string ]
+    hpath            = fpsegment *( "/" psegment )
+    fpsegment        = 1*hchar
+    psegment         = *hchar
+    hchar            = alpha | digit | safe | extra
+                       | ":" | "@" | "& | "="
+  
+

+ The Location + value is either an absolute URI with optional fragment, + as defined in RFC 1630 [1], or an absolute path + within the server's URI space (i.e., + omitting the scheme and network-related fields) and optional + query-string. If an absolute URI is returned by the script, + then the + server MUST generate a + '302 redirect' HTTP response + message unless the script has supplied an + explicit Status response header field. + Scripts returning an absolute URI MAY choose to + provide a message-body. Servers MUST make any appropriate modifications + to the script's output to ensure the response to the user-agent complies + with the response protocol version. + If the Location value is a path, then the server + MUST generate + the response that it would have produced in response to a request + containing the URL +

+

+
+    scheme "://" SERVER_NAME ":" SERVER_PORT rel-URL-abs-path
+  
+

+ Note: If the request was accompanied by a + message-body + (such as for a POST request), and the script + redirects the request with a Location field, the + message-body + may not be + available to the resource that is the target of the redirect. +

+ +

+ + 7.2.1.3. Status + +

+

+ The "Status" header field is used to indicate to the server what + status code the server MUST use in the response message. +

+

+
+    Status        = "Status" ":" digit digit digit SP reason-phrase NL
+    reason-phrase = *<CHAR, excluding CTLs, NL>
+  
+

+ The valid status codes are listed in section 6.1.1 of the HTTP/1.0 + specifications [3]. If the SERVER_PROTOCOL is + "HTTP/1.1", then the status codes defined in the HTTP/1.1 + specification [8] may + be used. If the script does not return a "Status" header + field, then "200 OK" SHOULD be assumed by the server. +

+

+ If a script is being used to handle a particular error or condition + encountered by the server, such as a '404 Not Found' error, the script + SHOULD use the "Status" CGI header field to propagate the error + condition back to the client. E.g., in the example mentioned it + SHOULD include a "Status: 404 Not Found" in the + header data returned to the server. +

+ +

+ + 7.2.1.4. Extension header fields + +

+

+ Scripts MAY include in their CGI response header additional fields + not defined in this or the HTTP specification. + These are called "extension" fields, + and have the syntax of a generic-field as defined in + section 7.2.1. The name of an extension field + MUST NOT conflict with a field name defined in this or any other + specification; extension field names SHOULD begin with "X-CGI-" + to ensure uniqueness. +

+ +

+ + 7.2.2. HTTP header fields + +

+

+ The script MAY return any other header fields defined by the + specification + for the SERVER_PROTOCOL (HTTP/1.0 [3] or HTTP/1.1 + [8]). + Servers MUST resolve conflicts beteen CGI header + and HTTP header formats or names (see section 8). +

+ +

+ + 8. Server Implementation + +

+

+ This section defines the requirements that must be met by HTTP + servers in order to provide a coherent and correct CGI/1.1 + environment in which scripts may function. It is intended + primarily for server implementors, but it is useful for + script authors to be familiar with the information as well. +

+ +

+ + 8.1. Requirements for Servers + +

+

+ In order to be considered CGI/1.1-compliant, a server must meet + certain basic criteria and provide certain minimal functionality. + The details of these requirements are described in the following sections. +

+ +

+ + 8.1.1. Script-URI + +

+

+ Servers MUST support the standard mechanism (described below) which + allows + script authors to determine + what URL to use in documents + which reference the script; + specifically, what URL to use in order to + achieve particular settings of the + metavariables. This + mechanism is as follows: +

+

+ The server + MUST translate the header data from the CGI header field syntax to + the HTTP + header field syntax if these differ. For example, the character + sequence for + newline (such as Unix's ASCII NL) used by CGI scripts may not be the + same as that used by HTTP (ASCII CR followed by LF). The server MUST + also resolve any conflicts between header fields returned by the script + and header fields that it would otherwise send itself. +

+ +

+ + 8.1.2. Request Message-body Handling + +

+

+ These are the requirements for server handling of message-bodies directed + to CGI/1.1 resources: +

+
    +
  1. The message-body the server provides to the CGI script MUST + have any transfer encodings removed. +
  2. +
  3. The server MUST derive and provide a value for the CONTENT_LENGTH + metavariable that reflects the length of the message-body after any + transfer decoding. +
  4. +
  5. The server MUST leave intact any content-encodings of the message-body. +
  6. +
+ +

+ + 8.1.3. Required Metavariables + +

+

+ Servers MUST provide scripts with certain information and + metavariables + as described in section 8.3. +

+ +

+ + 8.1.4. Response Compliance + +

+

+ Servers MUST ensure that responses sent to the user-agent meet all + requirements of the protocol level in effect. This may involve + modifying, deleting, or augmenting any header + fields and/or message-body supplied by the script. +

+ +

+ + 8.2. Recommendations for Servers + +

+

+ Servers SHOULD provide the "query" component of the script-URI + as command-line arguments to scripts if it does not + contain any unencoded '=' characters and the command-line arguments can + be generated in an unambiguous manner. + (See section 5.) +

+

+ Servers SHOULD set the AUTH_TYPE + metavariable to the value of the + 'auth-scheme' token of the "Authorization" + field if it was supplied as part of the request header. + (See section 6.1.1.) +

+

+ Where applicable, servers SHOULD set the current working directory + to the directory in which the script is located before invoking + it. +

+

+ Servers MAY reject with error '404 Not Found' + any requests that would result in + an encoded "/" being decoded into PATH_INFO or SCRIPT_NAME, as this + might represent a loss of information to the script. +

+

+ Although the server and the CGI script need not be consistent in + their handling of URL paths (client URLs and the PATH_INFO data, + respectively), server authors may wish to impose consistency. + So the server implementation SHOULD define its behaviour for the + following cases: +

+
    +
  1. define any restrictions on allowed characters, in particular + whether ASCII NUL is permitted; +
  2. +
  3. define any restrictions on allowed path segments, in particular + whether non-terminal NULL segments are permitted; +
  4. +
  5. define the behaviour for "." or ".." path + segments; i.e., whether they are prohibited, treated as + ordinary path + segments or interpreted in accordance with the relative URL + specification [7]; +
  6. +
  7. define any limits of the implementation, including limits on path or + search string lengths, and limits on the volume of header data the server + will parse. +
  8. +
+

+ Servers MAY generate the + Script-URI in + any way from the client URI, + or from any other data (but the behaviour SHOULD be documented). +

+

+ For non-parsed header (NPH) scripts (see + section 7.1), servers SHOULD + attempt to ensure that the script input comes directly from the + client, with minimal buffering. For all scripts the data will be + as supplied by the client. +

+ +

+ + 8.3. Summary of + MetaVariables + +

+

+ Servers MUST provide the following + metavariables to + scripts. See the individual descriptions for exceptions and semantics. +

+

+
+    CONTENT_LENGTH (section 6.1.2)
+    CONTENT_TYPE (section 6.1.3)
+    GATEWAY_INTERFACE (section 6.1.4)
+    PATH_INFO (section 6.1.6)
+    QUERY_STRING (section 6.1.8)
+    REMOTE_ADDR (section 6.1.9)
+    REQUEST_METHOD (section 6.1.13)
+    SCRIPT_NAME (section 6.1.14)
+    SERVER_NAME (section 6.1.15)
+    SERVER_PORT (section 6.1.16)
+    SERVER_PROTOCOL (section 6.1.17)
+    SERVER_SOFTWARE (section 6.1.18)
+  
+

+ Servers SHOULD define the following + metavariables for scripts. + See the individual descriptions for exceptions and semantics. +

+

+
+    AUTH_TYPE (section 6.1.1)
+    REMOTE_HOST (section 6.1.10)
+  
+

+ In addition, servers SHOULD provide + metavariables for all fields present + in the HTTP request header, with the exception of those involved with + access control. Servers MAY at their discretion provide + metavariables + for access control fields. +

+

+ Servers MAY define the following + metavariables. See the individual + descriptions for exceptions and semantics. +

+

+
+    PATH_TRANSLATED (section 6.1.7)
+    REMOTE_IDENT (section 6.1.11)
+    REMOTE_USER (section 6.1.12)
+  
+

+ Servers MAY + at their discretion define additional implementation-specific + extension metavariables + provided their names do not + conflict with defined header field names. Implementation-specific + metavariable names SHOULD + be prefixed with "X_" (e.g., + "X_DBA") to avoid the potential for such conflicts. +

+ +

+ + 9. + Script Implementation + +

+

+ This section defines the requirements and recommendations for scripts + that are intended to function in a CGI/1.1 environment. It is intended + primarily as a reference for script authors, but server implementors + should be familiar with these issues as well. +

+ +

+ + 9.1. Requirements for Scripts + +

+

+ Scripts using the parsed-header method to communicate with servers + MUST supply a response header to the server. + (See section 7.) +

+

+ Scripts using the NPH method to communicate with servers MUST + provide complete HTTP responses, and MUST use the value of the + SERVER_PROTOCOL metavariable + to determine the appropriate format. + (See section 7.1.) +

+

+ Scripts MUST check the value of the REQUEST_METHOD + metavariable in order + to provide an appropriate response. + (See section 6.1.13.) +

+

+ Scripts MUST be prepared to handled URL-encoded values in + metavariables. + In addition, they MUST recognise both "+" and "%20" in URL-encoded + quantities as representing the space character. + (See section 3.1.) +

+

+ Scripts MUST ignore leading zeros in the major and minor version numbers + in the GATEWAY_INTERFACE + metavariable value. (See + section 6.1.4.) +

+

+ When processing requests that include a + message-body, scripts + MUST NOT read more than CONTENT_LENGTH bytes from the input stream. + (See sections 6.1.2 and 6.2.) +

+ +

+ + 9.2. Recommendations for Scripts + +

+

+ Servers may interrupt or terminate script execution at any time + and without warning, so scripts SHOULD be prepared to deal with + abnormal termination. +

+

+ Scripts MUST + reject with + error '405 Method Not + Allowed' requests + made using methods that they do not support. If the script does + not intend + processing the PATH_INFO data, then it SHOULD reject the request with + '404 Not + Found' if PATH_INFO is not NULL. +

+

+ If a script is processing the output of a form, it SHOULD + verify that the CONTENT_TYPE + is "application/x-www-form-urlencoded" [2] + or whatever other media type is expected. +

+

+ Scripts parsing PATH_INFO, + PATH_TRANSLATED, or SCRIPT_NAME + SHOULD be careful + of void path segments ("//") and special path segments + ("." and + ".."). They SHOULD either be removed from the path before + use in OS + system calls, or the request SHOULD be rejected with + '404 Not Found'. +

+

+ As it is impossible for + scripts to determine the client URI that + initiated a + request without knowledge of the specific server in + use, the script SHOULD NOT return "text/html" + documents containing + relative URL links without including a "<BASE>" + tag in the document. +

+

+ When returning header fields, + scripts SHOULD try to send the CGI + header fields (see section + 7.2) as soon as possible, and + SHOULD send them + before any HTTP header fields. This may + help reduce the server's memory requirements. +

+ +

+ + 10. System Specifications + +

+ +

+ + 10.1. AmigaDOS + +

+

+ The implementation of the CGI on an AmigaDOS operating system platform + SHOULD use environment variables as the mechanism of providing + request metadata to CGI scripts. +

+
+
Environment variables +
+
+

+ These are accessed by the DOS library routine GetVar. The + flags argument SHOULD be 0. Case is ignored, but upper case is + recommended for compatibility with case-sensitive systems. +

+
+
The current working directory +
+
+

+ The current working directory for the script is set to the directory + containing the script. +

+
+
Character set +
+
+

+ The US-ASCII character set is used for the definition of environment + variable names and header + field names; the newline (NL) sequence is LF; + servers SHOULD also accept CR LF as a newline. +

+
+
+ +

+ + 10.2. Unix + +

+

+ The implementation of the CGI on a UNIX operating system platform + SHOULD use environment variables as the mechanism of providing + request metadata to CGI scripts. +

+

+ For Unix compatible operating systems, the following are defined: +

+
+
Environment variables +
+
+

+ These are accessed by the C library routine getenv. +

+
+
The command line +
+
+

+ This is accessed using the + argc and argv + arguments to main(). The words have any characters + that + are 'active' in the Bourne shell escaped with a backslash. + If the value of the QUERY_STRING + metavariable + contains an unencoded equals-sign '=', then the command line + SHOULD NOT be used by the script. +

+
+
The current working directory +
+
+

+ The current working directory for the script + SHOULD be set to the directory + containing the script. +

+
+
Character set +
+
+

+ The US-ASCII character set is used for the definition of environment + variable names and header field names; the newline (NL) sequence is LF; + servers SHOULD also accept CR LF as a newline. +

+
+
+ +

+ + 11. Security Considerations + +

+ +

+ + 11.1. Safe Methods + +

+

+ As discussed in the security considerations of the HTTP + specifications [3,8], the + convention has been established that the + GET and HEAD methods should be 'safe'; they should cause no + side-effects and only have the significance of resource retrieval. +

+

+ CGI scripts are responsible for enforcing any HTTP security considerations + [3,8] + with respect to the protocol version level of the request and + any side effects generated by the scripts on behalf of + the server. Primary + among these + are the considerations of safe and idempotent methods. Idempotent + requests are those that may be repeated an arbitrary number of times + and produce side effects identical to a single request. +

+ +

+ + 11.2. HTTP Header + Fields Containing Sensitive Information + +

+

+ Some HTTP header fields may carry sensitive information which the server + SHOULD NOT pass on to the script unless explicitly configured to do + so. For example, if the server protects the script using the + "Basic" + authentication scheme, then the client will send an + "Authorization" + header field containing a username and password. If the server, rather + than the script, validates this information then the password SHOULD + NOT be passed on to the script via the HTTP_AUTHORIZATION + metavariable + without careful consideration. + This also applies to the + Proxy-Authorization header field and the corresponding + HTTP_PROXY_AUTHORIZATION + metavariable. +

+ +

+ + 11.3. Script + Interference with the Server + +

+

+ The most common implementation of CGI invokes the script as a child + process using the same user and group as the server process. It + SHOULD therefore be ensured that the script cannot interfere with the + server process, its configuration, or documents. +

+

+ If the script is executed by calling a function linked in to the + server software (either at compile-time or run-time) then precautions + SHOULD be taken to protect the core memory of the server, or to + ensure that untrusted code cannot be executed. +

+ +

+ + 11.4. Data Length and Buffering Considerations + +

+

+ This specification places no limits on the length of message-bodies + presented to the script. Scripts should not assume that statically + allocated buffers of any size are sufficient to contain the entire + submission at one time. Use of a fixed length buffer without careful + overflow checking may result in an attacker exploiting 'stack-smashing' + or 'stack-overflow' vulnerabilities of the operating system. + Scripts may spool large submissions to disk or other buffering media, + but a rapid succession of large submissions may result in denial of + service conditions. If the CONTENT_LENGTH of a message-body is larger + than resource considerations allow, scripts should respond with an + error status appropriate for the protocol version; potentially applicable + status codes include '503 Service Unavailable' (HTTP/1.0 and HTTP/1.1), + '413 Request Entity Too Large' (HTTP/1.1), and + '414 Request-URI Too Long' (HTTP/1.1). +

+ +

+ + 11.5. Stateless Processing + +

+

+ The stateless nature of the Web makes each script execution and resource + retrieval independent of all others even when multiple requests constitute a + single conceptual Web transaction. Because of this, a script should not + make any assumptions about the context of the user-agent submitting a + request. In particular, scripts should examine data obtained from the client + and verify that they are valid, both in form and content, before allowing + them to be used for sensitive purposes such as input to other + applications, commands, or operating system services. These uses + include, but are not + limited to: system call arguments, database writes, dynamically evaluated + source code, and input to billing or other secure processes. It is important + that applications be protected from invalid input regardless of whether + the invalidity is the result of user error, logic error, or malicious action. +

+

+ Authors of scripts involved in multi-request transactions should be + particularly cautios about validating the state information; + undesirable effects may result from the substitution of dangerous + values for portions of the submission which might otherwise be + presumed safe. Subversion of this type occurs when alterations + are made to data from a prior stage of the transaction that were + not meant to be controlled by the client (e.g., hidden + HTML form elements, cookies, embedded URLs, etc.). +

+ +

+ + 12. Acknowledgements + +

+

+ This work is based on a draft published in 1997 by David R. Robinson, + which in turn was based on the original CGI interface that arose out of + discussions on the www-talk mailing list. In particular, + Rob McCool, John Franks, Ari Luotonen, + George Phillips and + Tony Sanders deserve special recognition for their efforts in + defining and implementing the early versions of this interface. +

+

+ This document has also greatly benefited from the comments and + suggestions made by Chris Adie, Dave Kristol, + Mike Meyer, David Morris, Jeremy Madea, + Patrick McManus, Adam Donahue, + Ross Patterson, and Harald Alvestrand. +

+ +

+ + 13. References + +

+
+
[1] +
+
Berners-Lee, T., 'Universal Resource Identifiers in WWW: A + Unifying Syntax for the Expression of Names and Addresses of + Objects on the Network as used in the World-Wide Web', RFC 1630, + CERN, June 1994. +

+

+
+
[2] +
+
Berners-Lee, T. and Connolly, D., 'Hypertext Markup Language - + 2.0', RFC 1866, MIT/W3C, November 1995. +

+

+
+
[3] +
+
Berners-Lee, T., Fielding, R. T. and Frystyk, H., + 'Hypertext Transfer Protocol -- HTTP/1.0', RFC 1945, MIT/LCS, + UC Irvine, May 1996. +

+

+
+ +
[4] +
+
Berners-Lee, T., Fielding, R., and Masinter, L., Editors, + 'Uniform Resource Identifiers (URI): Generic Syntax', RFC 2396, + MIT, U.C. Irvine, Xerox Corporation, August 1996. +

+

+
+ +
[5] +
+
Braden, R., Editor, 'Requirements for Internet Hosts -- + Application and Support', STD 3, RFC 1123, IETF, October 1989. +

+

+
+
[6] +
+
Crocker, D.H., 'Standard for the Format of ARPA Internet Text + Messages', STD 11, RFC 822, University of Delaware, August 1982. +

+

+
+
[7] +
+
Fielding, R., 'Relative Uniform Resource Locators', RFC 1808, + UC Irvine, June 1995. +

+

+
+
[8] +
+
Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and + Berners-Lee, T., 'Hypertext Transfer Protocol -- HTTP/1.1', + RFC 2068, UC Irvine, DEC, + MIT/LCS, January 1997. +

+

+
+
[9] +
+
Freed, N. and Borenstein N., 'Multipurpose Internet Mail + Extensions (MIME) Part Two: Media Types', RFC 2046, Innosoft, + First Virtual, November 1996. +

+

+
+
[10] +
+
Mockapetris, P., 'Domain Names - Concepts and Facilities', + STD 13, RFC 1034, ISI, November 1987. +

+

+
+
[11] +
+
St. Johns, M., 'Identification Protocol', RFC 1431, US + Department of Defense, February 1993. +

+

+
+
[12] +
+
'Coded Character Set -- 7-bit American Standard Code for + Information Interchange', ANSI X3.4-1986. +

+

+
+
[13] +
+
Hinden, R. and Deering, S., + 'IP Version 6 Addressing Architecture', RFC 2373, + Nokia, Cisco Systems, + July 1998. +

+

+
+
+ +

+ + 14. Authors' Addresses + +

+
+

+ Ken A L Coar +
+ MeepZor Consulting +
+ 7824 Mayfaire Crest Lane, Suite 202 +
+ Raleigh, NC 27615-4875 +
+ U.S.A. +

+

+ Tel: +1 (919) 254.4237 +
+ Fax: +1 (919) 254.5250 +
+ Email: + Ken.Coar@Golux.Com +

+
+
+

+ David Robinson +
+ E*TRADE UK Ltd +
+ Mount Pleasant House +
+ 2 Mount Pleasant +
+ Huntingdon Road +
+ Cambridge CB3 0RN +
+ UK +

+

+ Tel: +44 (1223) 566926 +
+ Fax: +44 (1223) 506288 +
+ Email: + drtr@etrade.co.uk +

+ + + diff --git a/docs/ifupdown_design.txt b/docs/ifupdown_design.txt new file mode 100644 index 000000000..8ab4e51ad --- /dev/null +++ b/docs/ifupdown_design.txt @@ -0,0 +1,44 @@ +This document is meant to convince you to not use ifup/ifdown. + + +The general problem with ifupdown is that it is "copulated in vertical +fashion" by design. It tries to do the job of shell script in C, +and this is invariably doomed to fail. You need ifup/ifdown +to be adaptable by local admins, and C is an extremely poor choice +for that. + +We are doomed to have problems with ifup/ifdown. Just look as this code: + +static const struct dhcp_client_t ext_dhcp_clients[] = { + { "dhcpcd", "", "" }, + { "dhclient", ........ }, + { "pump", ........ }, + { "udhcpc", ........ }, +}; + +static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) +{ +#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP + int i ; + for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { + if (exists_execable(ext_dhcp_clients[i].name)) + return execute(ext_dhcp_clients[i].stopcmd, ifd, exec); + } + bb_error_msg("no dhcp clients found, using static interface shutdown"); + return static_down(ifd, exec); +#elif ENABLE_UDHCPC + return execute("kill " + "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); +#else + return 0; /* no dhcp support */ +#endif +} + +How the hell it is supposed to work reliably this way? Just imagine that +admin is using pump and ifup/ifdown. It works. Then, for whatever reason, +admin installs dhclient, but does NOT use it. ifdown will STOP WORKING, +just because it will see installed dhclient binary in e.g. /usr/bin/dhclient! +This is stupid. + +I seriously urge people to not use ifup/ifdown. +Use something less brain damaged. diff --git a/docs/keep_data_small.txt b/docs/keep_data_small.txt new file mode 100644 index 000000000..21d732674 --- /dev/null +++ b/docs/keep_data_small.txt @@ -0,0 +1,256 @@ + Keeping data small + +When many applets are compiled into busybox, all rw data and +bss for each applet are concatenated. Including those from libc, +if static busybox is built. When busybox is started, _all_ this data +is allocated, not just that one part for selected applet. + +What "allocated" exactly means, depends on arch. +On NOMMU it's probably bites the most, actually using real +RAM for rwdata and bss. On i386, bss is lazily allocated +by COWed zero pages. Not sure about rwdata - also COW? + +In order to keep busybox NOMMU and small-mem systems friendly +we should avoid large global data in our applets, and should +minimize usage of libc functions which implicitly use +such structures. + +Small experiment to measure "parasitic" bbox memory consumption: +here we start 1000 "busybox sleep 10" in parallel. +busybox binary is practically allyesconfig static one, +built against uclibc. Run on x86-64 machine with 64-bit kernel: + +bash-3.2# nmeter '%t %c %m %p %[pn]' +23:17:28 .......... 168M 0 147 +23:17:29 .......... 168M 0 147 +23:17:30 U......... 168M 1 147 +23:17:31 SU........ 181M 244 391 +23:17:32 SSSSUUU... 223M 757 1147 +23:17:33 UUU....... 223M 0 1147 +23:17:34 U......... 223M 1 1147 +23:17:35 .......... 223M 0 1147 +23:17:36 .......... 223M 0 1147 +23:17:37 S......... 223M 0 1147 +23:17:38 .......... 223M 1 1147 +23:17:39 .......... 223M 0 1147 +23:17:40 .......... 223M 0 1147 +23:17:41 .......... 210M 0 906 +23:17:42 .......... 168M 1 147 +23:17:43 .......... 168M 0 147 + +This requires 55M of memory. Thus 1 trivial busybox applet +takes 55k of memory on 64-bit x86 kernel. + +On 32-bit kernel we need ~26k per applet. + +Script: + +i=1000; while test $i != 0; do + echo -n . + busybox sleep 30 & + i=$((i - 1)) +done +echo +wait + +(Data from NOMMU arches are sought. Provide 'size busybox' output too) + + + Example 1 + +One example how to reduce global data usage is in +archival/libarchive/decompress_unzip.c: + +/* This is somewhat complex-looking arrangement, but it allows + * to place decompressor state either in bss or in + * malloc'ed space simply by changing #defines below. + * Sizes on i386: + * text data bss dec hex + * 5256 0 108 5364 14f4 - bss + * 4915 0 0 4915 1333 - malloc + */ +#define STATE_IN_BSS 0 +#define STATE_IN_MALLOC 1 + +(see the rest of the file to get the idea) + +This example completely eliminates globals in that module. +Required memory is allocated in unpack_gz_stream() [its main module] +and then passed down to all subroutines which need to access 'globals' +as a parameter. + + + Example 2 + +In case you don't want to pass this additional parameter everywhere, +take a look at archival/gzip.c. Here all global data is replaced by +single global pointer (ptr_to_globals) to allocated storage. + +In order to not duplicate ptr_to_globals in every applet, you can +reuse single common one. It is defined in libbb/messages.c +as struct globals *const ptr_to_globals, but the struct globals is +NOT defined in libbb.h. You first define your own struct: + +struct globals { int a; char buf[1000]; }; + +and then declare that ptr_to_globals is a pointer to it: + +#define G (*ptr_to_globals) + +ptr_to_globals is declared as constant pointer. +This helps gcc understand that it won't change, resulting in noticeably +smaller code. In order to assign it, use SET_PTR_TO_GLOBALS macro: + + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); + +Typically it is done in _main(). + +Now you can reference "globals" by G.a, G.buf and so on, in any function. + + + bb_common_bufsiz1 + +There is one big common buffer in bss - bb_common_bufsiz1. It is a much +earlier mechanism to reduce bss usage. Each applet can use it for +its needs. Library functions are prohibited from using it. + +'G.' trick can be done using bb_common_bufsiz1 instead of malloced buffer: + +#define G (*(struct globals*)&bb_common_bufsiz1) + +Be careful, though, and use it only if globals fit into bb_common_bufsiz1. +Since bb_common_bufsiz1 is BUFSIZ + 1 bytes long and BUFSIZ can change +from one libc to another, you have to add compile-time check for it: + +if (sizeof(struct globals) > sizeof(bb_common_bufsiz1)) + BUG__globals_too_big(); + + + Drawbacks + +You have to initialize it by hand. xzalloc() can be helpful in clearing +allocated storage to 0, but anything more must be done by hand. + +All global variables are prefixed by 'G.' now. If this makes code +less readable, use #defines: + +#define dev_fd (G.dev_fd) +#define sector (G.sector) + + + Word of caution + +If applet doesn't use much of global data, converting it to use +one of above methods is not worth the resulting code obfuscation. +If you have less than ~300 bytes of global data - don't bother. + + + Finding non-shared duplicated strings + +strings busybox | sort | uniq -c | sort -nr + + + gcc's data alignment problem + +The following attribute added in vi.c: + +static int tabstop; +static struct termios term_orig __attribute__ ((aligned (4))); +static struct termios term_vi __attribute__ ((aligned (4))); + +reduces bss size by 32 bytes, because gcc sometimes aligns structures to +ridiculously large values. asm output diff for above example: + + tabstop: + .zero 4 + .section .bss.term_orig,"aw",@nobits +- .align 32 ++ .align 4 + .type term_orig, @object + .size term_orig, 60 + term_orig: + .zero 60 + .section .bss.term_vi,"aw",@nobits +- .align 32 ++ .align 4 + .type term_vi, @object + .size term_vi, 60 + +gcc doesn't seem to have options for altering this behaviour. + +gcc 3.4.3 and 4.1.1 tested: +char c = 1; +// gcc aligns to 32 bytes if sizeof(struct) >= 32 +struct { + int a,b,c,d; + int i1,i2,i3; +} s28 = { 1 }; // struct will be aligned to 4 bytes +struct { + int a,b,c,d; + int i1,i2,i3,i4; +} s32 = { 1 }; // struct will be aligned to 32 bytes +// same for arrays +char vc31[31] = { 1 }; // unaligned +char vc32[32] = { 1 }; // aligned to 32 bytes + +-fpack-struct=1 reduces alignment of s28 to 1 (but probably +will break layout of many libc structs) but s32 and vc32 +are still aligned to 32 bytes. + +I will try to cook up a patch to add a gcc option for disabling it. +Meanwhile, this is where it can be disabled in gcc source: + +gcc/config/i386/i386.c +int +ix86_data_alignment (tree type, int align) +{ +#if 0 + if (AGGREGATE_TYPE_P (type) + && TYPE_SIZE (type) + && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 256 + || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 256) + return 256; +#endif + +Result (non-static busybox built against glibc): + +# size /usr/srcdevel/bbox/fix/busybox.t0/busybox busybox + text data bss dec hex filename + 634416 2736 23856 661008 a1610 busybox + 632580 2672 22944 658196 a0b14 busybox_noalign + + + + Keeping code small + +Set CONFIG_EXTRA_CFLAGS="-fno-inline-functions-called-once", +produce "make bloatcheck", see the biggest auto-inlined functions. +Now, set CONFIG_EXTRA_CFLAGS back to "", but add NOINLINE +to some of these functions. In 1.16.x timeframe, the results were +(annotated "make bloatcheck" output): + +function old new delta +expand_vars_to_list - 1712 +1712 win +lzo1x_optimize - 1429 +1429 win +arith_apply - 1326 +1326 win +read_interfaces - 1163 +1163 loss, leave w/o NOINLINE +logdir_open - 1148 +1148 win +check_deps - 1148 +1148 loss +rewrite - 1039 +1039 win +run_pipe 358 1396 +1038 win +write_status_file - 1029 +1029 almost the same, leave w/o NOINLINE +dump_identity - 987 +987 win +mainQSort3 - 921 +921 win +parse_one_line - 916 +916 loss +summarize - 897 +897 almost the same +do_shm - 884 +884 win +cpio_o - 863 +863 win +subCommand - 841 +841 loss +receive - 834 +834 loss + +855 bytes saved in total. + +scripts/mkdiff_obj_bloat may be useful to automate this process: run +"scripts/mkdiff_obj_bloat NORMALLY_BUILT_TREE FORCED_NOINLINE_TREE" +and select modules which shrank. diff --git a/docs/logging_and_backgrounding.txt b/docs/logging_and_backgrounding.txt new file mode 100644 index 000000000..7e6885560 --- /dev/null +++ b/docs/logging_and_backgrounding.txt @@ -0,0 +1,96 @@ + Logging and backgrounding + +By default, bb_[p]error_msg[_and_die] messages go to stderr, +and of course, usually applets do not auto-background. :) + +Historically, daemons and inetd services are different. + +Busybox is trying to provide compatible behavior, thus if an applet +is emulating an existing utility, it should mimic it. If utility +auto-backgrounds itself, busybox applet should do the same. +If utility normally logs to syslog, busybox applet should do +the same too. + +However, busybox should not needlessly restrict the freedom +of the users. And users have different needs and different preferences. +Some might like logging everything from daemons to syslog. +Others prefer running stuff under runsv/svlogd and thus would like +logging to stderr and no daemonization. + +To help with that, busybox applets should have options to override +default behavior, whatever that is for a given applet. + + +Current situation is a bit of a mess: + +acpid - auto-backgrounds unless -d +crond - auto-backgrounds unless -f, logs to syslog unless -d or -L. + option -d logs to stderr, -L FILE logs to FILE +devfsd - (obsolete) +dnsd - option -d makes it background and log to syslog +fakeidentd - inetd service. Auto-backgrounds and logs to syslog + if no -f and no -i and no -w (-i is "inetd service" flag, + -w is "inetd-wait service" flag) +ftpd - inetd service. Logs to syslog with -S, with -v logs to strerr too +httpd - auto-backgrounds unless -f or -i (-i is "inetd service" flag) +inetd - auto-backgrounds unless -f, logs to syslog unless -e +klogd - auto-backgrounds unless -n +syslogd - auto-backgrounds unless -n +telnetd - auto-backgrounds unless -f or -i (-i is "inetd service" flag) +udhcpc - auto-backgrounds unless -f after lease is obtained, + option -b makes it background sooner (when lease attempt + fails and retries start), + after backgrounding it stops logging to stderr; + logs to stderr, but option -S makes it log *also* to syslog +udhcpd - auto-backgrounds and do not log to stderr unless -f, + otherwise logs to stderr, but option -S makes it log *also* to syslog +zcip - auto-backgrounds and logs *also* to syslog unless -f + +Total: 13 applets (+1 obsolete), + 4 log to syslog by default (crond fakeidentd inetd zcip), + 5 never log to syslog (acpid httpd telnetd klogd syslogd, last two + - for obviously correct reasons), + there are no daemons which always log to syslog, + 12 auto-background if not run as inetd services (all except dnsd. + Note that there is no "standard" dnsd AFAIKS). But see below + for daemons (tcpsvd etc) which don't auto-background. + +miscutils/crond.c: logmode = LOGMODE_SYSLOG; +networking/dnsd.c: logmode = LOGMODE_SYSLOG; +networking/ftpd.c: logmode = LOGMODE_NONE; +networking/ftpd.c: logmode |= LOGMODE_SYSLOG; +networking/inetd.c: logmode = LOGMODE_SYSLOG; +networking/isrv_identd.c: logmode = LOGMODE_SYSLOG; +networking/telnetd.c: logmode = LOGMODE_SYSLOG; +networking/udhcp/dhcpc.c: logmode = LOGMODE_NONE; +networking/udhcp/dhcpc.c: logmode |= LOGMODE_SYSLOG; +networking/udhcp/dhcpc.c: logmode &= ~LOGMODE_STDIO; +networking/udhcp/dhcpd.c: logmode = LOGMODE_NONE; +networking/udhcp/dhcpd.c: logmode |= LOGMODE_SYSLOG; +networking/zcip.c: logmode |= LOGMODE_SYSLOG; + + +These daemons never auto-background and never log to syslog: + +lpd - inetd service. Has nothing to log so far, though +dhcprelay - standard behavior +inotifyd - standard behavior +runsv - standard behavior +runsvdir - standard behavior +svlogd - standard behavior +tcpsvd, udpsvd - standard behavior +tftpd - standard behavior + + +Non-daemons (seems to be use syslog for a good reason): + +networking/nameif.c: logmode |= LOGMODE_SYSLOG; +loginutils/chpasswd.c: logmode = LOGMODE_BOTH; +loginutils/chpasswd.c: logmode = LOGMODE_STDIO; +loginutils/getty.c: logmode = LOGMODE_BOTH; +loginutils/getty.c: logmode = LOGMODE_NONE; +loginutils/passwd.c: logmode = LOGMODE_STDIO; +loginutils/passwd.c: logmode = LOGMODE_BOTH; +loginutils/sulogin.c: logmode = LOGMODE_SYSLOG; (used if stdio isn't a tty) +loginutils/sulogin.c: logmode = LOGMODE_BOTH; +util-linux/mount.c: logmode = LOGMODE_SYSLOG; (used in a backgrounded NFS mount helper) diff --git a/docs/mdev.txt b/docs/mdev.txt new file mode 100644 index 000000000..2d03bd8b2 --- /dev/null +++ b/docs/mdev.txt @@ -0,0 +1,145 @@ +------------- + MDEV Primer +------------- + +For those of us who know how to use mdev, a primer might seem lame. For +everyone else, mdev is a weird black box that they hear is awesome, but can't +seem to get their head around how it works. Thus, a primer. + +----------- + Basic Use +----------- + +Mdev has two primary uses: initial population and dynamic updates. Both +require sysfs support in the kernel and have it mounted at /sys. For dynamic +updates, you also need to have hotplugging enabled in your kernel. + +Here's a typical code snippet from the init script: +[0] mount -t proc proc /proc +[1] mount -t sysfs sysfs /sys +[2] echo /sbin/mdev > /proc/sys/kernel/hotplug +[3] mdev -s + +Alternatively, without procfs the above becomes: +[1] mount -t sysfs sysfs /sys +[2] sysctl -w kernel.hotplug=/sbin/mdev +[3] mdev -s + + +Of course, a more "full" setup would entail executing this before the previous +code snippet: +[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev +[5] mkdir /dev/pts +[6] mount -t devpts devpts /dev/pts + +The simple explanation here is that [1] you need to have /sys mounted before +executing mdev. Then you [2] instruct the kernel to execute /sbin/mdev whenever +a device is added or removed so that the device node can be created or +destroyed. Then you [3] seed /dev with all the device nodes that were created +while the system was booting. + +For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem +(assuming you're running out of flash). Then you want to [5] create the +/dev/pts mount point and finally [6] mount the devpts filesystem on it. + +------------- + MDEV Config (/etc/mdev.conf) +------------- + +Mdev has an optional config file for controlling ownership/permissions of +device nodes if your system needs something more than the default root/root +660 permissions. + +The file has the format: + : + or @ : + +For example: + hd[a-z][0-9]* 0:3 660 + +The config file parsing stops at the first matching line. If no line is +matched, then the default of 0:0 660 is used. To set your own default, simply +create your own total match like so: + .* 1:1 777 + +You can rename/move device nodes by using the next optional field. + : [=path] +So if you want to place the device node into a subdirectory, make sure the path +has a trailing /. If you want to rename the device node, just place the name. + hda 0:3 660 =drives/ +This will move "hda" into the drives/ subdirectory. + hdb 0:3 660 =cdrom +This will rename "hdb" to "cdrom". + +Similarly, ">path" renames/moves the device but it also creates +a direct symlink /dev/DEVNAME to the renamed/moved device. + +You can also prevent creation of device nodes with the 4th field as "!": + tty[a-z]. 0:0 660 ! + pty[a-z]. 0:0 660 ! + +If you also enable support for executing your own commands, then the file has +the format: + : [=path] [@|$|*] + or + : [>path] [@|$|*] + or + : [!] [@|$|*] + +For example: +---8<--- +# block devices +([hs]d[a-z]) root:disk 660 >disk/%1/0 +([hs]d[a-z])([0-9]+) root:disk 660 >disk/%1/%2 +mmcblk([0-9]+) root:disk 660 >disk/mmc/%1/0 +mmcblk([0-9]+)p([0-9]+) root:disk 660 >disk/mmc/%1/%2 +# network devices +(tun|tap) root:network 660 >net/%1 +---8<--- + +The special characters have the meaning: + @ Run after creating the device. + $ Run before removing the device. + * Run both after creating and before removing the device. + +The command is executed via the system() function (which means you're giving a +command to the shell), so make sure you have a shell installed at /bin/sh. You +should also keep in mind that the kernel executes hotplug helpers with stdin, +stdout, and stderr connected to /dev/null. + +For your convenience, the shell env var $MDEV is set to the device name. So if +the device "hdc" was matched, MDEV would be set to "hdc". + +---------- + FIRMWARE +---------- + +Some kernel device drivers need to request firmware at runtime in order to +properly initialize a device. Place all such firmware files into the +/lib/firmware/ directory. At runtime, the kernel will invoke mdev with the +filename of the firmware which mdev will load out of /lib/firmware/ and into +the kernel via the sysfs interface. The exact filename is hardcoded in the +kernel, so look there if you need to know how to name the file in userspace. + +------------ + SEQUENCING +------------ + +Kernel does not serialize hotplug events. It increments SEQNUM environmental +variable for each successive hotplug invocation. Normally, mdev doesn't care. +This may reorder hotplug and hot-unplug events, with typical symptoms of +device nodes sometimes not created as expected. + +However, if /dev/mdev.seq file is found, mdev will compare its +contents with SEQNUM. It will retry up to two seconds, waiting for them +to match. If they match exactly (not even trailing '\n' is allowed), +or if two seconds pass, mdev runs as usual, then it rewrites /dev/mdev.seq +with SEQNUM+1. + +IOW: this will serialize concurrent mdev invocations. + +If you want to activate this feature, execute "echo >/dev/mdev.seq" prior to +setting mdev to be the hotplug handler. This writes single '\n' to the file. +NB: mdev recognizes /dev/mdev.seq consisting of single '\n' character +as a special case. IOW: this will not make your first hotplug event +to stall for two seconds. diff --git a/docs/new-applet-HOWTO.txt b/docs/new-applet-HOWTO.txt new file mode 100644 index 000000000..6a8054d0e --- /dev/null +++ b/docs/new-applet-HOWTO.txt @@ -0,0 +1,182 @@ +How to Add a New Applet to BusyBox +================================== + +This document details the steps you must take to add a new applet to BusyBox. + +Credits: +Matt Kraai - initial writeup +Mark Whitley - the remix +Thomas Lundquist - Trying to keep it updated. + +When doing this you should consider using the latest git HEAD. +This is a good thing if you plan to getting it committed into mainline. + +Initial Write +------------- + +First, write your applet. Be sure to include copyright information at the top, +such as who you stole the code from and so forth. Also include the mini-GPL +boilerplate. Be sure to name the main function _main instead of main. +And be sure to put it in .c. Usage does not have to be taken care of by +your applet. +Make sure to #include "libbb.h" as the first include file in your applet. + +For a new applet mu, here is the code that would go in mu.c: + +(busybox.h already includes most usual header files. You do not need +#include etc...) + + +----begin example code------ + +/* vi: set sw=4 ts=4: */ +/* + * Mini mu implementation for busybox + * + * Copyright (C) [YEAR] by [YOUR NAME] + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "other.h" + +int mu_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mu_main(int argc, char **argv) +{ + int fd; + ssize_t n; + char mu; + + fd = xopen("/dev/random", O_RDONLY); + + if ((n = safe_read(fd, &mu, 1)) < 1) + bb_perror_msg_and_die("/dev/random"); + + return mu; +} + +----end example code------ + + +Coding Style +------------ + +Before you submit your applet for inclusion in BusyBox, (or better yet, before +you _write_ your applet) please read through the style guide in the docs +directory and make your program compliant. + + +Some Words on libbb +------------------- + +As you are writing your applet, please be aware of the body of pre-existing +useful functions in libbb. Use these instead of reinventing the wheel. + +Additionally, if you have any useful, general-purpose functions in your +applet that could be useful in other applets, consider putting them in libbb. + +And it may be possible that some of the other applets uses functions you +could use. If so, you have to rip the function out of the applet and make +a libbb function out of it. + +Adding a libbb function: +------------------------ + +Make a new file named .c + +----start example code------ + +#include "libbb.h" +#include "other.h" + +int function(char *a) +{ + return *a; +} + +----end example code------ + +Add .o in the right alphabetically sorted place +in libbb/Kbuild.src. You should look at the conditional part of +libbb/Kbuild.src as well. + +You should also try to find a suitable place in include/libbb.h for +the function declaration. If not, add it somewhere anyway, with or without +ifdefs to include or not. + +You can look at libbb/Config.src and try to find out if the function is +tunable and add it there if it is. + + +Placement / Directory +--------------------- + +Find the appropriate directory for your new applet. + +Make sure you find the appropriate places in the files, the applets are +sorted alphabetically. + +Add the applet to Kbuild.src in the chosen directory: + +lib-$(CONFIG_MU) += mu.o + +Add the applet to Config.src in the chosen directory: + +config MU + bool "MU" + default n + help + Returns an indeterminate value. + + +Usage String(s) +--------------- + +Next, add usage information for you applet to include/usage.src.h. +This should look like the following: + + #define mu_trivial_usage \ + "-[abcde] FILES" + #define mu_full_usage \ + "Returns an indeterminate value.\n\n" \ + "Options:\n" \ + "\t-a\t\tfirst function\n" \ + "\t-b\t\tsecond function\n" \ + ... + +If your program supports flags, the flags should be mentioned on the first +line (-[abcde]) and a detailed description of each flag should go in the +mu_full_usage section, one flag per line. (Numerous examples of this +currently exist in usage.src.h.) + + +Header Files +------------ + +Next, add an entry to include/applets.src.h. Be *sure* to keep the list +in alphabetical order, or else it will break the binary-search lookup +algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily: + +Be sure to read the top of applets.src.h before adding your applet. + + /* all programs above here are alphabetically "less than" 'mu' */ + IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP)) + /* all programs below here are alphabetically "greater than" 'mu' */ + + +The Grand Announcement +---------------------- + +Then create a diff by adding the new files to git (remember your libbb files) + git add /mu.c +eventually also: + git add libbb/function.c +then + git commit + git format-patch HEAD^ +and send it to the mailing list: + busybox@busybox.net + http://busybox.net/mailman/listinfo/busybox + +Sending patches as attachments is preferred, but not required. diff --git a/docs/nofork_noexec.txt b/docs/nofork_noexec.txt new file mode 100644 index 000000000..c58f5a83f --- /dev/null +++ b/docs/nofork_noexec.txt @@ -0,0 +1,98 @@ + NOEXEC and NOFORK applets. + +Unix shells traditionally execute some commands internally in the attempt +to dramatically speed up execution. It will be slow as hell if for every +"echo blah" shell will fork and exec /bin/echo. To this end, shells +have to _reimplement_ these commands internally. + +Busybox is unique in this regard because it already is a collection +of reimplemented Unix commands, and we can do the same trick +for speeding up busybox shells, and more. NOEXEC and NOFORK applets +are exactly those applets which are eligible for these tricks. + +Applet will be subject to NOFORK/NOEXEC tricks if it is marked as such +in applets.h. FEATURE_PREFER_APPLETS is a config option which +globally enables usage of NOFORK/NOEXEC tricks. +If it is enabled, FEATURE_SH_STANDALONE can be enabled too, +and then shells will use NOFORK/NOEXEC tricks for ordinary commands. +NB: shell builtins use these tricks regardless of FEATURE_SH_STANDALONE +or FEATURE_PREFER_APPLETS. + +In C, if you want to call a program and wait for it, use +spawn_and_wait(argv), BB_EXECVP(prog,argv) or BB_EXECLP(prog,argv0,...). +They check whether program name is an applet name and optionally +do NOFORK/NOEXEC thing depending on configuration. + + + NOEXEC + +NOEXEC applet should work correctly if another applet forks and then +executes exit(_main(argc,argv)) in the child. The rules +roughly are: + +* do not expect shared global variables/buffers to be in their + "initialized" state. Examples: xfunc_error_retval can be != 1, + bb_common_bufsiz1 can be scribbled over, ... +* do not expect that stdio wasn't used before. Calling set[v]buf() + can be disastrous. +* ... + +NOEXEC applets save only one half of fork+exec overhead. +NOEXEC trick is disabled for NOMMU build. + + + NOFORK + +NOFORK applet should work correctly if another applet simply runs +_main(argc,argv) and then continues with its business. +xargs, find, shells do it (grep for "spawn_and_wait" and +"run_nofork_applet" to find more users). + +This poses much more serious limitations on what applet can do: + +* all NOEXEC limitations apply. +* do not ever exit() or exec(). + - xfuncs are okay. They are using special trick to return + to the caller applet instead of dying when they detect "x" condition. + - you may "exit" to caller applet by calling xfunc_die(). Return value + is taken from xfunc_error_retval. + - fflush_stdout_and_exit(n) is ok to use. +* do not use shared global data, or save/restore shared global data + (e.g. bb_common_bufsiz1) prior to returning. + - getopt32() is ok to use. You do not need to save/restore option_mask32, + it is already done by core code. +* if you allocate memory, you can use xmalloc() only on the very first + allocation. All other allocations should use malloc[_or_warn](). + After first allocation, you cannot use any xfuncs. + Otherwise, failing xfunc will return to caller applet + without freeing malloced data! +* All allocated data, opened files, signal handlers, termios settings, + O_NONBLOCK flags etc should be freed/closed/restored prior to return. +* ... + +NOFORK applets give the most of speed advantage, but are trickiest +to implement. In order to minimize amount of bugs and maintenance, +prime candidates for NOFORK-ification are those applets which +are small and easy to audit, and those which are more likely to be +frequently executed from shell/find/xargs, particularly in shell +script loops. Applets which mess with signal handlers, termios etc +are probably not worth the effort. + +Any NOFORK applet is also a NOEXEC applet. + + + Relevant CONFIG options + +FEATURE_PREFER_APPLETS + BB_EXECVP(cmd, argv) will try to exec /proc/self/exe + if command's name matches some applet name + applet tables will contain NOFORK/NOEXEC bits + spawn_and_wait(argv) will do NOFORK/NOEXEC tricks + +FEATURE_SH_STANDALONE (needs FEATURE_PREFER_APPLETS=y) + shells will try to exec /proc/self/exe if command's name matches + some applet name + shells will do NOEXEC trick on NOEXEC applets + +FEATURE_SH_NOFORK (needs FEATURE_PREFER_APPLETS=y) + shells will do NOFORK trick on NOFORK applets diff --git a/docs/posix_conformance.txt b/docs/posix_conformance.txt new file mode 100644 index 000000000..5b616d701 --- /dev/null +++ b/docs/posix_conformance.txt @@ -0,0 +1,741 @@ + +Busybox POSIX conformance table + +See POSIX documentation (1003.1-2008) here: +http://www.opengroup.org/onlinepubs/9699919799/ +And the complete list of all utilities that POSIX covers: +http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html + +This listing is a work in progress, and currently only covers +tool options (not operands, environment variables, return codes, etc..). +For each option it is set if it (a) exists and (b) compliant to POSIX 2008. +Some options exist but there is no value in the 'compliant' column: that +means no one has yet bothered to make sure that the option does what it is +required to do. + +----------------------------------------------- + +POSIX Tools supported only as shell built-ins (ash shell): + alias, bg, cd, fg, getopts, hash, jobs, read, type, umask, ulimit, + unalias, wait, write + +POSIX Tools not supported: + asa, at, batch, bc, c99, command, compress, csplit, ex, fc, file, + gencat, getconf, iconv, join, link, locale, localedef, lp, m4, + mailx, newgrp, nl, paste, pathchk, pax, pr, qalter, qdel, qhold, qmove, + qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput, + tsort, unlink, uucp, uustat, uux + +POSIX Tools not supported (DEVELOPMENT): + admin, cflow, ctags, cxref, delta, fort77, get, lex, make, nm, prs, rmdel, + sact, sccs, strip, unget, val, what, yacc + + +POSIX Tools supported: + +Note: echo, printf, kill, pwd documented here as stand-alone applets, + not as ash built-ins. + + +ar POSIX options ********************* Failed to recognize zip & tar (did not compare to regular ar) + option | exists | compliant | remarks + -C | no | no | + -T | no | no | + -a | no | no | + -b | no | no | + -c | no | no | + -d | no | no | + -i | no | no | + -m | no | no | + -p | yes | | + -q | no | no | + -r | no | no | + -s | no | no | + -t | yes | | + -u | no | no | + -v | yes | | + -x | yes | | +ar Busybox specific options: + -o + +awk POSIX options + option | exists | compliant | remarks + -F ERE | yes | | + -f progfile | yes | | + -v assignment | yes | | +awk Busybox specific options: None + +basename POSIX options: None +basename Busybox specific options: None + +cal POSIX options: None +cal Busybox specific options: + -y, -j + +cat POSIX options + option | exists | compliant | remarks + -u | yes | no | option is ignored +cat Busybox specific options: None + +chgrp POSIX options + option | exists | compliant | remarks + -H | yes | | + -L | yes | | + -P | yes | | + -R | yes | | + -h | yes | | +chgrp Busybox specific options: + -f, -c, -v + +chmod POSIX options + option | exists | compliant | remarks + -R | yes | yes | +chmod Busybox specific options: + -f, -v, -c + +chown POSIX options ********************************************* + option | exists | compliant | remarks + -H | yes | | It seems like all flags are supported (according to printout), but + -L | yes | | it fails to work on my machine + -P | yes | | + -R | yes | | + -h | yes | | +chown Busybox specific options: + -f, -c, -v + +cksum POSIX options: None +cksum Busybox specific options: None + +cmp POSIX options + option | exists | compliant | remarks + -l | yes | yes | + -s | yes | yes | +cmp Busybox specific options: + + +comm POSIX options + option | exists | compliant | remarks + -1 | yes | yes | + -2 | yes | yes | + -3 | yes | yes | +comm Busybox specific options: None + +cp POSIX options + option | exists | compliant | remarks + -H | yes | yes | + -L | yes | yes | + -P | yes | yes | + -R | yes | yes | + -f | yes | yes | + -i | yes | yes | + -p | yes | yes | +cp Busybox specific options: + -d, -a, -s, -c, -r, -l + +crontab POSIX options + option | exists | compliant | remarks + -e | yes | | + -l | yes | | + -r | yes | | +crontab Busybox specific options: + -u, -c + +cut POSIX options + option | exists | compliant | remarks + -b list | yes | yes | + -c list | yes | yes | + -d delim | yes | yes | + -f list | yes | yes | + -n | yes | yes | + -s | yes | yes | +cut Busybox specific options: None + +date POSIX options + option | exists | compliant | remarks + -u | yes | yes | +date Busybox specific options: + -I[SPEC], -d TIME, -r FILE, -R, -D FMT + +dd POSIX options: + option | exists | compliant | remarks + if | yes | | + of | yes | | + ibs | yes | | + obs | yes | | + bs | yes | | + cbs | no | no | + skip | yes | | + seek | yes | | + count | yes | | + conv=ascii | no | no | + conv=ebcdic | no | no | + conv=ibm | no | no | + conv=block | no | no | + conv=unblock | no | no | + conv=lcase | no | no | + conv=ucase | no | no | + conv=swap | no | no | + conv=noerror | yes | | + conv=notrunc | yes | | + conv=sync | yes | | +dd Busybox specific options: + conv=fsync + +df POSIX options + option | exists | compliant | remarks + -P | yes | yes | + -k | yes | yes | + -t | no | no | +df Busybox specific options: + -a, -m, -B SIZE, -i, -h +Remark: +- It seems that GNU df does not round percents up in its output (thus its results are a bit different) + +diff POSIX options + option | exists | compliant | remarks + -C n | no | no | + -U n | yes | | + -b | yes | | + -c | no | no | + -e | no | no | + -f | no | no | + -r | yes | | + -u | no | no | +diff Busybox specific options: + -d, -a, -s, -t, -L, -N, -i, -T, -w, -q, -S + +dirname POSIX options: None +dirname Busybox specific options: None + +du POSIX options + option | exists | compliant | remarks + -H | yes | | + -L | yes | | + -a | yes | | + -k | yes | | + -s | yes | | + -x | yes | | +du Busybox specific options: + -c, -m, -h, -d N, -l + + +echo POSIX options: None + option | exists | compliant | remarks + -n | yes | yes | The result of -n is "implementation-defined" +echo Busybox specific options: + -e, -E + +ed POSIX options + option | exists | compliant | remarks + -p string | no | no | + -s | no | no | +ed Busybox specific options: None + +env POSIX options + option | exists | compliant | remarks + -i | no | no | +env Busybox specific options: + -u, -, -i + +expand POSIX options + option | exists | compliant | remarks + -t tablist | yes | yes | +expand Busybox specific options: + --tabs=N, -i, --initial + +expr POSIX operations: + option | exists | compliant | remarks + | | yes | yes | + & | yes | yes | + = | yes | yes | + > | yes | yes | + >= | yes | yes | + <= | yes | yes | + < | yes | yes | + != | yes | yes | + + | yes | yes | + - | yes | yes | + * | yes | yes | + / | yes | yes | + % | yes | yes | + : | yes | yes | + (expr) | yes | yes | + integer | yes | yes | + string | yes | yes | +expr Busybox specific operations: + match, substr, index, length, quote + +false POSIX options: None +false Busybox specific options: None + +find POSIX options + option | exists | compliant | remarks + -H | no | no | + -L | no | no | +find Busybox specific options: + -group NAME, -mtime DAYS, -print, -maxdepth N, -exec CMD ARG ;, -newer FILE, -context, -iname PATTERN, -follow, -depth, -xdev, -inum N, -type X, -print0, -mindepth N, -mmin MINS, -regex PATTERN, -prune, -path PATTERN, -user NAME, -delete, -perm NNN, -name PATTERN, -size N[bck] + +fold POSIX options + option | exists | compliant | remarks + -b | yes | yes | + -s | yes | yes | + -w width | yes | yes | +fold Busybox specific options: None + +fuser POSIX options + option | exists | compliant | remarks + -c | no | no | + -f | no | no | + -u | no | no | +fuser Busybox specific options: + -m, -k, -4, -SIGNAL, -6, -s + +grep POSIX options + option | exists | compliant | remarks + -E | yes | | + -F | yes | | + -c | yes | | + -e pattern_list | yes | | + -f pattern_file | yes | | + -i | yes | | + -l | yes | | + -n | yes | | + -q | yes | | + -s | yes | | + -v | yes | | + -x | no | no | +grep Busybox specific options: + -A, -C, -B, -L, -H, -o, -h, -w, -r, -z, -m MAX + +head POSIX options + option | exists | compliant | remarks + -n number | yes | yes | +head Busybox specific options: + -v, -c NUM, -q + +id POSIX options + option | exists | compliant | remarks + -G | yes | yes | + -g | yes | yes | + -n | yes | yes | + -r | yes | yes | + -u | yes | yes | +id Busybox specific options: + -Z + +ipcrm POSIX options + option | exists | compliant | remarks + -M shmkey | no | no | + -Q msgkey | no | no | + -S semkey | no | no | + -m shmid | no | no | + -q msgid | no | no | + -s semid | no | no | +ipcrm Busybox specific options: + -mM, -qQ, -sS + +ipcs POSIX options + option | exists | compliant | remarks + -a | yes | | + -b | no | no | + -c | yes | | + -m | yes | | + -o | no | no | + -p | yes | | + -q | yes | | + -s | yes | | + -t | yes | | +ipcs Busybox specific options: + -l, -i, -u + +kill POSIX options + option | exists | compliant | remarks + -l | yes | yes | + -s signal_name | yes | yes | + -signal_name | yes | yes | + -signal_number | yes | yes | +kill Busybox specific options: + -q, -o + +ln POSIX options + option | exists | compliant | remarks + -L | no | no | + -P | no | no | + -f | yes | yes | + -s | yes | yes | +ln Busybox specific options: + -S suf, -n, -b + +logger POSIX options: None +logger Busybox specific options: + -p PRIO, -t TAG, -s + +logname POSIX options: None +logname Busybox specific options: None + +ls POSIX options + option | exists | compliant | remarks + -1 | yes | yes | + -A | yes | yes | + -C | yes | yes | + -F | yes | yes | And more: '=' for sockets (not defined by POSIX) + -H | no | no | + -L | yes | yes | But coloring may be wrong (at least POSIX does not require correct colors :) ) + -R | yes | yes | + -S | yes | yes | + -a | yes | yes | + -c | yes | no | Sorts output with '-l' (should only show ctime with '-l', and sort only with '-t') + -d | yes | no | When invoked together with '-L' should read symbolic links, and doesn't + -f | no | no | + -g | no | no | + -i | yes | yes | + -k | yes | no | Does something completely unrelated! (Lists security context instead of specifying block size) + -l | yes | yes | + -m | no | no | + -n | yes | no | Works correctly only together with '-l' (but POSIX requires '-l' to be implicitly assumed) + -o | no | no | + -p | yes | yes | + -q | no | no | + -r | yes | yes | + -s | yes | yes | + -t | yes | yes | + -u | yes | yes | + -x | yes | yes | +ls Busybox specific options: + --color, -T NUM, -K, -X, -Z, -e, -h, -v, -w NUM + +man POSIX options + option | exists | compliant | remarks + -k | no | no | +man Busybox specific options: + -a Display all pages + + +mesg POSIX options: None +mesg Busybox specific options: None + +mkdir POSIX options + option | exists | compliant | remarks + -m mode | yes | yes | + -p | yes | yes | +mkdir Busybox specific options: + -Z + +mkfifo POSIX options + option | exists | compliant | remarks + -m mode | yes | yes | +mkfifo Busybox specific options: + -Z + +more POSIX options + option | exists | compliant | remarks + -c | no | no | + -e | no | no | + -i | no | no | + -n number | no | no | + -p command | no | no | + -s | no | no | + -t tagstring | no | no | + -u | no | no | +more Busybox specific options: None + +mv POSIX options + option | exists | compliant | remarks + -f | yes | yes | + -i | yes | yes | +mv Busybox specific options: None + +nice POSIX options + option | exists | compliant | remarks + -n increment | yes | yes | +nice Busybox specific options: None + +nohup POSIX options: None +nohup Busybox specific options: None + +od POSIX options + option | exists | compliant | remarks + -A address_base | no | no | + -N count | no | no | + -b | no | no | + -c | no | no | + -d | no | no | + -j skip | no | no | + -o | no | no | + -s | no | no | + -t type_string | no | no | + -v | no | no | + -x | no | no | +od Busybox specific options: None + +patch POSIX options + option | exists | compliant | remarks + -D define | no | no | + -N | no | no | + -R | yes | yes | + -b | no | no | + -c | no | no | + -d dir | no | no | + -e | no | no | + -i patchfile | yes | yes | + -l | no | no | + -n | no | no | + -o outfile | no | no | + -p num | yes | yes | + -r rejectfile | no | no | + -u | no | no | +patch Busybox specific options: None + +printf POSIX options: None +printf Busybox specific options: None + +ps POSIX options + option | exists | compliant | remarks + -A | no | no | + -G grouplist | no | no | + -U userlist | no | no | + -a | no | no | + -d | no | no | + -e | no | no | + -f | no | no | + -g grouplist | no | no | + -l | no | no | + -n namelist | no | no | + -o format | yes | no | not supported: ruser, group, rgroup, pcpu + -p proclist | no | no | + -t termlist | no | no | + -u userlist | no | no | +ps Busybox specific options: None + +pwd POSIX options + option | exists | compliant | remarks + -L | no | no | + -P | no | no | +pwd Busybox specific options: None + +renice POSIX options + option | exists | compliant | remarks + -g | yes | yes | + -n increment | yes | yes | Note POSIX allows only to run with this option (busybox also allows to run without '-n' and set niceness directly) + -p | yes | yes | + -u | yes | yes | +renice Busybox specific options: None + +rm POSIX options + option | exists | compliant | remarks + -R | yes | yes | + -f | yes | yes | + -i | yes | yes | + -r | yes | yes | +rm Busybox specific options: None + +rmdir POSIX options + option | exists | compliant | remarks + -p | yes | yes | +rmdir Busybox specific options: + --parents + +sed POSIX options + option | exists | compliant | remarks + -e script | yes | | + -f script_file | yes | | + -n | yes | | +sed Busybox specific options: + -i, -r + +sh POSIX options + option | exists | compliant | remarks + -c | no | no | + -i | no | no | + -s | no | no | +sh Busybox specific options: None + +sleep POSIX options: None +sleep Busybox specific options: None + +sort POSIX options + option | exists | compliant | remarks + -C | no | no | + -b | yes | yes | + -c | yes | yes | + -d | yes | yes | + -f | yes | yes | + -i | yes | yes | But is not like GNU sort, which isn't! (try to sort 'a\nA\nB\nb' with and without -f) + -k keydef | yes | | + -m | no | no | + -n | yes | yes | + -o output | yes | yes | + -r | yes | yes | + -t char | yes | | + -u | yes | yes | +sort Busybox specific options: + -mST, -g, -M, -s, -z + +split POSIX options + option | exists | compliant | remarks + -a suffix_length | yes | yes | + -b n | yes | yes | + -b nk | yes | yes | + -b nm | yes | yes | + -l line_count | yes | yes | +split Busybox specific options: None + +strings POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -n number | yes | yes | + -t format | no | no | +strings Busybox specific options: + -o, -f + +stty POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -g | yes | yes | +stty Busybox specific options: + -F DEVICE + +tail POSIX options + option | exists | compliant | remarks + -c number | yes | yes | + -f | yes | yes | + -n number | yes | yes | +tail Busybox specific options: + -v, -q, -s SEC + +tee POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -i | yes | yes | +tee Busybox specific options: None + +test POSIX options: None +test Busybox specific options: None + +time POSIX options + option | exists | compliant | remarks + -p | no | no | +time Busybox specific options: + -v + +touch POSIX options + option | exists | compliant | remarks + -a | no | no | + -c | yes | yes | + -d date_time | no | no | + -m | no | no | + -r ref_file | no | no | + -t time | no | no | +touch Busybox specific options: None + +tr POSIX options + option | exists | compliant | remarks + -C | no | no | + -c | yes | yes | + -d | yes | yes | + -s | yes | yes | +tr Busybox specific options: None + +true POSIX options: None +true Busybox specific options: None + +tty POSIX options: None +tty Busybox specific options: + -s + +uname POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -m | yes | yes | + -n | yes | yes | + -r | yes | yes | + -s | yes | yes | + -v | yes | yes | +uname Busybox specific options: + -p + +uncompress POSIX options + option | exists | compliant | remarks + -c | yes | yes | + -f | yes | yes | + -v | no | no | +uncompress Busybox specific options: None + +unexpand POSIX options + option | exists | compliant | remarks + -a | yes | no | POSIX requires converting two or more spaces to tabs, busybox converts one or more spaces + -t tablist | yes | yes | +unexpand Busybox specific options: + --tabs=N, -f, --first-only, --all + +uniq POSIX options + option | exists | compliant | remarks + -c | yes | yes | + -d | yes | yes | + -f fields | yes | yes | + -s chars | yes | yes | + -u | yes | yes | +uniq Busybox specific options: + -w N + +uudecode POSIX options + option | exists | compliant | remarks + -o outfile | no | no | +uudecode Busybox specific options: None + +uuencode POSIX options + option | exists | compliant | remarks + -m | yes | yes | +uuencode Busybox specific options: None + +vi POSIX options + option | exists | compliant | remarks + -R | yes | | + -c command | yes | | + -r | no | no | + -t tagstring | no | no | + -w size | no | no | +vi Busybox specific options: + -H + +wc POSIX options + option | exists | compliant | remarks + -c | yes | yes | + -l | yes | yes | + -m | no | no | + -w | yes | yes | +wc Busybox specific options: + -L + +who POSIX options + option | exists | compliant | remarks + -H | no | no | + -T | no | no | + -a | yes | no | just shows all + -b | no | no | + -d | no | no | + -l | no | no | + -m | no | no | + -p | no | no | + -q | no | no | + -r | no | no | + -s | no | no | + -t | no | no | + -u | no | no | +who Busybox specific options: None + +xargs POSIX options + option | exists | compliant | remarks + -E eofstr | no | no | + -I replstr | no | no | + -L number | no | no | + -n number | yes | yes | + -p | yes | yes | + -s size | yes | yes | + -t | yes | yes | + -x | yes | yes | +xargs Busybox specific options: + -e[STR], -0, -r + +zcat POSIX options: None +zcat Busybox specific options: None diff --git a/docs/sigint.htm b/docs/sigint.htm new file mode 100644 index 000000000..e230f4df7 --- /dev/null +++ b/docs/sigint.htm @@ -0,0 +1,627 @@ + + + +Proper handling of SIGINT/SIGQUIT [http://www.cons.org/cracauer/sigint.html] + + +

Proper handling of SIGINT/SIGQUIT

+ +

+ + + + + +
Abstract: +In UNIX terminal sessions, you usually have a key like +C-c (Control-C) to immediately end whatever program you +have running in the foreground. This should work even when the program +you called has called other programs in turn. Everything should be +aborted, giving you your command prompt back, no matter how deep the +call stack is. + +

Basically, it's trivial. But the existence of interactive +applications that use SIGINT and/or SIGQUIT for other purposes than a +complete immediate abort make matters complicated, and - as was to +expect - left us with several ways to solve the problems. Of course, +existing shells and applications follow different ways. + +

This Web pages outlines different ways to solve the problem and +argues that only one of them can do everything right, although it +means that we have to fix some existing software. + + + +

Intended audience: Programmers who implement programs that catch SIGINT/SIGQUIT. +
Programmers who implements shells or shell-like programs that +execute batches of programs. + +

Users who have problems problems getting rid of runaway shell +scripts using Control-C. Or have interactive applications +that don't behave right when sending SIGINT. Examples are emacs'es +that die on Control-g or shellscript statements that sometimes are +executed and sometimes not, apparently not determined by the user's +intention. + + +

Required knowledge: You have to know what it means to catch SIGINT or SIGQUIT and how +processes are waiting for other processes (childs) they spawned. + + +
+ + + +

Basic concepts

+ +What technically happens when you press Control-C is that all programs +running in the foreground in your current terminal (or virtual +terminal) get the signal SIGINT sent. + +

You may change the key that triggers the signal using +stty and running programs may remap the SIGINT-sending +key at any time they like, without your intervention and without +asking you first. + +

The usual reaction of a running program to SIGINT is to exit. +However, not all program do an exit on SIGINT, programs are free to +use the signal for other actions or to ignore it at all. + +

All programs running in the foreground receive the signal. This may +be a nested "stack" of programs: You started a program that started +another and the outer is waiting for the inner to exit. This nesting +may be arbitrarily deep. + +

The innermost program is the one that decides what to do on SIGINT. +It may exit, do something else or do nothing. Still, when the user hit +SIGINT, all the outer programs are awaken, get the signal and may +react on it. + +

What we try to achieve

+ +The problem is with shell scripts (or similar programs that call +several subprograms one after another). + +

Let us consider the most basic script: +

+#! /bin/sh
+program1
+program2
+
+and the usual run looks like this: +
+$ sh myscript
+[output of program1]
+[output of program2]
+$
+
+ +

Let us assume that both programs do nothing special on SIGINT, they +just exit. + +

Now imagine the user hits C-c while a shellscript is executing its +first program. The following programs receive SIGINT: program1 and +also the shell executing the script. program1 exits. + +

But what should the shell do? If we say that it is only the +innermost's programs business to react on SIGINT, the shell will do +nothing special (not exit) and it will continue the execution of the +script and run program2. But this is wrong: The user's intention in +hitting C-c is to abort the whole script, to get his prompt back. If +he hits C-c while the first program is running, he does not want +program2 to be even started. + +

here is what would happen if the shell doesn't do anything: +

+$ sh myscript
+[first half of program1's output]
+C-c   [users presses C-c]
+[second half of program1's output will not be displayed]
+[output of program2 will appear]
+
+ + +

Consider a more annoying example: +

+#! /bin/sh
+# let's assume there are 300 *.dat files
+for file in *.dat ; do
+	dat2ascii $dat
+done
+
+ +If your shell wouldn't end if the user hits C-c, +C-c would just end one dat2ascii run and +the script would continue. Thus, you had to hit C-c up to +300 times to end this script. + +

Alternatives to do so

+ +

There are several ways to handle abortion of shell scripts when +SIGINT is received while a foreground child runs: + +

+ +
  • As just outlined, the shellscript may just continue, ignoring the +fact that the user hit C-c. That way, your shellscript - +including any loops - would continue and you had no chance of aborting +it except using the kill command after finding out the outermost +shell's PID. This "solution" will not be discussed further, as it is +obviously not desirable. + +

  • The shell itself exits immediately when it receives SIGINT. Not +only the program called will exit, but the calling (the +script-executing) shell. The first variant is to exit the shell (and +therefore discontinuing execution of the script) immediately, while +the background program may still be executing (remember that although +the shell is just waiting for the called program to exit, it is woken +up and may act). I will call the way of doing things the "IUE" (for +"immediate unconditional exit") for the rest of this document. + +

  • As a variant of the former, when the shell receives SIGINT +while it is waiting for a child to exit, the shell does not exit +immediately. but it remembers the fact that a SIGINT happened. After +the called program exits and the shell's wait ends, the shell will +exit itself and hence discontinue the script. I will call the way of +doing things the "WUE" (for "wait and unconditional exit") for the +rest of this document. + +

  • There is also a way that the calling shell can tell whether the +called program exited on SIGINT and if it ignored SIGINT (or used it +for other purposes). As in the WUE way, the shell waits for +the child to complete. It figures whether the program was ended on +SIGINT and if so, it discontinue the script. If the program did any +other exit, the script will be continued. I will call the way of doing +things the "WCE" (for "wait and cooperative exit") for the rest of +this document. + +
  • + +

    The problem

    + +On first sight, all three solutions (IUE, WUE and WCE) all seem to do +what we want: If C-c is hit while the first program of the shell +script runs, the script is discontinued. The user gets his prompt back +immediately. So what are the difference between these way of handling +SIGINT? + +

    There are programs that use the signal SIGINT for other purposes +than exiting. They use it as a normal keystroke. The user is expected +to use the key that sends SIGINT during a perfectly normal program +run. As a result, the user sends SIGINT in situations where he/she +does not want the program or the script to end. + +

    The primary example is the emacs editor: C-g does what ESC does in +other applications: It cancels a partially executed or prepared +operation. Technically, emacs remaps the key that sends SIGINT from +C-c to C-g and catches SIGINT. + +

    Remember that the SIGINT is sent to all programs running in the +foreground. If emacs is executing from a shell script, both emacs and +the shell get SIGINT. emacs is the program that decides what to do: +Exit on SIGINT or not. emacs decides not to exit. The problem arises +when the shell draws its own conclusions from receiving SIGINT without +consulting emacs for its opinion. + +

    Consider this script: +

    +#! /bin/sh
    +emacs /tmp/foo
    +cp /tmp/foo /home/user/mail/sent
    +
    + +

    If C-g is used in emacs, both the shell and emacs will received +SIGINT. Emacs will not exit, the user used C-g as a normal editing +keystroke, he/she does not want the script to be aborted on C-g. + +

    The central problem is that the second command (cp) may +unintentionally be killed when the shell draws its own conclusion +about the user's intention. The innermost program is the only one to +judge. + +

    One more example

    + +

    Imagine a mail session using a curses mailer in a tty. You called +your mailer and started to compose a message. Your mailer calls emacs. +C-g is a normal editing key in emacs. Technically it +sends SIGINT (it was C-c, but emacs remapped the key) to +

    +
  • emacs +
  • the shell between your mailer and emacs, the one from your mailers + system("emacs /tmp/bla.44") command +
  • the mailer itself +
  • possibly another shell if your mailer was called by a shell script +or from another application using system(3) +
  • your interactive shell (which ignores it since it is interactive +and hence is not relevant to this discussion) +
  • + +

    If everyone just exits on SIGINT, you will be left with nothing but +your login shell, without asking. + +

    But for sure you don't want to be dropped out of your editor and +out of your mailer back to the commandline, having your edited data +and mailer status deleted. + +

    Understand the difference: While C-g is used an a kind +of abort key in emacs, it isn't the major "abort everything" key. When +you use C-g in emacs, you want to end some internal emacs +command. You don't want your whole emacs and mailer session to end. + +

    So, if the shell exits immediately if the user sends SIGINT (the +second of the four ways shown above), the parent of emacs would die, +leaving emacs without the controlling tty. The user will lose it's +editing session immediately and unrecoverable. If the "main" shell of +the operating system defaults to this behavior, every editor session +that is spawned from a mailer or such will break (because it is +usually executed by system(3), which calls /bin/sh). This was the case +in FreeBSD before I and Bruce Evans changed it in 1998. + +

    If the shell recognized that SIGINT was sent and exits after the +current foreground process exited (the third way of the four), the +editor session will not be disturbed, but things will still not work +right. + +

    A further look at the alternatives

    + +

    Still considering this script to examine the shell's actions in the +IUE, WUE and ICE way of handling SIGINT: +

    +#! /bin/sh
    +emacs /tmp/foo
    +cp /tmp/foo /home/user/mail/sent
    +
    + +

    The IUE ("immediate unconditional exit") way does not work at all: +emacs wants to survive the SIGINT (it's a normal editing key for +emacs), but its parent shell unconditionally thinks "We received +SIGINT. Abort everything. Now.". The shell will exit even before emacs +exits. But this will leave emacs in an unusable state, since the death +of its calling shell will leave it without required resources (file +descriptors). This way does not work at all for shellscripts that call +programs that use SIGINT for other purposes than immediate exit. Even +for programs that exit on SIGINT, but want to do some cleanup between +the signal and the exit, may fail before they complete their cleanup. + +

    It should be noted that this way has one advantage: If a child +blocks SIGINT and does not exit at all, this way will get control back +to the user's terminal. Since such programs should be banned from your +system anyway, I don't think that weighs against the disadvantages. + +

    WUE ("wait and unconditional exit") is a little more clever: If C-g +was used in emacs, the shell will get SIGINT. It will not immediately +exit, but remember the fact that a SIGINT happened. When emacs ends +(maybe a long time after the SIGINT), it will say "Ok, a SIGINT +happened sometime while the child was executing, the user wants the +script to be discontinued". It will then exit. The cp will not be +executed. But that's bad. The "cp" will be executed when the emacs +session ended without the C-g key ever used, but it will not be +executed when the user used C-g at least one time. That is clearly not +desired. Since C-g is a normal editing key in emacs, the user expects +the rest of the script to behave identically no matter what keys he +used. + +

    As a result, the "WUE" way is better than the "IUE" way in that it +does not break SIGINT-using programs completely. The emacs session +will end undisturbed. But it still does not support scripts where +other actions should be performed after a program that use SIGINT for +non-exit purposes. Since the behavior is basically undeterminable for +the user, this can lead to nasty surprises. + +

    The "WCE" way fixes this by "asking" the called program whether it +exited on SIGINT or not. While emacs receives SIGINT, it does not exit +on it and a calling shell waiting for its exit will not be told that +it exited on SIGINT. (Although it receives SIGINT at some point in +time, the system does not enforce that emacs will exit with +"I-exited-on-SIGINT" status. This is under emacs' control, see below). + +

    this still work for the normal script without SIGINT-using +programs:

    +
    +#! /bin/sh
    +program1
    +program2
    +
    + +Unless program1 and program2 mess around with signal handling, the +system will tell the calling shell whether the programs exited +normally or as a result of SIGINT. + +

    The "WCE" way then has an easy way to things right: When one called +program exited with "I-exited-on-SIGINT" status, it will discontinue +the script after this program. If the program ends without this +status, the next command in the script is started. + +

    It is important to understand that a shell in "WCE" modus does not +need to listen to the SIGINT signal at all. Both in the +"emacs-then-cp" script and in the "several-normal-programs" script, it +will be woken up and receive SIGINT when the user hits the +corresponding key. But the shell does not need to react on this event +and it doesn't need to remember the event of any SIGINT, either. +Telling whether the user wants to end a script is done by asking that +program that has to decide, that program that interprets keystrokes +from the user, the innermost program. + +

    So everything is well with WCE?

    + +Well, almost. + +

    The problem with the "WCE" modus is that there are broken programs +that do not properly communicate the required information up to the +calling program. + +

    Unless a program messes with signal handling, the system does this +automatically. + +

    There are programs that want to exit on SIGINT, but they don't let +the system do the automatic exit, because they want to do some +cleanup. To do so, they catch SIGINT, do the cleanup and then exit by +themselves. + +

    And here is where the problem arises: Once they catch the signal, +the system will no longer communicate the "I-exited-on-SIGINT" status +to the calling program automatically. Even if the program exit +immediately in the signal handler of SIGINT. Once it catches the +signal, it has to take care of communicating the signal status +itself. + +

    Some programs don't do this. On SIGINT, they do cleanup and exit +immediatly, but the calling shell isn't told about the non-normal exit +and it will call the next program in the script. + +

    As a result, the user hits SIGINT and while one program exits, the +shellscript continues. To him/her it looks like the shell fails to +obey to his abortion command. + +

    Both IUE or WUE shell would not have this problem, since they +discontinue the script on their own. But as I said, they don't support +programs using SIGINT for non-exiting purposes, no matter whether +these programs properly communicate their signal status to the calling +shell or not. + +

    Since some shell in wide use implement the WUE way (and some even +IUE), there is a considerable number of broken programs out there that +break WCE shells. The programmers just don't recognize it if their +shell isn't WCE. + +

    How to be a proper program

    + +

    (Short note in advance: What you need to achieve is that +WIFSIGNALED(status) is true in the calling program and that +WTERMSIG(status) returns SIGINT.) + +

    If you don't catch SIGINT, the system automatically does the right +thing for you: Your program exits and the calling program gets the +right "I-exited-on-SIGINT" status after waiting for your exit. + +

    But once you catch SIGINT, you have to act. + +

    Decide whether the SIGINT is used for exit/abort purposes and hence +a shellscript calling this program should discontinue. This is +hopefully obvious. If you just need to do some cleanup on SIGINT, but +then exit immediately, the answer is "yes". + +

    If so, you have to tell the calling program about it by exiting +with the "I-exited-on-SIGINT" status. + +

    There is no other way of doing this than to kill yourself with a +SIGINT signal. Do it by resetting the SIGINT handler to SIG_DFL, then +send yourself the signal. + +

    +void sigint_handler(int sig)
    +{
    +	
    +	signal(SIGINT, SIG_DFL);
    +	kill(getpid(), SIGINT);
    +}
    +
    + +Notes: + + + +
  • You cannot "fake" the proper exit status by an exit(3) with a +special numeric value. People often assume this since the manuals for +shells often list some return value for exactly this. But this is just +a convention for your shell script. It does not work from one UNIX API +program to another. + +

    All that happens is that the shell sets the "$?" variable to a +special numeric value for the convenience of your script, because your +script does not have access to the lower-lever UNIX status evaluation +functions. This is just an agreement between your script and the +executing shell, it does not have any meaning in other contexts. + +

  • Do not use kill(0, SIGINT) without consulting the manul for +your OS implementation. I.e. on BSD, this would not send the signal to +the current process, but to all processes in the group. + +

  • POSIX 1003.1 allows all these calls to appear in signal +handlers, so it is portable. + +
  • + +

    In a bourne shell script, you can catch signals using the +trap command. Here, the same as for C programs apply. If +the intention of SIGINT is to end your program, you have to exit in a +way that the calling programs "sees" that you have been killed. If +you don't catch SIGINT, this happend automatically, but of you catch +SIGINT, i.e. to do cleanup work, you have to end the program by +killing yourself, not by calling exit. + +

    Consider this example from FreeBSD's mkdep, which is a +bourne shell script. + +

    +TMP=_mkdep$$
    +trap 'rm -f $TMP ; trap 2 ; kill -2 $$' 1 2 3 13 15
    +
    + +Yes, you have to do it the hard way. It's even more annoying in shell +scripts than in C programs since you can't "pre-delete" temporary +files (which isn't really portable in C, though). + +

    All this applies to programs in all languages, not only C and +bourne shell. Every language implementation that lets you catch SIGINT +should also give you the option to reset the signal and kill yourself. + +

    It is always desireable to exit the right way, even if you don't +expect your usual callers to depend on it, some unusual one will come +along. This proper exit status will be needed for WCE and will not +hurt when the calling shell uses IUE or WUE. + +

    How to be a proper shell

    + +All this applies only for the script-executing case. Most shells will +also have interactive modes where things are different. + + + +
  • Do nothing special when SIGINT appears while you wait for a child. +You don't even have to remember that one happened. + +

  • Wait for child to exit, get the exit status. Do not truncate it +to type char. + +

  • Look at WIFSIGNALED(status) and WTERMSIG(status) to tell +whether the child says "I exited on SIGINT: in my opinion the user +wants the shellscript to be discontinued". + +

  • If the latter applies, discontinue the script. + +

  • Exit. But since a shellscript may in turn be called by a +shellscript, you need to make sure that you properly communicate the +discontinue intention to the calling program. As in any other program +(see above), do + +
    +	signal(SIGINT, SIG_DFL);
    +	kill(getpid(), SIGINT);
    +
    + +
  • + +

    Other remarks

    + +Although this web page talks about SIGINT only, almost the same issues +apply to SIGQUIT, including proper exiting by killing yourself after +catching the signal and proper reaction on the WIFSIGNALED(status) +value. One notable difference for SIGQUIT is that you have to make +sure that not the whole call tree dumps core. + +

    What to fight

    + +Make sure all programs really kill themselves if they react +to SIGINT or SIGQUIT and intend to abort their operation as a result +of this signal. Programs that don't use SIGINT/SIGQUIT as a +termination trigger - but as part of normal operation - don't kill +themselves, but do a normal exit instead. + +

    Make sure people understand why you can't fake an exit-on-signal by +doing exit(...) using any numerical status. + +

    Make sure you use a shell that behaves right. Especially if you +develop programs, since it will help seeing problems. + +

    Concrete examples how to fix programs:

    +
      + +
    • The fix for FreeBSD's +time(1). This fix is the best example, it's quite short and clear and +it fixes a case where someone tried to fake signal exit status by a +numerical value. And the complete program is small. + +

    • Fix for FreeBSD's +truss(1). + +

    • The fix for FreeBSD's +mkdep(1), a shell script. + + +

    • Fix for FreeBSD's make(1), part 1, +part 2. + +
    + +

    Testsuite for shells

    + +I have a collection of shellscripts that test shells for the +behavior. See my download dir to get the newest +"sh-interrupt" files, either as a tarfile or as individual file for +online browsing. This isn't really documented, besides from the +comments the scripts echo. + +

    Appendix 1 - table of implementation choices

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Method signDoes what?Example shells that implement it:What happens when a shellscript called emacs, the user used +C-g and the script has additional commands in it?What happens when a shellscript called emacs, the user did not use +C-c and the script has additional commands in it?What happens if a non-interactive child catches SIGINT?To behave properly, childs must do what?
    IUEThe shell executing a script exits immediately if it receives +SIGINT.4.4BSD ash (ash), NetBSD, FreeBSD prior to 3.0/22.8The editor session is lost and subsequent commands are not +executed.The editor continues as normal and the subsequent commands are +executed. The scripts ends immediately, returning to the caller even before +the current foreground child of the shell exits. It doesn't matter what the child does or how it exits, even if the +child continues to operate, the shell returns.
    WUEIf the shell executing a script received SIGINT while a foreground +process was running, it will exit after that child's exit.pdksh (OpenBSD /bin/sh)The editor continues as normal, but subsequent commands from the +script are not executed.The editor continues as normal and subsequent commands are +executed. The scripts returns to its caller after the current foreground +child exits, no matter how the child exited. It doesn't matter how the child exits (signal status or not), but +if it doesn't return at all, the shell will not return. In no case +will further commands from the script be executed.
    WCEThe shell exits if a child signaled that it was killed on a +signal (either it had the default handler for SIGINT or it killed +itself). bash (Linux /bin/sh), most commercial /bin/sh, FreeBSD /bin/sh +from 3.0/2.2.8.The editor continues as normal and subsequent commands are +executed. The editor continues as normal and subsequent commands are +executed. The scripts returns to its caller after the current foreground +child exits, but only if the child exited with signal status. If +the child did a normal exit (even if it received SIGINT, but catches +it), the script will continue. The child must be implemented right, or the user will not be able +to break shell scripts reliably.
    + +

     +
    ©2005 Martin Cracauer <cracauer @ cons.org> +http://www.cons.org/cracauer/ +
    Last changed: $Date: 2005/02/11 21:44:43 $ + diff --git a/docs/smallint.txt b/docs/smallint.txt new file mode 100644 index 000000000..b57dfd775 --- /dev/null +++ b/docs/smallint.txt @@ -0,0 +1,39 @@ + smalluint i = index_in_str_array(params, name) + 1; + if (i == 0) + return 0; + if (!(i == 4 || i == 5)) + i |= 0x80; + + return i; + +I think that this optimization is wrong. +index_in_str_array returns int. At best, compiler will use it as-is. +At worst, compiler will try to make sure that it is properly cast +into a byte, which probably results in "n = n & 0xff" on many architectures. + +You save nothing on space here because i is not stored on-stack, +gcc will keep it in register. And even if it *is* stored, +it is *stack* storage, which is cheap (unlike data/bss). + +small[u]ints are useful _mostly_ for: + +(a) flag variables + (a1) global flag variables - make data/bss smaller + (a2) local flag variables - "a = 5", "a |= 0x40" are smaller + for bytes than for full integers. + Example: + on i386, there is no widening constant store instruction + for some types of address modes, thus + movl $0x0,(%eax) is "c7 00 00 00 00 00" + movb $0x0,(%eax) is "c6 00 00" +(b) small integer structure members, when you have many such + structures allocated, + or when these are global objects of this structure type + +small[u]ints are *NOT* useful for: + +(a) function parameters and return values - + they are pushed on-stack or stored in registers, bytes here are *harder* + to deal with than ints +(b) "computational" variables - "a++", "a = b*3 + 7" may take more code to do + on bytes than on ints on some architectires. diff --git a/docs/style-guide.txt b/docs/style-guide.txt new file mode 100644 index 000000000..10ed893dc --- /dev/null +++ b/docs/style-guide.txt @@ -0,0 +1,713 @@ +Busybox Style Guide +=================== + +This document describes the coding style conventions used in Busybox. If you +add a new file to Busybox or are editing an existing file, please format your +code according to this style. If you are the maintainer of a file that does +not follow these guidelines, please -- at your own convenience -- modify the +file(s) you maintain to bring them into conformance with this style guide. +Please note that this is a low priority task. + +To help you format the whitespace of your programs, an ".indent.pro" file is +included in the main Busybox source directory that contains option flags to +format code as per this style guide. This way you can run GNU indent on your +files by typing 'indent myfile.c myfile.h' and it will magically apply all the +right formatting rules to your file. Please _do_not_ run this on all the files +in the directory, just your own. + + + +Declaration Order +----------------- + +Here is the preferred order in which code should be laid out in a file: + + - commented program name and one-line description + - commented author name and email address(es) + - commented GPL boilerplate + - commented longer description / notes for the program (if needed) + - #includes of .h files with angle brackets (<>) around them + - #includes of .h files with quotes ("") around them + - #defines (if any, note the section below titled "Avoid the Preprocessor") + - const and global variables + - function declarations (if necessary) + - function implementations + + + +Whitespace and Formatting +------------------------- + +This is everybody's favorite flame topic so let's get it out of the way right +up front. + + +Tabs vs. Spaces in Line Indentation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The preference in Busybox is to indent lines with tabs. Do not indent lines +with spaces and do not indents lines using a mixture of tabs and spaces. (The +indentation style in the Apache and Postfix source does this sort of thing: +\s\s\s\sif (expr) {\n\tstmt; --ick.) The only exception to this rule is +multi-line comments that use an asterisk at the beginning of each line, i.e.: + + \t/* + \t * This is a block comment. + \t * Note that it has multiple lines + \t * and that the beginning of each line has a tab plus a space + \t * except for the opening '/*' line where the slash + \t * is used instead of a space. + \t */ + +Furthermore, The preference is that tabs be set to display at four spaces +wide, but the beauty of using only tabs (and not spaces) at the beginning of +lines is that you can set your editor to display tabs at *whatever* number of +spaces is desired and the code will still look fine. + + +Operator Spacing +~~~~~~~~~~~~~~~~ + +Put spaces between terms and operators. Example: + + Don't do this: + + for(i=0;i 0) + + +Bracket Spacing +~~~~~~~~~~~~~~~ + +If an opening bracket starts a function, it should be on the +next line with no spacing before it. However, if a bracket follows an opening +control block, it should be on the same line with a single space (not a tab) +between it and the opening control block statement. Examples: + + Don't do this: + + while (!done) + { + + do + { + + Don't do this either: + + while (!done){ + + do{ + + And for heaven's sake, don't do this: + + while (!done) + { + + do + { + + Do this instead: + + while (!done) { + + do { + +If you have long logic statements that need to be wrapped, then uncuddling +the bracket to improve readability is allowed. Generally, this style makes +it easier for reader to notice that 2nd and following lines are still +inside 'if': + + if (some_really_long_checks && some_other_really_long_checks + && some_more_really_long_checks + && even_more_of_long_checks + ) { + do_foo_now; + +Spacing around Parentheses +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Put a space between C keywords and left parens, but not between function names +and the left paren that starts it's parameter list (whether it is being +declared or called). Examples: + + Don't do this: + + while(foo) { + for(i = 0; i < n; i++) { + + Do this instead: + + while (foo) { + for (i = 0; i < n; i++) { + + But do functions like this: + + static int my_func(int foo, char bar) + ... + baz = my_func(1, 2); + +Also, don't put a space between the left paren and the first term, nor between +the last arg and the right paren. + + Don't do this: + + if ( x < 1 ) + strcmp( thisstr, thatstr ) + + Do this instead: + + if (x < 1) + strcmp(thisstr, thatstr) + + +Cuddled Elses +~~~~~~~~~~~~~ + +Also, please "cuddle" your else statements by putting the else keyword on the +same line after the right bracket that closes an 'if' statement. + + Don't do this: + + if (foo) { + stmt; + } + else { + stmt; + } + + Do this instead: + + if (foo) { + stmt; + } else { + stmt; + } + +The exception to this rule is if you want to include a comment before the else +block. Example: + + if (foo) { + stmts... + } + /* otherwise, we're just kidding ourselves, so re-frob the input */ + else { + other_stmts... + } + + +Labels +~~~~~~ + +Labels should start at the beginning of the line, not indented to the block +level (because they do not "belong" to block scope, only to whole function). + + if (foo) { + stmt; + label: + stmt2; + stmt; + } + +(Putting label at position 1 prevents diff -p from confusing label for function +name, but it's not a policy of busybox project to enforce such a minor detail). + + + +Variable and Function Names +--------------------------- + +Use the K&R style with names in all lower-case and underscores occasionally +used to separate words (e.g., "variable_name" and "numchars" are both +acceptable). Using underscores makes variable and function names more readable +because it looks like whitespace; using lower-case is easy on the eyes. + + Frowned upon: + + hitList + TotalChars + szFileName + pf_Nfol_TriState + + Preferred: + + hit_list + total_chars + file_name + sensible_name + +Exceptions: + + - Enums, macros, and constant variables are occasionally written in all + upper-case with words optionally separated by underscores (i.e. FIFO_TYPE, + ISBLKDEV()). + + - Nobody is going to get mad at you for using 'pvar' as the name of a + variable that is a pointer to 'var'. + + +Converting to K&R +~~~~~~~~~~~~~~~~~ + +The Busybox codebase is very much a mixture of code gathered from a variety of +sources. This explains why the current codebase contains such a hodge-podge of +different naming styles (Java, Pascal, K&R, just-plain-weird, etc.). The K&R +guideline explained above should therefore be used on new files that are added +to the repository. Furthermore, the maintainer of an existing file that uses +alternate naming conventions should, at his own convenience, convert those +names over to K&R style. Converting variable names is a very low priority +task. + +If you want to do a search-and-replace of a single variable name in different +files, you can do the following in the busybox directory: + + $ perl -pi -e 's/\bOldVar\b/new_var/g' *.[ch] + +If you want to convert all the non-K&R vars in your file all at once, follow +these steps: + + - In the busybox directory type 'examples/mk2knr.pl files-to-convert'. This + does not do the actual conversion, rather, it generates a script called + 'convertme.pl' that shows what will be converted, giving you a chance to + review the changes beforehand. + + - Review the 'convertme.pl' script that gets generated in the busybox + directory and remove / edit any of the substitutions in there. Please + especially check for false positives (strings that should not be + converted). + + - Type './convertme.pl same-files-as-before' to perform the actual + conversion. + + - Compile and see if everything still works. + +Please be aware of changes that have cascading effects into other files. For +example, if you're changing the name of something in, say utility.c, you +should probably run 'examples/mk2knr.pl utility.c' at first, but when you run +the 'convertme.pl' script you should run it on _all_ files like so: +'./convertme.pl *.[ch]'. + + + +Avoid The Preprocessor +---------------------- + +At best, the preprocessor is a necessary evil, helping us account for platform +and architecture differences. Using the preprocessor unnecessarily is just +plain evil. + + +The Folly of #define +~~~~~~~~~~~~~~~~~~~~ + +Use 'const var' for declaring constants. + + Don't do this: + + #define CONST 80 + + Do this instead, when the variable is in a header file and will be used in + several source files: + + enum { CONST = 80 }; + +Although enum may look ugly to some people, it is better for code size. +With "const int" compiler may fail to optimize it out and will reserve +a real storage in rodata for it! (Hopefully, newer gcc will get better +at it...). With "define", you have slight risk of polluting namespace +(#define doesn't allow you to redefine the name in the inner scopes), +and complex "define" are evaluated each time they uesd, not once +at declarations like enums. Also, the preprocessor does _no_ type checking +whatsoever, making it much more error prone. + + +The Folly of Macros +~~~~~~~~~~~~~~~~~~~ + +Use 'static inline' instead of a macro. + + Don't do this: + + #define mini_func(param1, param2) (param1 << param2) + + Do this instead: + + static inline int mini_func(int param1, param2) + { + return (param1 << param2); + } + +Static inline functions are greatly preferred over macros. They provide type +safety, have no length limitations, no formatting limitations, have an actual +return value, and under gcc they are as cheap as macros. Besides, really long +macros with backslashes at the end of each line are ugly as sin. + + +The Folly of #ifdef +~~~~~~~~~~~~~~~~~~~ + +Code cluttered with ifdefs is difficult to read and maintain. Don't do it. +Instead, put your ifdefs at the top of your .c file (or in a header), and +conditionally define 'static inline' functions, (or *maybe* macros), which are +used in the code. + + Don't do this: + + ret = my_func(bar, baz); + if (!ret) + return -1; + #ifdef CONFIG_FEATURE_FUNKY + maybe_do_funky_stuff(bar, baz); + #endif + + Do this instead: + + (in .h header file) + + #if ENABLE_FEATURE_FUNKY + static inline void maybe_do_funky_stuff(int bar, int baz) + { + /* lotsa code in here */ + } + #else + static inline void maybe_do_funky_stuff(int bar, int baz) {} + #endif + + (in the .c source file) + + ret = my_func(bar, baz); + if (!ret) + return -1; + maybe_do_funky_stuff(bar, baz); + +The great thing about this approach is that the compiler will optimize away +the "no-op" case (the empty function) when the feature is turned off. + +Note also the use of the word 'maybe' in the function name to indicate +conditional execution. + + + +Notes on Strings +---------------- + +Strings in C can get a little thorny. Here's some guidelines for dealing with +strings in Busybox. (There is surely more that could be added to this +section.) + + +String Files +~~~~~~~~~~~~ + +Put all help/usage messages in usage.c. Put other strings in messages.c. +Putting these strings into their own file is a calculated decision designed to +confine spelling errors to a single place and aid internationalization +efforts, if needed. (Side Note: we might want to use a single file - maybe +called 'strings.c' - instead of two, food for thought). + + +Testing String Equivalence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There's a right way and a wrong way to test for string equivalence with +strcmp(): + + The wrong way: + + if (!strcmp(string, "foo")) { + ... + + The right way: + + if (strcmp(string, "foo") == 0){ + ... + +The use of the "equals" (==) operator in the latter example makes it much more +obvious that you are testing for equivalence. The former example with the +"not" (!) operator makes it look like you are testing for an error. In a more +perfect world, we would have a streq() function in the string library, but +that ain't the world we're living in. + + +Avoid Dangerous String Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unfortunately, the way C handles strings makes them prone to overruns when +certain library functions are (mis)used. The following table offers a summary +of some of the more notorious troublemakers: + +function overflows preferred +------------------------------------------------- +strcpy dest string safe_strncpy +strncpy may fail to 0-terminate dst safe_strncpy +strcat dest string strncat +gets string it gets fgets +getwd buf string getcwd +[v]sprintf str buffer [v]snprintf +realpath path buffer use with pathconf +[vf]scanf its arguments just avoid it + + +The above is by no means a complete list. Be careful out there. + + + +Avoid Big Static Buffers +------------------------ + +First, some background to put this discussion in context: static buffers look +like this in code: + + /* in a .c file outside any functions */ + static char buffer[BUFSIZ]; /* happily used by any function in this file, + but ick! big! */ + +The problem with these is that any time any busybox app is run, you pay a +memory penalty for this buffer, even if the applet that uses said buffer is +not run. This can be fixed, thusly: + + static char *buffer; + ... + other_func() + { + strcpy(buffer, lotsa_chars); /* happily uses global *buffer */ + ... + foo_main() + { + buffer = xmalloc(sizeof(char)*BUFSIZ); + ... + +However, this approach trades bss segment for text segment. Rather than +mallocing the buffers (and thus growing the text size), buffers can be +declared on the stack in the *_main() function and made available globally by +assigning them to a global pointer thusly: + + static char *pbuffer; + ... + other_func() + { + strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */ + ... + foo_main() + { + char *buffer[BUFSIZ]; /* declared locally, on stack */ + pbuffer = buffer; /* but available globally */ + ... + +This last approach has some advantages (low code size, space not used until +it's needed), but can be a problem in some low resource machines that have +very limited stack space (e.g., uCLinux). + +A macro is declared in busybox.h that implements compile-time selection +between xmalloc() and stack creation, so you can code the line in question as + + RESERVE_CONFIG_BUFFER(buffer, BUFSIZ); + +and the right thing will happen, based on your configuration. + +Another relatively new trick of similar nature is explained +in keep_data_small.txt. + + + +Miscellaneous Coding Guidelines +------------------------------- + +The following are important items that don't fit into any of the above +sections. + + +Model Busybox Applets After GNU Counterparts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When in doubt about the proper behavior of a Busybox program (output, +formatting, options, etc.), model it after the equivalent GNU program. +Doesn't matter how that program behaves on some other flavor of *NIX; doesn't +matter what the POSIX standard says or doesn't say, just model Busybox +programs after their GNU counterparts and it will make life easier on (nearly) +everyone. + +The only time we deviate from emulating the GNU behavior is when: + + - We are deliberately not supporting a feature (such as a command line + switch) + - Emulating the GNU behavior is prohibitively expensive (lots more code + would be required, lots more memory would be used, etc.) + - The difference is minor or cosmetic + +A note on the 'cosmetic' case: output differences might be considered +cosmetic, but if the output is significant enough to break other scripts that +use the output, it should really be fixed. + + +Scope +~~~~~ + +If a const variable is used only in a single source file, put it in the source +file and not in a header file. Likewise, if a const variable is used in only +one function, do not make it global to the file. Instead, declare it inside +the function body. Bottom line: Make a conscious effort to limit declarations +to the smallest scope possible. + +Inside applet files, all functions should be declared static so as to keep the +global name space clean. The only exception to this rule is the "applet_main" +function which must be declared extern. + +If you write a function that performs a task that could be useful outside the +immediate file, turn it into a general-purpose function with no ties to any +applet and put it in the utility.c file instead. + + +Brackets Are Your Friends +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please use brackets on all if and else statements, even if it is only one +line. Example: + + Don't do this: + + if (foo) + stmt1; + stmt2 + stmt3; + + Do this instead: + + if (foo) { + stmt1; + } + stmt2 + stmt3; + +The "bracketless" approach is error prone because someday you might add a line +like this: + + if (foo) + stmt1; + new_line(); + stmt2; + stmt3; + +And the resulting behavior of your program would totally bewilder you. (Don't +laugh, it happens to us all.) Remember folks, this is C, not Python. + + +Function Declarations +~~~~~~~~~~~~~~~~~~~~~ + +Do not use old-style function declarations that declare variable types between +the parameter list and opening bracket. Example: + + Don't do this: + + int foo(parm1, parm2) + char parm1; + float parm2; + { + .... + + Do this instead: + + int foo(char parm1, float parm2) + { + .... + +The only time you would ever need to use the old declaration syntax is to +support ancient, antediluvian compilers. To our good fortune, we have access +to more modern compilers and the old declaration syntax is neither necessary +nor desired. + + +Emphasizing Logical Blocks +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Organization and readability are improved by putting extra newlines around +blocks of code that perform a single task. These are typically blocks that +begin with a C keyword, but not always. + +Furthermore, you should put a single comment (not necessarily one line, just +one comment) before the block, rather than commenting each and every line. +There is an optimal amount of commenting that a program can have; you can +comment too much as well as too little. + +A picture is really worth a thousand words here, the following example +illustrates how to emphasize logical blocks: + + while (line = xmalloc_fgets(fp)) { + + /* eat the newline, if any */ + chomp(line); + + /* ignore blank lines */ + if (strlen(file_to_act_on) == 0) { + continue; + } + + /* if the search string is in this line, print it, + * unless we were told to be quiet */ + if (strstr(line, search) && !be_quiet) { + puts(line); + } + + /* clean up */ + free(line); + } + + +Processing Options with getopt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your applet needs to process command-line switches, please use getopt32() to +do so. Numerous examples can be seen in many of the existing applets, but +basically it boils down to two things: at the top of the .c file, have this +line in the midst of your #includes, if you need to parse long options: + + #include + +Then have long options defined: + + static const char _longopts[] ALIGN1 = + "list\0" No_argument "t" + "extract\0" No_argument "x" + ; + +And a code block similar to the following near the top of your applet_main() +routine: + + char *str_b; + + opt_complementary = "cryptic_string"; + applet_long_options = _longopts; /* if you have them */ + opt = getopt32(argc, argv, "ab:c", &str_b); + if (opt & 1) { + handle_option_a(); + } + if (opt & 2) { + handle_option_b(str_b); + } + if (opt & 4) { + handle_option_c(); + } + +If your applet takes no options (such as 'init'), there should be a line +somewhere in the file reads: + + /* no options, no getopt */ + +That way, when people go grepping to see which applets need to be converted to +use getopt, they won't get false positives. + +For more info and examples, examine getopt32.c, tar.c, wget.c etc. diff --git a/docs/syslog.conf.txt b/docs/syslog.conf.txt new file mode 100644 index 000000000..6d9c4a173 --- /dev/null +++ b/docs/syslog.conf.txt @@ -0,0 +1,28 @@ +If syslogd applet compiled with FEATURE_SYSLOGD_CFG=y, then it supports restricted syslog.conf. +The config resembles rsyslog.conf in RULES part: + +LINE = DELIM [RULE | COMMENT] +COMMENT = #.* +DELIM = SPACE TAB +RULE = SELECTOR [;SELECTOR]* DELIM* ACTION DELIM* +SELECTOR = FACILITY [,FACILITY]* .[[!]=] PRIORITY +FACILITY = * | kern | user ... (see syslog.h) +PRIORITY = * | emerg | alert ... (see syslog.h) +ACTION = FILE + +"mark" facility is NOT supported. +"none" priority is supported. +In FACILITY and PRIORITY "*" stands for "any". +FILE is a regular file or tty device. + +Here is an example: + +#syslog.conf +kern,user.* /var/log/messages #all messages of kern and user facilities +kern.!err /var/log/critical #all messages of kern facility with priorities lower than err (warn, notice ...) +*.*;auth,authpriv.none /var/log/noauth #all messages except ones with auth and authpriv facilities +kern,user.*;kern.!=notice;*.err;syslog.none /var/log/OMG #some whicked rule just as an example =) +*.* /dev/null #this prevents from logging to default log file (-O FILE or /var/log/messages) + +Even in the case of match with some rule another rules will be tried too. +If there was no match with any of the rules, logging to default log file or shared memory will be performed. diff --git a/docs/tar_pax.txt b/docs/tar_pax.txt new file mode 100644 index 000000000..e56c27b16 --- /dev/null +++ b/docs/tar_pax.txt @@ -0,0 +1,239 @@ +'pax headers' is POSIX 2003 (iirc) addition designed to fix +tar format limitations - older tar format has fixed fields +for everything (filename, uid, filesize etc) which can overflow. + +pax Header Block + +The pax header block shall be identical to the ustar header block +described in ustar Interchange Format, except that two additional +typeflag values are defined: + +x + Represents extended header records for the following file in +the archive (which shall have its own ustar header block). + +g + Represents global extended header records for the following +files in the archive. Each value shall affect all subsequent files +that do not override that value in their own extended header +record and until another global extended header record is reached +that provides another value for the same field. The typeflag g +global headers should not be used with interchange media that +could suffer partial data loss in transporting the archive. + +For both of these types, the size field shall be the size of the +extended header records in octets. The other fields in the header +block are not meaningful to this version of the pax utility. +However, if this archive is read by a pax utility conforming to +the ISO POSIX-2:1993 standard, the header block fields are used to +create a regular file that contains the extended header records as +data. Therefore, header block field values should be selected to +provide reasonable file access to this regular file. + +A further difference from the ustar header block is that data +blocks for files of typeflag 1 (the digit one) (hard link) may be +included, which means that the size field may be greater than +zero. + +pax Extended Header + +An extended header shall consist of one or more records, each +constructed as follows: + +"%d %s=%s\n", , , + +The field shall be the decimal length of the extended +header record in octets, including length string itself and the +trailing . + +[skip] + +atime + The file access time for the following file(s), equivalent to +the value of the st_atime member of the stat structure for a file, +as described by the stat() function. The access time shall be +restored if the process has the appropriate privilege required to +do so. The format of the shall be as described in pax +Extended Header File Times. + +charset + The name of the character set used to encode the data in the +following file(s). + + The encoding is included in an extended header for information +only; when pax is used as described in IEEE Std 1003.1-2001, it +shall not translate the file data into any other encoding. The +BINARY entry indicates unencoded binary data. + + When used in write or copy mode, it is implementation-defined +whether pax includes a charset extended header record for a file. + +comment + A series of characters used as a comment. All characters in +the field shall be ignored by pax. + +gid + The group ID of the group that owns the file, expressed as a +decimal number using digits from the ISO/IEC 646:1991 standard. +This record shall override the gid field in the following header +block(s). When used in write or copy mode, pax shall include a gid +extended header record for each file whose group ID is greater +than 2097151 (octal 7777777). + +gname + The group of the file(s), formatted as a group name in the +group database. This record shall override the gid and gname +fields in the following header block(s), and any gid extended +header record. When used in read, copy, or list mode, pax shall +translate the name from the UTF-8 encoding in the header record to +the character set appropriate for the group database on the +receiving system. If any of the UTF-8 characters cannot be +translated, and if the -o invalid= UTF-8 option is not specified, +the results are implementation-defined. When used in write or copy +mode, pax shall include a gname extended header record for each +file whose group name cannot be represented entirely with the +letters and digits of the portable character set. + +linkpath + The pathname of a link being created to another file, of any +type, previously archived. This record shall override the linkname +field in the following ustar header block(s). The following ustar +header block shall determine the type of link created. If typeflag +of the following header block is 1, it shall be a hard link. If +typeflag is 2, it shall be a symbolic link and the linkpath value +shall be the contents of the symbolic link. The pax utility shall +translate the name of the link (contents of the symbolic link) +from the UTF-8 encoding to the character set appropriate for the +local file system. When used in write or copy mode, pax shall +include a linkpath extended header record for each link whose +pathname cannot be represented entirely with the members of the +portable character set other than NUL. + +mtime + The file modification time of the following file(s), +equivalent to the value of the st_mtime member of the stat +structure for a file, as described in the stat() function. This +record shall override the mtime field in the following header +block(s). The modification time shall be restored if the process +has the appropriate privilege required to do so. The format of the + shall be as described in pax Extended Header File Times. + +path + The pathname of the following file(s). This record shall +override the name and prefix fields in the following header +block(s). The pax utility shall translate the pathname of the file +from the UTF-8 encoding to the character set appropriate for the +local file system. + + When used in write or copy mode, pax shall include a path +extended header record for each file whose pathname cannot be +represented entirely with the members of the portable character +set other than NUL. + +realtime.any + The keywords prefixed by "realtime." are reserved for future +standardization. + +security.any + The keywords prefixed by "security." are reserved for future +standardization. + +size + The size of the file in octets, expressed as a decimal number +using digits from the ISO/IEC 646:1991 standard. This record shall +override the size field in the following header block(s). When +used in write or copy mode, pax shall include a size extended +header record for each file with a size value greater than +8589934591 (octal 77777777777). + +uid + The user ID of the file owner, expressed as a decimal number +using digits from the ISO/IEC 646:1991 standard. This record shall +override the uid field in the following header block(s). When used +in write or copy mode, pax shall include a uid extended header +record for each file whose owner ID is greater than 2097151 (octal +7777777). + +uname + The owner of the following file(s), formatted as a user name +in the user database. This record shall override the uid and uname +fields in the following header block(s), and any uid extended +header record. When used in read, copy, or list mode, pax shall +translate the name from the UTF-8 encoding in the header record to +the character set appropriate for the user database on the +receiving system. If any of the UTF-8 characters cannot be +translated, and if the -o invalid= UTF-8 option is not specified, +the results are implementation-defined. When used in write or copy +mode, pax shall include a uname extended header record for each +file whose user name cannot be represented entirely with the +letters and digits of the portable character set. + +If the field is zero length, it shall delete any header +block field, previously entered extended header value, or global +extended header value of the same name. + +If a keyword in an extended header record (or in a -o +option-argument) overrides or deletes a corresponding field in the +ustar header block, pax shall ignore the contents of that header +block field. + +Unlike the ustar header block fields, NULs shall not delimit +s; all characters within the field shall be +considered data for the field. None of the length limitations of +the ustar header block fields in ustar Header Block shall apply to +the extended header records. + +pax Extended Header File Times + +Time records shall be formatted as a decimal representation of the +time in seconds since the Epoch. If a period ( '.' ) decimal point +character is present, the digits to the right of the point shall +represent the units of a subsecond timing granularity. In read or +copy mode, the pax utility shall truncate the time of a file to +the greatest value that is not greater than the input header +file time. In write or copy mode, the pax utility shall output a +time exactly if it can be represented exactly as a decimal number, +and otherwise shall generate only enough digits so that the same +time shall be recovered if the file is extracted on a system whose +underlying implementation supports the same time granularity. + +Example from Linux kernel archive tarball: + +00000000 70 61 78 5f 67 6c 6f 62 61 6c 5f 68 65 61 64 65 |pax_global_heade| +00000010 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |r...............| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000060 00 00 00 00 30 30 30 30 36 36 36 00 30 30 30 30 |....0000666.0000| +00000070 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000| +00000080 30 30 30 30 30 36 34 00 30 30 30 30 30 30 30 30 |0000064.00000000| +00000090 30 30 30 00 30 30 31 34 30 35 33 00 67 00 00 00 |000.0014053.g...| +000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000100 00 75 73 74 61 72 00 30 30 67 69 74 00 00 00 00 |.ustar.00git....| +00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000120 00 00 00 00 00 00 00 00 00 67 69 74 00 00 00 00 |.........git....| +00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000140 00 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 |.........0000000| +00000150 00 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 |.0000000........| +00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000200 35 32 20 63 6f 6d 6d 65 6e 74 3d 62 31 30 35 30 |52 comment=b1050| +00000210 32 62 32 32 61 31 32 30 39 64 36 62 34 37 36 33 |2b22a1209d6b4763| +00000220 39 64 38 38 62 38 31 32 62 32 31 66 62 35 39 34 |9d88b812b21fb594| +00000230 39 65 34 0a 00 00 00 00 00 00 00 00 00 00 00 00 |9e4.............| +00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +... diff --git a/docs/unicode.txt b/docs/unicode.txt new file mode 100644 index 000000000..9c159ce2a --- /dev/null +++ b/docs/unicode.txt @@ -0,0 +1,71 @@ + Unicode support in busybox + +There are several scenarios where we need to handle unicode +correctly. + + Shell input + +We want to correctly handle input of unicode characters. +There are several problems with it. Just handling input +as sequence of bytes would break any editing. This was fixed +and now lineedit operates on the array of wchar_t's. +But we also need to handle the following problematic moments: + +* It is unreasonable to expect that output device supports + _any_ unicode chars. Perhaps we need to avoid printing + those chars which are not supported by output device. + Examples: chars which are not present in the font, + chars which are not assigned in unicode, + combining chars (especially trying to combine bad pairs: + a_chinese_symbol + "combining grave accent" = ??!) + +* We need to account for the fact that unicode chars have + different widths: 0 for combining chars, 1 for usual, + 2 for ideograms (are there 3+ wide chars?). + +* Bidirectional handling. If user wants to echo a phrase + in Hebrew, he types: echo "srettel werbeH" + + Editors (vi, ed) + +This case is a bit similar to "shell input", but unlike shell, +editors may encounter many more unexpected unicode sequences +(try to load a random binary file...), and they need to preserve +them, unlike shell which can afford to drop bogus input. + + more, less + +Need to correctly display any input file. Ideally, with +ASCII/unicode/filtered_unicode option or keyboard switch. +Note: need to handle tabs and backspaces specially +(bksp is for manpage compat). + + cut, fold, watch + +May need ability to cut unicode string to specified number of wchars +and/or to specified screen width. Need to handle tabs specially. + + sed, awk, grep + +Handle unicode-aware regexp match + + ls (multi-column display) + +ls will fail to line up columnar output if it will not account +for character widths (and maybe filter out some of them, see +above). OTOH, non-columnar views (ls -1, ls -l, ls | car) +should NOT filter out bad unicode (but need to filter out +control chars (coreutils does that). Note that unlike more/less, +tabs and backspaces need not special handling. + + top, ps + +Need to perform filtering similar to ls. + + Filename display (in error messages and elsewhere) + +Need to perform filtering similar to ls. + + +TODO: write an email to Asmus Freytag (asmus@unicode.org), +author of http://unicode.org/reports/tr11/ diff --git a/docs/unicode_UTF-8-test.txt b/docs/unicode_UTF-8-test.txt new file mode 100644 index 000000000..abd16f725 Binary files /dev/null and b/docs/unicode_UTF-8-test.txt differ diff --git a/docs/unicode_full-bmp.txt b/docs/unicode_full-bmp.txt new file mode 100644 index 000000000..2aeaa1e2b --- /dev/null +++ b/docs/unicode_full-bmp.txt @@ -0,0 +1,2079 @@ + +Full BMP Test File +------------------ + +Markus Kuhn -- 2003-04-22 + +This file contains the UTF-8 sequences of all code positions in the +ISO 10646-1 Basic Multilingual Plane, except for the C0 and C1 control +character areas. This corresponds to all codes in the range U+0020 - +U+007E and U+00A0 - U+FFFF. [uniset +0000..ffff utf8-list] + + +Basic Latin (U+0000-U+007F): + + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +`abcdefghijklmnopqrstuvwxyz{|}~ + +Latin-1 Supplement (U+0080-U+00FF): + + ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞß +àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ + +Latin Extended-A (U+0100-U+017F): + +Ä€ÄĂ㥹ĆćĈĉĊċČÄÄŽÄÄđĒēĔĕĖėĘęĚěĜÄĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ +Å€ÅłŃńŅņŇňʼnŊŋŌÅÅŽÅÅőŒœŔŕŖŗŘřŚśŜÅŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ + +Latin Extended-B (U+0180-U+024F): + +Æ€ÆƂƃƄƅƆƇƈƉƊƋƌÆÆŽÆÆƑƒƓƔƕƖƗƘƙƚƛƜÆƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ +Ç€ÇǂǃDŽDždžLJLjljNJNjnjÇÇŽÇÇǑǒǓǔǕǖǗǘǙǚǛǜÇǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ +È€ÈȂȃȄȅȆȇȈȉȊȋȌÈÈŽÈÈȑȒȓȔȕȖȗȘșȚțȜÈȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ +É€ÉɂɃɄɅɆɇɈɉɊɋɌÉÉŽÉ + +IPA Extensions (U+0250-U+02AF): + +ÉɑɒɓɔɕɖɗɘəɚɛɜÉɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀÊʂʃʄʅʆʇʈʉʊʋʌÊÊŽÊ +ÊʑʒʓʔʕʖʗʘʙʚʛʜÊʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯ + +Spacing Modifier Letters (U+02B0-U+02FF): + +ʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀË˂˃˄˅ˆˇˈˉˊˋˌËËŽËËˑ˒˓˔˕˖˗˘˙˚˛˜Ë˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯ +˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿ + +Combining Diacritical Marks (U+0300-U+036F): + +◌̀◌Ì◌̂◌̃◌̄◌̅◌̆◌̇◌̈◌̉◌̊◌̋◌̌◌Ì◌̎◌Ìâ—ŒÌ◌̑◌̒◌̓◌̔◌̕◌̖◌̗◌̘◌̙◌̚◌̛◌̜◌Ì◌̞◌̟◌̠◌̡◌̢◌̣◌̤◌̥◌̦◌̧◌̨◌̩◌̪◌̫◌̬◌̭◌̮◌̯◌̰◌̱◌̲◌̳◌̴◌̵◌̶◌̷◌̸◌̹◌̺◌̻◌̼◌̽◌̾◌̿ +◌̀◌Í◌͂◌̓◌̈́◌ͅ◌͆◌͇◌͈◌͉◌͊◌͋◌͌◌Í◌͎◌ÍÍ͓͔͕͖͙͚͑͒͗͛͘͜Í͟͞◌͠◌͡◌͢◌ͣ◌ͤ◌ͥ◌ͦ◌ͧ◌ͨ◌ͩ◌ͪ◌ͫ◌ͬ◌ͭ◌ͮ◌ͯ + +Greek and Coptic (U+0370-U+03FF): + +ͰͱͲͳʹ͵Ͷͷ͸͹ͺͻͼͽ;Ϳ΀Î΂΃΄΅Ά·ΈΉΊ΋ΌÎÎŽÎÎΑΒΓΔΕΖΗΘΙΚΛΜÎΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήί +ΰαβγδεζηθικλμνξοπÏςστυφχψωϊϋόÏÏŽÏÏϑϒϓϔϕϖϗϘϙϚϛϜÏϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯ +ϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿ + +Cyrillic (U+0400-U+04FF): + +ЀÐЂЃЄЅІЇЈЉЊЋЌÐÐŽÐÐБВГДЕЖЗИЙКЛМÐОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп +Ñ€ÑтуфхцчшщъыьÑÑŽÑÑёђѓєѕіїјљњћќÑўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿ +Ò€Ò҂◌҃◌҄◌҅◌҆҇ Òˆ Ò‰ÒŠÒ‹ÒŒÒÒŽÒÒÒ‘Ò’Ò“Ò”Ò•Ò–Ò—Ò˜Ò™ÒšÒ›ÒœÒÒžÒŸÒ Ò¡Ò¢Ò£Ò¤Ò¥Ò¦Ò§Ò¨Ò©ÒªÒ«Ò¬Ò­Ò®Ò¯Ò°Ò±Ò²Ò³Ò´ÒµÒ¶Ò·Ò¸Ò¹ÒºÒ»Ò¼Ò½Ò¾Ò¿ +Ó€ÓÓ‚ÓƒÓ„Ó…Ó†Ó‡ÓˆÓ‰ÓŠÓ‹ÓŒÓÓŽÓÓÓ‘Ó’Ó“Ó”Ó•Ó–Ó—Ó˜Ó™ÓšÓ›ÓœÓÓžÓŸÓ Ó¡Ó¢Ó£Ó¤Ó¥Ó¦Ó§Ó¨Ó©ÓªÓ«Ó¬Ó­Ó®Ó¯Ó°Ó±Ó²Ó³Ó´ÓµÓ¶Ó·Ó¸Ó¹ÓºÓ»Ó¼Ó½Ó¾Ó¿ + +Cyrillic Supplementary (U+0500-U+052F): + +Ô€ÔÔ‚ÔƒÔ„Ô…Ô†Ô‡ÔˆÔ‰ÔŠÔ‹ÔŒÔÔŽÔÔÔ‘Ô’Ô“Ô”Ô•Ô–Ô—Ô˜Ô™ÔšÔ›ÔœÔÔžÔŸÔ Ô¡Ô¢Ô£Ô¤Ô¥Ô¦Ô§Ô¨Ô©ÔªÔ«Ô¬Ô­Ô®Ô¯ + +Armenian (U+0530-U+058F): + +Ô°Ô±Ô²Ô³Ô´ÔµÔ¶Ô·Ô¸Ô¹ÔºÔ»Ô¼Ô½Ô¾Ô¿Õ€ÕÕ‚ÕƒÕ„Õ…Õ†Õ‡ÕˆÕ‰ÕŠÕ‹ÕŒÕÕŽÕÕÕ‘Õ’Õ“Õ”Õ•Õ–Õ—Õ˜Õ™ÕšÕ›ÕœÕÕžÕŸÕ Õ¡Õ¢Õ£Õ¤Õ¥Õ¦Õ§Õ¨Õ©ÕªÕ«Õ¬Õ­Õ®Õ¯ +Õ°Õ±Õ²Õ³Õ´ÕµÕ¶Õ·Õ¸Õ¹ÕºÕ»Õ¼Õ½Õ¾Õ¿Ö€ÖÖ‚ÖƒÖ„Ö…Ö†Ö‡ÖˆÖ‰ÖŠÖ‹ÖŒÖÖŽÖ + +Hebrew (U+0590-U+05FF): + +Ö◌֑◌֒◌֓◌֔◌֕◌֖◌֗◌֘◌֙◌֚◌֛◌֜◌Ö◌֞◌֟◌֠◌֢֡◌֣◌֤◌֥◌֦◌֧◌֨◌֩◌֪◌֫◌֬◌֭◌֮◌֯◌ְ◌ֱ◌ֲ◌ֳ◌ִ◌ֵ◌ֶ◌ַ◌ָ◌ֹֺ◌ֻ◌ּ◌ֽ־◌ֿ׀◌×◌ׂ׃◌ׅׄ׆ׇ׈׉׊׋׌××Ž× +×בגדהוזחטיךכל×מןנסעףפץצקרשת׫׬׭׮ׯװױײ׳״׵׶׷׸׹׺׻׼׽׾׿ + +Arabic (U+0600-U+06FF): + +Ø€Ø؂؃؄؅؆؇؈؉؊؋،ØØŽØØؘؙؚؑؒؓؔؕؖؗ؛؜Ø؞؟ؠءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿ +Ù€Ùقكلمنهوىي◌ً◌ٌ◌Ù◌َ◌Ùâ—ŒÙ◌ّ◌ْ◌ٓ◌ٔ◌ٕٖٜٗ٘ٙٚٛÙٟٞ٠١٢٣٤٥٦٧٨٩٪٫٬٭ٮٯ◌ٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿ +Ú€ÚÚ‚ÚƒÚ„Ú…Ú†Ú‡ÚˆÚ‰ÚŠÚ‹ÚŒÚÚŽÚÚÚ‘Ú’Ú“Ú”Ú•Ú–Ú—Ú˜Ú™ÚšÚ›ÚœÚÚžÚŸÚ Ú¡Ú¢Ú£Ú¤Ú¥Ú¦Ú§Ú¨Ú©ÚªÚ«Ú¬Ú­Ú®Ú¯Ú°Ú±Ú²Ú³Ú´ÚµÚ¶Ú·Ú¸Ú¹ÚºÚ»Ú¼Ú½Ú¾Ú¿ +Û€ÛÛ‚ÛƒÛ„Û…Û†Û‡ÛˆÛ‰ÛŠÛ‹ÛŒÛÛŽÛÛÛ‘Û’Û“Û”Û•â—ŒÛ–â—ŒÛ—â—ŒÛ˜â—ŒÛ™â—ŒÛšâ—ŒÛ›â—ŒÛœÛ Ûžâ—ŒÛŸâ—ŒÛ â—ŒÛ¡â—ŒÛ¢â—ŒÛ£â—ŒÛ¤Û¥Û¦â—ŒÛ§â—ŒÛ¨Û©â—ŒÛªâ—ŒÛ«â—ŒÛ¬â—ŒÛ­Û®Û¯Û°Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹ÛºÛ»Û¼Û½Û¾Û¿ + +Syriac (U+0700-U+074F): + +Ü€Ü܂܃܄܅܆܇܈܉܊܋܌ÜÜŽÜÜ◌ܑܒܓܔܕܖܗܘܙܚܛܜÜܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯ◌ܰ◌ܱ◌ܲ◌ܳ◌ܴ◌ܵ◌ܶ◌ܷ◌ܸ◌ܹ◌ܺ◌ܻ◌ܼ◌ܽ◌ܾ◌ܿ +◌݀◌Ý◌݂◌݃◌݄◌݅◌݆◌݇◌݈◌݉◌݊݋݌ÝÝŽÝ + +Free block (U+0750-U+077F): + +ÝݑݒݓݔݕݖݗݘݙݚݛݜÝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿ + +Thaana (U+0780-U+07BF): + +Þ€ÞÞ‚ÞƒÞ„Þ…Þ†Þ‡ÞˆÞ‰ÞŠÞ‹ÞŒÞÞŽÞÞÞ‘Þ’Þ“Þ”Þ•Þ–Þ—Þ˜Þ™ÞšÞ›ÞœÞޞޟޠޡޢޣޤޥ◌ަ◌ާ◌ި◌ީ◌ު◌ޫ◌ެ◌ޭ◌ޮ◌ޯ◌ްޱ޲޳޴޵޶޷޸޹޺޻޼޽޾޿ + +Free block (U+07C0-U+08FF): + +߀ß߂߃߄߅߆߇߈߉ߊߋߌßߎßßߑߒߓߔߕߖߗߘߙߚߛߜßߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ߶߷߸߹ߺ߻߼߽߾߿ +à €à à ‚ࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌà à Žà à à ‘ࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜà à žà Ÿà  à ¡à ¢à £à ¤à ¥à ¦à §à ¨à ©à ªà «à ¬à ­à ®à ¯à °à ±à ²à ³à ´à µà ¶à ·à ¸à ¹à ºà »à ¼à ½à ¾à ¿ +à¡€à¡à¡‚ࡃࡄࡅࡆࡇࡈࡉࡊࡋࡌà¡à¡Žà¡à¡à¡‘ࡒࡓࡔࡕࡖࡗࡘ࡙࡚࡛࡜à¡à¡žà¡Ÿà¡ à¡¡à¡¢à¡£à¡¤à¡¥à¡¦à¡§à¡¨à¡©à¡ªà¡«à¡¬à¡­à¡®à¡¯à¡°à¡±à¡²à¡³à¡´à¡µà¡¶à¡·à¡¸à¡¹à¡ºà¡»à¡¼à¡½à¡¾à¡¿ +ࢀà¢à¢‚ࢃࢄࢅࢆࢇ࢈ࢉࢊࢋࢌà¢à¢Žà¢à¢à¢‘࢒࢓࢔࢕࢖࢙࢚࢛ࢗ࢘࢜à¢à¢žà¢Ÿà¢ à¢¡à¢¢à¢£à¢¤à¢¥à¢¦à¢§à¢¨à¢©à¢ªà¢«à¢¬à¢­à¢®à¢¯à¢°à¢±à¢²à¢³à¢´à¢µà¢¶à¢·à¢¸à¢¹à¢ºà¢»à¢¼à¢½à¢¾à¢¿ +ࣀà£à£‚ࣃࣄࣅࣆࣇࣈࣉ࣊࣋࣌à£à£Žà£à£à£‘࣒࣓ࣔࣕࣖࣗࣘࣙࣚࣛࣜà£à£žà£Ÿà£ à£¡à£¢à££à£¤à£¥à£¦à£§à£¨à£©à£ªà£«à£¬à£­à£®à£¯à£°à£±à£²à£³à£´à£µà£¶à£·à£¸à£¹à£ºà£»à£¼à£½à£¾à£¿ + +Devanagari (U+0900-U+097F): + +ऀ◌à¤â—Œà¤‚ःऄअआइईउऊऋऌà¤à¤Žà¤à¤à¤‘ऒओऔकखगघङचछजà¤à¤žà¤Ÿà¤ à¤¡à¤¢à¤£à¤¤à¤¥à¤¦à¤§à¤¨à¤©à¤ªà¤«à¤¬à¤­à¤®à¤¯à¤°à¤±à¤²à¤³à¤´à¤µà¤¶à¤·à¤¸à¤¹à¤ºà¤»â—Œà¤¼à¤½à¤¾à¤¿ +ी◌à¥â—Œà¥‚◌ृ◌ॄ◌ॅ◌ॆ◌े◌ैॉॊोौ◌à¥à¥Žà¥à¥â—Œà¥‘◌॒◌॓◌॔ॕॖॗक़ख़ग़ज़ड़à¥à¥žà¥Ÿà¥ à¥¡â—Œà¥¢â—Œà¥£à¥¤à¥¥à¥¦à¥§à¥¨à¥©à¥ªà¥«à¥¬à¥­à¥®à¥¯à¥°à¥±à¥²à¥³à¥´à¥µà¥¶à¥·à¥¸à¥¹à¥ºà¥»à¥¼à¥½à¥¾à¥¿ + +Bengali (U+0980-U+09FF): + +ঀ◌à¦à¦‚ঃ঄অআইঈউঊঋঌà¦à¦Žà¦à¦à¦‘঒ওঔকখগঘঙচছজà¦à¦žà¦Ÿà¦ à¦¡à¦¢à¦£à¦¤à¦¥à¦¦à¦§à¦¨à¦©à¦ªà¦«à¦¬à¦­à¦®à¦¯à¦°à¦±à¦²à¦³à¦´à¦µà¦¶à¦·à¦¸à¦¹à¦ºà¦»â—Œà¦¼à¦½à¦¾à¦¿ +ী◌à§â—Œà§‚◌ৃ◌ৄ৅৆েৈ৉৊োৌ◌à§à§Žà§à§à§‘৒৓৔৕৖ৗ৘৙৚৛ড়à§à§žà§Ÿà§ à§¡â—Œà§¢â—Œà§£à§¤à§¥à§¦à§§à§¨à§©à§ªà§«à§¬à§­à§®à§¯à§°à§±à§²à§³à§´à§µà§¶à§·à§¸à§¹à§ºà§»à§¼à§½à§¾à§¿ + +Gurmukhi (U+0A00-U+0A7F): + +਀à¨â—Œà¨‚ਃ਄ਅਆਇਈਉਊ਋਌à¨à¨Žà¨à¨à¨‘਒ਓਔਕਖਗਘਙਚਛਜà¨à¨žà¨Ÿà¨ à¨¡à¨¢à¨£à¨¤à¨¥à¨¦à¨§à¨¨à¨©à¨ªà¨«à¨¬à¨­à¨®à¨¯à¨°à¨±à¨²à¨³à¨´à¨µà¨¶à¨·à¨¸à¨¹à¨ºà¨»â—Œà¨¼à¨½à¨¾à¨¿ +ੀ◌à©â—Œà©‚੃੄੅੆◌ੇ◌ੈ੉੊◌ੋ◌ੌ◌à©à©Žà©à©à©‘੒੓੔੕੖੗੘ਖ਼ਗ਼ਜ਼ੜà©à©žà©Ÿà© à©¡à©¢à©£à©¤à©¥à©¦à©§à©¨à©©à©ªà©«à©¬à©­à©®à©¯â—Œà©°â—Œà©±à©²à©³à©´à©µà©¶à©·à©¸à©¹à©ºà©»à©¼à©½à©¾à©¿ + +Gujarati (U+0A80-U+0AFF): + +઀◌àªâ—Œàª‚ઃ઄અઆઇઈઉઊઋઌàªàªŽàªàªàª‘઒ઓઔકખગઘઙચછજàªàªžàªŸàª àª¡àª¢àª£àª¤àª¥àª¦àª§àª¨àª©àªªàª«àª¬àª­àª®àª¯àª°àª±àª²àª³àª´àªµàª¶àª·àª¸àª¹àªºàª»â—Œàª¼àª½àª¾àª¿ +ી◌à«â—Œà«‚◌ૃ◌ૄ◌ૅ૆◌ે◌ૈૉ૊ોૌ◌à«à«Žà«à«à«‘૒૓૔૕૖૗૘૙૚૛૜à«à«žà«Ÿà« à«¡à«¢à«£à«¤à«¥à«¦à«§à«¨à«©à«ªà««à«¬à«­à«®à«¯à«°à«±à«²à«³à«´à«µà«¶à«·à«¸à«¹à«ºà«»à«¼à«½à«¾à«¿ + +Oriya (U+0B00-U+0B7F): + +଀◌à¬à¬‚ଃ଄ଅଆଇଈଉଊଋଌà¬à¬Žà¬à¬à¬‘଒ଓଔକଖଗଘଙଚଛଜà¬à¬žà¬Ÿà¬ à¬¡à¬¢à¬£à¬¤à¬¥à¬¦à¬§à¬¨à¬©à¬ªà¬«à¬¬à¬­à¬®à¬¯à¬°à¬±à¬²à¬³à¬´à¬µà¬¶à¬·à¬¸à¬¹à¬ºà¬»â—Œà¬¼à¬½à¬¾â—Œà¬¿ +ୀ◌à­â—Œà­‚◌ୃୄ୅୆େୈ୉୊ୋୌ◌à­à­Žà­à­à­‘୒୓୔୕◌ୖୗ୘୙୚୛ଡ଼à­à­žà­Ÿà­ à­¡à­¢à­£à­¤à­¥à­¦à­§à­¨à­©à­ªà­«à­¬à­­à­®à­¯à­°à­±à­²à­³à­´à­µà­¶à­·à­¸à­¹à­ºà­»à­¼à­½à­¾à­¿ + +Tamil (U+0B80-U+0BFF): + +஀à®â—Œà®‚ஃ஄அஆஇஈஉஊ஋஌à®à®Žà®à®à®‘ஒஓஔக஖஗஘ஙச஛ஜà®à®žà®Ÿà® à®¡à®¢à®£à®¤à®¥à®¦à®§à®¨à®©à®ªà®«à®¬à®­à®®à®¯à®°à®±à®²à®³à®´à®µà®¶à®·à®¸à®¹à®ºà®»à®¼à®½à®¾à®¿ +◌ீà¯à¯‚௃௄௅ெேை௉ொோௌ◌à¯à¯Žà¯à¯à¯‘௒௓௔௕௖ௗ௘௙௚௛௜à¯à¯žà¯Ÿà¯ à¯¡à¯¢à¯£à¯¤à¯¥à¯¦à¯§à¯¨à¯©à¯ªà¯«à¯¬à¯­à¯®à¯¯à¯°à¯±à¯²à¯³à¯´à¯µà¯¶à¯·à¯¸à¯¹à¯ºà¯»à¯¼à¯½à¯¾à¯¿ + +Telugu (U+0C00-U+0C7F): + +à°€à°à°‚ఃఄఅఆఇఈఉఊఋఌà°à°Žà°à°à°‘ఒఓఔకఖగఘఙచఛజà°à°žà°Ÿà° à°¡à°¢à°£à°¤à°¥à°¦à°§à°¨à°©à°ªà°«à°¬à°­à°®à°¯à°°à°±à°²à°³à°´à°µà°¶à°·à°¸à°¹à°ºà°»à°¼à°½â—Œà°¾â—Œà°¿ +◌ీà±à±‚ృౄ౅◌ె◌ే◌ై౉◌ొ◌ో◌ౌ◌à±à±Žà±à±à±‘౒౓౔◌ౕ◌ౖ౗ౘౙౚ౛౜à±à±žà±Ÿà± à±¡à±¢à±£à±¤à±¥à±¦à±§à±¨à±©à±ªà±«à±¬à±­à±®à±¯à±°à±±à±²à±³à±´à±µà±¶à±·à±¸à±¹à±ºà±»à±¼à±½à±¾à±¿ + +Kannada (U+0C80-U+0CFF): + +ಀà²à²‚ಃ಄ಅಆಇಈಉಊಋಌà²à²Žà²à²à²‘ಒಓಔಕಖಗಘಙಚಛಜà²à²žà²Ÿà² à²¡à²¢à²£à²¤à²¥à²¦à²§à²¨à²©à²ªà²«à²¬à²­à²®à²¯à²°à²±à²²à²³à²´à²µà²¶à²·à²¸à²¹à²ºà²»à²¼à²½à²¾â—Œà²¿ +à³€à³à³‚ೃೄ೅◌ೆೇೈ೉ೊೋ◌ೌ◌à³à³Žà³à³à³‘೒೓೔ೕೖ೗೘೙೚೛೜à³à³žà³Ÿà³ à³¡à³¢à³£à³¤à³¥à³¦à³§à³¨à³©à³ªà³«à³¬à³­à³®à³¯à³°à³±à³²à³³à³´à³µà³¶à³·à³¸à³¹à³ºà³»à³¼à³½à³¾à³¿ + +Malayalam (U+0D00-U+0D7F): + +à´€à´à´‚ഃഄഅആഇഈഉഊഋഌà´à´Žà´à´à´‘ഒഓഔകഖഗഘങചഛജà´à´žà´Ÿà´ à´¡à´¢à´£à´¤à´¥à´¦à´§à´¨à´©à´ªà´«à´¬à´­à´®à´¯à´°à´±à´²à´³à´´à´µà´¶à´·à´¸à´¹à´ºà´»à´¼à´½à´¾à´¿ +ീ◌àµâ—Œàµ‚◌ൃൄ൅െേൈ൉ൊോൌ◌àµàµŽàµàµàµ‘൒൓ൔൕൖൗ൘൙൚൛൜àµàµžàµŸàµ àµ¡àµ¢àµ£àµ¤àµ¥àµ¦àµ§àµ¨àµ©àµªàµ«àµ¬àµ­àµ®àµ¯àµ°àµ±àµ²àµ³àµ´àµµàµ¶àµ·àµ¸àµ¹àµºàµ»àµ¼àµ½àµ¾àµ¿ + +Sinhala (U+0D80-U+0DFF): + +඀à¶à¶‚ඃ඄අආඇඈඉඊඋඌà¶à¶Žà¶à¶à¶‘ඒඓඔඕඖ඗඘඙කඛගà¶à¶žà¶Ÿà¶ à¶¡à¶¢à¶£à¶¤à¶¥à¶¦à¶§à¶¨à¶©à¶ªà¶«à¶¬à¶­à¶®à¶¯à¶°à¶±à¶²à¶³à¶´à¶µà¶¶à¶·à¶¸à¶¹à¶ºà¶»à¶¼à¶½à¶¾à¶¿ +à·€à·à·‚සහළෆ෇෈෉◌්෋෌à·à·Žà·à·à·‘◌ි◌ී◌ු෕◌ූ෗ෘෙේෛොà·à·žà·Ÿà· à·¡à·¢à·£à·¤à·¥à·¦à·§à·¨à·©à·ªà·«à·¬à·­à·®à·¯à·°à·±à·²à·³à·´à·µà·¶à··à·¸à·¹à·ºà·»à·¼à·½à·¾à·¿ + +Thai (U+0E00-U+0E7F): + +฀à¸à¸‚ฃคฅฆงจฉชซฌà¸à¸Žà¸à¸à¸‘ฒณดตถทธนบปผà¸à¸žà¸Ÿà¸ à¸¡à¸¢à¸£à¸¤à¸¥à¸¦à¸§à¸¨à¸©à¸ªà¸«à¸¬à¸­à¸®à¸¯à¸°â—Œà¸±à¸²à¸³â—Œà¸´â—Œà¸µâ—Œà¸¶â—Œà¸·â—Œà¸¸â—Œà¸¹â—Œà¸ºà¸»à¸¼à¸½à¸¾à¸¿ +เà¹à¹‚ใไๅๆ◌็◌่◌้◌๊◌๋◌์◌à¹â—Œà¹Žà¹à¹à¹‘๒๓๔๕๖๗๘๙๚๛๜à¹à¹žà¹Ÿà¹ à¹¡à¹¢à¹£à¹¤à¹¥à¹¦à¹§à¹¨à¹©à¹ªà¹«à¹¬à¹­à¹®à¹¯à¹°à¹±à¹²à¹³à¹´à¹µà¹¶à¹·à¹¸à¹¹à¹ºà¹»à¹¼à¹½à¹¾à¹¿ + +Lao (U+0E80-U+0EFF): + +຀àºàº‚຃ຄ຅ຆງຈຉຊ຋ຌàºàºŽàºàºàº‘ຒຓດຕຖທຘນບປຜàºàºžàºŸàº àº¡àº¢àº£àº¤àº¥àº¦àº§àº¨àº©àºªàº«àº¬àº­àº®àº¯àº°â—Œàº±àº²àº³â—Œàº´â—Œàºµâ—Œàº¶â—Œàº·â—Œàº¸â—Œàº¹àººâ—Œàº»â—Œàº¼àº½àº¾àº¿ +ເà»à»‚ໃໄ໅ໆ໇◌່◌້◌໊◌໋◌໌◌à»à»Žà»à»à»‘໒໓໔໕໖໗໘໙໚໛ໜà»à»žà»Ÿà» à»¡à»¢à»£à»¤à»¥à»¦à»§à»¨à»©à»ªà»«à»¬à»­à»®à»¯à»°à»±à»²à»³à»´à»µà»¶à»·à»¸à»¹à»ºà»»à»¼à»½à»¾à»¿ + +Tibetan (U+0F00-U+0FFF): + +ༀà¼à¼‚༃༄༅༆༇༈༉༊་༌à¼à¼Žà¼à¼à¼‘༒༓༔༕༖༗◌༘◌༙༚༛༜à¼à¼žà¼Ÿà¼ à¼¡à¼¢à¼£à¼¤à¼¥à¼¦à¼§à¼¨à¼©à¼ªà¼«à¼¬à¼­à¼®à¼¯à¼°à¼±à¼²à¼³à¼´â—Œà¼µà¼¶â—Œà¼·à¼¸â—Œà¼¹à¼ºà¼»à¼¼à¼½à¼¾à¼¿ +ཀà½à½‚གྷངཅཆཇ཈ཉཊཋཌà½à½Žà½à½à½‘དྷནཔཕབབྷམཙཚཛཛྷà½à½žà½Ÿà½ à½¡à½¢à½£à½¤à½¥à½¦à½§à½¨à½©à½ªà½«à½¬à½­à½®à½¯à½°â—Œà½±â—Œà½²â—Œà½³â—Œà½´â—Œà½µâ—Œà½¶â—Œà½·â—Œà½¸â—Œà½¹â—Œà½ºâ—Œà½»â—Œà½¼â—Œà½½â—Œà½¾à½¿ +◌ྀ◌à¾â—Œà¾‚◌ྃ◌྄྅◌྆◌྇ྈྉྊྋྌà¾à¾Žà¾â—Œà¾â—Œà¾‘◌ྒ◌ྒྷ◌ྔ◌ྕ◌ྖ◌ྗ྘◌ྙ◌ྚ◌ྛ◌ྜ◌à¾â—Œà¾žâ—Œà¾Ÿâ—Œà¾ â—Œà¾¡â—Œà¾¢â—Œà¾£â—Œà¾¤â—Œà¾¥â—Œà¾¦â—Œà¾§â—Œà¾¨â—Œà¾©â—Œà¾ªâ—Œà¾«â—Œà¾¬â—Œà¾­â—Œà¾®â—Œà¾¯â—Œà¾°â—Œà¾±â—Œà¾²â—Œà¾³â—Œà¾´â—Œà¾µâ—Œà¾¶â—Œà¾·â—Œà¾¸â—Œà¾¹â—Œà¾ºâ—Œà¾»â—Œà¾¼à¾½à¾¾à¾¿ +à¿€à¿à¿‚࿃࿄࿅◌࿆࿇࿈࿉࿊࿋࿌à¿à¿Žà¿à¿à¿‘࿒࿓࿔࿕࿖࿗࿘࿙࿚࿛࿜à¿à¿žà¿Ÿà¿ à¿¡à¿¢à¿£à¿¤à¿¥à¿¦à¿§à¿¨à¿©à¿ªà¿«à¿¬à¿­à¿®à¿¯à¿°à¿±à¿²à¿³à¿´à¿µà¿¶à¿·à¿¸à¿¹à¿ºà¿»à¿¼à¿½à¿¾à¿¿ + +Myanmar (U+1000-U+109F): + +ကá€á€‚ဃငစဆဇဈဉညဋဌá€á€Žá€á€á€‘ဒဓနပဖဗဘမယရလá€á€žá€Ÿá€ á€¡á€¢á€£á€¤á€¥á€¦á€§á€¨á€©á€ªá€«á€¬â—Œá€­â—Œá€®â—Œá€¯â—Œá€°á€±â—Œá€²á€³á€´á€µâ—Œá€¶â—Œá€·á€¸â—Œá€¹á€ºá€»á€¼á€½á€¾á€¿ +á€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—â—Œá˜â—Œá™ášá›áœáážáŸá á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿ +á‚€á‚ႂႃႄႅႆႇႈႉႊႋႌá‚á‚Žá‚á‚႑႒႓႔႕႖႗႘႙ႚႛႜá‚á‚žá‚Ÿ + +Georgian (U+10A0-U+10FF): + +ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀáƒáƒ‚ჃჄჅ჆Ⴧ჈჉჊჋჌áƒáƒŽáƒáƒáƒ‘გდევზთიკლმნáƒáƒžáƒŸ +რსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ჻ჼჽჾჿ + +Hangul Jamo (U+1100-U+11FF): + +á„€á„ᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌá„á„Žá„á„ᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜá„á„žá„Ÿ +ᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿ +á…€á…ᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌá…á…Žá…á…ᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜá…á…žá…Ÿ +ᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀá†á†‚ᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌá†á†Žá†á†á†‘ᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜá†á†žá†Ÿ +ᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀá‡á‡‚ᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌá‡á‡Žá‡á‡á‡‘ᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜá‡á‡žá‡Ÿ +ᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿ + +Ethiopic (U+1200-U+137F): + +ሀáˆáˆ‚ሃሄህሆሇለሉሊላሌáˆáˆŽáˆáˆáˆ‘ሒሓሔሕሖሗመሙሚማሜáˆáˆžáˆŸáˆ áˆ¡áˆ¢áˆ£áˆ¤áˆ¥áˆ¦áˆ§áˆ¨áˆ©áˆªáˆ«áˆ¬áˆ­áˆ®áˆ¯áˆ°áˆ±áˆ²áˆ³áˆ´áˆµáˆ¶áˆ·áˆ¸áˆ¹áˆºáˆ»áˆ¼áˆ½áˆ¾áˆ¿ +ቀá‰á‰‚ቃቄቅቆቇቈ቉ቊቋቌá‰á‰Žá‰á‰á‰‘ቒቓቔቕቖ቗ቘ቙ቚቛቜá‰á‰žá‰Ÿá‰ á‰¡á‰¢á‰£á‰¤á‰¥á‰¦á‰§á‰¨á‰©á‰ªá‰«á‰¬á‰­á‰®á‰¯á‰°á‰±á‰²á‰³á‰´á‰µá‰¶á‰·á‰¸á‰¹á‰ºá‰»á‰¼á‰½á‰¾á‰¿ +ኀáŠáŠ‚ኃኄኅኆኇኈ኉ኊኋኌáŠáŠŽáŠáŠáŠ‘ኒናኔንኖኗኘኙኚኛኜáŠáŠžáŠŸáŠ áŠ¡áŠ¢áŠ£áŠ¤áŠ¥áŠ¦áŠ§áŠ¨áŠ©áŠªáŠ«áŠ¬áŠ­áŠ®áŠ¯áŠ°áŠ±áŠ²áŠ³áŠ´áŠµáŠ¶áŠ·áŠ¸áŠ¹áŠºáŠ»áŠ¼áŠ½áŠ¾áŠ¿ +á‹€á‹á‹‚ዃዄዅ዆዇ወዉዊዋዌá‹á‹Žá‹á‹á‹‘ዒዓዔዕዖ዗ዘዙዚዛዜá‹á‹žá‹Ÿá‹ á‹¡á‹¢á‹£á‹¤á‹¥á‹¦á‹§á‹¨á‹©á‹ªá‹«á‹¬á‹­á‹®á‹¯á‹°á‹±á‹²á‹³á‹´á‹µá‹¶á‹·á‹¸á‹¹á‹ºá‹»á‹¼á‹½á‹¾á‹¿ +ጀáŒáŒ‚ጃጄጅጆጇገጉጊጋጌáŒáŒŽáŒáŒáŒ‘ጒጓጔጕ጖጗ጘጙጚጛጜáŒáŒžáŒŸáŒ áŒ¡áŒ¢áŒ£áŒ¤áŒ¥áŒ¦áŒ§áŒ¨áŒ©áŒªáŒ«áŒ¬áŒ­áŒ®áŒ¯áŒ°áŒ±áŒ²áŒ³áŒ´áŒµáŒ¶áŒ·áŒ¸áŒ¹áŒºáŒ»áŒ¼áŒ½áŒ¾áŒ¿ +á€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—á˜á™ášá›áœáážáŸá á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿ + +Free block (U+1380-U+139F): + +ᎀáŽáŽ‚ᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌáŽáŽŽáŽáŽáŽ‘᎒᎓᎔᎕᎖᎗᎘᎙᎚᎛᎜áŽáŽžáŽŸ + +Cherokee (U+13A0-U+13FF): + +ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿá€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—á˜á™ášá›áœáážáŸ +á á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿ + +Unified Canadian Aboriginal Syllabics (U+1400-U+167F): + +á€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—á˜á™ášá›áœáážáŸá á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿ +á‘€á‘ᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌá‘á‘Žá‘á‘ᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜá‘ᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿ +á’€á’ᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌá’á’Žá’á’ᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜá’ᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿ +á“€á“ᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌá“á“Žá“á“ᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜá“ᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿ +ᔀá”ᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌá”ᔎá”á”ᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜá”ᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿ +á•€á•á•‚ᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌá•á•Žá•á•á•‘ᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜá•á•žá•Ÿá• á•¡á•¢á•£á•¤á•¥á•¦á•§á•¨á•©á•ªá•«á•¬á•­á•®á•¯á•°á•±á•²á•³á•´á•µá•¶á•·á•¸á•¹á•ºá•»á•¼á•½á•¾á•¿ +á–€á–ᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌá–á–Žá–á–ᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜá–ᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿ +á—€á—ᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌá—á—Žá—á—ᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜá—ᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿ +ᘀá˜á˜‚ᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌá˜á˜Žá˜á˜á˜‘ᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜá˜á˜žá˜Ÿá˜ á˜¡á˜¢á˜£á˜¤á˜¥á˜¦á˜§á˜¨á˜©á˜ªá˜«á˜¬á˜­á˜®á˜¯á˜°á˜±á˜²á˜³á˜´á˜µá˜¶á˜·á˜¸á˜¹á˜ºá˜»á˜¼á˜½á˜¾á˜¿ +ᙀá™á™‚ᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌá™á™Žá™á™á™‘ᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜá™á™žá™Ÿá™ á™¡á™¢á™£á™¤á™¥á™¦á™§á™¨á™©á™ªá™«á™¬á™­á™®á™¯á™°á™±á™²á™³á™´á™µá™¶á™·á™¸á™¹á™ºá™»á™¼á™½á™¾á™¿ + +Ogham (U+1680-U+169F): + + ášáš‚ᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌášášŽášášáš‘ᚒᚓᚔᚕᚖᚗᚘᚙᚚ᚛᚜ášášžášŸ + +Runic (U+16A0-U+16FF): + +ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀá›á›‚ᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌá›á›Žá›á›á›‘ᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜá›á›žá›Ÿ +ᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰᛱᛲᛳᛴᛵᛶᛷᛸ᛹᛺᛻᛼᛽᛾᛿ + +Tagalog (U+1700-U+171F): + +ᜀáœáœ‚ᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌáœáœŽáœáœáœ‘◌ᜒ◌ᜓ◌᜔᜕᜖᜗᜘᜙᜚᜛᜜áœáœžáœŸ + +Hanunoo (U+1720-U+173F): + +ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱ◌ᜲ◌ᜳ◌᜴᜵᜶᜷᜸᜹᜺᜻᜼᜽᜾᜿ + +Buhid (U+1740-U+175F): + +á€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘â—Œá’â—Œá“á”á•á–á—á˜á™ášá›áœáážáŸ + +Tagbanwa (U+1760-U+177F): + +á á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±â—Œá²â—Œá³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿ + +Khmer (U+1780-U+17FF): + +កážáž‚ឃងចឆជឈញដឋឌážážŽážážáž‘ធនបផពភមយរលវážážžážŸáž áž¡áž¢áž£áž¤áž¥áž¦áž§áž¨áž©ážªáž«áž¬áž­áž®áž¯áž°áž±áž²áž³áž´ážµáž¶â—Œáž·â—Œáž¸â—Œáž¹â—Œážºâ—Œáž»â—Œáž¼â—Œáž½áž¾áž¿ +ៀáŸáŸ‚ៃោៅ◌ំះៈ◌៉◌៊◌់◌៌◌áŸâ—ŒáŸŽâ—ŒáŸâ—ŒáŸâ—ŒáŸ‘◌្◌៓។៕៖ៗ៘៙៚៛ៜáŸáŸžáŸŸáŸ áŸ¡áŸ¢áŸ£áŸ¤áŸ¥áŸ¦áŸ§áŸ¨áŸ©áŸªáŸ«áŸ¬áŸ­áŸ®áŸ¯áŸ°áŸ±áŸ²áŸ³áŸ´áŸµáŸ¶áŸ·áŸ¸áŸ¹áŸºáŸ»áŸ¼áŸ½áŸ¾áŸ¿ + +Mongolian (U+1800-U+18AF): + +á €á á ‚᠃᠄᠅᠆᠇᠈᠉᠊◌᠋◌᠌◌á á Žá á á ‘᠒᠓᠔᠕᠖᠗᠘᠙᠚᠛᠜á á žá Ÿá  á ¡á ¢á £á ¤á ¥á ¦á §á ¨á ©á ªá «á ¬á ­á ®á ¯á °á ±á ²á ³á ´á µá ¶á ·á ¸á ¹á ºá »á ¼á ½á ¾á ¿ +á¡€á¡á¡‚ᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌá¡á¡Žá¡á¡á¡‘ᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜá¡á¡žá¡Ÿá¡ á¡¡á¡¢á¡£á¡¤á¡¥á¡¦á¡§á¡¨á¡©á¡ªá¡«á¡¬á¡­á¡®á¡¯á¡°á¡±á¡²á¡³á¡´á¡µá¡¶á¡·á¡¸á¡¹á¡ºá¡»á¡¼á¡½á¡¾á¡¿ +ᢀá¢á¢‚ᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌá¢á¢Žá¢á¢á¢‘ᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜá¢á¢žá¢Ÿá¢ á¢¡á¢¢á¢£á¢¤á¢¥á¢¦á¢§á¢¨â—Œá¢©á¢ªá¢«á¢¬á¢­á¢®á¢¯ + +Free block (U+18B0-U+18FF): + +ᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀá£á£‚ᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌá£á£Žá£á£á£‘ᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜá£á£žá£Ÿá£ á£¡á£¢á££á£¤á£¥á£¦á£§á£¨á£©á£ªá£«á£¬á£­á£®á£¯ +ᣰᣱᣲᣳᣴᣵ᣶᣷᣸᣹᣺᣻᣼᣽᣾᣿ + +Limbu (U+1900-U+194F): + +ᤀá¤á¤‚ᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌá¤á¤Žá¤á¤á¤‘ᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜá¤á¤žá¤Ÿá¤ á¤¡á¤¢á¤£á¤¤á¤¥á¤¦á¤§á¤¨á¤©á¤ªá¤«á¤¬á¤­á¤®á¤¯á¤°á¤±á¤²á¤³á¤´á¤µá¤¶á¤·á¤¸á¤¹á¤ºá¤»á¤¼á¤½á¤¾á¤¿ +᥀á¥á¥‚᥃᥄᥅᥆᥇᥈᥉᥊᥋᥌á¥á¥Žá¥ + +Tai Le (U+1950-U+197F): + +á¥á¥‘ᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜá¥á¥žá¥Ÿá¥ á¥¡á¥¢á¥£á¥¤á¥¥á¥¦á¥§á¥¨á¥©á¥ªá¥«á¥¬á¥­á¥®á¥¯á¥°á¥±á¥²á¥³á¥´á¥µá¥¶á¥·á¥¸á¥¹á¥ºá¥»á¥¼á¥½á¥¾á¥¿ + +Free block (U+1980-U+19DF): + +ᦀá¦á¦‚ᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌá¦á¦Žá¦á¦á¦‘ᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜá¦á¦žá¦Ÿá¦ á¦¡á¦¢á¦£á¦¤á¦¥á¦¦á¦§á¦¨á¦©á¦ªá¦«á¦¬á¦­á¦®á¦¯á¦°á¦±á¦²á¦³á¦´á¦µá¦¶á¦·á¦¸á¦¹á¦ºá¦»á¦¼á¦½á¦¾á¦¿ +ᧀá§á§‚ᧃᧄᧅᧆᧇᧈᧉ᧊᧋᧌á§á§Žá§á§á§‘᧒᧓᧔᧕᧖᧗᧘᧙᧚᧛᧜á§á§žá§Ÿ + +Khmer Symbols (U+19E0-U+19FF): + +᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ + +Free block (U+1A00-U+1CFF): + +ᨀá¨á¨‚ᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌá¨á¨Žá¨á¨á¨‘ᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ᨜á¨á¨žá¨Ÿá¨ á¨¡á¨¢á¨£á¨¤á¨¥á¨¦á¨§á¨¨á¨©á¨ªá¨«á¨¬á¨­á¨®á¨¯á¨°á¨±á¨²á¨³á¨´á¨µá¨¶á¨·á¨¸á¨¹á¨ºá¨»á¨¼á¨½á¨¾á¨¿ +á©€á©á©‚ᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌá©á©Žá©á©á©‘ᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜá©á©žá©Ÿá© á©¡á©¢á©£á©¤á©¥á©¦á©§á©¨á©©á©ªá©«á©¬á©­á©®á©¯á©°á©±á©²á©³á©´á©µá©¶á©·á©¸á©¹á©ºá©»á©¼á©½á©¾á©¿ +᪀áªáª‚᪃᪄᪅᪆᪇᪈᪉᪊᪋᪌áªáªŽáªáªáª‘᪒᪓᪔᪕᪖᪗᪘᪙᪚᪛᪜áªáªžáªŸáª áª¡áª¢áª£áª¤áª¥áª¦áª§áª¨áª©áªªáª«áª¬áª­áª®áª¯áª°áª±áª²áª³áª´áªµáª¶áª·áª¸áª¹áªºáª»áª¼áª½áª¾áª¿ +á«€á«á«‚᫃᫄᫊᫅᫆᫇᫈᫉᫋ᫌá«á«Žá«á«á«‘᫒᫓᫔᫕᫖᫗᫘᫙᫚᫛᫜á«á«žá«Ÿá« á«¡á«¢á«£á«¤á«¥á«¦á«§á«¨á«©á«ªá««á«¬á«­á«®á«¯á«°á«±á«²á«³á«´á«µá«¶á«·á«¸á«¹á«ºá«»á«¼á«½á«¾á«¿ +ᬀá¬á¬‚ᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌá¬á¬Žá¬á¬á¬‘ᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜá¬á¬žá¬Ÿá¬ á¬¡á¬¢á¬£á¬¤á¬¥á¬¦á¬§á¬¨á¬©á¬ªá¬«á¬¬á¬­á¬®á¬¯á¬°á¬±á¬²á¬³á¬´á¬µá¬¶á¬·á¬¸á¬¹á¬ºá¬»á¬¼á¬½á¬¾á¬¿ +á­€á­á­‚ᭃ᭄ᭅᭆᭇᭈᭉᭊᭋᭌá­á­Žá­á­á­‘᭒᭓᭔᭕᭖᭗᭘᭙᭚᭛᭜á­á­žá­Ÿá­ á­¡á­¢á­£á­¤á­¥á­¦á­§á­¨á­©á­ªá­«á­¬á­­á­®á­¯á­°á­±á­²á­³á­´á­µá­¶á­·á­¸á­¹á­ºá­»á­¼á­½á­¾á­¿ +ᮀá®á®‚ᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌá®á®Žá®á®á®‘ᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜá®á®žá®Ÿá® á®¡á®¢á®£á®¤á®¥á®¦á®§á®¨á®©á®ªá®«á®¬á®­á®®á®¯á®°á®±á®²á®³á®´á®µá®¶á®·á®¸á®¹á®ºá®»á®¼á®½á®¾á®¿ +ᯀá¯á¯‚ᯃᯄᯅᯆᯇᯈᯉᯊᯋᯌá¯á¯Žá¯á¯á¯‘ᯒᯓᯔᯕᯖᯗᯘᯙᯚᯛᯜá¯á¯žá¯Ÿá¯ á¯¡á¯¢á¯£á¯¤á¯¥á¯¦á¯§á¯¨á¯©á¯ªá¯«á¯¬á¯­á¯®á¯¯á¯°á¯±á¯²á¯³á¯´á¯µá¯¶á¯·á¯¸á¯¹á¯ºá¯»á¯¼á¯½á¯¾á¯¿ +á°€á°á°‚ᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌá°á°Žá°á°á°‘ᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜá°á°žá°Ÿá° á°¡á°¢á°£á°¤á°¥á°¦á°§á°¨á°©á°ªá°«á°¬á°­á°®á°¯á°°á°±á°²á°³á°´á°µá°¶á°·á°¸á°¹á°ºá°»á°¼á°½á°¾á°¿ +á±€á±á±‚᱃᱄᱅᱆᱇᱈᱉᱊᱋᱌á±á±Žá±á±á±‘᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜá±á±žá±Ÿá± á±¡á±¢á±£á±¤á±¥á±¦á±§á±¨á±©á±ªá±«á±¬á±­á±®á±¯á±°á±±á±²á±³á±´á±µá±¶á±·á±¸á±¹á±ºá±»á±¼á±½á±¾á±¿ +á²€á²á²‚ᲃᲄᲅᲆᲇᲈᲉᲊ᲋᲌á²á²Žá²á²á²‘ᲒᲓᲔᲕᲖᲗᲘᲙᲚᲛᲜá²á²žá²Ÿá² á²¡á²¢á²£á²¤á²¥á²¦á²§á²¨á²©á²ªá²«á²¬á²­á²®á²¯á²°á²±á²²á²³á²´á²µá²¶á²·á²¸á²¹á²ºá²»á²¼á²½á²¾á²¿ +á³€á³á³‚᳃᳄᳅᳆᳇᳈᳉᳊᳋᳌á³á³Žá³á³á³‘᳒᳓᳔᳕᳖᳗᳘᳙᳜᳚᳛á³á³žá³Ÿá³ á³¡á³¢á³£á³¤á³¥á³¦á³§á³¨á³©á³ªá³«á³¬á³­á³®á³¯á³°á³±á³²á³³á³´á³µá³¶á³·á³¸á³¹á³ºá³»á³¼á³½á³¾á³¿ + +Phonetic Extensions (U+1D00-U+1D7F): + +á´€á´á´‚ᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌá´á´Žá´á´á´‘ᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜá´á´žá´Ÿá´ á´¡á´¢á´£á´¤á´¥á´¦á´§á´¨á´©á´ªá´«á´¬á´­á´®á´¯á´°á´±á´²á´³á´´á´µá´¶á´·á´¸á´¹á´ºá´»á´¼á´½á´¾á´¿ +áµ€áµáµ‚ᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌáµáµŽáµáµáµ‘ᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜáµáµžáµŸáµ áµ¡áµ¢áµ£áµ¤áµ¥áµ¦áµ§áµ¨áµ©áµªáµ«áµ¬áµ­áµ®áµ¯áµ°áµ±áµ²áµ³áµ´áµµáµ¶áµ·áµ¸áµ¹áµºáµ»áµ¼áµ½áµ¾áµ¿ + +Free block (U+1D80-U+1DFF): + +ᶀá¶á¶‚ᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌá¶á¶Žá¶á¶á¶‘ᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜá¶á¶žá¶Ÿá¶ á¶¡á¶¢á¶£á¶¤á¶¥á¶¦á¶§á¶¨á¶©á¶ªá¶«á¶¬á¶­á¶®á¶¯á¶°á¶±á¶²á¶³á¶´á¶µá¶¶á¶·á¶¸á¶¹á¶ºá¶»á¶¼á¶½á¶¾á¶¿ +á·€á·á·‚᷊᷃᷄᷅᷆᷇᷈᷉᷋᷌á·á·Žá·á·á·‘᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜá·á·žá·Ÿá· á·¡á·¢á·£á·¤á·¥á·¦á·§á·¨á·©á·ªá·«á·¬á·­á·®á·¯á·°á·±á·²á·³á·´á·µá·¶á··á·¸á·¹á·ºá·»á·¼á·½á·¾á·¿ + +Latin Extended Additional (U+1E00-U+1EFF): + +Ḁá¸á¸‚ḃḄḅḆḇḈḉḊḋḌá¸á¸Žá¸á¸á¸‘ḒḓḔḕḖḗḘḙḚḛḜá¸á¸žá¸Ÿá¸ á¸¡á¸¢á¸£á¸¤á¸¥á¸¦á¸§á¸¨á¸©á¸ªá¸«á¸¬á¸­á¸®á¸¯á¸°á¸±á¸²á¸³á¸´á¸µá¸¶á¸·á¸¸á¸¹á¸ºá¸»á¸¼á¸½á¸¾á¸¿ +á¹€á¹á¹‚ṃṄṅṆṇṈṉṊṋṌá¹á¹Žá¹á¹á¹‘ṒṓṔṕṖṗṘṙṚṛṜá¹á¹žá¹Ÿá¹ á¹¡á¹¢á¹£á¹¤á¹¥á¹¦á¹§á¹¨á¹©á¹ªá¹«á¹¬á¹­á¹®á¹¯á¹°á¹±á¹²á¹³á¹´á¹µá¹¶á¹·á¹¸á¹¹á¹ºá¹»á¹¼á¹½á¹¾á¹¿ +Ẁáºáº‚ẃẄẅẆẇẈẉẊẋẌáºáºŽáºáºáº‘ẒẓẔẕẖẗẘẙẚẛẜáºáºžáºŸáº áº¡áº¢áº£áº¤áº¥áº¦áº§áº¨áº©áºªáº«áº¬áº­áº®áº¯áº°áº±áº²áº³áº´áºµáº¶áº·áº¸áº¹áººáº»áº¼áº½áº¾áº¿ +Ềá»á»‚ểỄễỆệỈỉỊịỌá»á»Žá»á»á»‘ỒồỔổỖỗỘộỚớỜá»á»žá»Ÿá» á»¡á»¢á»£á»¤á»¥á»¦á»§á»¨á»©á»ªá»«á»¬á»­á»®á»¯á»°á»±á»²á»³á»´á»µá»¶á»·á»¸á»¹á»ºá»»á»¼á»½á»¾á»¿ + +Greek Extended (U+1F00-U+1FFF): + +á¼€á¼á¼‚ἃἄἅἆἇἈἉἊἋἌá¼á¼Žá¼á¼á¼‘ἒἓἔἕ἖἗ἘἙἚἛἜá¼á¼žá¼Ÿá¼ á¼¡á¼¢á¼£á¼¤á¼¥á¼¦á¼§á¼¨á¼©á¼ªá¼«á¼¬á¼­á¼®á¼¯á¼°á¼±á¼²á¼³á¼´á¼µá¼¶á¼·á¼¸á¼¹á¼ºá¼»á¼¼á¼½á¼¾á¼¿ +á½€á½á½‚ὃὄὅ὆὇ὈὉὊὋὌá½á½Žá½á½á½‘ὒὓὔὕὖὗ὘Ὑ὚Ὓ὜á½á½žá½Ÿá½ á½¡á½¢á½£á½¤á½¥á½¦á½§á½¨á½©á½ªá½«á½¬á½­á½®á½¯á½°á½±á½²á½³á½´á½µá½¶á½·á½¸á½¹á½ºá½»á½¼á½½á½¾á½¿ +á¾€á¾á¾‚ᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌá¾á¾Žá¾á¾á¾‘ᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜá¾á¾žá¾Ÿá¾ á¾¡á¾¢á¾£á¾¤á¾¥á¾¦á¾§á¾¨á¾©á¾ªá¾«á¾¬á¾­á¾®á¾¯á¾°á¾±á¾²á¾³á¾´á¾µá¾¶á¾·á¾¸á¾¹á¾ºá¾»á¾¼á¾½á¾¾á¾¿ +á¿€á¿á¿‚ῃῄ῅ῆῇῈΈῊΉῌá¿á¿Žá¿á¿á¿‘ῒΐ῔῕ῖῗῘῙῚΊ῜á¿á¿žá¿Ÿá¿ á¿¡á¿¢á¿£á¿¤á¿¥á¿¦á¿§á¿¨á¿©á¿ªá¿«á¿¬á¿­á¿®á¿¯á¿°á¿±á¿²á¿³á¿´á¿µá¿¶á¿·á¿¸á¿¹á¿ºá¿»á¿¼á¿½á¿¾á¿¿ + +General Punctuation (U+2000-U+206F): + + â€â€‚        ​‌â€â€Žâ€â€â€‘‒–—―‖‗‘’‚‛“â€â€žâ€Ÿâ€ â€¡â€¢â€£â€¤â€¥â€¦â€§â€¨â€©â€ªâ€«â€¬â€­â€®â€¯â€°â€±â€²â€³â€´â€µâ€¶â€·â€¸â€¹â€ºâ€»â€¼â€½â€¾â€¿ +â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯ + +Superscripts and Subscripts (U+2070-U+209F): + +â°â±â²â³â´âµâ¶â·â¸â¹âºâ»â¼â½â¾â¿â‚€â‚₂₃₄₅₆₇₈₉₊₋₌â‚â‚Žâ‚â‚ₑₒₓₔₕₖₗₘₙₚₛₜâ‚â‚žâ‚Ÿ + +Currency Symbols (U+20A0-U+20CF): + +₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃀âƒâƒ‚⃃⃄⃅⃆⃇⃈⃉⃊⃋⃌âƒâƒŽâƒ + +Combining Diacritical Marks for Symbols (U+20D0-U+20FF): + +â—Œâƒâ—Œâƒ‘◌⃒◌⃓◌⃔◌⃕◌⃖◌⃗◌⃘◌⃙◌⃚◌⃛◌⃜ ⃠⃞ ⃟ ⃠◌⃡ ⃢ ⃣ ⃤◌⃥◌⃦◌⃧◌⃨◌⃩◌⃪⃫⃬⃭⃮⃯⃰⃱⃲⃳⃴⃵⃶⃷⃸⃹⃺⃻⃼⃽⃾⃿ + +Letterlike Symbols (U+2100-U+214F): + +â„€â„ℂ℃℄℅℆ℇ℈℉ℊℋℌâ„â„Žâ„â„ℑℒℓ℔ℕ№℗℘ℙℚℛℜâ„℞℟℠℡™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺℻ℼℽℾℿ +â…€â…⅂⅃⅄ⅅⅆⅇⅈⅉ⅊⅋⅌â…â…Žâ… + +Number Forms (U+2150-U+218F): + +â…⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜â…⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀâ†â†‚Ↄↄↅↆↇↈ↉↊↋↌â†â†Žâ† + +Arrows (U+2190-U+21FF): + +â†â†‘→↓↔↕↖↗↘↙↚↛↜â†â†žâ†Ÿâ† â†¡â†¢â†£â†¤â†¥â†¦â†§â†¨â†©â†ªâ†«â†¬â†­â†®â†¯â†°â†±â†²â†³â†´â†µâ†¶â†·â†¸â†¹â†ºâ†»â†¼â†½â†¾â†¿â‡€â‡â‡‚⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌â‡â‡Žâ‡ +â‡â‡‘⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜â‡â‡žâ‡Ÿâ‡ â‡¡â‡¢â‡£â‡¤â‡¥â‡¦â‡§â‡¨â‡©â‡ªâ‡«â‡¬â‡­â‡®â‡¯â‡°â‡±â‡²â‡³â‡´â‡µâ‡¶â‡·â‡¸â‡¹â‡ºâ‡»â‡¼â‡½â‡¾â‡¿ + +Mathematical Operators (U+2200-U+22FF): + +∀âˆâˆ‚∃∄∅∆∇∈∉∊∋∌âˆâˆŽâˆâˆâˆ‘−∓∔∕∖∗∘∙√∛∜âˆâˆžâˆŸâˆ âˆ¡âˆ¢âˆ£âˆ¤âˆ¥âˆ¦âˆ§âˆ¨âˆ©âˆªâˆ«âˆ¬âˆ­âˆ®âˆ¯âˆ°âˆ±âˆ²âˆ³âˆ´âˆµâˆ¶âˆ·âˆ¸âˆ¹âˆºâˆ»âˆ¼âˆ½âˆ¾âˆ¿ +≀â‰â‰‚≃≄≅≆≇≈≉≊≋≌â‰â‰Žâ‰â‰â‰‘≒≓≔≕≖≗≘≙≚≛≜â‰â‰žâ‰Ÿâ‰ â‰¡â‰¢â‰£â‰¤â‰¥â‰¦â‰§â‰¨â‰©â‰ªâ‰«â‰¬â‰­â‰®â‰¯â‰°â‰±â‰²â‰³â‰´â‰µâ‰¶â‰·â‰¸â‰¹â‰ºâ‰»â‰¼â‰½â‰¾â‰¿ +⊀âŠâŠ‚⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌âŠâŠŽâŠâŠâŠ‘⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜âŠâŠžâŠŸâŠ âŠ¡âŠ¢âŠ£âŠ¤âŠ¥âŠ¦âŠ§âŠ¨âŠ©âŠªâŠ«âŠ¬âŠ­âŠ®âŠ¯âŠ°âŠ±âŠ²âŠ³âŠ´âŠµâŠ¶âŠ·âŠ¸âŠ¹âŠºâŠ»âŠ¼âŠ½âŠ¾âŠ¿ +â‹€â‹â‹‚⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌â‹â‹Žâ‹â‹â‹‘⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜â‹â‹žâ‹Ÿâ‹ â‹¡â‹¢â‹£â‹¤â‹¥â‹¦â‹§â‹¨â‹©â‹ªâ‹«â‹¬â‹­â‹®â‹¯â‹°â‹±â‹²â‹³â‹´â‹µâ‹¶â‹·â‹¸â‹¹â‹ºâ‹»â‹¼â‹½â‹¾â‹¿ + +Miscellaneous Technical (U+2300-U+23FF): + +⌀âŒâŒ‚⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌âŒâŒŽâŒâŒâŒ‘⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜âŒâŒžâŒŸâŒ âŒ¡âŒ¢âŒ£âŒ¤âŒ¥âŒ¦âŒ§âŒ¨âŒ©âŒªâŒ«âŒ¬âŒ­âŒ®âŒ¯âŒ°âŒ±âŒ²âŒ³âŒ´âŒµâŒ¶âŒ·âŒ¸âŒ¹âŒºâŒ»âŒ¼âŒ½ +⌾⌿â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯â°â±â²â³â´âµâ¶â·â¸â¹âºâ»â¼â½ +â¾â¿âŽ€âŽâŽ‚⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌âŽâŽŽâŽâŽâŽ‘⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜âŽâŽžâŽŸâŽ âŽ¡âŽ¢âŽ£âŽ¤âŽ¥âŽ¦âŽ§âŽ¨âŽ©âŽªâŽ«âŽ¬âŽ­âŽ®âŽ¯âŽ°âŽ±âŽ²âŽ³âŽ´âŽµâŽ¶âŽ·âŽ¸âŽ¹âŽºâŽ»âŽ¼âŽ½ +⎾⎿â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯â°â±â²â³â´âµâ¶â·â¸â¹âºâ»â¼â½ +â¾â¿ + +Control Pictures (U+2400-U+243F): + +â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯â°â±â²â³â´âµâ¶â·â¸â¹âºâ»â¼â½â¾â¿ + +Optical Character Recognition (U+2440-U+245F): + +â‘€â‘⑂⑃⑄⑅⑆⑇⑈⑉⑊⑋⑌â‘â‘Žâ‘â‘⑑⑒⑓⑔⑕⑖⑗⑘⑙⑚⑛⑜â‘â‘žâ‘Ÿ + +Enclosed Alphanumerics (U+2460-U+24FF): + +①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀â’⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌â’â’Žâ’â’⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜â’â’žâ’Ÿ +⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀâ“ⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌâ“â“Žâ“â“ⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜâ“â“žâ“Ÿ +ⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿ + +Box Drawing (U+2500-U+257F): + +─â”│┃┄┅┆┇┈┉┊┋┌â”┎â”â”┑┒┓└┕┖┗┘┙┚┛├â”┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿ +â•€â•â•‚╃╄╅╆╇╈╉╊╋╌â•â•Žâ•â•â•‘╒╓╔╕╖╗╘╙╚╛╜â•â•žâ•Ÿâ• â•¡â•¢â•£â•¤â•¥â•¦â•§â•¨â•©â•ªâ•«â•¬â•­â•®â•¯â•°â•±â•²â•³â•´â•µâ•¶â•·â•¸â•¹â•ºâ•»â•¼â•½â•¾â•¿ + +Block Elements (U+2580-U+259F): + +â–€â–▂▃▄▅▆▇█▉▊▋▌â–â–Žâ–â–░▒▓▔▕▖▗▘▙▚▛▜â–â–žâ–Ÿ + +Geometric Shapes (U+25A0-U+25FF): + +■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀â—◂◃◄◅◆◇◈◉◊○◌â—â—Žâ—â—◑◒◓◔◕◖◗◘◙◚◛◜â—â—žâ—Ÿ +◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿ + +Miscellaneous Symbols (U+2600-U+26FF): + +☀â˜â˜‚☃☄★☆☇☈☉☊☋☌â˜â˜Žâ˜â˜â˜‘☒☓☔☕☖☗☘☙☚☛☜â˜â˜žâ˜Ÿâ˜ â˜¡â˜¢â˜£â˜¤â˜¥â˜¦â˜§â˜¨â˜©â˜ªâ˜«â˜¬â˜­â˜®â˜¯â˜°â˜±â˜²â˜³â˜´â˜µâ˜¶â˜·â˜¸â˜¹â˜ºâ˜»â˜¼â˜½â˜¾â˜¿ +♀â™â™‚♃♄♅♆♇♈♉♊♋♌â™â™Žâ™â™â™‘♒♓♔♕♖♗♘♙♚♛♜â™â™žâ™Ÿâ™ â™¡â™¢â™£â™¤â™¥â™¦â™§â™¨â™©â™ªâ™«â™¬â™­â™®â™¯â™°â™±â™²â™³â™´â™µâ™¶â™·â™¸â™¹â™ºâ™»â™¼â™½â™¾â™¿ +⚀âšâš‚⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌âšâšŽâšâšâš‘⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜âšâšžâšŸâš âš¡âš¢âš£âš¤âš¥âš¦âš§âš¨âš©âšªâš«âš¬âš­âš®âš¯âš°âš±âš²âš³âš´âšµâš¶âš·âš¸âš¹âšºâš»âš¼âš½âš¾âš¿ +⛀â›â›‚⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌â›â›Žâ›â›â›‘⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜â›â›žâ›Ÿâ› â›¡â›¢â›£â›¤â›¥â›¦â›§â›¨â›©â›ªâ›«â›¬â›­â›®â›¯â›°â›±â›²â›³â›´â›µâ›¶â›·â›¸â›¹â›ºâ›»â›¼â›½â›¾â›¿ + +Dingbats (U+2700-U+27BF): + +✀âœâœ‚✃✄✅✆✇✈✉✊✋✌âœâœŽâœâœâœ‘✒✓✔✕✖✗✘✙✚✛✜âœâœžâœŸâœ âœ¡âœ¢âœ£âœ¤âœ¥âœ¦âœ§âœ¨âœ©âœªâœ«âœ¬âœ­âœ®âœ¯âœ°âœ±âœ²âœ³âœ´âœµâœ¶âœ·âœ¸âœ¹âœºâœ»âœ¼âœ½âœ¾âœ¿ +â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯â°â±â²â³â´âµâ¶â·â¸â¹âºâ»â¼â½â¾â¿ +➀âžâž‚➃➄➅➆➇➈➉➊➋➌âžâžŽâžâžâž‘➒➓➔➕➖➗➘➙➚➛➜âžâžžâžŸâž âž¡âž¢âž£âž¤âž¥âž¦âž§âž¨âž©âžªâž«âž¬âž­âž®âž¯âž°âž±âž²âž³âž´âžµâž¶âž·âž¸âž¹âžºâž»âž¼âž½âž¾âž¿ + +Miscellaneous Mathematical Symbols-A (U+27C0-U+27EF): + +⟀âŸâŸ‚⟃⟄⟅⟆⟇⟈⟉⟊⟋⟌âŸâŸŽâŸâŸâŸ‘⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜âŸâŸžâŸŸâŸ âŸ¡âŸ¢âŸ£âŸ¤âŸ¥âŸ¦âŸ§âŸ¨âŸ©âŸªâŸ«âŸ¬âŸ­âŸ®âŸ¯ + +Supplemental Arrows-A (U+27F0-U+27FF): + +⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿ + +Braille Patterns (U+2800-U+28FF): + +â €â â ‚⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌â â Žâ â â ‘⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜â â žâ Ÿâ  â ¡â ¢â £â ¤â ¥â ¦â §â ¨â ©â ªâ «â ¬â ­â ®â ¯â °â ±â ²â ³â ´â µâ ¶â ·â ¸â ¹â ºâ »â ¼â ½â ¾â ¿ +â¡€â¡â¡‚⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌â¡â¡Žâ¡â¡â¡‘⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜â¡â¡žâ¡Ÿâ¡ â¡¡â¡¢â¡£â¡¤â¡¥â¡¦â¡§â¡¨â¡©â¡ªâ¡«â¡¬â¡­â¡®â¡¯â¡°â¡±â¡²â¡³â¡´â¡µâ¡¶â¡·â¡¸â¡¹â¡ºâ¡»â¡¼â¡½â¡¾â¡¿ +⢀â¢â¢‚⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌â¢â¢Žâ¢â¢â¢‘⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜â¢â¢žâ¢Ÿâ¢ â¢¡â¢¢â¢£â¢¤â¢¥â¢¦â¢§â¢¨â¢©â¢ªâ¢«â¢¬â¢­â¢®â¢¯â¢°â¢±â¢²â¢³â¢´â¢µâ¢¶â¢·â¢¸â¢¹â¢ºâ¢»â¢¼â¢½â¢¾â¢¿ +⣀â£â£‚⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌â£â£Žâ£â£â£‘⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜â£â£žâ£Ÿâ£ â£¡â£¢â££â£¤â£¥â£¦â£§â£¨â£©â£ªâ£«â£¬â£­â£®â£¯â£°â£±â£²â£³â£´â£µâ£¶â£·â£¸â£¹â£ºâ£»â£¼â£½â£¾â£¿ + +Supplemental Arrows-B (U+2900-U+297F): + +⤀â¤â¤‚⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌â¤â¤Žâ¤â¤â¤‘⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜â¤â¤žâ¤Ÿâ¤ â¤¡â¤¢â¤£â¤¤â¤¥â¤¦â¤§â¤¨â¤©â¤ªâ¤«â¤¬â¤­â¤®â¤¯â¤°â¤±â¤²â¤³â¤´â¤µâ¤¶â¤·â¤¸â¤¹â¤ºâ¤»â¤¼â¤½â¤¾â¤¿ +⥀â¥â¥‚⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌â¥â¥Žâ¥â¥â¥‘⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜â¥â¥žâ¥Ÿâ¥ â¥¡â¥¢â¥£â¥¤â¥¥â¥¦â¥§â¥¨â¥©â¥ªâ¥«â¥¬â¥­â¥®â¥¯â¥°â¥±â¥²â¥³â¥´â¥µâ¥¶â¥·â¥¸â¥¹â¥ºâ¥»â¥¼â¥½â¥¾â¥¿ + +Miscellaneous Mathematical Symbols-B (U+2980-U+29FF): + +⦀â¦â¦‚⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌â¦â¦Žâ¦â¦â¦‘⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜â¦â¦žâ¦Ÿâ¦ â¦¡â¦¢â¦£â¦¤â¦¥â¦¦â¦§â¦¨â¦©â¦ªâ¦«â¦¬â¦­â¦®â¦¯â¦°â¦±â¦²â¦³â¦´â¦µâ¦¶â¦·â¦¸â¦¹â¦ºâ¦»â¦¼â¦½â¦¾â¦¿ +⧀â§â§‚⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌â§â§Žâ§â§â§‘⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜â§â§žâ§Ÿâ§ â§¡â§¢â§£â§¤â§¥â§¦â§§â§¨â§©â§ªâ§«â§¬â§­â§®â§¯â§°â§±â§²â§³â§´â§µâ§¶â§·â§¸â§¹â§ºâ§»â§¼â§½â§¾â§¿ + +Supplemental Mathematical Operators (U+2A00-U+2AFF): + +⨀â¨â¨‚⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌â¨â¨Žâ¨â¨â¨‘⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜â¨â¨žâ¨Ÿâ¨ â¨¡â¨¢â¨£â¨¤â¨¥â¨¦â¨§â¨¨â¨©â¨ªâ¨«â¨¬â¨­â¨®â¨¯â¨°â¨±â¨²â¨³â¨´â¨µâ¨¶â¨·â¨¸â¨¹â¨ºâ¨»â¨¼â¨½â¨¾â¨¿ +â©€â©â©‚⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌â©â©Žâ©â©â©‘⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜â©â©žâ©Ÿâ© â©¡â©¢â©£â©¤â©¥â©¦â©§â©¨â©©â©ªâ©«â©¬â©­â©®â©¯â©°â©±â©²â©³â©´â©µâ©¶â©·â©¸â©¹â©ºâ©»â©¼â©½â©¾â©¿ +⪀âªâª‚⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌âªâªŽâªâªâª‘⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜âªâªžâªŸâª âª¡âª¢âª£âª¤âª¥âª¦âª§âª¨âª©âªªâª«âª¬âª­âª®âª¯âª°âª±âª²âª³âª´âªµâª¶âª·âª¸âª¹âªºâª»âª¼âª½âª¾âª¿ +â«€â«â«‚⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌â«â«Žâ«â«â«‘⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸â«â«žâ«Ÿâ« â«¡â«¢â«£â«¤â«¥â«¦â«§â«¨â«©â«ªâ««â«¬â«­â«®â«¯â«°â«±â«²â«³â«´â«µâ«¶â«·â«¸â«¹â«ºâ«»â«¼â«½â«¾â«¿ + +Miscellaneous Symbols and Arrows (U+2B00-U+2BFF): + +⬀â¬â¬‚⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌â¬â¬Žâ¬â¬â¬‘⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜â¬â¬žâ¬Ÿâ¬ â¬¡â¬¢â¬£â¬¤â¬¥â¬¦â¬§â¬¨â¬©â¬ªâ¬«â¬¬â¬­â¬®â¬¯â¬°â¬±â¬²â¬³â¬´â¬µâ¬¶â¬·â¬¸â¬¹â¬ºâ¬»â¬¼â¬½â¬¾â¬¿ +â­€â­â­‚⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌â­â­Žâ­â­â­‘⭒⭓⭔⭕⭖⭗⭘⭙⭚⭛⭜â­â­žâ­Ÿâ­ â­¡â­¢â­£â­¤â­¥â­¦â­§â­¨â­©â­ªâ­«â­¬â­­â­®â­¯â­°â­±â­²â­³â­´â­µâ­¶â­·â­¸â­¹â­ºâ­»â­¼â­½â­¾â­¿ +⮀â®â®‚⮃⮄⮅⮆⮇⮈⮉⮊⮋⮌â®â®Žâ®â®â®‘⮒⮓⮔⮕⮖⮗⮘⮙⮚⮛⮜â®â®žâ®Ÿâ® â®¡â®¢â®£â®¤â®¥â®¦â®§â®¨â®©â®ªâ®«â®¬â®­â®®â®¯â®°â®±â®²â®³â®´â®µâ®¶â®·â®¸â®¹â®ºâ®»â®¼â®½â®¾â®¿ +⯀â¯â¯‚⯃⯄⯅⯆⯇⯈⯉⯊⯋⯌â¯â¯Žâ¯â¯â¯‘⯒⯓⯔⯕⯖⯗⯘⯙⯚⯛⯜â¯â¯žâ¯Ÿâ¯ â¯¡â¯¢â¯£â¯¤â¯¥â¯¦â¯§â¯¨â¯©â¯ªâ¯«â¯¬â¯­â¯®â¯¯â¯°â¯±â¯²â¯³â¯´â¯µâ¯¶â¯·â¯¸â¯¹â¯ºâ¯»â¯¼â¯½â¯¾â¯¿ + +Free block (U+2C00-U+2E7F): + +â°€â°â°‚ⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌâ°â°Žâ°â°â°‘ⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜâ°â°žâ°Ÿâ° â°¡â°¢â°£â°¤â°¥â°¦â°§â°¨â°©â°ªâ°«â°¬â°­â°®â°¯â°°â°±â°²â°³â°´â°µâ°¶â°·â°¸â°¹â°ºâ°»â°¼â°½â°¾â°¿ +â±€â±â±‚ⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌâ±â±Žâ±â±â±‘ⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜâ±â±žâ±Ÿâ± â±¡â±¢â±£â±¤â±¥â±¦â±§â±¨â±©â±ªâ±«â±¬â±­â±®â±¯â±°â±±â±²â±³â±´â±µâ±¶â±·â±¸â±¹â±ºâ±»â±¼â±½â±¾â±¿ +â²€â²â²‚ⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌâ²â²Žâ²â²â²‘ⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜâ²â²žâ²Ÿâ² â²¡â²¢â²£â²¤â²¥â²¦â²§â²¨â²©â²ªâ²«â²¬â²­â²®â²¯â²°â²±â²²â²³â²´â²µâ²¶â²·â²¸â²¹â²ºâ²»â²¼â²½â²¾â²¿ +â³€â³â³‚ⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌâ³â³Žâ³â³â³‘ⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜâ³â³žâ³Ÿâ³ â³¡â³¢â³£â³¤â³¥â³¦â³§â³¨â³©â³ªâ³«â³¬â³­â³®â³¯â³°â³±â³²â³³â³´â³µâ³¶â³·â³¸â³¹â³ºâ³»â³¼â³½â³¾â³¿ +â´€â´â´‚ⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌâ´â´Žâ´â´â´‘ⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜâ´â´žâ´Ÿâ´ â´¡â´¢â´£â´¤â´¥â´¦â´§â´¨â´©â´ªâ´«â´¬â´­â´®â´¯â´°â´±â´²â´³â´´â´µâ´¶â´·â´¸â´¹â´ºâ´»â´¼â´½â´¾â´¿ +âµ€âµâµ‚ⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌâµâµŽâµâµâµ‘ⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜâµâµžâµŸâµ âµ¡âµ¢âµ£âµ¤âµ¥âµ¦âµ§âµ¨âµ©âµªâµ«âµ¬âµ­âµ®âµ¯âµ°âµ±âµ²âµ³âµ´âµµâµ¶âµ·âµ¸âµ¹âµºâµ»âµ¼âµ½âµ¾âµ¿ +ⶀâ¶â¶‚ⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌâ¶â¶Žâ¶â¶â¶‘ⶒⶓⶔⶕⶖ⶗⶘⶙⶚⶛⶜â¶â¶žâ¶Ÿâ¶ â¶¡â¶¢â¶£â¶¤â¶¥â¶¦â¶§â¶¨â¶©â¶ªâ¶«â¶¬â¶­â¶®â¶¯â¶°â¶±â¶²â¶³â¶´â¶µâ¶¶â¶·â¶¸â¶¹â¶ºâ¶»â¶¼â¶½â¶¾â¶¿ +â·€â·â·‚ⷃⷄⷅⷆ⷇ⷈⷉⷊⷋⷌâ·â·Žâ·â·â·‘ⷒⷓⷔⷕⷖ⷗ⷘⷙⷚⷛⷜâ·â·žâ·Ÿâ· â·¡â·¢â·£â·¤â·¥â·¦â·§â·¨â·©â·ªâ·«â·¬â·­â·®â·¯â·°â·±â·²â·³â·´â·µâ·¶â··â·¸â·¹â·ºâ·»â·¼â·½â·¾â·¿ +⸀â¸â¸‚⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌â¸â¸Žâ¸â¸â¸‘⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜â¸â¸žâ¸Ÿâ¸ â¸¡â¸¢â¸£â¸¤â¸¥â¸¦â¸§â¸¨â¸©â¸ªâ¸«â¸¬â¸­â¸®â¸¯â¸°â¸±â¸²â¸³â¸´â¸µâ¸¶â¸·â¸¸â¸¹â¸ºâ¸»â¸¼â¸½â¸¾â¸¿ +â¹€â¹â¹‚⹃⹄⹅⹆⹇⹈⹉⹊⹋⹌â¹â¹Žâ¹â¹â¹‘⹒⹓⹔⹕⹖⹗⹘⹙⹚⹛⹜â¹â¹žâ¹Ÿâ¹ â¹¡â¹¢â¹£â¹¤â¹¥â¹¦â¹§â¹¨â¹©â¹ªâ¹«â¹¬â¹­â¹®â¹¯â¹°â¹±â¹²â¹³â¹´â¹µâ¹¶â¹·â¹¸â¹¹â¹ºâ¹»â¹¼â¹½â¹¾â¹¿ + +CJK Radicals Supplement (U+2E80-U+2EFF): + +⺀âºâº‚⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌âºâºŽâºâºâº‘⺒⺓⺔⺕⺖⺗⺘⺙⺚⺛⺜âºâºžâºŸ +⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿ +⻀â»â»‚⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌â»â»Žâ»â»â»‘⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜â»â»žâ»Ÿ +⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⻴⻵⻶⻷⻸⻹⻺⻻⻼⻽⻾⻿ + +Kangxi Radicals (U+2F00-U+2FDF): + +â¼€â¼â¼‚⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌â¼â¼Žâ¼â¼â¼‘⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜â¼â¼žâ¼Ÿ +⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿ +â½€â½â½‚⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌â½â½Žâ½â½â½‘⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜â½â½žâ½Ÿ +⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿ +â¾€â¾â¾‚⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌â¾â¾Žâ¾â¾â¾‘⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜â¾â¾žâ¾Ÿ +⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿ +â¿€â¿â¿‚⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌â¿â¿Žâ¿â¿â¿‘⿒⿓⿔⿕⿖⿗⿘⿙⿚⿛⿜â¿â¿žâ¿Ÿ + +Free block (U+2FE0-U+2FEF): + +⿠⿡⿢⿣⿤⿥⿦⿧⿨⿩⿪⿫⿬⿭⿮⿯ + +Ideographic Description Characters (U+2FF0-U+2FFF): + +⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻⿼⿽⿾⿿ + +CJK Symbols and Punctuation (U+3000-U+303F): + + ã€ã€‚〃〄々〆〇〈〉《》「ã€ã€Žã€ã€ã€‘〒〓〔〕〖〗〘〙〚〛〜ã€ã€žã€Ÿ +〠〡〢〣〤〥〦〧〨〩◌〪◌〫◌〬◌〭◌〮◌〯〰〱〲〳〴〵〶〷〸〹〺〻〼〽〾〿 + +Hiragana (U+3040-U+309F): + +ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸ +ã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ +ã‚€ã‚もゃやゅゆょよらりるれã‚ã‚Žã‚ã‚ゑをんゔゕゖ゗゘◌゙◌゚゛゜ã‚ã‚žã‚Ÿ + +Katakana (U+30A0-U+30FF): + +゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタ +ダãƒãƒ‚ッツヅテデトドナニヌãƒãƒŽãƒãƒãƒ‘ヒビピフブプヘベペホボãƒãƒžãƒŸ +ムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ + +Bopomofo (U+3100-U+312F): + +ã„€ã„㄂㄃㄄ㄅㄆㄇㄈㄉㄊㄋㄌã„ã„Žã„ã„ㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜã„ã„žã„Ÿ +ㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄮㄯ + +Hangul Compatibility Jamo (U+3130-U+318F): + +㄰ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀã…ㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌã…ã…Žã… +ã…ㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜã…ㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯ +ㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀã†ã†‚ㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌã†ã†Žã† + +Kanbun (U+3190-U+319F): + +ã†ã†‘㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜ã†ã†žã†Ÿ + +Bopomofo Extended (U+31A0-U+31BF): + +ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷㆸㆹㆺㆻㆼㆽㆾㆿ + +Free block (U+31C0-U+31EF): + +㇀ã‡ã‡‚㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌ã‡ã‡Žã‡ã‡ã‡‘㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜ã‡ã‡žã‡Ÿ +㇠㇡㇢㇣㇤㇥㇦㇧㇨㇩㇪㇫㇬㇭㇮㇯ + +Katakana Phonetic Extensions (U+31F0-U+31FF): + +ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ + +Enclosed CJK Letters and Months (U+3200-U+32FF): + +㈀ãˆãˆ‚㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌ãˆãˆŽãˆãˆãˆ‘㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜ãˆãˆžãˆŸ +㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿ +㉀ã‰ã‰‚㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌ã‰ã‰Žã‰ã‰ã‰‘㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜ã‰ã‰žã‰Ÿ +㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿ +㊀ãŠãŠ‚㊃㊄㊅㊆㊇㊈㊉㊊㊋㊌ãŠãŠŽãŠãŠãŠ‘㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜ãŠãŠžãŠŸ +㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿ +ã‹€ã‹ã‹‚㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌ã‹ã‹Žã‹ã‹ã‹‘㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜ã‹ã‹žã‹Ÿ +㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㋿ + +CJK Compatibility (U+3300-U+33FF): + +㌀ãŒãŒ‚㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌ãŒãŒŽãŒãŒãŒ‘㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜ãŒãŒžãŒŸ +㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿ +ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸ +ã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ +㎀ãŽãŽ‚㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌ãŽãŽŽãŽãŽãŽ‘㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜ãŽãŽžãŽŸ +㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿ +ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸ +ã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ + +CJK Unified Ideographs Extension A (U+3400-U+4DBF): + +ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸ +ã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ +ã‘€ã‘㑂㑃㑄㑅㑆㑇㑈㑉㑊㑋㑌ã‘ã‘Žã‘ã‘㑑㑒㑓㑔㑕㑖㑗㑘㑙㑚㑛㑜ã‘ã‘žã‘Ÿ +㑠㑡㑢㑣㑤㑥㑦㑧㑨㑩㑪㑫㑬㑭㑮㑯㑰㑱㑲㑳㑴㑵㑶㑷㑸㑹㑺㑻㑼㑽㑾㑿 +ã’€ã’㒂㒃㒄㒅㒆㒇㒈㒉㒊㒋㒌ã’ã’Žã’ã’㒑㒒㒓㒔㒕㒖㒗㒘㒙㒚㒛㒜ã’ã’žã’Ÿ +㒠㒡㒢㒣㒤㒥㒦㒧㒨㒩㒪㒫㒬㒭㒮㒯㒰㒱㒲㒳㒴㒵㒶㒷㒸㒹㒺㒻㒼㒽㒾㒿 +ã“€ã“㓂㓃㓄㓅㓆㓇㓈㓉㓊㓋㓌ã“ã“Žã“ã“㓑㓒㓓㓔㓕㓖㓗㓘㓙㓚㓛㓜ã“ã“žã“Ÿ +㓠㓡㓢㓣㓤㓥㓦㓧㓨㓩㓪㓫㓬㓭㓮㓯㓰㓱㓲㓳㓴㓵㓶㓷㓸㓹㓺㓻㓼㓽㓾㓿 +㔀ã”㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌ã”㔎ã”ã”㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜ã”㔞㔟 +㔠㔡㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿 +ã•€ã•ã•‚㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌ã•ã•Žã•ã•ã•‘㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜ã•ã•žã•Ÿ +㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿 +ã–€ã–㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌ã–ã–Žã–ã–㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜ã–ã–žã–Ÿ +㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿 +ã—€ã—㗂㗃㗄㗅㗆㗇㗈㗉㗊㗋㗌ã—ã—Žã—ã—㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜ã—ã—žã—Ÿ +㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿 +㘀ã˜ã˜‚㘃㘄㘅㘆㘇㘈㘉㘊㘋㘌ã˜ã˜Žã˜ã˜ã˜‘㘒㘓㘔㘕㘖㘗㘘㘙㘚㘛㘜ã˜ã˜žã˜Ÿ +㘠㘡㘢㘣㘤㘥㘦㘧㘨㘩㘪㘫㘬㘭㘮㘯㘰㘱㘲㘳㘴㘵㘶㘷㘸㘹㘺㘻㘼㘽㘾㘿 +㙀ã™ã™‚㙃㙄㙅㙆㙇㙈㙉㙊㙋㙌ã™ã™Žã™ã™ã™‘㙒㙓㙔㙕㙖㙗㙘㙙㙚㙛㙜ã™ã™žã™Ÿ +㙠㙡㙢㙣㙤㙥㙦㙧㙨㙩㙪㙫㙬㙭㙮㙯㙰㙱㙲㙳㙴㙵㙶㙷㙸㙹㙺㙻㙼㙽㙾㙿 +㚀ãšãš‚㚃㚄㚅㚆㚇㚈㚉㚊㚋㚌ãšãšŽãšãšãš‘㚒㚓㚔㚕㚖㚗㚘㚙㚚㚛㚜ãšãšžãšŸ +㚠㚡㚢㚣㚤㚥㚦㚧㚨㚩㚪㚫㚬㚭㚮㚯㚰㚱㚲㚳㚴㚵㚶㚷㚸㚹㚺㚻㚼㚽㚾㚿 +㛀ã›ã›‚㛃㛄㛅㛆㛇㛈㛉㛊㛋㛌ã›ã›Žã›ã›ã›‘㛒㛓㛔㛕㛖㛗㛘㛙㛚㛛㛜ã›ã›žã›Ÿ +㛠㛡㛢㛣㛤㛥㛦㛧㛨㛩㛪㛫㛬㛭㛮㛯㛰㛱㛲㛳㛴㛵㛶㛷㛸㛹㛺㛻㛼㛽㛾㛿 +㜀ãœãœ‚㜃㜄㜅㜆㜇㜈㜉㜊㜋㜌ãœãœŽãœãœãœ‘㜒㜓㜔㜕㜖㜗㜘㜙㜚㜛㜜ãœãœžãœŸ +㜠㜡㜢㜣㜤㜥㜦㜧㜨㜩㜪㜫㜬㜭㜮㜯㜰㜱㜲㜳㜴㜵㜶㜷㜸㜹㜺㜻㜼㜽㜾㜿 +ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸ +ã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ +㞀ãžãž‚㞃㞄㞅㞆㞇㞈㞉㞊㞋㞌ãžãžŽãžãžãž‘㞒㞓㞔㞕㞖㞗㞘㞙㞚㞛㞜ãžãžžãžŸ +㞠㞡㞢㞣㞤㞥㞦㞧㞨㞩㞪㞫㞬㞭㞮㞯㞰㞱㞲㞳㞴㞵㞶㞷㞸㞹㞺㞻㞼㞽㞾㞿 +㟀ãŸãŸ‚㟃㟄㟅㟆㟇㟈㟉㟊㟋㟌ãŸãŸŽãŸãŸãŸ‘㟒㟓㟔㟕㟖㟗㟘㟙㟚㟛㟜ãŸãŸžãŸŸ +㟠㟡㟢㟣㟤㟥㟦㟧㟨㟩㟪㟫㟬㟭㟮㟯㟰㟱㟲㟳㟴㟵㟶㟷㟸㟹㟺㟻㟼㟽㟾㟿 +ã €ã ã ‚㠃㠄㠅㠆㠇㠈㠉㠊㠋㠌ã ã Žã ã ã ‘㠒㠓㠔㠕㠖㠗㠘㠙㠚㠛㠜ã ã žã Ÿ +㠠㠡㠢㠣㠤㠥㠦㠧㠨㠩㠪㠫㠬㠭㠮㠯㠰㠱㠲㠳㠴㠵㠶㠷㠸㠹㠺㠻㠼㠽㠾㠿 +ã¡€ã¡ã¡‚㡃㡄㡅㡆㡇㡈㡉㡊㡋㡌ã¡ã¡Žã¡ã¡ã¡‘㡒㡓㡔㡕㡖㡗㡘㡙㡚㡛㡜ã¡ã¡žã¡Ÿ +㡠㡡㡢㡣㡤㡥㡦㡧㡨㡩㡪㡫㡬㡭㡮㡯㡰㡱㡲㡳㡴㡵㡶㡷㡸㡹㡺㡻㡼㡽㡾㡿 +㢀ã¢ã¢‚㢃㢄㢅㢆㢇㢈㢉㢊㢋㢌ã¢ã¢Žã¢ã¢ã¢‘㢒㢓㢔㢕㢖㢗㢘㢙㢚㢛㢜ã¢ã¢žã¢Ÿ +㢠㢡㢢㢣㢤㢥㢦㢧㢨㢩㢪㢫㢬㢭㢮㢯㢰㢱㢲㢳㢴㢵㢶㢷㢸㢹㢺㢻㢼㢽㢾㢿 +㣀ã£ã£‚㣃㣄㣅㣆㣇㣈㣉㣊㣋㣌ã£ã£Žã£ã£ã£‘㣒㣓㣔㣕㣖㣗㣘㣙㣚㣛㣜ã£ã£žã£Ÿ +㣠㣡㣢㣣㣤㣥㣦㣧㣨㣩㣪㣫㣬㣭㣮㣯㣰㣱㣲㣳㣴㣵㣶㣷㣸㣹㣺㣻㣼㣽㣾㣿 +㤀ã¤ã¤‚㤃㤄㤅㤆㤇㤈㤉㤊㤋㤌ã¤ã¤Žã¤ã¤ã¤‘㤒㤓㤔㤕㤖㤗㤘㤙㤚㤛㤜ã¤ã¤žã¤Ÿ +㤠㤡㤢㤣㤤㤥㤦㤧㤨㤩㤪㤫㤬㤭㤮㤯㤰㤱㤲㤳㤴㤵㤶㤷㤸㤹㤺㤻㤼㤽㤾㤿 +㥀ã¥ã¥‚㥃㥄㥅㥆㥇㥈㥉㥊㥋㥌ã¥ã¥Žã¥ã¥ã¥‘㥒㥓㥔㥕㥖㥗㥘㥙㥚㥛㥜ã¥ã¥žã¥Ÿ +㥠㥡㥢㥣㥤㥥㥦㥧㥨㥩㥪㥫㥬㥭㥮㥯㥰㥱㥲㥳㥴㥵㥶㥷㥸㥹㥺㥻㥼㥽㥾㥿 +㦀ã¦ã¦‚㦃㦄㦅㦆㦇㦈㦉㦊㦋㦌ã¦ã¦Žã¦ã¦ã¦‘㦒㦓㦔㦕㦖㦗㦘㦙㦚㦛㦜ã¦ã¦žã¦Ÿ +㦠㦡㦢㦣㦤㦥㦦㦧㦨㦩㦪㦫㦬㦭㦮㦯㦰㦱㦲㦳㦴㦵㦶㦷㦸㦹㦺㦻㦼㦽㦾㦿 +㧀ã§ã§‚㧃㧄㧅㧆㧇㧈㧉㧊㧋㧌ã§ã§Žã§ã§ã§‘㧒㧓㧔㧕㧖㧗㧘㧙㧚㧛㧜ã§ã§žã§Ÿ +㧠㧡㧢㧣㧤㧥㧦㧧㧨㧩㧪㧫㧬㧭㧮㧯㧰㧱㧲㧳㧴㧵㧶㧷㧸㧹㧺㧻㧼㧽㧾㧿 +㨀ã¨ã¨‚㨃㨄㨅㨆㨇㨈㨉㨊㨋㨌ã¨ã¨Žã¨ã¨ã¨‘㨒㨓㨔㨕㨖㨗㨘㨙㨚㨛㨜ã¨ã¨žã¨Ÿ +㨠㨡㨢㨣㨤㨥㨦㨧㨨㨩㨪㨫㨬㨭㨮㨯㨰㨱㨲㨳㨴㨵㨶㨷㨸㨹㨺㨻㨼㨽㨾㨿 +ã©€ã©ã©‚㩃㩄㩅㩆㩇㩈㩉㩊㩋㩌ã©ã©Žã©ã©ã©‘㩒㩓㩔㩕㩖㩗㩘㩙㩚㩛㩜ã©ã©žã©Ÿ +㩠㩡㩢㩣㩤㩥㩦㩧㩨㩩㩪㩫㩬㩭㩮㩯㩰㩱㩲㩳㩴㩵㩶㩷㩸㩹㩺㩻㩼㩽㩾㩿 +㪀ãªãª‚㪃㪄㪅㪆㪇㪈㪉㪊㪋㪌ãªãªŽãªãªãª‘㪒㪓㪔㪕㪖㪗㪘㪙㪚㪛㪜ãªãªžãªŸ +㪠㪡㪢㪣㪤㪥㪦㪧㪨㪩㪪㪫㪬㪭㪮㪯㪰㪱㪲㪳㪴㪵㪶㪷㪸㪹㪺㪻㪼㪽㪾㪿 +ã«€ã«ã«‚㫃㫄㫅㫆㫇㫈㫉㫊㫋㫌ã«ã«Žã«ã«ã«‘㫒㫓㫔㫕㫖㫗㫘㫙㫚㫛㫜ã«ã«žã«Ÿ +㫠㫡㫢㫣㫤㫥㫦㫧㫨㫩㫪㫫㫬㫭㫮㫯㫰㫱㫲㫳㫴㫵㫶㫷㫸㫹㫺㫻㫼㫽㫾㫿 +㬀ã¬ã¬‚㬃㬄㬅㬆㬇㬈㬉㬊㬋㬌ã¬ã¬Žã¬ã¬ã¬‘㬒㬓㬔㬕㬖㬗㬘㬙㬚㬛㬜ã¬ã¬žã¬Ÿ +㬠㬡㬢㬣㬤㬥㬦㬧㬨㬩㬪㬫㬬㬭㬮㬯㬰㬱㬲㬳㬴㬵㬶㬷㬸㬹㬺㬻㬼㬽㬾㬿 +ã­€ã­ã­‚㭃㭄㭅㭆㭇㭈㭉㭊㭋㭌ã­ã­Žã­ã­ã­‘㭒㭓㭔㭕㭖㭗㭘㭙㭚㭛㭜ã­ã­žã­Ÿ +㭠㭡㭢㭣㭤㭥㭦㭧㭨㭩㭪㭫㭬㭭㭮㭯㭰㭱㭲㭳㭴㭵㭶㭷㭸㭹㭺㭻㭼㭽㭾㭿 +㮀ã®ã®‚㮃㮄㮅㮆㮇㮈㮉㮊㮋㮌ã®ã®Žã®ã®ã®‘㮒㮓㮔㮕㮖㮗㮘㮙㮚㮛㮜ã®ã®žã®Ÿ +㮠㮡㮢㮣㮤㮥㮦㮧㮨㮩㮪㮫㮬㮭㮮㮯㮰㮱㮲㮳㮴㮵㮶㮷㮸㮹㮺㮻㮼㮽㮾㮿 +㯀ã¯ã¯‚㯃㯄㯅㯆㯇㯈㯉㯊㯋㯌ã¯ã¯Žã¯ã¯ã¯‘㯒㯓㯔㯕㯖㯗㯘㯙㯚㯛㯜ã¯ã¯žã¯Ÿ +㯠㯡㯢㯣㯤㯥㯦㯧㯨㯩㯪㯫㯬㯭㯮㯯㯰㯱㯲㯳㯴㯵㯶㯷㯸㯹㯺㯻㯼㯽㯾㯿 +ã°€ã°ã°‚㰃㰄㰅㰆㰇㰈㰉㰊㰋㰌ã°ã°Žã°ã°ã°‘㰒㰓㰔㰕㰖㰗㰘㰙㰚㰛㰜ã°ã°žã°Ÿ +㰠㰡㰢㰣㰤㰥㰦㰧㰨㰩㰪㰫㰬㰭㰮㰯㰰㰱㰲㰳㰴㰵㰶㰷㰸㰹㰺㰻㰼㰽㰾㰿 +ã±€ã±ã±‚㱃㱄㱅㱆㱇㱈㱉㱊㱋㱌ã±ã±Žã±ã±ã±‘㱒㱓㱔㱕㱖㱗㱘㱙㱚㱛㱜ã±ã±žã±Ÿ +㱠㱡㱢㱣㱤㱥㱦㱧㱨㱩㱪㱫㱬㱭㱮㱯㱰㱱㱲㱳㱴㱵㱶㱷㱸㱹㱺㱻㱼㱽㱾㱿 +ã²€ã²ã²‚㲃㲄㲅㲆㲇㲈㲉㲊㲋㲌ã²ã²Žã²ã²ã²‘㲒㲓㲔㲕㲖㲗㲘㲙㲚㲛㲜ã²ã²žã²Ÿ +㲠㲡㲢㲣㲤㲥㲦㲧㲨㲩㲪㲫㲬㲭㲮㲯㲰㲱㲲㲳㲴㲵㲶㲷㲸㲹㲺㲻㲼㲽㲾㲿 +ã³€ã³ã³‚㳃㳄㳅㳆㳇㳈㳉㳊㳋㳌ã³ã³Žã³ã³ã³‘㳒㳓㳔㳕㳖㳗㳘㳙㳚㳛㳜ã³ã³žã³Ÿ +㳠㳡㳢㳣㳤㳥㳦㳧㳨㳩㳪㳫㳬㳭㳮㳯㳰㳱㳲㳳㳴㳵㳶㳷㳸㳹㳺㳻㳼㳽㳾㳿 +ã´€ã´ã´‚㴃㴄㴅㴆㴇㴈㴉㴊㴋㴌ã´ã´Žã´ã´ã´‘㴒㴓㴔㴕㴖㴗㴘㴙㴚㴛㴜ã´ã´žã´Ÿ +㴠㴡㴢㴣㴤㴥㴦㴧㴨㴩㴪㴫㴬㴭㴮㴯㴰㴱㴲㴳㴴㴵㴶㴷㴸㴹㴺㴻㴼㴽㴾㴿 +ãµ€ãµãµ‚㵃㵄㵅㵆㵇㵈㵉㵊㵋㵌ãµãµŽãµãµãµ‘㵒㵓㵔㵕㵖㵗㵘㵙㵚㵛㵜ãµãµžãµŸ +㵠㵡㵢㵣㵤㵥㵦㵧㵨㵩㵪㵫㵬㵭㵮㵯㵰㵱㵲㵳㵴㵵㵶㵷㵸㵹㵺㵻㵼㵽㵾㵿 +㶀ã¶ã¶‚㶃㶄㶅㶆㶇㶈㶉㶊㶋㶌ã¶ã¶Žã¶ã¶ã¶‘㶒㶓㶔㶕㶖㶗㶘㶙㶚㶛㶜ã¶ã¶žã¶Ÿ +㶠㶡㶢㶣㶤㶥㶦㶧㶨㶩㶪㶫㶬㶭㶮㶯㶰㶱㶲㶳㶴㶵㶶㶷㶸㶹㶺㶻㶼㶽㶾㶿 +ã·€ã·ã·‚㷃㷄㷅㷆㷇㷈㷉㷊㷋㷌ã·ã·Žã·ã·ã·‘㷒㷓㷔㷕㷖㷗㷘㷙㷚㷛㷜ã·ã·žã·Ÿ +㷠㷡㷢㷣㷤㷥㷦㷧㷨㷩㷪㷫㷬㷭㷮㷯㷰㷱㷲㷳㷴㷵㷶㷷㷸㷹㷺㷻㷼㷽㷾㷿 +㸀ã¸ã¸‚㸃㸄㸅㸆㸇㸈㸉㸊㸋㸌ã¸ã¸Žã¸ã¸ã¸‘㸒㸓㸔㸕㸖㸗㸘㸙㸚㸛㸜ã¸ã¸žã¸Ÿ +㸠㸡㸢㸣㸤㸥㸦㸧㸨㸩㸪㸫㸬㸭㸮㸯㸰㸱㸲㸳㸴㸵㸶㸷㸸㸹㸺㸻㸼㸽㸾㸿 +ã¹€ã¹ã¹‚㹃㹄㹅㹆㹇㹈㹉㹊㹋㹌ã¹ã¹Žã¹ã¹ã¹‘㹒㹓㹔㹕㹖㹗㹘㹙㹚㹛㹜ã¹ã¹žã¹Ÿ +㹠㹡㹢㹣㹤㹥㹦㹧㹨㹩㹪㹫㹬㹭㹮㹯㹰㹱㹲㹳㹴㹵㹶㹷㹸㹹㹺㹻㹼㹽㹾㹿 +㺀ãºãº‚㺃㺄㺅㺆㺇㺈㺉㺊㺋㺌ãºãºŽãºãºãº‘㺒㺓㺔㺕㺖㺗㺘㺙㺚㺛㺜ãºãºžãºŸ +㺠㺡㺢㺣㺤㺥㺦㺧㺨㺩㺪㺫㺬㺭㺮㺯㺰㺱㺲㺳㺴㺵㺶㺷㺸㺹㺺㺻㺼㺽㺾㺿 +㻀ã»ã»‚㻃㻄㻅㻆㻇㻈㻉㻊㻋㻌ã»ã»Žã»ã»ã»‘㻒㻓㻔㻕㻖㻗㻘㻙㻚㻛㻜ã»ã»žã»Ÿ +㻠㻡㻢㻣㻤㻥㻦㻧㻨㻩㻪㻫㻬㻭㻮㻯㻰㻱㻲㻳㻴㻵㻶㻷㻸㻹㻺㻻㻼㻽㻾㻿 +ã¼€ã¼ã¼‚㼃㼄㼅㼆㼇㼈㼉㼊㼋㼌ã¼ã¼Žã¼ã¼ã¼‘㼒㼓㼔㼕㼖㼗㼘㼙㼚㼛㼜ã¼ã¼žã¼Ÿ +㼠㼡㼢㼣㼤㼥㼦㼧㼨㼩㼪㼫㼬㼭㼮㼯㼰㼱㼲㼳㼴㼵㼶㼷㼸㼹㼺㼻㼼㼽㼾㼿 +ã½€ã½ã½‚㽃㽄㽅㽆㽇㽈㽉㽊㽋㽌ã½ã½Žã½ã½ã½‘㽒㽓㽔㽕㽖㽗㽘㽙㽚㽛㽜ã½ã½žã½Ÿ +㽠㽡㽢㽣㽤㽥㽦㽧㽨㽩㽪㽫㽬㽭㽮㽯㽰㽱㽲㽳㽴㽵㽶㽷㽸㽹㽺㽻㽼㽽㽾㽿 +ã¾€ã¾ã¾‚㾃㾄㾅㾆㾇㾈㾉㾊㾋㾌ã¾ã¾Žã¾ã¾ã¾‘㾒㾓㾔㾕㾖㾗㾘㾙㾚㾛㾜ã¾ã¾žã¾Ÿ +㾠㾡㾢㾣㾤㾥㾦㾧㾨㾩㾪㾫㾬㾭㾮㾯㾰㾱㾲㾳㾴㾵㾶㾷㾸㾹㾺㾻㾼㾽㾾㾿 +ã¿€ã¿ã¿‚㿃㿄㿅㿆㿇㿈㿉㿊㿋㿌ã¿ã¿Žã¿ã¿ã¿‘㿒㿓㿔㿕㿖㿗㿘㿙㿚㿛㿜ã¿ã¿žã¿Ÿ +㿠㿡㿢㿣㿤㿥㿦㿧㿨㿩㿪㿫㿬㿭㿮㿯㿰㿱㿲㿳㿴㿵㿶㿷㿸㿹㿺㿻㿼㿽㿾㿿 +䀀ä€ä€‚䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌ä€ä€Žä€ä€ä€‘䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜ä€ä€žä€Ÿ +䀠䀡䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿 +ä€ää‚äƒä„ä…ä†ä‡äˆä‰äŠä‹äŒääŽäää‘ä’ä“ä”ä•ä–ä—ä˜ä™äšä›äœääžäŸ +ä ä¡ä¢ä£ä¤ä¥ä¦ä§ä¨ä©äªä«ä¬ä­ä®ä¯ä°ä±ä²ä³ä´äµä¶ä·ä¸ä¹äºä»ä¼ä½ä¾ä¿ +ä‚€ä‚䂂䂃䂄䂅䂆䂇䂈䂉䂊䂋䂌ä‚ä‚Žä‚ä‚䂑䂒䂓䂔䂕䂖䂗䂘䂙䂚䂛䂜ä‚ä‚žä‚Ÿ +䂠䂡䂢䂣䂤䂥䂦䂧䂨䂩䂪䂫䂬䂭䂮䂯䂰䂱䂲䂳䂴䂵䂶䂷䂸䂹䂺䂻䂼䂽䂾䂿 +䃀äƒäƒ‚䃃䃄䃅䃆䃇䃈䃉䃊䃋䃌äƒäƒŽäƒäƒäƒ‘䃒䃓䃔䃕䃖䃗䃘䃙䃚䃛䃜äƒäƒžäƒŸ +䃠䃡䃢䃣䃤䃥䃦䃧䃨䃩䃪䃫䃬䃭䃮䃯䃰䃱䃲䃳䃴䃵䃶䃷䃸䃹䃺䃻䃼䃽䃾䃿 +ä„€ä„䄂䄃䄄䄅䄆䄇䄈䄉䄊䄋䄌ä„ä„Žä„ä„䄑䄒䄓䄔䄕䄖䄗䄘䄙䄚䄛䄜ä„ä„žä„Ÿ +䄠䄡䄢䄣䄤䄥䄦䄧䄨䄩䄪䄫䄬䄭䄮䄯䄰䄱䄲䄳䄴䄵䄶䄷䄸䄹䄺䄻䄼䄽䄾䄿 +ä…€ä…䅂䅃䅄䅅䅆䅇䅈䅉䅊䅋䅌ä…ä…Žä…ä…䅑䅒䅓䅔䅕䅖䅗䅘䅙䅚䅛䅜ä…ä…žä…Ÿ +䅠䅡䅢䅣䅤䅥䅦䅧䅨䅩䅪䅫䅬䅭䅮䅯䅰䅱䅲䅳䅴䅵䅶䅷䅸䅹䅺䅻䅼䅽䅾䅿 +䆀ä†ä†‚䆃䆄䆅䆆䆇䆈䆉䆊䆋䆌ä†ä†Žä†ä†ä†‘䆒䆓䆔䆕䆖䆗䆘䆙䆚䆛䆜ä†ä†žä†Ÿ +䆠䆡䆢䆣䆤䆥䆦䆧䆨䆩䆪䆫䆬䆭䆮䆯䆰䆱䆲䆳䆴䆵䆶䆷䆸䆹䆺䆻䆼䆽䆾䆿 +䇀ä‡ä‡‚䇃䇄䇅䇆䇇䇈䇉䇊䇋䇌ä‡ä‡Žä‡ä‡ä‡‘䇒䇓䇔䇕䇖䇗䇘䇙䇚䇛䇜ä‡ä‡žä‡Ÿ +䇠䇡䇢䇣䇤䇥䇦䇧䇨䇩䇪䇫䇬䇭䇮䇯䇰䇱䇲䇳䇴䇵䇶䇷䇸䇹䇺䇻䇼䇽䇾䇿 +䈀äˆäˆ‚䈃䈄䈅䈆䈇䈈䈉䈊䈋䈌äˆäˆŽäˆäˆäˆ‘䈒䈓䈔䈕䈖䈗䈘䈙䈚䈛䈜äˆäˆžäˆŸ +䈠䈡䈢䈣䈤䈥䈦䈧䈨䈩䈪䈫䈬䈭䈮䈯䈰䈱䈲䈳䈴䈵䈶䈷䈸䈹䈺䈻䈼䈽䈾䈿 +䉀ä‰ä‰‚䉃䉄䉅䉆䉇䉈䉉䉊䉋䉌ä‰ä‰Žä‰ä‰ä‰‘䉒䉓䉔䉕䉖䉗䉘䉙䉚䉛䉜ä‰ä‰žä‰Ÿ +䉠䉡䉢䉣䉤䉥䉦䉧䉨䉩䉪䉫䉬䉭䉮䉯䉰䉱䉲䉳䉴䉵䉶䉷䉸䉹䉺䉻䉼䉽䉾䉿 +䊀äŠäŠ‚䊃䊄䊅䊆䊇䊈䊉䊊䊋䊌äŠäŠŽäŠäŠäŠ‘䊒䊓䊔䊕䊖䊗䊘䊙䊚䊛䊜äŠäŠžäŠŸ +䊠䊡䊢䊣䊤䊥䊦䊧䊨䊩䊪䊫䊬䊭䊮䊯䊰䊱䊲䊳䊴䊵䊶䊷䊸䊹䊺䊻䊼䊽䊾䊿 +ä‹€ä‹ä‹‚䋃䋄䋅䋆䋇䋈䋉䋊䋋䋌ä‹ä‹Žä‹ä‹ä‹‘䋒䋓䋔䋕䋖䋗䋘䋙䋚䋛䋜ä‹ä‹žä‹Ÿ +䋠䋡䋢䋣䋤䋥䋦䋧䋨䋩䋪䋫䋬䋭䋮䋯䋰䋱䋲䋳䋴䋵䋶䋷䋸䋹䋺䋻䋼䋽䋾䋿 +䌀äŒäŒ‚䌃䌄䌅䌆䌇䌈䌉䌊䌋䌌äŒäŒŽäŒäŒäŒ‘䌒䌓䌔䌕䌖䌗䌘䌙䌚䌛䌜äŒäŒžäŒŸ +䌠䌡䌢䌣䌤䌥䌦䌧䌨䌩䌪䌫䌬䌭䌮䌯䌰䌱䌲䌳䌴䌵䌶䌷䌸䌹䌺䌻䌼䌽䌾䌿 +ä€ää‚äƒä„ä…ä†ä‡äˆä‰äŠä‹äŒääŽäää‘ä’ä“ä”ä•ä–ä—ä˜ä™äšä›äœääžäŸ +ä ä¡ä¢ä£ä¤ä¥ä¦ä§ä¨ä©äªä«ä¬ä­ä®ä¯ä°ä±ä²ä³ä´äµä¶ä·ä¸ä¹äºä»ä¼ä½ä¾ä¿ +䎀äŽäŽ‚䎃䎄䎅䎆䎇䎈䎉䎊䎋䎌äŽäŽŽäŽäŽäŽ‘䎒䎓䎔䎕䎖䎗䎘䎙䎚䎛䎜äŽäŽžäŽŸ +䎠䎡䎢䎣䎤䎥䎦䎧䎨䎩䎪䎫䎬䎭䎮䎯䎰䎱䎲䎳䎴䎵䎶䎷䎸䎹䎺䎻䎼䎽䎾䎿 +ä€ää‚äƒä„ä…ä†ä‡äˆä‰äŠä‹äŒääŽäää‘ä’ä“ä”ä•ä–ä—ä˜ä™äšä›äœääžäŸ +ä ä¡ä¢ä£ä¤ä¥ä¦ä§ä¨ä©äªä«ä¬ä­ä®ä¯ä°ä±ä²ä³ä´äµä¶ä·ä¸ä¹äºä»ä¼ä½ä¾ä¿ +ä€ää‚äƒä„ä…ä†ä‡äˆä‰äŠä‹äŒääŽäää‘ä’ä“ä”ä•ä–ä—ä˜ä™äšä›äœääžäŸ +ä ä¡ä¢ä£ä¤ä¥ä¦ä§ä¨ä©äªä«ä¬ä­ä®ä¯ä°ä±ä²ä³ä´äµä¶ä·ä¸ä¹äºä»ä¼ä½ä¾ä¿ +ä‘€ä‘䑂䑃䑄䑅䑆䑇䑈䑉䑊䑋䑌ä‘ä‘Žä‘ä‘䑑䑒䑓䑔䑕䑖䑗䑘䑙䑚䑛䑜ä‘ä‘žä‘Ÿ +䑠䑡䑢䑣䑤䑥䑦䑧䑨䑩䑪䑫䑬䑭䑮䑯䑰䑱䑲䑳䑴䑵䑶䑷䑸䑹䑺䑻䑼䑽䑾䑿 +ä’€ä’䒂䒃䒄䒅䒆䒇䒈䒉䒊䒋䒌ä’ä’Žä’ä’䒑䒒䒓䒔䒕䒖䒗䒘䒙䒚䒛䒜ä’ä’žä’Ÿ +䒠䒡䒢䒣䒤䒥䒦䒧䒨䒩䒪䒫䒬䒭䒮䒯䒰䒱䒲䒳䒴䒵䒶䒷䒸䒹䒺䒻䒼䒽䒾䒿 +ä“€ä“䓂䓃䓄䓅䓆䓇䓈䓉䓊䓋䓌ä“ä“Žä“ä“䓑䓒䓓䓔䓕䓖䓗䓘䓙䓚䓛䓜ä“ä“žä“Ÿ +䓠䓡䓢䓣䓤䓥䓦䓧䓨䓩䓪䓫䓬䓭䓮䓯䓰䓱䓲䓳䓴䓵䓶䓷䓸䓹䓺䓻䓼䓽䓾䓿 +䔀ä”䔂䔃䔄䔅䔆䔇䔈䔉䔊䔋䔌ä”䔎ä”ä”䔑䔒䔓䔔䔕䔖䔗䔘䔙䔚䔛䔜ä”䔞䔟 +䔠䔡䔢䔣䔤䔥䔦䔧䔨䔩䔪䔫䔬䔭䔮䔯䔰䔱䔲䔳䔴䔵䔶䔷䔸䔹䔺䔻䔼䔽䔾䔿 +ä•€ä•ä•‚䕃䕄䕅䕆䕇䕈䕉䕊䕋䕌ä•ä•Žä•ä•ä•‘䕒䕓䕔䕕䕖䕗䕘䕙䕚䕛䕜ä•ä•žä•Ÿ +䕠䕡䕢䕣䕤䕥䕦䕧䕨䕩䕪䕫䕬䕭䕮䕯䕰䕱䕲䕳䕴䕵䕶䕷䕸䕹䕺䕻䕼䕽䕾䕿 +ä–€ä–䖂䖃䖄䖅䖆䖇䖈䖉䖊䖋䖌ä–ä–Žä–ä–䖑䖒䖓䖔䖕䖖䖗䖘䖙䖚䖛䖜ä–ä–žä–Ÿ +䖠䖡䖢䖣䖤䖥䖦䖧䖨䖩䖪䖫䖬䖭䖮䖯䖰䖱䖲䖳䖴䖵䖶䖷䖸䖹䖺䖻䖼䖽䖾䖿 +ä—€ä—䗂䗃䗄䗅䗆䗇䗈䗉䗊䗋䗌ä—ä—Žä—ä—䗑䗒䗓䗔䗕䗖䗗䗘䗙䗚䗛䗜ä—ä—žä—Ÿ +䗠䗡䗢䗣䗤䗥䗦䗧䗨䗩䗪䗫䗬䗭䗮䗯䗰䗱䗲䗳䗴䗵䗶䗷䗸䗹䗺䗻䗼䗽䗾䗿 +䘀ä˜ä˜‚䘃䘄䘅䘆䘇䘈䘉䘊䘋䘌ä˜ä˜Žä˜ä˜ä˜‘䘒䘓䘔䘕䘖䘗䘘䘙䘚䘛䘜ä˜ä˜žä˜Ÿ +䘠䘡䘢䘣䘤䘥䘦䘧䘨䘩䘪䘫䘬䘭䘮䘯䘰䘱䘲䘳䘴䘵䘶䘷䘸䘹䘺䘻䘼䘽䘾䘿 +䙀ä™ä™‚䙃䙄䙅䙆䙇䙈䙉䙊䙋䙌ä™ä™Žä™ä™ä™‘䙒䙓䙔䙕䙖䙗䙘䙙䙚䙛䙜ä™ä™žä™Ÿ +䙠䙡䙢䙣䙤䙥䙦䙧䙨䙩䙪䙫䙬䙭䙮䙯䙰䙱䙲䙳䙴䙵䙶䙷䙸䙹䙺䙻䙼䙽䙾䙿 +䚀äšäš‚䚃䚄䚅䚆䚇䚈䚉䚊䚋䚌äšäšŽäšäšäš‘䚒䚓䚔䚕䚖䚗䚘䚙䚚䚛䚜äšäšžäšŸ +䚠䚡䚢䚣䚤䚥䚦䚧䚨䚩䚪䚫䚬䚭䚮䚯䚰䚱䚲䚳䚴䚵䚶䚷䚸䚹䚺䚻䚼䚽䚾䚿 +䛀ä›ä›‚䛃䛄䛅䛆䛇䛈䛉䛊䛋䛌ä›ä›Žä›ä›ä›‘䛒䛓䛔䛕䛖䛗䛘䛙䛚䛛䛜ä›ä›žä›Ÿ +䛠䛡䛢䛣䛤䛥䛦䛧䛨䛩䛪䛫䛬䛭䛮䛯䛰䛱䛲䛳䛴䛵䛶䛷䛸䛹䛺䛻䛼䛽䛾䛿 +䜀äœäœ‚䜃䜄䜅䜆䜇䜈䜉䜊䜋䜌äœäœŽäœäœäœ‘䜒䜓䜔䜕䜖䜗䜘䜙䜚䜛䜜äœäœžäœŸ +䜠䜡䜢䜣䜤䜥䜦䜧䜨䜩䜪䜫䜬䜭䜮䜯䜰䜱䜲䜳䜴䜵䜶䜷䜸䜹䜺䜻䜼䜽䜾䜿 +ä€ää‚äƒä„ä…ä†ä‡äˆä‰äŠä‹äŒääŽäää‘ä’ä“ä”ä•ä–ä—ä˜ä™äšä›äœääžäŸ +ä ä¡ä¢ä£ä¤ä¥ä¦ä§ä¨ä©äªä«ä¬ä­ä®ä¯ä°ä±ä²ä³ä´äµä¶ä·ä¸ä¹äºä»ä¼ä½ä¾ä¿ +䞀äžäž‚䞃䞄䞅䞆䞇䞈䞉䞊䞋䞌äžäžŽäžäžäž‘䞒䞓䞔䞕䞖䞗䞘䞙䞚䞛䞜äžäžžäžŸ +䞠䞡䞢䞣䞤䞥䞦䞧䞨䞩䞪䞫䞬䞭䞮䞯䞰䞱䞲䞳䞴䞵䞶䞷䞸䞹䞺䞻䞼䞽䞾䞿 +䟀äŸäŸ‚䟃䟄䟅䟆䟇䟈䟉䟊䟋䟌äŸäŸŽäŸäŸäŸ‘䟒䟓䟔䟕䟖䟗䟘䟙䟚䟛䟜äŸäŸžäŸŸ +䟠䟡䟢䟣䟤䟥䟦䟧䟨䟩䟪䟫䟬䟭䟮䟯䟰䟱䟲䟳䟴䟵䟶䟷䟸䟹䟺䟻䟼䟽䟾䟿 +ä €ä ä ‚䠃䠄䠅䠆䠇䠈䠉䠊䠋䠌ä ä Žä ä ä ‘䠒䠓䠔䠕䠖䠗䠘䠙䠚䠛䠜ä ä žä Ÿ +䠠䠡䠢䠣䠤䠥䠦䠧䠨䠩䠪䠫䠬䠭䠮䠯䠰䠱䠲䠳䠴䠵䠶䠷䠸䠹䠺䠻䠼䠽䠾䠿 +ä¡€ä¡ä¡‚䡃䡄䡅䡆䡇䡈䡉䡊䡋䡌ä¡ä¡Žä¡ä¡ä¡‘䡒䡓䡔䡕䡖䡗䡘䡙䡚䡛䡜ä¡ä¡žä¡Ÿ +䡠䡡䡢䡣䡤䡥䡦䡧䡨䡩䡪䡫䡬䡭䡮䡯䡰䡱䡲䡳䡴䡵䡶䡷䡸䡹䡺䡻䡼䡽䡾䡿 +䢀ä¢ä¢‚䢃䢄䢅䢆䢇䢈䢉䢊䢋䢌ä¢ä¢Žä¢ä¢ä¢‘䢒䢓䢔䢕䢖䢗䢘䢙䢚䢛䢜ä¢ä¢žä¢Ÿ +䢠䢡䢢䢣䢤䢥䢦䢧䢨䢩䢪䢫䢬䢭䢮䢯䢰䢱䢲䢳䢴䢵䢶䢷䢸䢹䢺䢻䢼䢽䢾䢿 +䣀ä£ä£‚䣃䣄䣅䣆䣇䣈䣉䣊䣋䣌ä£ä£Žä£ä£ä£‘䣒䣓䣔䣕䣖䣗䣘䣙䣚䣛䣜ä£ä£žä£Ÿ +䣠䣡䣢䣣䣤䣥䣦䣧䣨䣩䣪䣫䣬䣭䣮䣯䣰䣱䣲䣳䣴䣵䣶䣷䣸䣹䣺䣻䣼䣽䣾䣿 +䤀ä¤ä¤‚䤃䤄䤅䤆䤇䤈䤉䤊䤋䤌ä¤ä¤Žä¤ä¤ä¤‘䤒䤓䤔䤕䤖䤗䤘䤙䤚䤛䤜ä¤ä¤žä¤Ÿ +䤠䤡䤢䤣䤤䤥䤦䤧䤨䤩䤪䤫䤬䤭䤮䤯䤰䤱䤲䤳䤴䤵䤶䤷䤸䤹䤺䤻䤼䤽䤾䤿 +䥀ä¥ä¥‚䥃䥄䥅䥆䥇䥈䥉䥊䥋䥌ä¥ä¥Žä¥ä¥ä¥‘䥒䥓䥔䥕䥖䥗䥘䥙䥚䥛䥜ä¥ä¥žä¥Ÿ +䥠䥡䥢䥣䥤䥥䥦䥧䥨䥩䥪䥫䥬䥭䥮䥯䥰䥱䥲䥳䥴䥵䥶䥷䥸䥹䥺䥻䥼䥽䥾䥿 +䦀ä¦ä¦‚䦃䦄䦅䦆䦇䦈䦉䦊䦋䦌ä¦ä¦Žä¦ä¦ä¦‘䦒䦓䦔䦕䦖䦗䦘䦙䦚䦛䦜ä¦ä¦žä¦Ÿ +䦠䦡䦢䦣䦤䦥䦦䦧䦨䦩䦪䦫䦬䦭䦮䦯䦰䦱䦲䦳䦴䦵䦶䦷䦸䦹䦺䦻䦼䦽䦾䦿 +䧀ä§ä§‚䧃䧄䧅䧆䧇䧈䧉䧊䧋䧌ä§ä§Žä§ä§ä§‘䧒䧓䧔䧕䧖䧗䧘䧙䧚䧛䧜ä§ä§žä§Ÿ +䧠䧡䧢䧣䧤䧥䧦䧧䧨䧩䧪䧫䧬䧭䧮䧯䧰䧱䧲䧳䧴䧵䧶䧷䧸䧹䧺䧻䧼䧽䧾䧿 +䨀ä¨ä¨‚䨃䨄䨅䨆䨇䨈䨉䨊䨋䨌ä¨ä¨Žä¨ä¨ä¨‘䨒䨓䨔䨕䨖䨗䨘䨙䨚䨛䨜ä¨ä¨žä¨Ÿ +䨠䨡䨢䨣䨤䨥䨦䨧䨨䨩䨪䨫䨬䨭䨮䨯䨰䨱䨲䨳䨴䨵䨶䨷䨸䨹䨺䨻䨼䨽䨾䨿 +ä©€ä©ä©‚䩃䩄䩅䩆䩇䩈䩉䩊䩋䩌ä©ä©Žä©ä©ä©‘䩒䩓䩔䩕䩖䩗䩘䩙䩚䩛䩜ä©ä©žä©Ÿ +䩠䩡䩢䩣䩤䩥䩦䩧䩨䩩䩪䩫䩬䩭䩮䩯䩰䩱䩲䩳䩴䩵䩶䩷䩸䩹䩺䩻䩼䩽䩾䩿 +䪀äªäª‚䪃䪄䪅䪆䪇䪈䪉䪊䪋䪌äªäªŽäªäªäª‘䪒䪓䪔䪕䪖䪗䪘䪙䪚䪛䪜äªäªžäªŸ +䪠䪡䪢䪣䪤䪥䪦䪧䪨䪩䪪䪫䪬䪭䪮䪯䪰䪱䪲䪳䪴䪵䪶䪷䪸䪹䪺䪻䪼䪽䪾䪿 +ä«€ä«ä«‚䫃䫄䫅䫆䫇䫈䫉䫊䫋䫌ä«ä«Žä«ä«ä«‘䫒䫓䫔䫕䫖䫗䫘䫙䫚䫛䫜ä«ä«žä«Ÿ +䫠䫡䫢䫣䫤䫥䫦䫧䫨䫩䫪䫫䫬䫭䫮䫯䫰䫱䫲䫳䫴䫵䫶䫷䫸䫹䫺䫻䫼䫽䫾䫿 +䬀ä¬ä¬‚䬃䬄䬅䬆䬇䬈䬉䬊䬋䬌ä¬ä¬Žä¬ä¬ä¬‘䬒䬓䬔䬕䬖䬗䬘䬙䬚䬛䬜ä¬ä¬žä¬Ÿ +䬠䬡䬢䬣䬤䬥䬦䬧䬨䬩䬪䬫䬬䬭䬮䬯䬰䬱䬲䬳䬴䬵䬶䬷䬸䬹䬺䬻䬼䬽䬾䬿 +ä­€ä­ä­‚䭃䭄䭅䭆䭇䭈䭉䭊䭋䭌ä­ä­Žä­ä­ä­‘䭒䭓䭔䭕䭖䭗䭘䭙䭚䭛䭜ä­ä­žä­Ÿ +䭠䭡䭢䭣䭤䭥䭦䭧䭨䭩䭪䭫䭬䭭䭮䭯䭰䭱䭲䭳䭴䭵䭶䭷䭸䭹䭺䭻䭼䭽䭾䭿 +䮀ä®ä®‚䮃䮄䮅䮆䮇䮈䮉䮊䮋䮌ä®ä®Žä®ä®ä®‘䮒䮓䮔䮕䮖䮗䮘䮙䮚䮛䮜ä®ä®žä®Ÿ +䮠䮡䮢䮣䮤䮥䮦䮧䮨䮩䮪䮫䮬䮭䮮䮯䮰䮱䮲䮳䮴䮵䮶䮷䮸䮹䮺䮻䮼䮽䮾䮿 +䯀ä¯ä¯‚䯃䯄䯅䯆䯇䯈䯉䯊䯋䯌ä¯ä¯Žä¯ä¯ä¯‘䯒䯓䯔䯕䯖䯗䯘䯙䯚䯛䯜ä¯ä¯žä¯Ÿ +䯠䯡䯢䯣䯤䯥䯦䯧䯨䯩䯪䯫䯬䯭䯮䯯䯰䯱䯲䯳䯴䯵䯶䯷䯸䯹䯺䯻䯼䯽䯾䯿 +ä°€ä°ä°‚䰃䰄䰅䰆䰇䰈䰉䰊䰋䰌ä°ä°Žä°ä°ä°‘䰒䰓䰔䰕䰖䰗䰘䰙䰚䰛䰜ä°ä°žä°Ÿ +䰠䰡䰢䰣䰤䰥䰦䰧䰨䰩䰪䰫䰬䰭䰮䰯䰰䰱䰲䰳䰴䰵䰶䰷䰸䰹䰺䰻䰼䰽䰾䰿 +ä±€ä±ä±‚䱃䱄䱅䱆䱇䱈䱉䱊䱋䱌ä±ä±Žä±ä±ä±‘䱒䱓䱔䱕䱖䱗䱘䱙䱚䱛䱜ä±ä±žä±Ÿ +䱠䱡䱢䱣䱤䱥䱦䱧䱨䱩䱪䱫䱬䱭䱮䱯䱰䱱䱲䱳䱴䱵䱶䱷䱸䱹䱺䱻䱼䱽䱾䱿 +ä²€ä²ä²‚䲃䲄䲅䲆䲇䲈䲉䲊䲋䲌ä²ä²Žä²ä²ä²‘䲒䲓䲔䲕䲖䲗䲘䲙䲚䲛䲜ä²ä²žä²Ÿ +䲠䲡䲢䲣䲤䲥䲦䲧䲨䲩䲪䲫䲬䲭䲮䲯䲰䲱䲲䲳䲴䲵䲶䲷䲸䲹䲺䲻䲼䲽䲾䲿 +ä³€ä³ä³‚䳃䳄䳅䳆䳇䳈䳉䳊䳋䳌ä³ä³Žä³ä³ä³‘䳒䳓䳔䳕䳖䳗䳘䳙䳚䳛䳜ä³ä³žä³Ÿ +䳠䳡䳢䳣䳤䳥䳦䳧䳨䳩䳪䳫䳬䳭䳮䳯䳰䳱䳲䳳䳴䳵䳶䳷䳸䳹䳺䳻䳼䳽䳾䳿 +ä´€ä´ä´‚䴃䴄䴅䴆䴇䴈䴉䴊䴋䴌ä´ä´Žä´ä´ä´‘䴒䴓䴔䴕䴖䴗䴘䴙䴚䴛䴜ä´ä´žä´Ÿ +䴠䴡䴢䴣䴤䴥䴦䴧䴨䴩䴪䴫䴬䴭䴮䴯䴰䴱䴲䴳䴴䴵䴶䴷䴸䴹䴺䴻䴼䴽䴾䴿 +äµ€äµäµ‚䵃䵄䵅䵆䵇䵈䵉䵊䵋䵌äµäµŽäµäµäµ‘䵒䵓䵔䵕䵖䵗䵘䵙䵚䵛䵜äµäµžäµŸ +䵠䵡䵢䵣䵤䵥䵦䵧䵨䵩䵪䵫䵬䵭䵮䵯䵰䵱䵲䵳䵴䵵䵶䵷䵸䵹䵺䵻䵼䵽䵾䵿 +䶀ä¶ä¶‚䶃䶄䶅䶆䶇䶈䶉䶊䶋䶌ä¶ä¶Žä¶ä¶ä¶‘䶒䶓䶔䶕䶖䶗䶘䶙䶚䶛䶜ä¶ä¶žä¶Ÿ +䶠䶡䶢䶣䶤䶥䶦䶧䶨䶩䶪䶫䶬䶭䶮䶯䶰䶱䶲䶳䶴䶵䶶䶷䶸䶹䶺䶻䶼䶽䶾䶿 + +Yijing Hexagram Symbols (U+4DC0-U+4DFF): + +ä·€ä·ä·‚䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌ä·ä·Žä·ä·ä·‘䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜ä·ä·žä·Ÿ +䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿ + +CJK Unified Ideographs (U+4E00-U+9FFF): + +一ä¸ä¸‚七丄丅丆万丈三上下丌ä¸ä¸Žä¸ä¸ä¸‘丒专且丕世丗丘丙业丛东ä¸ä¸žä¸Ÿ +丠両丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿 +ä¹€ä¹ä¹‚乃乄久乆乇么义乊之乌ä¹ä¹Žä¹ä¹ä¹‘乒乓乔乕乖乗乘乙乚乛乜ä¹ä¹žä¹Ÿ +习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿 +亀äºäº‚亃亄亅了亇予争亊事二äºäºŽäºäºäº‘互亓五井亖亗亘亙亚些亜äºäºžäºŸ +亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿 +什ä»ä»‚仃仄仅仆仇仈仉今介仌ä»ä»Žä»ä»ä»‘仒仓仔仕他仗付仙仚仛仜ä»ä»žä»Ÿ +仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿 +ä¼€ä¼ä¼‚伃伄伅伆伇伈伉伊伋伌ä¼ä¼Žä¼ä¼ä¼‘伒伓伔伕伖众优伙会伛伜ä¼ä¼žä¼Ÿ +传伡伢伣伤伥伦伧伨伩伪伫伬伭伮伯估伱伲伳伴伵伶伷伸伹伺伻似伽伾伿 +ä½€ä½ä½‚佃佄佅但佇佈佉佊佋佌ä½ä½Žä½ä½ä½‘佒体佔何佖佗佘余佚佛作ä½ä½žä½Ÿ +你佡佢佣佤佥佦佧佨佩佪佫佬佭佮佯佰佱佲佳佴併佶佷佸佹佺佻佼佽佾使 +ä¾€ä¾ä¾‚侃侄侅來侇侈侉侊例侌ä¾ä¾Žä¾ä¾ä¾‘侒侓侔侕侖侗侘侙侚供侜ä¾ä¾žä¾Ÿ +侠価侢侣侤侥侦侧侨侩侪侫侬侭侮侯侰侱侲侳侴侵侶侷侸侹侺侻侼侽侾便 +ä¿€ä¿ä¿‚促俄俅俆俇俈俉俊俋俌ä¿ä¿Žä¿ä¿ä¿‘俒俓俔俕俖俗俘俙俚俛俜ä¿ä¿žä¿Ÿ +俠信俢俣俤俥俦俧俨俩俪俫俬俭修俯俰俱俲俳俴俵俶俷俸俹俺俻俼俽俾俿 +倀å€å€‚倃倄倅倆倇倈倉倊個倌å€å€Žå€å€å€‘倒倓倔倕倖倗倘候倚倛倜å€å€žå€Ÿ +倠倡倢倣値倥倦倧倨倩倪倫倬倭倮倯倰倱倲倳倴倵倶倷倸倹债倻值倽倾倿 +å€åå‚åƒå„å…å†å‡åˆå‰åŠå‹åŒååŽååå‘å’å“å”å•å–å—å˜å™åšå›åœååžåŸ +å å¡å¢å£å¤å¥å¦å§å¨å©åªå«å¬å­å®å¯å°å±å²å³å´åµå¶å·å¸å¹åºå»å¼å½å¾å¿ +å‚€å‚傂傃傄傅傆傇傈傉傊傋傌å‚å‚Žå‚å‚傑傒傓傔傕傖傗傘備傚傛傜å‚å‚žå‚Ÿ +傠傡傢傣傤傥傦傧储傩傪傫催傭傮傯傰傱傲傳傴債傶傷傸傹傺傻傼傽傾傿 +僀åƒåƒ‚僃僄僅僆僇僈僉僊僋僌åƒåƒŽåƒåƒåƒ‘僒僓僔僕僖僗僘僙僚僛僜åƒåƒžåƒŸ +僠僡僢僣僤僥僦僧僨僩僪僫僬僭僮僯僰僱僲僳僴僵僶僷僸價僺僻僼僽僾僿 +å„€å„儂儃億儅儆儇儈儉儊儋儌å„å„Žå„å„儑儒儓儔儕儖儗儘儙儚儛儜å„å„žå„Ÿ +儠儡儢儣儤儥儦儧儨儩優儫儬儭儮儯儰儱儲儳儴儵儶儷儸儹儺儻儼儽儾儿 +å…€å…兂元兄充兆兇先光兊克兌å…å…Žå…å…兑兒兓兔兕兖兗兘兙党兛兜å…å…žå…Ÿ +兠兡兢兣兤入兦內全兩兪八公六兮兯兰共兲关兴兵其具典兹兺养兼兽兾兿 +冀å†å†‚冃冄内円冇冈冉冊冋册å†å†Žå†å†å†‘冒冓冔冕冖冗冘写冚军农å†å†žå†Ÿ +冠冡冢冣冤冥冦冧冨冩冪冫冬冭冮冯冰冱冲决冴况冶冷冸冹冺冻冼冽冾冿 +净å‡å‡‚凃凄凅准凇凈凉凊凋凌å‡å‡Žå‡å‡å‡‘凒凓凔凕凖凗凘凙凚凛凜å‡å‡žå‡Ÿ +几凡凢凣凤凥処凧凨凩凪凫凬凭凮凯凰凱凲凳凴凵凶凷凸凹出击凼函凾凿 +刀åˆåˆ‚刃刄刅分切刈刉刊刋刌åˆåˆŽåˆåˆåˆ‘划刓刔刕刖列刘则刚创刜åˆåˆžåˆŸ +删刡刢刣判別刦刧刨利刪别刬刭刮刯到刱刲刳刴刵制刷券刹刺刻刼刽刾刿 +剀å‰å‰‚剃剄剅剆則剈剉削剋剌å‰å‰Žå‰å‰å‰‘剒剓剔剕剖剗剘剙剚剛剜å‰å‰žå‰Ÿ +剠剡剢剣剤剥剦剧剨剩剪剫剬剭剮副剰剱割剳剴創剶剷剸剹剺剻剼剽剾剿 +劀åŠåŠ‚劃劄劅劆劇劈劉劊劋劌åŠåŠŽåŠåŠåŠ‘劒劓劔劕劖劗劘劙劚力劜åŠåŠžåŠŸ +加务劢劣劤劥劦劧动助努劫劬劭劮劯劰励劲劳労劵劶劷劸効劺劻劼劽劾势 +å‹€å‹å‹‚勃勄勅勆勇勈勉勊勋勌å‹å‹Žå‹å‹å‹‘勒勓勔動勖勗勘務勚勛勜å‹å‹žå‹Ÿ +勠勡勢勣勤勥勦勧勨勩勪勫勬勭勮勯勰勱勲勳勴勵勶勷勸勹勺勻勼勽勾勿 +匀åŒåŒ‚匃匄包匆匇匈匉匊匋匌åŒåŒŽåŒåŒåŒ‘匒匓匔匕化北匘匙匚匛匜åŒåŒžåŒŸ +匠匡匢匣匤匥匦匧匨匩匪匫匬匭匮匯匰匱匲匳匴匵匶匷匸匹区医匼匽匾匿 +å€åå‚åƒå„å…å†å‡åˆå‰åŠå‹åŒååŽååå‘å’å“å”å•å–å—å˜å™åšå›åœååžåŸ +å å¡å¢å£å¤å¥å¦å§å¨å©åªå«å¬å­å®å¯å°å±å²å³å´åµå¶å·å¸å¹åºå»å¼å½å¾å¿ +厀åŽåŽ‚厃厄厅历厇厈厉厊压厌åŽåŽŽåŽåŽåŽ‘厒厓厔厕厖厗厘厙厚厛厜åŽåŽžåŽŸ +厠厡厢厣厤厥厦厧厨厩厪厫厬厭厮厯厰厱厲厳厴厵厶厷厸厹厺去厼厽厾县 +å€åå‚åƒå„å…å†å‡åˆå‰åŠå‹åŒååŽååå‘å’å“å”å•å–å—å˜å™åšå›åœååžåŸ +å å¡å¢å£å¤å¥å¦å§å¨å©åªå«å¬å­å®å¯å°å±å²å³å´åµå¶å·å¸å¹åºå»å¼å½å¾å¿ +å€åå‚åƒå„å…å†å‡åˆå‰åŠå‹åŒååŽååå‘å’å“å”å•å–å—å˜å™åšå›åœååžåŸ +å å¡å¢å£å¤å¥å¦å§å¨å©åªå«å¬å­å®å¯å°å±å²å³å´åµå¶å·å¸å¹åºå»å¼å½å¾å¿ +å‘€å‘呂呃呄呅呆呇呈呉告呋呌å‘å‘Žå‘å‘呑呒呓呔呕呖呗员呙呚呛呜å‘å‘žå‘Ÿ +呠呡呢呣呤呥呦呧周呩呪呫呬呭呮呯呰呱呲味呴呵呶呷呸呹呺呻呼命呾呿 +å’€å’咂咃咄咅咆咇咈咉咊咋和å’å’Žå’å’咑咒咓咔咕咖咗咘咙咚咛咜å’å’žå’Ÿ +咠咡咢咣咤咥咦咧咨咩咪咫咬咭咮咯咰咱咲咳咴咵咶咷咸咹咺咻咼咽咾咿 +å“€å“哂哃哄哅哆哇哈哉哊哋哌å“å“Žå“å“哑哒哓哔哕哖哗哘哙哚哛哜å“å“žå“Ÿ +哠員哢哣哤哥哦哧哨哩哪哫哬哭哮哯哰哱哲哳哴哵哶哷哸哹哺哻哼哽哾哿 +唀å”唂唃唄唅唆唇唈唉唊唋唌å”唎å”å”唑唒唓唔唕唖唗唘唙唚唛唜å”唞唟 +唠唡唢唣唤唥唦唧唨唩唪唫唬唭售唯唰唱唲唳唴唵唶唷唸唹唺唻唼唽唾唿 +å•€å•å•‚啃啄啅商啇啈啉啊啋啌å•å•Žå•å•å•‘啒啓啔啕啖啗啘啙啚啛啜å•å•žå•Ÿ +啠啡啢啣啤啥啦啧啨啩啪啫啬啭啮啯啰啱啲啳啴啵啶啷啸啹啺啻啼啽啾啿 +å–€å–喂喃善喅喆喇喈喉喊喋喌å–å–Žå–å–喑喒喓喔喕喖喗喘喙喚喛喜å–å–žå–Ÿ +喠喡喢喣喤喥喦喧喨喩喪喫喬喭單喯喰喱喲喳喴喵営喷喸喹喺喻喼喽喾喿 +å—€å—嗂嗃嗄嗅嗆嗇嗈嗉嗊嗋嗌å—å—Žå—å—嗑嗒嗓嗔嗕嗖嗗嗘嗙嗚嗛嗜å—å—žå—Ÿ +嗠嗡嗢嗣嗤嗥嗦嗧嗨嗩嗪嗫嗬嗭嗮嗯嗰嗱嗲嗳嗴嗵嗶嗷嗸嗹嗺嗻嗼嗽嗾嗿 +嘀å˜å˜‚嘃嘄嘅嘆嘇嘈嘉嘊嘋嘌å˜å˜Žå˜å˜å˜‘嘒嘓嘔嘕嘖嘗嘘嘙嘚嘛嘜å˜å˜žå˜Ÿ +嘠嘡嘢嘣嘤嘥嘦嘧嘨嘩嘪嘫嘬嘭嘮嘯嘰嘱嘲嘳嘴嘵嘶嘷嘸嘹嘺嘻嘼嘽嘾嘿 +噀å™å™‚噃噄噅噆噇噈噉噊噋噌å™å™Žå™å™å™‘噒噓噔噕噖噗噘噙噚噛噜å™å™žå™Ÿ +噠噡噢噣噤噥噦噧器噩噪噫噬噭噮噯噰噱噲噳噴噵噶噷噸噹噺噻噼噽噾噿 +嚀åšåš‚嚃嚄嚅嚆嚇嚈嚉嚊嚋嚌åšåšŽåšåšåš‘嚒嚓嚔嚕嚖嚗嚘嚙嚚嚛嚜åšåšžåšŸ +嚠嚡嚢嚣嚤嚥嚦嚧嚨嚩嚪嚫嚬嚭嚮嚯嚰嚱嚲嚳嚴嚵嚶嚷嚸嚹嚺嚻嚼嚽嚾嚿 +囀å›å›‚囃囄囅囆囇囈囉囊囋囌å›å›Žå›å›å›‘囒囓囔囕囖囗囘囙囚四囜å›å›žå›Ÿ +因囡团団囤囥囦囧囨囩囪囫囬园囮囯困囱囲図围囵囶囷囸囹固囻囼国图囿 +圀åœåœ‚圃圄圅圆圇圈圉圊國圌åœåœŽåœåœåœ‘園圓圔圕圖圗團圙圚圛圜åœåœžåœŸ +圠圡圢圣圤圥圦圧在圩圪圫圬圭圮圯地圱圲圳圴圵圶圷圸圹场圻圼圽圾圿 +å€åå‚åƒå„å…å†å‡åˆå‰åŠå‹åŒååŽååå‘å’å“å”å•å–å—å˜å™åšå›åœååžåŸ +å å¡å¢å£å¤å¥å¦å§å¨å©åªå«å¬å­å®å¯å°å±å²å³å´åµå¶å·å¸å¹åºå»å¼å½å¾å¿ +垀åžåž‚垃垄垅垆垇垈垉垊型垌åžåžŽåžåžåž‘垒垓垔垕垖垗垘垙垚垛垜åžåžžåžŸ +垠垡垢垣垤垥垦垧垨垩垪垫垬垭垮垯垰垱垲垳垴垵垶垷垸垹垺垻垼垽垾垿 +埀åŸåŸ‚埃埄埅埆埇埈埉埊埋埌åŸåŸŽåŸåŸåŸ‘埒埓埔埕埖埗埘埙埚埛埜åŸåŸžåŸŸ +埠埡埢埣埤埥埦埧埨埩埪埫埬埭埮埯埰埱埲埳埴埵埶執埸培基埻埼埽埾埿 +å €å å ‚堃堄堅堆堇堈堉堊堋堌å å Žå å å ‘堒堓堔堕堖堗堘堙堚堛堜å å žå Ÿ +堠堡堢堣堤堥堦堧堨堩堪堫堬堭堮堯堰報堲堳場堵堶堷堸堹堺堻堼堽堾堿 +å¡€å¡å¡‚塃塄塅塆塇塈塉塊塋塌å¡å¡Žå¡å¡å¡‘塒塓塔塕塖塗塘塙塚塛塜å¡å¡žå¡Ÿ +塠塡塢塣塤塥塦塧塨塩塪填塬塭塮塯塰塱塲塳塴塵塶塷塸塹塺塻塼塽塾塿 +墀å¢å¢‚境墄墅墆墇墈墉墊墋墌å¢å¢Žå¢å¢å¢‘墒墓墔墕墖増墘墙墚墛墜å¢å¢žå¢Ÿ +墠墡墢墣墤墥墦墧墨墩墪墫墬墭墮墯墰墱墲墳墴墵墶墷墸墹墺墻墼墽墾墿 +壀å£å£‚壃壄壅壆壇壈壉壊壋壌å£å£Žå£å£å£‘壒壓壔壕壖壗壘壙壚壛壜å£å£žå£Ÿ +壠壡壢壣壤壥壦壧壨壩壪士壬壭壮壯声壱売壳壴壵壶壷壸壹壺壻壼壽壾壿 +夀å¤å¤‚夃处夅夆备夈変夊夋夌å¤å¤Žå¤å¤å¤‘夒夓夔夕外夗夘夙多夛夜å¤å¤žå¤Ÿ +夠夡夢夣夤夥夦大夨天太夫夬夭央夯夰失夲夳头夵夶夷夸夹夺夻夼夽夾夿 +奀å¥å¥‚奃奄奅奆奇奈奉奊奋奌å¥å¥Žå¥å¥å¥‘奒奓奔奕奖套奘奙奚奛奜å¥å¥žå¥Ÿ +奠奡奢奣奤奥奦奧奨奩奪奫奬奭奮奯奰奱奲女奴奵奶奷奸她奺奻奼好奾奿 +妀å¦å¦‚妃妄妅妆妇妈妉妊妋妌å¦å¦Žå¦å¦å¦‘妒妓妔妕妖妗妘妙妚妛妜å¦å¦žå¦Ÿ +妠妡妢妣妤妥妦妧妨妩妪妫妬妭妮妯妰妱妲妳妴妵妶妷妸妹妺妻妼妽妾妿 +姀å§å§‚姃姄姅姆姇姈姉姊始姌å§å§Žå§å§å§‘姒姓委姕姖姗姘姙姚姛姜å§å§žå§Ÿ +姠姡姢姣姤姥姦姧姨姩姪姫姬姭姮姯姰姱姲姳姴姵姶姷姸姹姺姻姼姽姾姿 +娀å¨å¨‚娃娄娅娆娇娈娉娊娋娌å¨å¨Žå¨å¨å¨‘娒娓娔娕娖娗娘娙娚娛娜å¨å¨žå¨Ÿ +娠娡娢娣娤娥娦娧娨娩娪娫娬娭娮娯娰娱娲娳娴娵娶娷娸娹娺娻娼娽娾娿 +å©€å©å©‚婃婄婅婆婇婈婉婊婋婌å©å©Žå©å©å©‘婒婓婔婕婖婗婘婙婚婛婜å©å©žå©Ÿ +婠婡婢婣婤婥婦婧婨婩婪婫婬婭婮婯婰婱婲婳婴婵婶婷婸婹婺婻婼婽婾婿 +媀åªåª‚媃媄媅媆媇媈媉媊媋媌åªåªŽåªåªåª‘媒媓媔媕媖媗媘媙媚媛媜åªåªžåªŸ +媠媡媢媣媤媥媦媧媨媩媪媫媬媭媮媯媰媱媲媳媴媵媶媷媸媹媺媻媼媽媾媿 +å«€å«å«‚嫃嫄嫅嫆嫇嫈嫉嫊嫋嫌å«å«Žå«å«å«‘嫒嫓嫔嫕嫖嫗嫘嫙嫚嫛嫜å«å«žå«Ÿ +嫠嫡嫢嫣嫤嫥嫦嫧嫨嫩嫪嫫嫬嫭嫮嫯嫰嫱嫲嫳嫴嫵嫶嫷嫸嫹嫺嫻嫼嫽嫾嫿 +嬀å¬å¬‚嬃嬄嬅嬆嬇嬈嬉嬊嬋嬌å¬å¬Žå¬å¬å¬‘嬒嬓嬔嬕嬖嬗嬘嬙嬚嬛嬜å¬å¬žå¬Ÿ +嬠嬡嬢嬣嬤嬥嬦嬧嬨嬩嬪嬫嬬嬭嬮嬯嬰嬱嬲嬳嬴嬵嬶嬷嬸嬹嬺嬻嬼嬽嬾嬿 +å­€å­å­‚孃孄孅孆孇孈孉孊孋孌å­å­Žå­å­å­‘孒孓孔孕孖字存孙孚孛孜å­å­žå­Ÿ +孠孡孢季孤孥学孧孨孩孪孫孬孭孮孯孰孱孲孳孴孵孶孷學孹孺孻孼孽孾孿 +宀å®å®‚它宄宅宆宇守安宊宋完å®å®Žå®å®å®‘宒宓宔宕宖宗官宙定宛宜å®å®žå®Ÿ +宠审客宣室宥宦宧宨宩宪宫宬宭宮宯宰宱宲害宴宵家宷宸容宺宻宼宽宾宿 +寀å¯å¯‚寃寄寅密寇寈寉寊寋富å¯å¯Žå¯å¯å¯‘寒寓寔寕寖寗寘寙寚寛寜å¯å¯žå¯Ÿ +寠寡寢寣寤寥實寧寨審寪寫寬寭寮寯寰寱寲寳寴寵寶寷寸对寺寻导寽対寿 +å°€å°å°‚尃射尅将將專尉尊尋尌å°å°Žå°å°å°‘尒尓尔尕尖尗尘尙尚尛尜å°å°žå°Ÿ +尠尡尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹尺尻尼尽尾尿 +å±€å±å±‚屃屄居屆屇屈屉届屋屌å±å±Žå±å±å±‘屒屓屔展屖屗屘屙屚屛屜å±å±žå±Ÿ +屠屡屢屣層履屦屧屨屩屪屫屬屭屮屯屰山屲屳屴屵屶屷屸屹屺屻屼屽屾屿 +å²€å²å²‚岃岄岅岆岇岈岉岊岋岌å²å²Žå²å²å²‘岒岓岔岕岖岗岘岙岚岛岜å²å²žå²Ÿ +岠岡岢岣岤岥岦岧岨岩岪岫岬岭岮岯岰岱岲岳岴岵岶岷岸岹岺岻岼岽岾岿 +å³€å³å³‚峃峄峅峆峇峈峉峊峋峌å³å³Žå³å³å³‘峒峓峔峕峖峗峘峙峚峛峜å³å³žå³Ÿ +峠峡峢峣峤峥峦峧峨峩峪峫峬峭峮峯峰峱峲峳峴峵島峷峸峹峺峻峼峽峾峿 +å´€å´å´‚崃崄崅崆崇崈崉崊崋崌å´å´Žå´å´å´‘崒崓崔崕崖崗崘崙崚崛崜å´å´žå´Ÿ +崠崡崢崣崤崥崦崧崨崩崪崫崬崭崮崯崰崱崲崳崴崵崶崷崸崹崺崻崼崽崾崿 +åµ€åµåµ‚嵃嵄嵅嵆嵇嵈嵉嵊嵋嵌åµåµŽåµåµåµ‘嵒嵓嵔嵕嵖嵗嵘嵙嵚嵛嵜åµåµžåµŸ +嵠嵡嵢嵣嵤嵥嵦嵧嵨嵩嵪嵫嵬嵭嵮嵯嵰嵱嵲嵳嵴嵵嵶嵷嵸嵹嵺嵻嵼嵽嵾嵿 +嶀å¶å¶‚嶃嶄嶅嶆嶇嶈嶉嶊嶋嶌å¶å¶Žå¶å¶å¶‘嶒嶓嶔嶕嶖嶗嶘嶙嶚嶛嶜å¶å¶žå¶Ÿ +嶠嶡嶢嶣嶤嶥嶦嶧嶨嶩嶪嶫嶬嶭嶮嶯嶰嶱嶲嶳嶴嶵嶶嶷嶸嶹嶺嶻嶼嶽嶾嶿 +å·€å·å·‚巃巄巅巆巇巈巉巊巋巌å·å·Žå·å·å·‘巒巓巔巕巖巗巘巙巚巛巜å·å·žå·Ÿ +巠巡巢巣巤工左巧巨巩巪巫巬巭差巯巰己已巳巴巵巶巷巸巹巺巻巼巽巾巿 +帀å¸å¸‚布帄帅帆帇师帉帊帋希å¸å¸Žå¸å¸å¸‘帒帓帔帕帖帗帘帙帚帛帜å¸å¸žå¸Ÿ +帠帡帢帣帤帥带帧帨帩帪師帬席帮帯帰帱帲帳帴帵帶帷常帹帺帻帼帽帾帿 +å¹€å¹å¹‚幃幄幅幆幇幈幉幊幋幌å¹å¹Žå¹å¹å¹‘幒幓幔幕幖幗幘幙幚幛幜å¹å¹žå¹Ÿ +幠幡幢幣幤幥幦幧幨幩幪幫幬幭幮幯幰幱干平年幵并幷幸幹幺幻幼幽幾广 +庀åºåº‚広庄庅庆庇庈庉床庋庌åºåºŽåºåºåº‘庒库应底庖店庘庙庚庛府åºåºžåºŸ +庠庡庢庣庤庥度座庨庩庪庫庬庭庮庯庰庱庲庳庴庵庶康庸庹庺庻庼庽庾庿 +廀å»å»‚廃廄廅廆廇廈廉廊廋廌å»å»Žå»å»å»‘廒廓廔廕廖廗廘廙廚廛廜å»å»žå»Ÿ +廠廡廢廣廤廥廦廧廨廩廪廫廬廭廮廯廰廱廲廳廴廵延廷廸廹建廻廼廽廾廿 +å¼€å¼å¼‚弃弄弅弆弇弈弉弊弋弌å¼å¼Žå¼å¼å¼‘弒弓弔引弖弗弘弙弚弛弜å¼å¼žå¼Ÿ +张弡弢弣弤弥弦弧弨弩弪弫弬弭弮弯弰弱弲弳弴張弶強弸弹强弻弼弽弾弿 +å½€å½å½‚彃彄彅彆彇彈彉彊彋彌å½å½Žå½å½å½‘归当彔录彖彗彘彙彚彛彜å½å½žå½Ÿ +彠彡形彣彤彥彦彧彨彩彪彫彬彭彮彯彰影彲彳彴彵彶彷彸役彺彻彼彽彾彿 +å¾€å¾å¾‚徃径待徆徇很徉徊律後å¾å¾Žå¾å¾å¾‘徒従徔徕徖得徘徙徚徛徜å¾å¾žå¾Ÿ +徠御徢徣徤徥徦徧徨復循徫徬徭微徯徰徱徲徳徴徵徶德徸徹徺徻徼徽徾徿 +å¿€å¿å¿‚心忄必忆忇忈忉忊忋忌å¿å¿Žå¿å¿å¿‘忒忓忔忕忖志忘忙忚忛応å¿å¿žå¿Ÿ +忠忡忢忣忤忥忦忧忨忩忪快忬忭忮忯忰忱忲忳忴念忶忷忸忹忺忻忼忽忾忿 +怀æ€æ€‚怃怄怅怆怇怈怉怊怋怌æ€æ€Žæ€æ€æ€‘怒怓怔怕怖怗怘怙怚怛怜æ€æ€žæ€Ÿ +怠怡怢怣怤急怦性怨怩怪怫怬怭怮怯怰怱怲怳怴怵怶怷怸怹怺总怼怽怾怿 +æ€ææ‚æƒæ„æ…æ†æ‡æˆæ‰æŠæ‹æŒææŽæææ‘æ’æ“æ”æ•æ–æ—æ˜æ™æšæ›æœææžæŸ +æ æ¡æ¢æ£æ¤æ¥æ¦æ§æ¨æ©æªæ«æ¬æ­æ®æ¯æ°æ±æ²æ³æ´æµæ¶æ·æ¸æ¹æºæ»æ¼æ½æ¾æ¿ +æ‚€æ‚悂悃悄悅悆悇悈悉悊悋悌æ‚æ‚Žæ‚æ‚悑悒悓悔悕悖悗悘悙悚悛悜æ‚æ‚žæ‚Ÿ +悠悡悢患悤悥悦悧您悩悪悫悬悭悮悯悰悱悲悳悴悵悶悷悸悹悺悻悼悽悾悿 +惀æƒæƒ‚惃惄情惆惇惈惉惊惋惌æƒæƒŽæƒæƒæƒ‘惒惓惔惕惖惗惘惙惚惛惜æƒæƒžæƒŸ +惠惡惢惣惤惥惦惧惨惩惪惫惬惭惮惯惰惱惲想惴惵惶惷惸惹惺惻惼惽惾惿 +æ„€æ„愂愃愄愅愆愇愈愉愊愋愌æ„æ„Žæ„æ„愑愒愓愔愕愖愗愘愙愚愛愜æ„æ„žæ„Ÿ +愠愡愢愣愤愥愦愧愨愩愪愫愬愭愮愯愰愱愲愳愴愵愶愷愸愹愺愻愼愽愾愿 +æ…€æ…慂慃慄慅慆慇慈慉慊態慌æ…æ…Žæ…æ…慑慒慓慔慕慖慗慘慙慚慛慜æ…æ…žæ…Ÿ +慠慡慢慣慤慥慦慧慨慩慪慫慬慭慮慯慰慱慲慳慴慵慶慷慸慹慺慻慼慽慾慿 +憀æ†æ†‚憃憄憅憆憇憈憉憊憋憌æ†æ†Žæ†æ†æ†‘憒憓憔憕憖憗憘憙憚憛憜æ†æ†žæ†Ÿ +憠憡憢憣憤憥憦憧憨憩憪憫憬憭憮憯憰憱憲憳憴憵憶憷憸憹憺憻憼憽憾憿 +懀æ‡æ‡‚懃懄懅懆懇懈應懊懋懌æ‡æ‡Žæ‡æ‡æ‡‘懒懓懔懕懖懗懘懙懚懛懜æ‡æ‡žæ‡Ÿ +懠懡懢懣懤懥懦懧懨懩懪懫懬懭懮懯懰懱懲懳懴懵懶懷懸懹懺懻懼懽懾懿 +戀æˆæˆ‚戃戄戅戆戇戈戉戊戋戌æˆæˆŽæˆæˆæˆ‘戒戓戔戕或戗战戙戚戛戜æˆæˆžæˆŸ +戠戡戢戣戤戥戦戧戨戩截戫戬戭戮戯戰戱戲戳戴戵戶户戸戹戺戻戼戽戾房 +所æ‰æ‰‚扃扄扅扆扇扈扉扊手扌æ‰æ‰Žæ‰æ‰æ‰‘扒打扔払扖扗托扙扚扛扜æ‰æ‰žæ‰Ÿ +扠扡扢扣扤扥扦执扨扩扪扫扬扭扮扯扰扱扲扳扴扵扶扷扸批扺扻扼扽找承 +技æŠæŠ‚抃抄抅抆抇抈抉把抋抌æŠæŠŽæŠæŠæŠ‘抒抓抔投抖抗折抙抚抛抜æŠæŠžæŠŸ +抠抡抢抣护报抦抧抨抩抪披抬抭抮抯抰抱抲抳抴抵抶抷抸抹抺抻押抽抾抿 +æ‹€æ‹æ‹‚拃拄担拆拇拈拉拊拋拌æ‹æ‹Žæ‹æ‹æ‹‘拒拓拔拕拖拗拘拙拚招拜æ‹æ‹žæ‹Ÿ +拠拡拢拣拤拥拦拧拨择拪拫括拭拮拯拰拱拲拳拴拵拶拷拸拹拺拻拼拽拾拿 +挀æŒæŒ‚挃挄挅挆指挈按挊挋挌æŒæŒŽæŒæŒæŒ‘挒挓挔挕挖挗挘挙挚挛挜æŒæŒžæŒŸ +挠挡挢挣挤挥挦挧挨挩挪挫挬挭挮振挰挱挲挳挴挵挶挷挸挹挺挻挼挽挾挿 +æ€ææ‚æƒæ„æ…æ†æ‡æˆæ‰æŠæ‹æŒææŽæææ‘æ’æ“æ”æ•æ–æ—æ˜æ™æšæ›æœææžæŸ +æ æ¡æ¢æ£æ¤æ¥æ¦æ§æ¨æ©æªæ«æ¬æ­æ®æ¯æ°æ±æ²æ³æ´æµæ¶æ·æ¸æ¹æºæ»æ¼æ½æ¾æ¿ +掀æŽæŽ‚掃掄掅掆掇授掉掊掋掌æŽæŽŽæŽæŽæŽ‘排掓掔掕掖掗掘掙掚掛掜æŽæŽžæŽŸ +掠採探掣掤接掦控推掩措掫掬掭掮掯掰掱掲掳掴掵掶掷掸掹掺掻掼掽掾掿 +æ€ææ‚æƒæ„æ…æ†æ‡æˆæ‰æŠæ‹æŒææŽæææ‘æ’æ“æ”æ•æ–æ—æ˜æ™æšæ›æœææžæŸ +æ æ¡æ¢æ£æ¤æ¥æ¦æ§æ¨æ©æªæ«æ¬æ­æ®æ¯æ°æ±æ²æ³æ´æµæ¶æ·æ¸æ¹æºæ»æ¼æ½æ¾æ¿ +æ€ææ‚æƒæ„æ…æ†æ‡æˆæ‰æŠæ‹æŒææŽæææ‘æ’æ“æ”æ•æ–æ—æ˜æ™æšæ›æœææžæŸ +æ æ¡æ¢æ£æ¤æ¥æ¦æ§æ¨æ©æªæ«æ¬æ­æ®æ¯æ°æ±æ²æ³æ´æµæ¶æ·æ¸æ¹æºæ»æ¼æ½æ¾æ¿ +æ‘€æ‘摂摃摄摅摆摇摈摉摊摋摌æ‘æ‘Žæ‘æ‘摑摒摓摔摕摖摗摘摙摚摛摜æ‘æ‘žæ‘Ÿ +摠摡摢摣摤摥摦摧摨摩摪摫摬摭摮摯摰摱摲摳摴摵摶摷摸摹摺摻摼摽摾摿 +æ’€æ’撂撃撄撅撆撇撈撉撊撋撌æ’æ’Žæ’æ’撑撒撓撔撕撖撗撘撙撚撛撜æ’æ’žæ’Ÿ +撠撡撢撣撤撥撦撧撨撩撪撫撬播撮撯撰撱撲撳撴撵撶撷撸撹撺撻撼撽撾撿 +æ“€æ“擂擃擄擅擆擇擈擉擊擋擌æ“æ“Žæ“æ“擑擒擓擔擕擖擗擘擙據擛擜æ“æ“žæ“Ÿ +擠擡擢擣擤擥擦擧擨擩擪擫擬擭擮擯擰擱擲擳擴擵擶擷擸擹擺擻擼擽擾擿 +攀æ”攂攃攄攅攆攇攈攉攊攋攌æ”攎æ”æ”攑攒攓攔攕攖攗攘攙攚攛攜æ”攞攟 +攠攡攢攣攤攥攦攧攨攩攪攫攬攭攮支攰攱攲攳攴攵收攷攸改攺攻攼攽放政 +æ•€æ•æ•‚敃敄故敆敇效敉敊敋敌æ•æ•Žæ•æ•æ•‘敒敓敔敕敖敗敘教敚敛敜æ•æ•žæ•Ÿ +敠敡敢散敤敥敦敧敨敩敪敫敬敭敮敯数敱敲敳整敵敶敷數敹敺敻敼敽敾敿 +æ–€æ–斂斃斄斅斆文斈斉斊斋斌æ–æ–Žæ–æ–斑斒斓斔斕斖斗斘料斚斛斜æ–æ–žæ–Ÿ +斠斡斢斣斤斥斦斧斨斩斪斫斬断斮斯新斱斲斳斴斵斶斷斸方斺斻於施斾斿 +æ—€æ—旂旃旄旅旆旇旈旉旊旋旌æ—æ—Žæ—æ—旑旒旓旔旕旖旗旘旙旚旛旜æ—æ—žæ—Ÿ +无旡既旣旤日旦旧旨早旪旫旬旭旮旯旰旱旲旳旴旵时旷旸旹旺旻旼旽旾旿 +昀æ˜æ˜‚昃昄昅昆昇昈昉昊昋昌æ˜æ˜Žæ˜æ˜æ˜‘昒易昔昕昖昗昘昙昚昛昜æ˜æ˜žæ˜Ÿ +映昡昢昣昤春昦昧昨昩昪昫昬昭昮是昰昱昲昳昴昵昶昷昸昹昺昻昼昽显昿 +晀æ™æ™‚晃晄晅晆晇晈晉晊晋晌æ™æ™Žæ™æ™æ™‘晒晓晔晕晖晗晘晙晚晛晜æ™æ™žæ™Ÿ +晠晡晢晣晤晥晦晧晨晩晪晫晬晭普景晰晱晲晳晴晵晶晷晸晹智晻晼晽晾晿 +暀æšæš‚暃暄暅暆暇暈暉暊暋暌æšæšŽæšæšæš‘暒暓暔暕暖暗暘暙暚暛暜æšæšžæšŸ +暠暡暢暣暤暥暦暧暨暩暪暫暬暭暮暯暰暱暲暳暴暵暶暷暸暹暺暻暼暽暾暿 +曀æ›æ›‚曃曄曅曆曇曈曉曊曋曌æ›æ›Žæ›æ›æ›‘曒曓曔曕曖曗曘曙曚曛曜æ›æ›žæ›Ÿ +曠曡曢曣曤曥曦曧曨曩曪曫曬曭曮曯曰曱曲曳更曵曶曷書曹曺曻曼曽曾替 +最æœæœ‚會朄朅朆朇月有朊朋朌æœæœŽæœæœæœ‘朒朓朔朕朖朗朘朙朚望朜æœæœžæœŸ +朠朡朢朣朤朥朦朧木朩未末本札朮术朰朱朲朳朴朵朶朷朸朹机朻朼朽朾朿 +æ€ææ‚æƒæ„æ…æ†æ‡æˆæ‰æŠæ‹æŒææŽæææ‘æ’æ“æ”æ•æ–æ—æ˜æ™æšæ›æœææžæŸ +æ æ¡æ¢æ£æ¤æ¥æ¦æ§æ¨æ©æªæ«æ¬æ­æ®æ¯æ°æ±æ²æ³æ´æµæ¶æ·æ¸æ¹æºæ»æ¼æ½æ¾æ¿ +枀æžæž‚枃构枅枆枇枈枉枊枋枌æžæžŽæžæžæž‘枒枓枔枕枖林枘枙枚枛果æžæžžæžŸ +枠枡枢枣枤枥枦枧枨枩枪枫枬枭枮枯枰枱枲枳枴枵架枷枸枹枺枻枼枽枾枿 +柀æŸæŸ‚柃柄柅柆柇柈柉柊柋柌æŸæŸŽæŸæŸæŸ‘柒染柔柕柖柗柘柙柚柛柜æŸæŸžæŸŸ +柠柡柢柣柤查柦柧柨柩柪柫柬柭柮柯柰柱柲柳柴柵柶柷柸柹柺査柼柽柾柿 +æ €æ æ ‚栃栄栅栆标栈栉栊栋栌æ æ Žæ æ æ ‘栒栓栔栕栖栗栘栙栚栛栜æ æ žæ Ÿ +栠校栢栣栤栥栦栧栨栩株栫栬栭栮栯栰栱栲栳栴栵栶样核根栺栻格栽栾栿 +æ¡€æ¡æ¡‚桃桄桅框桇案桉桊桋桌æ¡æ¡Žæ¡æ¡æ¡‘桒桓桔桕桖桗桘桙桚桛桜æ¡æ¡žæ¡Ÿ +桠桡桢档桤桥桦桧桨桩桪桫桬桭桮桯桰桱桲桳桴桵桶桷桸桹桺桻桼桽桾桿 +梀æ¢æ¢‚梃梄梅梆梇梈梉梊梋梌æ¢æ¢Žæ¢æ¢æ¢‘梒梓梔梕梖梗梘梙梚梛梜æ¢æ¢žæ¢Ÿ +梠梡梢梣梤梥梦梧梨梩梪梫梬梭梮梯械梱梲梳梴梵梶梷梸梹梺梻梼梽梾梿 +检æ£æ£‚棃棄棅棆棇棈棉棊棋棌æ£æ£Žæ£æ£æ£‘棒棓棔棕棖棗棘棙棚棛棜æ£æ£žæ£Ÿ +棠棡棢棣棤棥棦棧棨棩棪棫棬棭森棯棰棱棲棳棴棵棶棷棸棹棺棻棼棽棾棿 +椀æ¤æ¤‚椃椄椅椆椇椈椉椊椋椌æ¤æ¤Žæ¤æ¤æ¤‘椒椓椔椕椖椗椘椙椚椛検æ¤æ¤žæ¤Ÿ +椠椡椢椣椤椥椦椧椨椩椪椫椬椭椮椯椰椱椲椳椴椵椶椷椸椹椺椻椼椽椾椿 +楀æ¥æ¥‚楃楄楅楆楇楈楉楊楋楌æ¥æ¥Žæ¥æ¥æ¥‘楒楓楔楕楖楗楘楙楚楛楜æ¥æ¥žæ¥Ÿ +楠楡楢楣楤楥楦楧楨楩楪楫楬業楮楯楰楱楲楳楴極楶楷楸楹楺楻楼楽楾楿 +榀æ¦æ¦‚榃榄榅榆榇榈榉榊榋榌æ¦æ¦Žæ¦æ¦æ¦‘榒榓榔榕榖榗榘榙榚榛榜æ¦æ¦žæ¦Ÿ +榠榡榢榣榤榥榦榧榨榩榪榫榬榭榮榯榰榱榲榳榴榵榶榷榸榹榺榻榼榽榾榿 +槀æ§æ§‚槃槄槅槆槇槈槉槊構槌æ§æ§Žæ§æ§æ§‘槒槓槔槕槖槗様槙槚槛槜æ§æ§žæ§Ÿ +槠槡槢槣槤槥槦槧槨槩槪槫槬槭槮槯槰槱槲槳槴槵槶槷槸槹槺槻槼槽槾槿 +樀æ¨æ¨‚樃樄樅樆樇樈樉樊樋樌æ¨æ¨Žæ¨æ¨æ¨‘樒樓樔樕樖樗樘標樚樛樜æ¨æ¨žæ¨Ÿ +樠模樢樣樤樥樦樧樨権横樫樬樭樮樯樰樱樲樳樴樵樶樷樸樹樺樻樼樽樾樿 +æ©€æ©æ©‚橃橄橅橆橇橈橉橊橋橌æ©æ©Žæ©æ©æ©‘橒橓橔橕橖橗橘橙橚橛橜æ©æ©žæ©Ÿ +橠橡橢橣橤橥橦橧橨橩橪橫橬橭橮橯橰橱橲橳橴橵橶橷橸橹橺橻橼橽橾橿 +檀æªæª‚檃檄檅檆檇檈檉檊檋檌æªæªŽæªæªæª‘檒檓檔檕檖檗檘檙檚檛檜æªæªžæªŸ +檠檡檢檣檤檥檦檧檨檩檪檫檬檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿 +æ«€æ«æ«‚櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌æ«æ«Žæ«æ«æ«‘櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜æ«æ«žæ«Ÿ +櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿 +欀æ¬æ¬‚欃欄欅欆欇欈欉權欋欌æ¬æ¬Žæ¬æ¬æ¬‘欒欓欔欕欖欗欘欙欚欛欜æ¬æ¬žæ¬Ÿ +欠次欢欣欤欥欦欧欨欩欪欫欬欭欮欯欰欱欲欳欴欵欶欷欸欹欺欻欼欽款欿 +æ­€æ­æ­‚歃歄歅歆歇歈歉歊歋歌æ­æ­Žæ­æ­æ­‘歒歓歔歕歖歗歘歙歚歛歜æ­æ­žæ­Ÿ +歠歡止正此步武歧歨歩歪歫歬歭歮歯歰歱歲歳歴歵歶歷歸歹歺死歼歽歾歿 +殀æ®æ®‚殃殄殅殆殇殈殉殊残殌æ®æ®Žæ®æ®æ®‘殒殓殔殕殖殗殘殙殚殛殜æ®æ®žæ®Ÿ +殠殡殢殣殤殥殦殧殨殩殪殫殬殭殮殯殰殱殲殳殴段殶殷殸殹殺殻殼殽殾殿 +毀æ¯æ¯‚毃毄毅毆毇毈毉毊毋毌æ¯æ¯Žæ¯æ¯æ¯‘毒毓比毕毖毗毘毙毚毛毜æ¯æ¯žæ¯Ÿ +毠毡毢毣毤毥毦毧毨毩毪毫毬毭毮毯毰毱毲毳毴毵毶毷毸毹毺毻毼毽毾毿 +æ°€æ°æ°‚氃氄氅氆氇氈氉氊氋氌æ°æ°Žæ°æ°æ°‘氒氓气氕氖気氘氙氚氛氜æ°æ°žæ°Ÿ +氠氡氢氣氤氥氦氧氨氩氪氫氬氭氮氯氰氱氲氳水氵氶氷永氹氺氻氼氽氾氿 +æ±€æ±æ±‚汃汄汅汆汇汈汉汊汋汌æ±æ±Žæ±æ±æ±‘汒汓汔汕汖汗汘汙汚汛汜æ±æ±žæ±Ÿ +池污汢汣汤汥汦汧汨汩汪汫汬汭汮汯汰汱汲汳汴汵汶汷汸汹決汻汼汽汾汿 +æ²€æ²æ²‚沃沄沅沆沇沈沉沊沋沌æ²æ²Žæ²æ²æ²‘沒沓沔沕沖沗沘沙沚沛沜æ²æ²žæ²Ÿ +沠没沢沣沤沥沦沧沨沩沪沫沬沭沮沯沰沱沲河沴沵沶沷沸油沺治沼沽沾沿 +æ³€æ³æ³‚泃泄泅泆泇泈泉泊泋泌æ³æ³Žæ³æ³æ³‘泒泓泔法泖泗泘泙泚泛泜æ³æ³žæ³Ÿ +泠泡波泣泤泥泦泧注泩泪泫泬泭泮泯泰泱泲泳泴泵泶泷泸泹泺泻泼泽泾泿 +æ´€æ´æ´‚洃洄洅洆洇洈洉洊洋洌æ´æ´Žæ´æ´æ´‘洒洓洔洕洖洗洘洙洚洛洜æ´æ´žæ´Ÿ +洠洡洢洣洤津洦洧洨洩洪洫洬洭洮洯洰洱洲洳洴洵洶洷洸洹洺活洼洽派洿 +æµ€æµæµ‚浃浄浅浆浇浈浉浊测浌æµæµŽæµæµæµ‘浒浓浔浕浖浗浘浙浚浛浜æµæµžæµŸ +浠浡浢浣浤浥浦浧浨浩浪浫浬浭浮浯浰浱浲浳浴浵浶海浸浹浺浻浼浽浾浿 +涀æ¶æ¶‚涃涄涅涆涇消涉涊涋涌æ¶æ¶Žæ¶æ¶æ¶‘涒涓涔涕涖涗涘涙涚涛涜æ¶æ¶žæ¶Ÿ +涠涡涢涣涤涥润涧涨涩涪涫涬涭涮涯涰涱液涳涴涵涶涷涸涹涺涻涼涽涾涿 +æ·€æ·æ·‚淃淄淅淆淇淈淉淊淋淌æ·æ·Žæ·æ·æ·‘淒淓淔淕淖淗淘淙淚淛淜æ·æ·žæ·Ÿ +淠淡淢淣淤淥淦淧淨淩淪淫淬淭淮淯淰深淲淳淴淵淶混淸淹淺添淼淽淾淿 +渀æ¸æ¸‚渃渄清渆渇済渉渊渋渌æ¸æ¸Žæ¸æ¸æ¸‘渒渓渔渕渖渗渘渙渚減渜æ¸æ¸žæ¸Ÿ +渠渡渢渣渤渥渦渧渨温渪渫測渭渮港渰渱渲渳渴渵渶渷游渹渺渻渼渽渾渿 +æ¹€æ¹æ¹‚湃湄湅湆湇湈湉湊湋湌æ¹æ¹Žæ¹æ¹æ¹‘湒湓湔湕湖湗湘湙湚湛湜æ¹æ¹žæ¹Ÿ +湠湡湢湣湤湥湦湧湨湩湪湫湬湭湮湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽湾湿 +満æºæº‚溃溄溅溆溇溈溉溊溋溌æºæºŽæºæºæº‘溒溓溔溕準溗溘溙溚溛溜æºæºžæºŸ +溠溡溢溣溤溥溦溧溨溩溪溫溬溭溮溯溰溱溲溳溴溵溶溷溸溹溺溻溼溽溾溿 +滀æ»æ»‚滃滄滅滆滇滈滉滊滋滌æ»æ»Žæ»æ»æ»‘滒滓滔滕滖滗滘滙滚滛滜æ»æ»žæ»Ÿ +滠满滢滣滤滥滦滧滨滩滪滫滬滭滮滯滰滱滲滳滴滵滶滷滸滹滺滻滼滽滾滿 +æ¼€æ¼æ¼‚漃漄漅漆漇漈漉漊漋漌æ¼æ¼Žæ¼æ¼æ¼‘漒漓演漕漖漗漘漙漚漛漜æ¼æ¼žæ¼Ÿ +漠漡漢漣漤漥漦漧漨漩漪漫漬漭漮漯漰漱漲漳漴漵漶漷漸漹漺漻漼漽漾漿 +æ½€æ½æ½‚潃潄潅潆潇潈潉潊潋潌æ½æ½Žæ½æ½æ½‘潒潓潔潕潖潗潘潙潚潛潜æ½æ½žæ½Ÿ +潠潡潢潣潤潥潦潧潨潩潪潫潬潭潮潯潰潱潲潳潴潵潶潷潸潹潺潻潼潽潾潿 +æ¾€æ¾æ¾‚澃澄澅澆澇澈澉澊澋澌æ¾æ¾Žæ¾æ¾æ¾‘澒澓澔澕澖澗澘澙澚澛澜æ¾æ¾žæ¾Ÿ +澠澡澢澣澤澥澦澧澨澩澪澫澬澭澮澯澰澱澲澳澴澵澶澷澸澹澺澻澼澽澾澿 +æ¿€æ¿æ¿‚濃濄濅濆濇濈濉濊濋濌æ¿æ¿Žæ¿æ¿æ¿‘濒濓濔濕濖濗濘濙濚濛濜æ¿æ¿žæ¿Ÿ +濠濡濢濣濤濥濦濧濨濩濪濫濬濭濮濯濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿 +瀀ç€ç€‚瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌ç€ç€Žç€ç€ç€‘瀒瀓瀔瀕瀖瀗瀘瀙瀚瀛瀜ç€ç€žç€Ÿ +瀠瀡瀢瀣瀤瀥瀦瀧瀨瀩瀪瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀵瀶瀷瀸瀹瀺瀻瀼瀽瀾瀿 +ç€çç‚çƒç„ç…ç†ç‡çˆç‰çŠç‹çŒççŽççç‘ç’ç“ç”ç•ç–ç—ç˜ç™çšç›çœççžçŸ +ç ç¡ç¢ç£ç¤ç¥ç¦ç§ç¨ç©çªç«ç¬ç­ç®ç¯ç°ç±ç²ç³ç´çµç¶ç·ç¸ç¹çºç»ç¼ç½ç¾ç¿ +ç‚€ç‚炂炃炄炅炆炇炈炉炊炋炌ç‚ç‚Žç‚ç‚炑炒炓炔炕炖炗炘炙炚炛炜ç‚ç‚žç‚Ÿ +炠炡炢炣炤炥炦炧炨炩炪炫炬炭炮炯炰炱炲炳炴炵炶炷炸点為炻炼炽炾炿 +烀çƒçƒ‚烃烄烅烆烇烈烉烊烋烌çƒçƒŽçƒçƒçƒ‘烒烓烔烕烖烗烘烙烚烛烜çƒçƒžçƒŸ +烠烡烢烣烤烥烦烧烨烩烪烫烬热烮烯烰烱烲烳烴烵烶烷烸烹烺烻烼烽烾烿 +ç„€ç„焂焃焄焅焆焇焈焉焊焋焌ç„ç„Žç„ç„焑焒焓焔焕焖焗焘焙焚焛焜ç„ç„žç„Ÿ +焠無焢焣焤焥焦焧焨焩焪焫焬焭焮焯焰焱焲焳焴焵然焷焸焹焺焻焼焽焾焿 +ç…€ç…煂煃煄煅煆煇煈煉煊煋煌ç…ç…Žç…ç…煑煒煓煔煕煖煗煘煙煚煛煜ç…ç…žç…Ÿ +煠煡煢煣煤煥煦照煨煩煪煫煬煭煮煯煰煱煲煳煴煵煶煷煸煹煺煻煼煽煾煿 +熀ç†ç†‚熃熄熅熆熇熈熉熊熋熌ç†ç†Žç†ç†ç†‘熒熓熔熕熖熗熘熙熚熛熜ç†ç†žç†Ÿ +熠熡熢熣熤熥熦熧熨熩熪熫熬熭熮熯熰熱熲熳熴熵熶熷熸熹熺熻熼熽熾熿 +燀ç‡ç‡‚燃燄燅燆燇燈燉燊燋燌ç‡ç‡Žç‡ç‡ç‡‘燒燓燔燕燖燗燘燙燚燛燜ç‡ç‡žç‡Ÿ +燠燡燢燣燤燥燦燧燨燩燪燫燬燭燮燯燰燱燲燳燴燵燶燷燸燹燺燻燼燽燾燿 +爀çˆçˆ‚爃爄爅爆爇爈爉爊爋爌çˆçˆŽçˆçˆçˆ‘爒爓爔爕爖爗爘爙爚爛爜çˆçˆžçˆŸ +爠爡爢爣爤爥爦爧爨爩爪爫爬爭爮爯爰爱爲爳爴爵父爷爸爹爺爻爼爽爾爿 +牀ç‰ç‰‚牃牄牅牆片版牉牊牋牌ç‰ç‰Žç‰ç‰ç‰‘牒牓牔牕牖牗牘牙牚牛牜ç‰ç‰žç‰Ÿ +牠牡牢牣牤牥牦牧牨物牪牫牬牭牮牯牰牱牲牳牴牵牶牷牸特牺牻牼牽牾牿 +犀çŠçŠ‚犃犄犅犆犇犈犉犊犋犌çŠçŠŽçŠçŠçŠ‘犒犓犔犕犖犗犘犙犚犛犜çŠçŠžçŠŸ +犠犡犢犣犤犥犦犧犨犩犪犫犬犭犮犯犰犱犲犳犴犵状犷犸犹犺犻犼犽犾犿 +ç‹€ç‹ç‹‚狃狄狅狆狇狈狉狊狋狌ç‹ç‹Žç‹ç‹ç‹‘狒狓狔狕狖狗狘狙狚狛狜ç‹ç‹žç‹Ÿ +狠狡狢狣狤狥狦狧狨狩狪狫独狭狮狯狰狱狲狳狴狵狶狷狸狹狺狻狼狽狾狿 +猀çŒçŒ‚猃猄猅猆猇猈猉猊猋猌çŒçŒŽçŒçŒçŒ‘猒猓猔猕猖猗猘猙猚猛猜çŒçŒžçŒŸ +猠猡猢猣猤猥猦猧猨猩猪猫猬猭献猯猰猱猲猳猴猵猶猷猸猹猺猻猼猽猾猿 +ç€çç‚çƒç„ç…ç†ç‡çˆç‰çŠç‹çŒççŽççç‘ç’ç“ç”ç•ç–ç—ç˜ç™çšç›çœççžçŸ +ç ç¡ç¢ç£ç¤ç¥ç¦ç§ç¨ç©çªç«ç¬ç­ç®ç¯ç°ç±ç²ç³ç´çµç¶ç·ç¸ç¹çºç»ç¼ç½ç¾ç¿ +玀çŽçŽ‚玃玄玅玆率玈玉玊王玌çŽçŽŽçŽçŽçŽ‘玒玓玔玕玖玗玘玙玚玛玜çŽçŽžçŽŸ +玠玡玢玣玤玥玦玧玨玩玪玫玬玭玮环现玱玲玳玴玵玶玷玸玹玺玻玼玽玾玿 +ç€çç‚çƒç„ç…ç†ç‡çˆç‰çŠç‹çŒççŽççç‘ç’ç“ç”ç•ç–ç—ç˜ç™çšç›çœççžçŸ +ç ç¡ç¢ç£ç¤ç¥ç¦ç§ç¨ç©çªç«ç¬ç­ç®ç¯ç°ç±ç²ç³ç´çµç¶ç·ç¸ç¹çºç»ç¼ç½ç¾ç¿ +ç€çç‚çƒç„ç…ç†ç‡çˆç‰çŠç‹çŒççŽççç‘ç’ç“ç”ç•ç–ç—ç˜ç™çšç›çœççžçŸ +ç ç¡ç¢ç£ç¤ç¥ç¦ç§ç¨ç©çªç«ç¬ç­ç®ç¯ç°ç±ç²ç³ç´çµç¶ç·ç¸ç¹çºç»ç¼ç½ç¾ç¿ +ç‘€ç‘瑂瑃瑄瑅瑆瑇瑈瑉瑊瑋瑌ç‘ç‘Žç‘ç‘瑑瑒瑓瑔瑕瑖瑗瑘瑙瑚瑛瑜ç‘ç‘žç‘Ÿ +瑠瑡瑢瑣瑤瑥瑦瑧瑨瑩瑪瑫瑬瑭瑮瑯瑰瑱瑲瑳瑴瑵瑶瑷瑸瑹瑺瑻瑼瑽瑾瑿 +ç’€ç’璂璃璄璅璆璇璈璉璊璋璌ç’ç’Žç’ç’璑璒璓璔璕璖璗璘璙璚璛璜ç’ç’žç’Ÿ +璠璡璢璣璤璥璦璧璨璩璪璫璬璭璮璯環璱璲璳璴璵璶璷璸璹璺璻璼璽璾璿 +ç“€ç“瓂瓃瓄瓅瓆瓇瓈瓉瓊瓋瓌ç“ç“Žç“ç“瓑瓒瓓瓔瓕瓖瓗瓘瓙瓚瓛瓜ç“ç“žç“Ÿ +瓠瓡瓢瓣瓤瓥瓦瓧瓨瓩瓪瓫瓬瓭瓮瓯瓰瓱瓲瓳瓴瓵瓶瓷瓸瓹瓺瓻瓼瓽瓾瓿 +甀ç”甂甃甄甅甆甇甈甉甊甋甌ç”甎ç”ç”甑甒甓甔甕甖甗甘甙甚甛甜ç”甞生 +甠甡產産甤甥甦甧用甩甪甫甬甭甮甯田由甲申甴电甶男甸甹町画甼甽甾甿 +ç•€ç•ç•‚畃畄畅畆畇畈畉畊畋界ç•ç•Žç•ç•ç•‘畒畓畔畕畖畗畘留畚畛畜ç•ç•žç•Ÿ +畠畡畢畣畤略畦畧畨畩番畫畬畭畮畯異畱畲畳畴畵當畷畸畹畺畻畼畽畾畿 +ç–€ç–疂疃疄疅疆疇疈疉疊疋疌ç–ç–Žç–ç–疑疒疓疔疕疖疗疘疙疚疛疜ç–ç–žç–Ÿ +疠疡疢疣疤疥疦疧疨疩疪疫疬疭疮疯疰疱疲疳疴疵疶疷疸疹疺疻疼疽疾疿 +ç—€ç—痂痃痄病痆症痈痉痊痋痌ç—ç—Žç—ç—痑痒痓痔痕痖痗痘痙痚痛痜ç—ç—žç—Ÿ +痠痡痢痣痤痥痦痧痨痩痪痫痬痭痮痯痰痱痲痳痴痵痶痷痸痹痺痻痼痽痾痿 +瘀ç˜ç˜‚瘃瘄瘅瘆瘇瘈瘉瘊瘋瘌ç˜ç˜Žç˜ç˜ç˜‘瘒瘓瘔瘕瘖瘗瘘瘙瘚瘛瘜ç˜ç˜žç˜Ÿ +瘠瘡瘢瘣瘤瘥瘦瘧瘨瘩瘪瘫瘬瘭瘮瘯瘰瘱瘲瘳瘴瘵瘶瘷瘸瘹瘺瘻瘼瘽瘾瘿 +癀ç™ç™‚癃癄癅癆癇癈癉癊癋癌ç™ç™Žç™ç™ç™‘癒癓癔癕癖癗癘癙癚癛癜ç™ç™žç™Ÿ +癠癡癢癣癤癥癦癧癨癩癪癫癬癭癮癯癰癱癲癳癴癵癶癷癸癹発登發白百癿 +皀çšçš‚皃的皅皆皇皈皉皊皋皌çšçšŽçšçšçš‘皒皓皔皕皖皗皘皙皚皛皜çšçšžçšŸ +皠皡皢皣皤皥皦皧皨皩皪皫皬皭皮皯皰皱皲皳皴皵皶皷皸皹皺皻皼皽皾皿 +盀ç›ç›‚盃盄盅盆盇盈盉益盋盌ç›ç›Žç›ç›ç›‘盒盓盔盕盖盗盘盙盚盛盜ç›ç›žç›Ÿ +盠盡盢監盤盥盦盧盨盩盪盫盬盭目盯盰盱盲盳直盵盶盷相盹盺盻盼盽盾盿 +眀çœçœ‚眃眄眅眆眇眈眉眊看県çœçœŽçœçœçœ‘眒眓眔眕眖眗眘眙眚眛眜çœçœžçœŸ +眠眡眢眣眤眥眦眧眨眩眪眫眬眭眮眯眰眱眲眳眴眵眶眷眸眹眺眻眼眽眾眿 +ç€çç‚çƒç„ç…ç†ç‡çˆç‰çŠç‹çŒççŽççç‘ç’ç“ç”ç•ç–ç—ç˜ç™çšç›çœççžçŸ +ç ç¡ç¢ç£ç¤ç¥ç¦ç§ç¨ç©çªç«ç¬ç­ç®ç¯ç°ç±ç²ç³ç´çµç¶ç·ç¸ç¹çºç»ç¼ç½ç¾ç¿ +瞀çžçž‚瞃瞄瞅瞆瞇瞈瞉瞊瞋瞌çžçžŽçžçžçž‘瞒瞓瞔瞕瞖瞗瞘瞙瞚瞛瞜çžçžžçžŸ +瞠瞡瞢瞣瞤瞥瞦瞧瞨瞩瞪瞫瞬瞭瞮瞯瞰瞱瞲瞳瞴瞵瞶瞷瞸瞹瞺瞻瞼瞽瞾瞿 +矀çŸçŸ‚矃矄矅矆矇矈矉矊矋矌çŸçŸŽçŸçŸçŸ‘矒矓矔矕矖矗矘矙矚矛矜çŸçŸžçŸŸ +矠矡矢矣矤知矦矧矨矩矪矫矬短矮矯矰矱矲石矴矵矶矷矸矹矺矻矼矽矾矿 +ç €ç ç ‚砃砄砅砆砇砈砉砊砋砌ç ç Žç ç ç ‘砒砓研砕砖砗砘砙砚砛砜ç ç žç Ÿ +砠砡砢砣砤砥砦砧砨砩砪砫砬砭砮砯砰砱砲砳破砵砶砷砸砹砺砻砼砽砾砿 +ç¡€ç¡ç¡‚硃硄硅硆硇硈硉硊硋硌ç¡ç¡Žç¡ç¡ç¡‘硒硓硔硕硖硗硘硙硚硛硜ç¡ç¡žç¡Ÿ +硠硡硢硣硤硥硦硧硨硩硪硫硬硭确硯硰硱硲硳硴硵硶硷硸硹硺硻硼硽硾硿 +碀ç¢ç¢‚碃碄碅碆碇碈碉碊碋碌ç¢ç¢Žç¢ç¢ç¢‘碒碓碔碕碖碗碘碙碚碛碜ç¢ç¢žç¢Ÿ +碠碡碢碣碤碥碦碧碨碩碪碫碬碭碮碯碰碱碲碳碴碵碶碷碸碹確碻碼碽碾碿 +磀ç£ç£‚磃磄磅磆磇磈磉磊磋磌ç£ç£Žç£ç£ç£‘磒磓磔磕磖磗磘磙磚磛磜ç£ç£žç£Ÿ +磠磡磢磣磤磥磦磧磨磩磪磫磬磭磮磯磰磱磲磳磴磵磶磷磸磹磺磻磼磽磾磿 +礀ç¤ç¤‚礃礄礅礆礇礈礉礊礋礌ç¤ç¤Žç¤ç¤ç¤‘礒礓礔礕礖礗礘礙礚礛礜ç¤ç¤žç¤Ÿ +礠礡礢礣礤礥礦礧礨礩礪礫礬礭礮礯礰礱礲礳礴礵礶礷礸礹示礻礼礽社礿 +祀ç¥ç¥‚祃祄祅祆祇祈祉祊祋祌ç¥ç¥Žç¥ç¥ç¥‘祒祓祔祕祖祗祘祙祚祛祜ç¥ç¥žç¥Ÿ +祠祡祢祣祤祥祦祧票祩祪祫祬祭祮祯祰祱祲祳祴祵祶祷祸祹祺祻祼祽祾祿 +禀ç¦ç¦‚禃禄禅禆禇禈禉禊禋禌ç¦ç¦Žç¦ç¦ç¦‘禒禓禔禕禖禗禘禙禚禛禜ç¦ç¦žç¦Ÿ +禠禡禢禣禤禥禦禧禨禩禪禫禬禭禮禯禰禱禲禳禴禵禶禷禸禹禺离禼禽禾禿 +秀ç§ç§‚秃秄秅秆秇秈秉秊秋秌ç§ç§Žç§ç§ç§‘秒秓秔秕秖秗秘秙秚秛秜ç§ç§žç§Ÿ +秠秡秢秣秤秥秦秧秨秩秪秫秬秭秮积称秱秲秳秴秵秶秷秸秹秺移秼秽秾秿 +稀ç¨ç¨‚稃稄稅稆稇稈稉稊程稌ç¨ç¨Žç¨ç¨ç¨‘稒稓稔稕稖稗稘稙稚稛稜ç¨ç¨žç¨Ÿ +稠稡稢稣稤稥稦稧稨稩稪稫稬稭種稯稰稱稲稳稴稵稶稷稸稹稺稻稼稽稾稿 +ç©€ç©ç©‚穃穄穅穆穇穈穉穊穋穌ç©ç©Žç©ç©ç©‘穒穓穔穕穖穗穘穙穚穛穜ç©ç©žç©Ÿ +穠穡穢穣穤穥穦穧穨穩穪穫穬穭穮穯穰穱穲穳穴穵究穷穸穹空穻穼穽穾穿 +窀çªçª‚窃窄窅窆窇窈窉窊窋窌çªçªŽçªçªçª‘窒窓窔窕窖窗窘窙窚窛窜çªçªžçªŸ +窠窡窢窣窤窥窦窧窨窩窪窫窬窭窮窯窰窱窲窳窴窵窶窷窸窹窺窻窼窽窾窿 +ç«€ç«ç«‚竃竄竅竆竇竈竉竊立竌ç«ç«Žç«ç«ç«‘竒竓竔竕竖竗竘站竚竛竜ç«ç«žç«Ÿ +章竡竢竣竤童竦竧竨竩竪竫竬竭竮端竰竱竲竳竴竵競竷竸竹竺竻竼竽竾竿 +笀ç¬ç¬‚笃笄笅笆笇笈笉笊笋笌ç¬ç¬Žç¬ç¬ç¬‘笒笓笔笕笖笗笘笙笚笛笜ç¬ç¬žç¬Ÿ +笠笡笢笣笤笥符笧笨笩笪笫第笭笮笯笰笱笲笳笴笵笶笷笸笹笺笻笼笽笾笿 +ç­€ç­ç­‚筃筄筅筆筇筈等筊筋筌ç­ç­Žç­ç­ç­‘筒筓答筕策筗筘筙筚筛筜ç­ç­žç­Ÿ +筠筡筢筣筤筥筦筧筨筩筪筫筬筭筮筯筰筱筲筳筴筵筶筷筸筹筺筻筼筽签筿 +简ç®ç®‚箃箄箅箆箇箈箉箊箋箌ç®ç®Žç®ç®ç®‘箒箓箔箕箖算箘箙箚箛箜ç®ç®žç®Ÿ +箠管箢箣箤箥箦箧箨箩箪箫箬箭箮箯箰箱箲箳箴箵箶箷箸箹箺箻箼箽箾箿 +節ç¯ç¯‚篃範篅篆篇篈築篊篋篌ç¯ç¯Žç¯ç¯ç¯‘篒篓篔篕篖篗篘篙篚篛篜ç¯ç¯žç¯Ÿ +篠篡篢篣篤篥篦篧篨篩篪篫篬篭篮篯篰篱篲篳篴篵篶篷篸篹篺篻篼篽篾篿 +ç°€ç°ç°‚簃簄簅簆簇簈簉簊簋簌ç°ç°Žç°ç°ç°‘簒簓簔簕簖簗簘簙簚簛簜ç°ç°žç°Ÿ +簠簡簢簣簤簥簦簧簨簩簪簫簬簭簮簯簰簱簲簳簴簵簶簷簸簹簺簻簼簽簾簿 +ç±€ç±ç±‚籃籄籅籆籇籈籉籊籋籌ç±ç±Žç±ç±ç±‘籒籓籔籕籖籗籘籙籚籛籜ç±ç±žç±Ÿ +籠籡籢籣籤籥籦籧籨籩籪籫籬籭籮籯籰籱籲米籴籵籶籷籸籹籺类籼籽籾籿 +ç²€ç²ç²‚粃粄粅粆粇粈粉粊粋粌ç²ç²Žç²ç²ç²‘粒粓粔粕粖粗粘粙粚粛粜ç²ç²žç²Ÿ +粠粡粢粣粤粥粦粧粨粩粪粫粬粭粮粯粰粱粲粳粴粵粶粷粸粹粺粻粼粽精粿 +ç³€ç³ç³‚糃糄糅糆糇糈糉糊糋糌ç³ç³Žç³ç³ç³‘糒糓糔糕糖糗糘糙糚糛糜ç³ç³žç³Ÿ +糠糡糢糣糤糥糦糧糨糩糪糫糬糭糮糯糰糱糲糳糴糵糶糷糸糹糺系糼糽糾糿 +ç´€ç´ç´‚紃約紅紆紇紈紉紊紋紌ç´ç´Žç´ç´ç´‘紒紓純紕紖紗紘紙級紛紜ç´ç´žç´Ÿ +素紡索紣紤紥紦紧紨紩紪紫紬紭紮累細紱紲紳紴紵紶紷紸紹紺紻紼紽紾紿 +çµ€çµçµ‚絃組絅絆絇絈絉絊絋経çµçµŽçµçµçµ‘絒絓絔絕絖絗絘絙絚絛絜çµçµžçµŸ +絠絡絢絣絤絥給絧絨絩絪絫絬絭絮絯絰統絲絳絴絵絶絷絸絹絺絻絼絽絾絿 +綀ç¶ç¶‚綃綄綅綆綇綈綉綊綋綌ç¶ç¶Žç¶ç¶ç¶‘綒經綔綕綖綗綘継続綛綜ç¶ç¶žç¶Ÿ +綠綡綢綣綤綥綦綧綨綩綪綫綬維綮綯綰綱網綳綴綵綶綷綸綹綺綻綼綽綾綿 +ç·€ç·ç·‚緃緄緅緆緇緈緉緊緋緌ç·ç·Žç·ç·ç·‘緒緓緔緕緖緗緘緙線緛緜ç·ç·žç·Ÿ +締緡緢緣緤緥緦緧編緩緪緫緬緭緮緯緰緱緲緳練緵緶緷緸緹緺緻緼緽緾緿 +縀ç¸ç¸‚縃縄縅縆縇縈縉縊縋縌ç¸ç¸Žç¸ç¸ç¸‘縒縓縔縕縖縗縘縙縚縛縜ç¸ç¸žç¸Ÿ +縠縡縢縣縤縥縦縧縨縩縪縫縬縭縮縯縰縱縲縳縴縵縶縷縸縹縺縻縼總績縿 +ç¹€ç¹ç¹‚繃繄繅繆繇繈繉繊繋繌ç¹ç¹Žç¹ç¹ç¹‘繒繓織繕繖繗繘繙繚繛繜ç¹ç¹žç¹Ÿ +繠繡繢繣繤繥繦繧繨繩繪繫繬繭繮繯繰繱繲繳繴繵繶繷繸繹繺繻繼繽繾繿 +纀çºçº‚纃纄纅纆纇纈纉纊纋續çºçºŽçºçºçº‘纒纓纔纕纖纗纘纙纚纛纜çºçºžçºŸ +纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线 +绀ç»ç»‚练组绅细织终绉绊绋绌ç»ç»Žç»ç»ç»‘绒结绔绕绖绗绘给绚绛络ç»ç»žç»Ÿ +绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿 +ç¼€ç¼ç¼‚缃缄缅缆缇缈缉缊缋缌ç¼ç¼Žç¼ç¼ç¼‘缒缓缔缕编缗缘缙缚缛缜ç¼ç¼žç¼Ÿ +缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缷缸缹缺缻缼缽缾缿 +ç½€ç½ç½‚罃罄罅罆罇罈罉罊罋罌ç½ç½Žç½ç½ç½‘罒罓罔罕罖罗罘罙罚罛罜ç½ç½žç½Ÿ +罠罡罢罣罤罥罦罧罨罩罪罫罬罭置罯罰罱署罳罴罵罶罷罸罹罺罻罼罽罾罿 +ç¾€ç¾ç¾‚羃羄羅羆羇羈羉羊羋羌ç¾ç¾Žç¾ç¾ç¾‘羒羓羔羕羖羗羘羙羚羛羜ç¾ç¾žç¾Ÿ +羠羡羢羣群羥羦羧羨義羪羫羬羭羮羯羰羱羲羳羴羵羶羷羸羹羺羻羼羽羾羿 +ç¿€ç¿ç¿‚翃翄翅翆翇翈翉翊翋翌ç¿ç¿Žç¿ç¿ç¿‘習翓翔翕翖翗翘翙翚翛翜ç¿ç¿žç¿Ÿ +翠翡翢翣翤翥翦翧翨翩翪翫翬翭翮翯翰翱翲翳翴翵翶翷翸翹翺翻翼翽翾翿 +耀è€è€‚考耄者耆耇耈耉耊耋而è€è€Žè€è€è€‘耒耓耔耕耖耗耘耙耚耛耜è€è€žè€Ÿ +耠耡耢耣耤耥耦耧耨耩耪耫耬耭耮耯耰耱耲耳耴耵耶耷耸耹耺耻耼耽耾耿 +è€èè‚èƒè„è…è†è‡èˆè‰èŠè‹èŒèèŽèèè‘è’è“è”è•è–è—è˜è™èšè›èœèèžèŸ +è è¡è¢è£è¤è¥è¦è§è¨è©èªè«è¬è­è®è¯è°è±è²è³è´èµè¶è·è¸è¹èºè»è¼è½è¾è¿ +è‚€è‚肂肃肄肅肆肇肈肉肊肋肌è‚è‚Žè‚è‚肑肒肓肔肕肖肗肘肙肚肛肜è‚è‚žè‚Ÿ +肠股肢肣肤肥肦肧肨肩肪肫肬肭肮肯肰肱育肳肴肵肶肷肸肹肺肻肼肽肾肿 +胀èƒèƒ‚胃胄胅胆胇胈胉胊胋背èƒèƒŽèƒèƒèƒ‘胒胓胔胕胖胗胘胙胚胛胜èƒèƒžèƒŸ +胠胡胢胣胤胥胦胧胨胩胪胫胬胭胮胯胰胱胲胳胴胵胶胷胸胹胺胻胼能胾胿 +è„€è„脂脃脄脅脆脇脈脉脊脋脌è„è„Žè„è„脑脒脓脔脕脖脗脘脙脚脛脜è„è„žè„Ÿ +脠脡脢脣脤脥脦脧脨脩脪脫脬脭脮脯脰脱脲脳脴脵脶脷脸脹脺脻脼脽脾脿 +è…€è…腂腃腄腅腆腇腈腉腊腋腌è…è…Žè…è…腑腒腓腔腕腖腗腘腙腚腛腜è…è…žè…Ÿ +腠腡腢腣腤腥腦腧腨腩腪腫腬腭腮腯腰腱腲腳腴腵腶腷腸腹腺腻腼腽腾腿 +膀è†è†‚膃膄膅膆膇膈膉膊膋膌è†è†Žè†è†è†‘膒膓膔膕膖膗膘膙膚膛膜è†è†žè†Ÿ +膠膡膢膣膤膥膦膧膨膩膪膫膬膭膮膯膰膱膲膳膴膵膶膷膸膹膺膻膼膽膾膿 +臀è‡è‡‚臃臄臅臆臇臈臉臊臋臌è‡è‡Žè‡è‡è‡‘臒臓臔臕臖臗臘臙臚臛臜è‡è‡žè‡Ÿ +臠臡臢臣臤臥臦臧臨臩自臫臬臭臮臯臰臱臲至致臵臶臷臸臹臺臻臼臽臾臿 +舀èˆèˆ‚舃舄舅舆與興舉舊舋舌èˆèˆŽèˆèˆèˆ‘舒舓舔舕舖舗舘舙舚舛舜èˆèˆžèˆŸ +舠舡舢舣舤舥舦舧舨舩航舫般舭舮舯舰舱舲舳舴舵舶舷舸船舺舻舼舽舾舿 +艀è‰è‰‚艃艄艅艆艇艈艉艊艋艌è‰è‰Žè‰è‰è‰‘艒艓艔艕艖艗艘艙艚艛艜è‰è‰žè‰Ÿ +艠艡艢艣艤艥艦艧艨艩艪艫艬艭艮良艰艱色艳艴艵艶艷艸艹艺艻艼艽艾艿 +芀èŠèŠ‚芃芄芅芆芇芈芉芊芋芌èŠèŠŽèŠèŠèŠ‘芒芓芔芕芖芗芘芙芚芛芜èŠèŠžèŠŸ +芠芡芢芣芤芥芦芧芨芩芪芫芬芭芮芯芰花芲芳芴芵芶芷芸芹芺芻芼芽芾芿 +è‹€è‹è‹‚苃苄苅苆苇苈苉苊苋苌è‹è‹Žè‹è‹è‹‘苒苓苔苕苖苗苘苙苚苛苜è‹è‹žè‹Ÿ +苠苡苢苣苤若苦苧苨苩苪苫苬苭苮苯苰英苲苳苴苵苶苷苸苹苺苻苼苽苾苿 +茀èŒèŒ‚范茄茅茆茇茈茉茊茋茌èŒèŒŽèŒèŒèŒ‘茒茓茔茕茖茗茘茙茚茛茜èŒèŒžèŒŸ +茠茡茢茣茤茥茦茧茨茩茪茫茬茭茮茯茰茱茲茳茴茵茶茷茸茹茺茻茼茽茾茿 +è€èè‚èƒè„è…è†è‡èˆè‰èŠè‹èŒèèŽèèè‘è’è“è”è•è–è—è˜è™èšè›èœèèžèŸ +è è¡è¢è£è¤è¥è¦è§è¨è©èªè«è¬è­è®è¯è°è±è²è³è´èµè¶è·è¸è¹èºè»è¼è½è¾è¿ +莀èŽèŽ‚莃莄莅莆莇莈莉莊莋莌èŽèŽŽèŽèŽèŽ‘莒莓莔莕莖莗莘莙莚莛莜èŽèŽžèŽŸ +莠莡莢莣莤莥莦莧莨莩莪莫莬莭莮莯莰莱莲莳莴莵莶获莸莹莺莻莼莽莾莿 +è€èè‚èƒè„è…è†è‡èˆè‰èŠè‹èŒèèŽèèè‘è’è“è”è•è–è—è˜è™èšè›èœèèžèŸ +è è¡è¢è£è¤è¥è¦è§è¨è©èªè«è¬è­è®è¯è°è±è²è³è´èµè¶è·è¸è¹èºè»è¼è½è¾è¿ +è€èè‚èƒè„è…è†è‡èˆè‰èŠè‹èŒèèŽèèè‘è’è“è”è•è–è—è˜è™èšè›èœèèžèŸ +è è¡è¢è£è¤è¥è¦è§è¨è©èªè«è¬è­è®è¯è°è±è²è³è´èµè¶è·è¸è¹èºè»è¼è½è¾è¿ +è‘€è‘葂葃葄葅葆葇葈葉葊葋葌è‘è‘Žè‘è‘葑葒葓葔葕葖著葘葙葚葛葜è‘è‘žè‘Ÿ +葠葡葢董葤葥葦葧葨葩葪葫葬葭葮葯葰葱葲葳葴葵葶葷葸葹葺葻葼葽葾葿 +è’€è’蒂蒃蒄蒅蒆蒇蒈蒉蒊蒋蒌è’è’Žè’è’蒑蒒蒓蒔蒕蒖蒗蒘蒙蒚蒛蒜è’è’žè’Ÿ +蒠蒡蒢蒣蒤蒥蒦蒧蒨蒩蒪蒫蒬蒭蒮蒯蒰蒱蒲蒳蒴蒵蒶蒷蒸蒹蒺蒻蒼蒽蒾蒿 +è“€è“蓂蓃蓄蓅蓆蓇蓈蓉蓊蓋蓌è“è“Žè“è“蓑蓒蓓蓔蓕蓖蓗蓘蓙蓚蓛蓜è“è“žè“Ÿ +蓠蓡蓢蓣蓤蓥蓦蓧蓨蓩蓪蓫蓬蓭蓮蓯蓰蓱蓲蓳蓴蓵蓶蓷蓸蓹蓺蓻蓼蓽蓾蓿 +蔀è”蔂蔃蔄蔅蔆蔇蔈蔉蔊蔋蔌è”蔎è”è”蔑蔒蔓蔔蔕蔖蔗蔘蔙蔚蔛蔜è”蔞蔟 +蔠蔡蔢蔣蔤蔥蔦蔧蔨蔩蔪蔫蔬蔭蔮蔯蔰蔱蔲蔳蔴蔵蔶蔷蔸蔹蔺蔻蔼蔽蔾蔿 +è•€è•è•‚蕃蕄蕅蕆蕇蕈蕉蕊蕋蕌è•è•Žè•è•è•‘蕒蕓蕔蕕蕖蕗蕘蕙蕚蕛蕜è•è•žè•Ÿ +蕠蕡蕢蕣蕤蕥蕦蕧蕨蕩蕪蕫蕬蕭蕮蕯蕰蕱蕲蕳蕴蕵蕶蕷蕸蕹蕺蕻蕼蕽蕾蕿 +è–€è–薂薃薄薅薆薇薈薉薊薋薌è–è–Žè–è–薑薒薓薔薕薖薗薘薙薚薛薜è–è–žè–Ÿ +薠薡薢薣薤薥薦薧薨薩薪薫薬薭薮薯薰薱薲薳薴薵薶薷薸薹薺薻薼薽薾薿 +è—€è—藂藃藄藅藆藇藈藉藊藋藌è—è—Žè—è—藑藒藓藔藕藖藗藘藙藚藛藜è—è—žè—Ÿ +藠藡藢藣藤藥藦藧藨藩藪藫藬藭藮藯藰藱藲藳藴藵藶藷藸藹藺藻藼藽藾藿 +蘀è˜è˜‚蘃蘄蘅蘆蘇蘈蘉蘊蘋蘌è˜è˜Žè˜è˜è˜‘蘒蘓蘔蘕蘖蘗蘘蘙蘚蘛蘜è˜è˜žè˜Ÿ +蘠蘡蘢蘣蘤蘥蘦蘧蘨蘩蘪蘫蘬蘭蘮蘯蘰蘱蘲蘳蘴蘵蘶蘷蘸蘹蘺蘻蘼蘽蘾蘿 +虀è™è™‚虃虄虅虆虇虈虉虊虋虌è™è™Žè™è™è™‘虒虓虔處虖虗虘虙虚虛虜è™è™žè™Ÿ +虠虡虢虣虤虥虦虧虨虩虪虫虬虭虮虯虰虱虲虳虴虵虶虷虸虹虺虻虼虽虾虿 +蚀èšèš‚蚃蚄蚅蚆蚇蚈蚉蚊蚋蚌èšèšŽèšèšèš‘蚒蚓蚔蚕蚖蚗蚘蚙蚚蚛蚜èšèšžèšŸ +蚠蚡蚢蚣蚤蚥蚦蚧蚨蚩蚪蚫蚬蚭蚮蚯蚰蚱蚲蚳蚴蚵蚶蚷蚸蚹蚺蚻蚼蚽蚾蚿 +蛀è›è›‚蛃蛄蛅蛆蛇蛈蛉蛊蛋蛌è›è›Žè›è›è›‘蛒蛓蛔蛕蛖蛗蛘蛙蛚蛛蛜è›è›žè›Ÿ +蛠蛡蛢蛣蛤蛥蛦蛧蛨蛩蛪蛫蛬蛭蛮蛯蛰蛱蛲蛳蛴蛵蛶蛷蛸蛹蛺蛻蛼蛽蛾蛿 +蜀èœèœ‚蜃蜄蜅蜆蜇蜈蜉蜊蜋蜌èœèœŽèœèœèœ‘蜒蜓蜔蜕蜖蜗蜘蜙蜚蜛蜜èœèœžèœŸ +蜠蜡蜢蜣蜤蜥蜦蜧蜨蜩蜪蜫蜬蜭蜮蜯蜰蜱蜲蜳蜴蜵蜶蜷蜸蜹蜺蜻蜼蜽蜾蜿 +è€èè‚èƒè„è…è†è‡èˆè‰èŠè‹èŒèèŽèèè‘è’è“è”è•è–è—è˜è™èšè›èœèèžèŸ +è è¡è¢è£è¤è¥è¦è§è¨è©èªè«è¬è­è®è¯è°è±è²è³è´èµè¶è·è¸è¹èºè»è¼è½è¾è¿ +螀èžèž‚螃螄螅螆螇螈螉螊螋螌èžèžŽèžèžèž‘螒螓螔螕螖螗螘螙螚螛螜èžèžžèžŸ +螠螡螢螣螤螥螦螧螨螩螪螫螬螭螮螯螰螱螲螳螴螵螶螷螸螹螺螻螼螽螾螿 +蟀èŸèŸ‚蟃蟄蟅蟆蟇蟈蟉蟊蟋蟌èŸèŸŽèŸèŸèŸ‘蟒蟓蟔蟕蟖蟗蟘蟙蟚蟛蟜èŸèŸžèŸŸ +蟠蟡蟢蟣蟤蟥蟦蟧蟨蟩蟪蟫蟬蟭蟮蟯蟰蟱蟲蟳蟴蟵蟶蟷蟸蟹蟺蟻蟼蟽蟾蟿 +è €è è ‚蠃蠄蠅蠆蠇蠈蠉蠊蠋蠌è è Žè è è ‘蠒蠓蠔蠕蠖蠗蠘蠙蠚蠛蠜è è žè Ÿ +蠠蠡蠢蠣蠤蠥蠦蠧蠨蠩蠪蠫蠬蠭蠮蠯蠰蠱蠲蠳蠴蠵蠶蠷蠸蠹蠺蠻蠼蠽蠾蠿 +è¡€è¡è¡‚衃衄衅衆衇衈衉衊衋行è¡è¡Žè¡è¡è¡‘衒術衔衕衖街衘衙衚衛衜è¡è¡žè¡Ÿ +衠衡衢衣衤补衦衧表衩衪衫衬衭衮衯衰衱衲衳衴衵衶衷衸衹衺衻衼衽衾衿 +袀è¢è¢‚袃袄袅袆袇袈袉袊袋袌è¢è¢Žè¢è¢è¢‘袒袓袔袕袖袗袘袙袚袛袜è¢è¢žè¢Ÿ +袠袡袢袣袤袥袦袧袨袩袪被袬袭袮袯袰袱袲袳袴袵袶袷袸袹袺袻袼袽袾袿 +裀è£è£‚裃裄装裆裇裈裉裊裋裌è£è£Žè£è£è£‘裒裓裔裕裖裗裘裙裚裛補è£è£žè£Ÿ +裠裡裢裣裤裥裦裧裨裩裪裫裬裭裮裯裰裱裲裳裴裵裶裷裸裹裺裻裼製裾裿 +褀è¤è¤‚褃褄褅褆複褈褉褊褋褌è¤è¤Žè¤è¤è¤‘褒褓褔褕褖褗褘褙褚褛褜è¤è¤žè¤Ÿ +褠褡褢褣褤褥褦褧褨褩褪褫褬褭褮褯褰褱褲褳褴褵褶褷褸褹褺褻褼褽褾褿 +襀è¥è¥‚襃襄襅襆襇襈襉襊襋襌è¥è¥Žè¥è¥è¥‘襒襓襔襕襖襗襘襙襚襛襜è¥è¥žè¥Ÿ +襠襡襢襣襤襥襦襧襨襩襪襫襬襭襮襯襰襱襲襳襴襵襶襷襸襹襺襻襼襽襾西 +覀è¦è¦‚覃覄覅覆覇覈覉覊見覌è¦è¦Žè¦è¦è¦‘覒覓覔覕視覗覘覙覚覛覜è¦è¦žè¦Ÿ +覠覡覢覣覤覥覦覧覨覩親覫覬覭覮覯覰覱覲観覴覵覶覷覸覹覺覻覼覽覾覿 +觀è§è§‚觃规觅视觇览觉觊觋觌è§è§Žè§è§è§‘角觓觔觕觖觗觘觙觚觛觜è§è§žè§Ÿ +觠觡觢解觤觥触觧觨觩觪觫觬觭觮觯觰觱觲觳觴觵觶觷觸觹觺觻觼觽觾觿 +言è¨è¨‚訃訄訅訆訇計訉訊訋訌è¨è¨Žè¨è¨è¨‘訒訓訔訕訖託記訙訚訛訜è¨è¨žè¨Ÿ +訠訡訢訣訤訥訦訧訨訩訪訫訬設訮訯訰許訲訳訴訵訶訷訸訹診註証訽訾訿 +è©€è©è©‚詃詄詅詆詇詈詉詊詋詌è©è©Žè©è©è©‘詒詓詔評詖詗詘詙詚詛詜è©è©žè©Ÿ +詠詡詢詣詤詥試詧詨詩詪詫詬詭詮詯詰話該詳詴詵詶詷詸詹詺詻詼詽詾詿 +誀èªèª‚誃誄誅誆誇誈誉誊誋誌èªèªŽèªèªèª‘誒誓誔誕誖誗誘誙誚誛誜èªèªžèªŸ +誠誡誢誣誤誥誦誧誨誩說誫説読誮誯誰誱課誳誴誵誶誷誸誹誺誻誼誽誾調 +è«€è«è«‚諃諄諅諆談諈諉諊請諌è«è«Žè«è«è«‘諒諓諔諕論諗諘諙諚諛諜è«è«žè«Ÿ +諠諡諢諣諤諥諦諧諨諩諪諫諬諭諮諯諰諱諲諳諴諵諶諷諸諹諺諻諼諽諾諿 +謀è¬è¬‚謃謄謅謆謇謈謉謊謋謌è¬è¬Žè¬è¬è¬‘謒謓謔謕謖謗謘謙謚講謜è¬è¬žè¬Ÿ +謠謡謢謣謤謥謦謧謨謩謪謫謬謭謮謯謰謱謲謳謴謵謶謷謸謹謺謻謼謽謾謿 +è­€è­è­‚譃譄譅譆譇譈證譊譋譌è­è­Žè­è­è­‘譒譓譔譕譖譗識譙譚譛譜è­è­žè­Ÿ +譠譡譢譣譤譥警譧譨譩譪譫譬譭譮譯議譱譲譳譴譵譶護譸譹譺譻譼譽譾譿 +讀è®è®‚讃讄讅讆讇讈讉變讋讌è®è®Žè®è®è®‘讒讓讔讕讖讗讘讙讚讛讜è®è®žè®Ÿ +讠计订讣认讥讦讧讨让讪讫讬训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访 +诀è¯è¯‚诃评诅识诇诈诉诊诋诌è¯è¯Žè¯è¯è¯‘诒诓诔试诖诗诘诙诚诛诜è¯è¯žè¯Ÿ +诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿 +è°€è°è°‚调谄谅谆谇谈谉谊谋谌è°è°Žè°è°è°‘谒谓谔谕谖谗谘谙谚谛谜è°è°žè°Ÿ +谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷谸谹谺谻谼谽谾谿 +è±€è±è±‚豃豄豅豆豇豈豉豊豋豌è±è±Žè±è±è±‘豒豓豔豕豖豗豘豙豚豛豜è±è±žè±Ÿ +豠象豢豣豤豥豦豧豨豩豪豫豬豭豮豯豰豱豲豳豴豵豶豷豸豹豺豻豼豽豾豿 +è²€è²è²‚貃貄貅貆貇貈貉貊貋貌è²è²Žè²è²è²‘貒貓貔貕貖貗貘貙貚貛貜è²è²žè²Ÿ +負財貢貣貤貥貦貧貨販貪貫責貭貮貯貰貱貲貳貴貵貶買貸貹貺費貼貽貾貿 +è³€è³è³‚賃賄賅賆資賈賉賊賋賌è³è³Žè³è³è³‘賒賓賔賕賖賗賘賙賚賛賜è³è³žè³Ÿ +賠賡賢賣賤賥賦賧賨賩質賫賬賭賮賯賰賱賲賳賴賵賶賷賸賹賺賻購賽賾賿 +è´€è´è´‚贃贄贅贆贇贈贉贊贋贌è´è´Žè´è´è´‘贒贓贔贕贖贗贘贙贚贛贜è´è´žè´Ÿ +贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿 +èµ€èµèµ‚赃资赅赆赇赈赉赊赋赌èµèµŽèµèµèµ‘赒赓赔赕赖赗赘赙赚赛赜èµèµžèµŸ +赠赡赢赣赤赥赦赧赨赩赪赫赬赭赮赯走赱赲赳赴赵赶起赸赹赺赻赼赽赾赿 +趀è¶è¶‚趃趄超趆趇趈趉越趋趌è¶è¶Žè¶è¶è¶‘趒趓趔趕趖趗趘趙趚趛趜è¶è¶žè¶Ÿ +趠趡趢趣趤趥趦趧趨趩趪趫趬趭趮趯趰趱趲足趴趵趶趷趸趹趺趻趼趽趾趿 +è·€è·è·‚跃跄跅跆跇跈跉跊跋跌è·è·Žè·è·è·‘跒跓跔跕跖跗跘跙跚跛跜è·è·žè·Ÿ +跠跡跢跣跤跥跦跧跨跩跪跫跬跭跮路跰跱跲跳跴践跶跷跸跹跺跻跼跽跾跿 +踀è¸è¸‚踃踄踅踆踇踈踉踊踋踌è¸è¸Žè¸è¸è¸‘踒踓踔踕踖踗踘踙踚踛踜è¸è¸žè¸Ÿ +踠踡踢踣踤踥踦踧踨踩踪踫踬踭踮踯踰踱踲踳踴踵踶踷踸踹踺踻踼踽踾踿 +è¹€è¹è¹‚蹃蹄蹅蹆蹇蹈蹉蹊蹋蹌è¹è¹Žè¹è¹è¹‘蹒蹓蹔蹕蹖蹗蹘蹙蹚蹛蹜è¹è¹žè¹Ÿ +蹠蹡蹢蹣蹤蹥蹦蹧蹨蹩蹪蹫蹬蹭蹮蹯蹰蹱蹲蹳蹴蹵蹶蹷蹸蹹蹺蹻蹼蹽蹾蹿 +躀èºèº‚躃躄躅躆躇躈躉躊躋躌èºèºŽèºèºèº‘躒躓躔躕躖躗躘躙躚躛躜èºèºžèºŸ +躠躡躢躣躤躥躦躧躨躩躪身躬躭躮躯躰躱躲躳躴躵躶躷躸躹躺躻躼躽躾躿 +軀è»è»‚軃軄軅軆軇軈軉車軋軌è»è»Žè»è»è»‘軒軓軔軕軖軗軘軙軚軛軜è»è»žè»Ÿ +軠軡転軣軤軥軦軧軨軩軪軫軬軭軮軯軰軱軲軳軴軵軶軷軸軹軺軻軼軽軾軿 +è¼€è¼è¼‚較輄輅輆輇輈載輊輋輌è¼è¼Žè¼è¼è¼‘輒輓輔輕輖輗輘輙輚輛輜è¼è¼žè¼Ÿ +輠輡輢輣輤輥輦輧輨輩輪輫輬輭輮輯輰輱輲輳輴輵輶輷輸輹輺輻輼輽輾輿 +è½€è½è½‚轃轄轅轆轇轈轉轊轋轌è½è½Žè½è½è½‘轒轓轔轕轖轗轘轙轚轛轜è½è½žè½Ÿ +轠轡轢轣轤轥车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿 +è¾€è¾è¾‚较辄辅辆辇辈辉辊辋辌è¾è¾Žè¾è¾è¾‘辒输辔辕辖辗辘辙辚辛辜è¾è¾žè¾Ÿ +辠辡辢辣辤辥辦辧辨辩辪辫辬辭辮辯辰辱農辳辴辵辶辷辸边辺辻込辽达辿 +è¿€è¿è¿‚迃迄迅迆过迈迉迊迋迌è¿è¿Žè¿è¿è¿‘迒迓返迕迖迗还这迚进远è¿è¿žè¿Ÿ +迠迡迢迣迤迥迦迧迨迩迪迫迬迭迮迯述迱迲迳迴迵迶迷迸迹迺迻迼追迾迿 +退é€é€‚逃逄逅逆逇逈选逊逋逌é€é€Žé€é€é€‘递逓途逕逖逗逘這通逛逜é€é€žé€Ÿ +造逡逢連逤逥逦逧逨逩逪逫逬逭逮逯逰週進逳逴逵逶逷逸逹逺逻逼逽逾逿 +é€éé‚éƒé„é…é†é‡éˆé‰éŠé‹éŒééŽééé‘é’é“é”é•é–é—é˜é™éšé›éœééžéŸ +é é¡é¢é£é¤é¥é¦é§é¨é©éªé«é¬é­é®é¯é°é±é²é³é´éµé¶é·é¸é¹éºé»é¼é½é¾é¿ +é‚€é‚邂邃還邅邆邇邈邉邊邋邌é‚é‚Žé‚é‚邑邒邓邔邕邖邗邘邙邚邛邜é‚é‚žé‚Ÿ +邠邡邢那邤邥邦邧邨邩邪邫邬邭邮邯邰邱邲邳邴邵邶邷邸邹邺邻邼邽邾邿 +郀éƒéƒ‚郃郄郅郆郇郈郉郊郋郌éƒéƒŽéƒéƒéƒ‘郒郓郔郕郖郗郘郙郚郛郜éƒéƒžéƒŸ +郠郡郢郣郤郥郦郧部郩郪郫郬郭郮郯郰郱郲郳郴郵郶郷郸郹郺郻郼都郾郿 +é„€é„鄂鄃鄄鄅鄆鄇鄈鄉鄊鄋鄌é„é„Žé„é„鄑鄒鄓鄔鄕鄖鄗鄘鄙鄚鄛鄜é„é„žé„Ÿ +鄠鄡鄢鄣鄤鄥鄦鄧鄨鄩鄪鄫鄬鄭鄮鄯鄰鄱鄲鄳鄴鄵鄶鄷鄸鄹鄺鄻鄼鄽鄾鄿 +é…€é…酂酃酄酅酆酇酈酉酊酋酌é…é…Žé…é…酑酒酓酔酕酖酗酘酙酚酛酜é…é…žé…Ÿ +酠酡酢酣酤酥酦酧酨酩酪酫酬酭酮酯酰酱酲酳酴酵酶酷酸酹酺酻酼酽酾酿 +醀é†é†‚醃醄醅醆醇醈醉醊醋醌é†é†Žé†é†é†‘醒醓醔醕醖醗醘醙醚醛醜é†é†žé†Ÿ +醠醡醢醣醤醥醦醧醨醩醪醫醬醭醮醯醰醱醲醳醴醵醶醷醸醹醺醻醼醽醾醿 +釀é‡é‡‚釃釄釅釆采釈釉释釋里é‡é‡Žé‡é‡é‡‘釒釓釔釕釖釗釘釙釚釛釜é‡é‡žé‡Ÿ +釠釡釢釣釤釥釦釧釨釩釪釫釬釭釮釯釰釱釲釳釴釵釶釷釸釹釺釻釼釽釾釿 +鈀éˆéˆ‚鈃鈄鈅鈆鈇鈈鈉鈊鈋鈌éˆéˆŽéˆéˆéˆ‘鈒鈓鈔鈕鈖鈗鈘鈙鈚鈛鈜éˆéˆžéˆŸ +鈠鈡鈢鈣鈤鈥鈦鈧鈨鈩鈪鈫鈬鈭鈮鈯鈰鈱鈲鈳鈴鈵鈶鈷鈸鈹鈺鈻鈼鈽鈾鈿 +鉀é‰é‰‚鉃鉄鉅鉆鉇鉈鉉鉊鉋鉌é‰é‰Žé‰é‰é‰‘鉒鉓鉔鉕鉖鉗鉘鉙鉚鉛鉜é‰é‰žé‰Ÿ +鉠鉡鉢鉣鉤鉥鉦鉧鉨鉩鉪鉫鉬鉭鉮鉯鉰鉱鉲鉳鉴鉵鉶鉷鉸鉹鉺鉻鉼鉽鉾鉿 +銀éŠéŠ‚銃銄銅銆銇銈銉銊銋銌éŠéŠŽéŠéŠéŠ‘銒銓銔銕銖銗銘銙銚銛銜éŠéŠžéŠŸ +銠銡銢銣銤銥銦銧銨銩銪銫銬銭銮銯銰銱銲銳銴銵銶銷銸銹銺銻銼銽銾銿 +é‹€é‹é‹‚鋃鋄鋅鋆鋇鋈鋉鋊鋋鋌é‹é‹Žé‹é‹é‹‘鋒鋓鋔鋕鋖鋗鋘鋙鋚鋛鋜é‹é‹žé‹Ÿ +鋠鋡鋢鋣鋤鋥鋦鋧鋨鋩鋪鋫鋬鋭鋮鋯鋰鋱鋲鋳鋴鋵鋶鋷鋸鋹鋺鋻鋼鋽鋾鋿 +錀éŒéŒ‚錃錄錅錆錇錈錉錊錋錌éŒéŒŽéŒéŒéŒ‘錒錓錔錕錖錗錘錙錚錛錜éŒéŒžéŒŸ +錠錡錢錣錤錥錦錧錨錩錪錫錬錭錮錯錰錱録錳錴錵錶錷錸錹錺錻錼錽錾錿 +é€éé‚éƒé„é…é†é‡éˆé‰éŠé‹éŒééŽééé‘é’é“é”é•é–é—é˜é™éšé›éœééžéŸ +é é¡é¢é£é¤é¥é¦é§é¨é©éªé«é¬é­é®é¯é°é±é²é³é´éµé¶é·é¸é¹éºé»é¼é½é¾é¿ +鎀éŽéŽ‚鎃鎄鎅鎆鎇鎈鎉鎊鎋鎌éŽéŽŽéŽéŽéŽ‘鎒鎓鎔鎕鎖鎗鎘鎙鎚鎛鎜éŽéŽžéŽŸ +鎠鎡鎢鎣鎤鎥鎦鎧鎨鎩鎪鎫鎬鎭鎮鎯鎰鎱鎲鎳鎴鎵鎶鎷鎸鎹鎺鎻鎼鎽鎾鎿 +é€éé‚éƒé„é…é†é‡éˆé‰éŠé‹éŒééŽééé‘é’é“é”é•é–é—é˜é™éšé›éœééžéŸ +é é¡é¢é£é¤é¥é¦é§é¨é©éªé«é¬é­é®é¯é°é±é²é³é´éµé¶é·é¸é¹éºé»é¼é½é¾é¿ +é€éé‚éƒé„é…é†é‡éˆé‰éŠé‹éŒééŽééé‘é’é“é”é•é–é—é˜é™éšé›éœééžéŸ +é é¡é¢é£é¤é¥é¦é§é¨é©éªé«é¬é­é®é¯é°é±é²é³é´éµé¶é·é¸é¹éºé»é¼é½é¾é¿ +é‘€é‘鑂鑃鑄鑅鑆鑇鑈鑉鑊鑋鑌é‘é‘Žé‘é‘鑑鑒鑓鑔鑕鑖鑗鑘鑙鑚鑛鑜é‘é‘žé‘Ÿ +鑠鑡鑢鑣鑤鑥鑦鑧鑨鑩鑪鑫鑬鑭鑮鑯鑰鑱鑲鑳鑴鑵鑶鑷鑸鑹鑺鑻鑼鑽鑾鑿 +é’€é’钂钃钄钅钆钇针钉钊钋钌é’é’Žé’é’钑钒钓钔钕钖钗钘钙钚钛钜é’é’žé’Ÿ +钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿 +é“€é“铂铃铄铅铆铇铈铉铊铋铌é“é“Žé“é“铑铒铓铔铕铖铗铘铙铚铛铜é“é“žé“Ÿ +铠铡铢铣铤铥铦铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿 +销é”锂锃锄锅锆锇锈锉锊锋锌é”锎é”é”锑锒锓锔锕锖锗锘错锚锛锜é”锞锟 +锠锡锢锣锤锥锦锧锨锩锪锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿 +é•€é•é•‚镃镄镅镆镇镈镉镊镋镌é•é•Žé•é•é•‘镒镓镔镕镖镗镘镙镚镛镜é•é•žé•Ÿ +镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镵镶長镸镹镺镻镼镽镾长 +é–€é–閂閃閄閅閆閇閈閉閊開閌é–é–Žé–é–閑閒間閔閕閖閗閘閙閚閛閜é–é–žé–Ÿ +閠閡関閣閤閥閦閧閨閩閪閫閬閭閮閯閰閱閲閳閴閵閶閷閸閹閺閻閼閽閾閿 +é—€é—闂闃闄闅闆闇闈闉闊闋闌é—é—Žé—é—闑闒闓闔闕闖闗闘闙闚闛關é—é—žé—Ÿ +闠闡闢闣闤闥闦闧门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿 +阀é˜é˜‚阃阄阅阆阇阈阉阊阋阌é˜é˜Žé˜é˜é˜‘阒阓阔阕阖阗阘阙阚阛阜é˜é˜žé˜Ÿ +阠阡阢阣阤阥阦阧阨阩阪阫阬阭阮阯阰阱防阳阴阵阶阷阸阹阺阻阼阽阾阿 +陀é™é™‚陃附际陆陇陈陉陊陋陌é™é™Žé™é™é™‘陒陓陔陕陖陗陘陙陚陛陜é™é™žé™Ÿ +陠陡院陣除陥陦陧陨险陪陫陬陭陮陯陰陱陲陳陴陵陶陷陸陹険陻陼陽陾陿 +隀éšéš‚隃隄隅隆隇隈隉隊隋隌éšéšŽéšéšéš‘隒隓隔隕隖隗隘隙隚際障éšéšžéšŸ +隠隡隢隣隤隥隦隧隨隩險隫隬隭隮隯隰隱隲隳隴隵隶隷隸隹隺隻隼隽难隿 +雀é›é›‚雃雄雅集雇雈雉雊雋雌é›é›Žé›é›é›‘雒雓雔雕雖雗雘雙雚雛雜é›é›žé›Ÿ +雠雡離難雤雥雦雧雨雩雪雫雬雭雮雯雰雱雲雳雴雵零雷雸雹雺電雼雽雾雿 +需éœéœ‚霃霄霅霆震霈霉霊霋霌éœéœŽéœéœéœ‘霒霓霔霕霖霗霘霙霚霛霜éœéœžéœŸ +霠霡霢霣霤霥霦霧霨霩霪霫霬霭霮霯霰霱露霳霴霵霶霷霸霹霺霻霼霽霾霿 +é€éé‚éƒé„é…é†é‡éˆé‰éŠé‹éŒééŽééé‘é’é“é”é•é–é—é˜é™éšé›éœééžéŸ +é é¡é¢é£é¤é¥é¦é§é¨é©éªé«é¬é­é®é¯é°é±é²é³é´éµé¶é·é¸é¹éºé»é¼é½é¾é¿ +鞀éžéž‚鞃鞄鞅鞆鞇鞈鞉鞊鞋鞌éžéžŽéžéžéž‘鞒鞓鞔鞕鞖鞗鞘鞙鞚鞛鞜éžéžžéžŸ +鞠鞡鞢鞣鞤鞥鞦鞧鞨鞩鞪鞫鞬鞭鞮鞯鞰鞱鞲鞳鞴鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿 +韀éŸéŸ‚韃韄韅韆韇韈韉韊韋韌éŸéŸŽéŸéŸéŸ‘韒韓韔韕韖韗韘韙韚韛韜éŸéŸžéŸŸ +韠韡韢韣韤韥韦韧韨韩韪韫韬韭韮韯韰韱韲音韴韵韶韷韸韹韺韻韼韽韾響 +é €é é ‚頃頄項順頇須頉頊頋頌é é Žé é é ‘頒頓頔頕頖頗領頙頚頛頜é é žé Ÿ +頠頡頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿 +é¡€é¡é¡‚顃顄顅顆顇顈顉顊顋題é¡é¡Žé¡é¡é¡‘顒顓顔顕顖顗願顙顚顛顜é¡é¡žé¡Ÿ +顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮顯顰顱顲顳顴页顶顷顸项顺须顼顽顾顿 +颀é¢é¢‚颃预颅领颇颈颉颊颋颌é¢é¢Žé¢é¢é¢‘颒颓颔颕颖颗题颙颚颛颜é¢é¢žé¢Ÿ +颠颡颢颣颤颥颦颧風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿 +飀é£é£‚飃飄飅飆飇飈飉飊飋飌é£é£Žé£é£é£‘飒飓飔飕飖飗飘飙飚飛飜é£é£žé£Ÿ +飠飡飢飣飤飥飦飧飨飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿 +餀é¤é¤‚餃餄餅餆餇餈餉養餋餌é¤é¤Žé¤é¤é¤‘餒餓餔餕餖餗餘餙餚餛餜é¤é¤žé¤Ÿ +餠餡餢餣餤餥餦餧館餩餪餫餬餭餮餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿 +饀é¥é¥‚饃饄饅饆饇饈饉饊饋饌é¥é¥Žé¥é¥é¥‘饒饓饔饕饖饗饘饙饚饛饜é¥é¥žé¥Ÿ +饠饡饢饣饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿 +馀é¦é¦‚馃馄馅馆馇馈馉馊馋馌é¦é¦Žé¦é¦é¦‘馒馓馔馕首馗馘香馚馛馜é¦é¦žé¦Ÿ +馠馡馢馣馤馥馦馧馨馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿 +駀é§é§‚駃駄駅駆駇駈駉駊駋駌é§é§Žé§é§é§‘駒駓駔駕駖駗駘駙駚駛駜é§é§žé§Ÿ +駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹駺駻駼駽駾駿 +騀é¨é¨‚騃騄騅騆騇騈騉騊騋騌é¨é¨Žé¨é¨é¨‘騒験騔騕騖騗騘騙騚騛騜é¨é¨žé¨Ÿ +騠騡騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸騹騺騻騼騽騾騿 +é©€é©é©‚驃驄驅驆驇驈驉驊驋驌é©é©Žé©é©é©‘驒驓驔驕驖驗驘驙驚驛驜é©é©žé©Ÿ +驠驡驢驣驤驥驦驧驨驩驪驫马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿 +骀éªéª‚骃骄骅骆骇骈骉骊骋验éªéªŽéªéªéª‘骒骓骔骕骖骗骘骙骚骛骜éªéªžéªŸ +骠骡骢骣骤骥骦骧骨骩骪骫骬骭骮骯骰骱骲骳骴骵骶骷骸骹骺骻骼骽骾骿 +é«€é«é«‚髃髄髅髆髇髈髉髊髋髌é«é«Žé«é«é«‘髒髓體髕髖髗高髙髚髛髜é«é«žé«Ÿ +髠髡髢髣髤髥髦髧髨髩髪髫髬髭髮髯髰髱髲髳髴髵髶髷髸髹髺髻髼髽髾髿 +鬀é¬é¬‚鬃鬄鬅鬆鬇鬈鬉鬊鬋鬌é¬é¬Žé¬é¬é¬‘鬒鬓鬔鬕鬖鬗鬘鬙鬚鬛鬜é¬é¬žé¬Ÿ +鬠鬡鬢鬣鬤鬥鬦鬧鬨鬩鬪鬫鬬鬭鬮鬯鬰鬱鬲鬳鬴鬵鬶鬷鬸鬹鬺鬻鬼鬽鬾鬿 +é­€é­é­‚魃魄魅魆魇魈魉魊魋魌é­é­Žé­é­é­‘魒魓魔魕魖魗魘魙魚魛魜é­é­žé­Ÿ +魠魡魢魣魤魥魦魧魨魩魪魫魬魭魮魯魰魱魲魳魴魵魶魷魸魹魺魻魼魽魾魿 +鮀é®é®‚鮃鮄鮅鮆鮇鮈鮉鮊鮋鮌é®é®Žé®é®é®‘鮒鮓鮔鮕鮖鮗鮘鮙鮚鮛鮜é®é®žé®Ÿ +鮠鮡鮢鮣鮤鮥鮦鮧鮨鮩鮪鮫鮬鮭鮮鮯鮰鮱鮲鮳鮴鮵鮶鮷鮸鮹鮺鮻鮼鮽鮾鮿 +鯀é¯é¯‚鯃鯄鯅鯆鯇鯈鯉鯊鯋鯌é¯é¯Žé¯é¯é¯‘鯒鯓鯔鯕鯖鯗鯘鯙鯚鯛鯜é¯é¯žé¯Ÿ +鯠鯡鯢鯣鯤鯥鯦鯧鯨鯩鯪鯫鯬鯭鯮鯯鯰鯱鯲鯳鯴鯵鯶鯷鯸鯹鯺鯻鯼鯽鯾鯿 +é°€é°é°‚鰃鰄鰅鰆鰇鰈鰉鰊鰋鰌é°é°Žé°é°é°‘鰒鰓鰔鰕鰖鰗鰘鰙鰚鰛鰜é°é°žé°Ÿ +鰠鰡鰢鰣鰤鰥鰦鰧鰨鰩鰪鰫鰬鰭鰮鰯鰰鰱鰲鰳鰴鰵鰶鰷鰸鰹鰺鰻鰼鰽鰾鰿 +é±€é±é±‚鱃鱄鱅鱆鱇鱈鱉鱊鱋鱌é±é±Žé±é±é±‘鱒鱓鱔鱕鱖鱗鱘鱙鱚鱛鱜é±é±žé±Ÿ +鱠鱡鱢鱣鱤鱥鱦鱧鱨鱩鱪鱫鱬鱭鱮鱯鱰鱱鱲鱳鱴鱵鱶鱷鱸鱹鱺鱻鱼鱽鱾鱿 +é²€é²é²‚鲃鲄鲅鲆鲇鲈鲉鲊鲋鲌é²é²Žé²é²é²‘鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜é²é²žé²Ÿ +鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿 +é³€é³é³‚鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌é³é³Žé³é³é³‘鳒鳓鳔鳕鳖鳗鳘鳙鳚鳛鳜é³é³žé³Ÿ +鳠鳡鳢鳣鳤鳥鳦鳧鳨鳩鳪鳫鳬鳭鳮鳯鳰鳱鳲鳳鳴鳵鳶鳷鳸鳹鳺鳻鳼鳽鳾鳿 +é´€é´é´‚鴃鴄鴅鴆鴇鴈鴉鴊鴋鴌é´é´Žé´é´é´‘鴒鴓鴔鴕鴖鴗鴘鴙鴚鴛鴜é´é´žé´Ÿ +鴠鴡鴢鴣鴤鴥鴦鴧鴨鴩鴪鴫鴬鴭鴮鴯鴰鴱鴲鴳鴴鴵鴶鴷鴸鴹鴺鴻鴼鴽鴾鴿 +éµ€éµéµ‚鵃鵄鵅鵆鵇鵈鵉鵊鵋鵌éµéµŽéµéµéµ‘鵒鵓鵔鵕鵖鵗鵘鵙鵚鵛鵜éµéµžéµŸ +鵠鵡鵢鵣鵤鵥鵦鵧鵨鵩鵪鵫鵬鵭鵮鵯鵰鵱鵲鵳鵴鵵鵶鵷鵸鵹鵺鵻鵼鵽鵾鵿 +鶀é¶é¶‚鶃鶄鶅鶆鶇鶈鶉鶊鶋鶌é¶é¶Žé¶é¶é¶‘鶒鶓鶔鶕鶖鶗鶘鶙鶚鶛鶜é¶é¶žé¶Ÿ +鶠鶡鶢鶣鶤鶥鶦鶧鶨鶩鶪鶫鶬鶭鶮鶯鶰鶱鶲鶳鶴鶵鶶鶷鶸鶹鶺鶻鶼鶽鶾鶿 +é·€é·é·‚鷃鷄鷅鷆鷇鷈鷉鷊鷋鷌é·é·Žé·é·é·‘鷒鷓鷔鷕鷖鷗鷘鷙鷚鷛鷜é·é·žé·Ÿ +鷠鷡鷢鷣鷤鷥鷦鷧鷨鷩鷪鷫鷬鷭鷮鷯鷰鷱鷲鷳鷴鷵鷶鷷鷸鷹鷺鷻鷼鷽鷾鷿 +鸀é¸é¸‚鸃鸄鸅鸆鸇鸈鸉鸊鸋鸌é¸é¸Žé¸é¸é¸‘鸒鸓鸔鸕鸖鸗鸘鸙鸚鸛鸜é¸é¸žé¸Ÿ +鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿 +é¹€é¹é¹‚鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌é¹é¹Žé¹é¹é¹‘鹒鹓鹔鹕鹖鹗鹘鹙鹚鹛鹜é¹é¹žé¹Ÿ +鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹮鹯鹰鹱鹲鹳鹴鹵鹶鹷鹸鹹鹺鹻鹼鹽鹾鹿 +麀éºéº‚麃麄麅麆麇麈麉麊麋麌éºéºŽéºéºéº‘麒麓麔麕麖麗麘麙麚麛麜éºéºžéºŸ +麠麡麢麣麤麥麦麧麨麩麪麫麬麭麮麯麰麱麲麳麴麵麶麷麸麹麺麻麼麽麾麿 +黀é»é»‚黃黄黅黆黇黈黉黊黋黌é»é»Žé»é»é»‘黒黓黔黕黖黗默黙黚黛黜é»é»žé»Ÿ +黠黡黢黣黤黥黦黧黨黩黪黫黬黭黮黯黰黱黲黳黴黵黶黷黸黹黺黻黼黽黾黿 +é¼€é¼é¼‚鼃鼄鼅鼆鼇鼈鼉鼊鼋鼌é¼é¼Žé¼é¼é¼‘鼒鼓鼔鼕鼖鼗鼘鼙鼚鼛鼜é¼é¼žé¼Ÿ +鼠鼡鼢鼣鼤鼥鼦鼧鼨鼩鼪鼫鼬鼭鼮鼯鼰鼱鼲鼳鼴鼵鼶鼷鼸鼹鼺鼻鼼鼽鼾鼿 +é½€é½é½‚齃齄齅齆齇齈齉齊齋齌é½é½Žé½é½é½‘齒齓齔齕齖齗齘齙齚齛齜é½é½žé½Ÿ +齠齡齢齣齤齥齦齧齨齩齪齫齬齭齮齯齰齱齲齳齴齵齶齷齸齹齺齻齼齽齾齿 +é¾€é¾é¾‚龃龄龅龆龇龈龉龊龋龌é¾é¾Žé¾é¾é¾‘龒龓龔龕龖龗龘龙龚龛龜é¾é¾žé¾Ÿ +龠龡龢龣龤龥龦龧龨龩龪龫龬龭龮龯龰龱龲龳龴龵龶龷龸龹龺龻龼龽龾龿 +é¿€é¿é¿‚鿃鿄鿅鿆鿇鿈鿉鿊鿋鿌é¿é¿Žé¿é¿é¿‘鿒鿓鿔鿕鿖鿗鿘鿙鿚鿛鿜é¿é¿žé¿Ÿ +鿠鿡鿢鿣鿤鿥鿦鿧鿨鿩鿪鿫鿬鿭鿮鿯鿰鿱鿲鿳鿴鿵鿶鿷鿸鿹鿺鿻鿼鿽鿾鿿 + +Yi Syllables (U+A000-U+A48F): + +ꀀê€ê€‚ꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌê€ê€Žê€ê€ê€‘ꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜê€ê€žê€Ÿ +ꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿ +ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸ +ê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ +ê‚€ê‚ꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌê‚ê‚Žê‚ê‚ꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜê‚ê‚žê‚Ÿ +ꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿ +ꃀêƒêƒ‚ꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌêƒêƒŽêƒêƒêƒ‘ꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜêƒêƒžêƒŸ +ꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿ +ê„€ê„ꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌê„ê„Žê„ê„ꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜê„ê„žê„Ÿ +ꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿ +ê…€ê…ꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌê…ê…Žê…ê…ꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜê…ê…žê…Ÿ +ꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿ +ꆀê†ê†‚ꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌê†ê†Žê†ê†ê†‘ꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜê†ê†žê†Ÿ +ꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿ +ꇀê‡ê‡‚ꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌê‡ê‡Žê‡ê‡ê‡‘ꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜê‡ê‡žê‡Ÿ +ꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿ +ꈀêˆêˆ‚ꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌêˆêˆŽêˆêˆêˆ‘ꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜêˆêˆžêˆŸ +ꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿ +ꉀê‰ê‰‚ꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌê‰ê‰Žê‰ê‰ê‰‘ꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜê‰ê‰žê‰Ÿ +ꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿ +ꊀêŠêŠ‚ꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌêŠêŠŽêŠêŠêŠ‘ꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜêŠêŠžêŠŸ +ꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿ +ê‹€ê‹ê‹‚ꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌê‹ê‹Žê‹ê‹ê‹‘ꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜê‹ê‹žê‹Ÿ +ꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿ +ꌀêŒêŒ‚ꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌêŒêŒŽêŒêŒêŒ‘ꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜêŒêŒžêŒŸ +ꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿ +ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸ +ê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ +ꎀêŽêŽ‚ꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌêŽêŽŽêŽêŽêŽ‘ꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜêŽêŽžêŽŸ +ꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿ +ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸ +ê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ +ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸ +ê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ +ê‘€ê‘ꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌê‘ê‘Žê‘ê‘ꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜê‘ê‘žê‘Ÿ +ꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿ +ê’€ê’ꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌê’ê’Žê’ + +Yi Radicals (U+A490-U+A4CF): + +ê’꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜ê’꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯ +꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀ê“꓂꓃꓄꓅꓆꓇꓈꓉꓊꓋꓌ê“ê“Žê“ + +Free block (U+A4D0-U+ABFF): + +ê“ꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜê“ꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ꓾꓿ꔀê”ꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌê”ê”Žê” +ê”ꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜê”ꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀê•ê•‚ꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌê•ê•Žê• +ê•ê•‘ꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜê•ê•žê•Ÿê• ê•¡ê•¢ê•£ê•¤ê•¥ê•¦ê•§ê•¨ê•©ê•ªê•«ê•¬ê•­ê•®ê•¯ê•°ê•±ê•²ê•³ê•´ê•µê•¶ê•·ê•¸ê•¹ê•ºê•»ê•¼ê•½ê•¾ê•¿ê–€ê–ꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌê–ê–Žê– +ê–ꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜê–ꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀê—ꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌê—ê—Žê— +ê—ꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜê—ꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀê˜ê˜‚ꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌê˜ê˜Žê˜ +ê˜ê˜‘ꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜê˜ê˜žê˜Ÿê˜ ê˜¡ê˜¢ê˜£ê˜¤ê˜¥ê˜¦ê˜§ê˜¨ê˜©ê˜ªê˜«ê˜¬ê˜­ê˜®ê˜¯ê˜°ê˜±ê˜²ê˜³ê˜´ê˜µê˜¶ê˜·ê˜¸ê˜¹ê˜ºê˜»ê˜¼ê˜½ê˜¾ê˜¿ê™€ê™ê™‚ꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌê™ê™Žê™ +ê™ê™‘ꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜê™ê™žê™Ÿê™ ê™¡ê™¢ê™£ê™¤ê™¥ê™¦ê™§ê™¨ê™©ê™ªê™«ê™¬ê™­ê™®ê™¯ê™°ê™±ê™²ê™³ê™´ê™µê™¶ê™·ê™¸ê™¹ê™ºê™»ê™¼ê™½ê™¾ê™¿êš€êšêš‚ꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌêšêšŽêš +êšêš‘ꚒꚓꚔꚕꚖꚗꚘꚙꚚꚛꚜêšêšžêšŸêš êš¡êš¢êš£êš¤êš¥êš¦êš§êš¨êš©êšªêš«êš¬êš­êš®êš¯êš°êš±êš²êš³êš´êšµêš¶êš·êš¸êš¹êšºêš»êš¼êš½êš¾êš¿ê›€ê›ê›‚ꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌê›ê›Žê› +ê›ê›‘ꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜê›ê›žê›Ÿê› ê›¡ê›¢ê›£ê›¤ê›¥ê›¦ê›§ê›¨ê›©ê›ªê›«ê›¬ê›­ê›®ê›¯ê›°ê›±ê›²ê›³ê›´ê›µê›¶ê›·ê›¸ê›¹ê›ºê›»ê›¼ê›½ê›¾ê›¿êœ€êœêœ‚꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌êœêœŽêœ +êœêœ‘꜒꜓꜔꜕꜖ꜗꜘꜙꜚꜛꜜêœêœžêœŸêœ êœ¡êœ¢êœ£êœ¤êœ¥êœ¦êœ§êœ¨êœ©êœªêœ«êœ¬êœ­êœ®êœ¯êœ°êœ±êœ²êœ³êœ´êœµêœ¶êœ·êœ¸êœ¹êœºêœ»êœ¼êœ½êœ¾êœ¿ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽê +êê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿êž€êžêž‚ꞃꞄꞅꞆꞇꞈ꞉꞊ꞋꞌêžêžŽêž +êžêž‘ꞒꞓꞔꞕꞖꞗꞘꞙꞚꞛꞜêžêžžêžŸêž êž¡êž¢êž£êž¤êž¥êž¦êž§êž¨êž©êžªêž«êž¬êž­êž®êž¯êž°êž±êž²êž³êž´êžµêž¶êž·êž¸êž¹êžºêž»êž¼êž½êž¾êž¿êŸ€êŸêŸ‚ꟃꟄꟅꟆꟇꟈꟉꟊꟋꟌêŸêŸŽêŸ +êŸêŸ‘꟒ꟓ꟔ꟕꟖꟗꟘꟙꟚꟛꟜêŸêŸžêŸŸêŸ êŸ¡êŸ¢êŸ£êŸ¤êŸ¥êŸ¦êŸ§êŸ¨êŸ©êŸªêŸ«êŸ¬êŸ­êŸ®êŸ¯êŸ°êŸ±êŸ²êŸ³êŸ´êŸµêŸ¶êŸ·êŸ¸êŸ¹êŸºêŸ»êŸ¼êŸ½êŸ¾êŸ¿ê €ê ê ‚ꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌê ê Žê  +ê ê ‘ꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜê ê žê Ÿê  ê ¡ê ¢ê £ê ¤ê ¥ê ¦ê §ê ¨ê ©ê ªê «ê ¬ê ­ê ®ê ¯ê °ê ±ê ²ê ³ê ´ê µê ¶ê ·ê ¸ê ¹ê ºê »ê ¼ê ½ê ¾ê ¿ê¡€ê¡ê¡‚ꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌê¡ê¡Žê¡ +ê¡ê¡‘ꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜê¡ê¡žê¡Ÿê¡ ê¡¡ê¡¢ê¡£ê¡¤ê¡¥ê¡¦ê¡§ê¡¨ê¡©ê¡ªê¡«ê¡¬ê¡­ê¡®ê¡¯ê¡°ê¡±ê¡²ê¡³ê¡´ê¡µê¡¶ê¡·ê¡¸ê¡¹ê¡ºê¡»ê¡¼ê¡½ê¡¾ê¡¿ê¢€ê¢ê¢‚ꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌê¢ê¢Žê¢ +ê¢ê¢‘ꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜê¢ê¢žê¢Ÿê¢ ê¢¡ê¢¢ê¢£ê¢¤ê¢¥ê¢¦ê¢§ê¢¨ê¢©ê¢ªê¢«ê¢¬ê¢­ê¢®ê¢¯ê¢°ê¢±ê¢²ê¢³ê¢´ê¢µê¢¶ê¢·ê¢¸ê¢¹ê¢ºê¢»ê¢¼ê¢½ê¢¾ê¢¿ê£€ê£ê£‚ꣃ꣄ꣅ꣆꣇꣈꣉꣊꣋꣌ê£ê£Žê£ +ê£ê£‘꣒꣓꣔꣕꣖꣗꣘꣙꣚꣛꣜ê£ê£žê£Ÿê£ ê£¡ê£¢ê££ê£¤ê£¥ê£¦ê£§ê£¨ê£©ê£ªê£«ê£¬ê£­ê£®ê£¯ê£°ê£±ê£²ê£³ê£´ê£µê£¶ê£·ê£¸ê£¹ê£ºê£»ê£¼ê£½ê£¾ê£¿ê¤€ê¤ê¤‚꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌê¤ê¤Žê¤ +ê¤ê¤‘ꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜê¤ê¤žê¤Ÿê¤ ê¤¡ê¤¢ê¤£ê¤¤ê¤¥ê¤¦ê¤§ê¤¨ê¤©ê¤ªê¤«ê¤¬ê¤­ê¤®ê¤¯ê¤°ê¤±ê¤²ê¤³ê¤´ê¤µê¤¶ê¤·ê¤¸ê¤¹ê¤ºê¤»ê¤¼ê¤½ê¤¾ê¤¿ê¥€ê¥ê¥‚ꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌê¥ê¥Žê¥ +ê¥ê¥‘ꥒ꥓꥔꥕꥖꥗꥘꥙꥚꥛꥜ê¥ê¥žê¥Ÿê¥ ê¥¡ê¥¢ê¥£ê¥¤ê¥¥ê¥¦ê¥§ê¥¨ê¥©ê¥ªê¥«ê¥¬ê¥­ê¥®ê¥¯ê¥°ê¥±ê¥²ê¥³ê¥´ê¥µê¥¶ê¥·ê¥¸ê¥¹ê¥ºê¥»ê¥¼ê¥½ê¥¾ê¥¿ê¦€ê¦ê¦‚ꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌê¦ê¦Žê¦ +ê¦ê¦‘ꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜê¦ê¦žê¦Ÿê¦ ê¦¡ê¦¢ê¦£ê¦¤ê¦¥ê¦¦ê¦§ê¦¨ê¦©ê¦ªê¦«ê¦¬ê¦­ê¦®ê¦¯ê¦°ê¦±ê¦²ê¦³ê¦´ê¦µê¦¶ê¦·ê¦¸ê¦¹ê¦ºê¦»ê¦¼ê¦½ê¦¾ê¦¿ê§€ê§ê§‚꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌ê§ê§Žê§ +ê§ê§‘꧒꧓꧔꧕꧖꧗꧘꧙꧚꧛꧜ê§ê§žê§Ÿê§ ê§¡ê§¢ê§£ê§¤ê§¥ê§¦ê§§ê§¨ê§©ê§ªê§«ê§¬ê§­ê§®ê§¯ê§°ê§±ê§²ê§³ê§´ê§µê§¶ê§·ê§¸ê§¹ê§ºê§»ê§¼ê§½ê§¾ê§¿ê¨€ê¨ê¨‚ꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌê¨ê¨Žê¨ +ê¨ê¨‘ꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜê¨ê¨žê¨Ÿê¨ ê¨¡ê¨¢ê¨£ê¨¤ê¨¥ê¨¦ê¨§ê¨¨ê¨©ê¨ªê¨«ê¨¬ê¨­ê¨®ê¨¯ê¨°ê¨±ê¨²ê¨³ê¨´ê¨µê¨¶ê¨·ê¨¸ê¨¹ê¨ºê¨»ê¨¼ê¨½ê¨¾ê¨¿ê©€ê©ê©‚ꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌê©ê©Žê© +ê©ê©‘꩒꩓꩔꩕꩖꩗꩘꩙꩚꩛꩜ê©ê©žê©Ÿê© ê©¡ê©¢ê©£ê©¤ê©¥ê©¦ê©§ê©¨ê©©ê©ªê©«ê©¬ê©­ê©®ê©¯ê©°ê©±ê©²ê©³ê©´ê©µê©¶ê©·ê©¸ê©¹ê©ºê©»ê©¼ê©½ê©¾ê©¿êª€êªêª‚ꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌêªêªŽêª +êªêª‘ꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜêªêªžêªŸêª êª¡êª¢êª£êª¤êª¥êª¦êª§êª¨êª©êªªêª«êª¬êª­êª®êª¯êª°êª±êª²êª³êª´êªµêª¶êª·êª¸êª¹êªºêª»êª¼êª½êª¾êª¿ê«€ê«ê«‚꫃꫄꫅꫆꫇꫈꫉꫊꫋꫌ê«ê«Žê« +ê«ê«‘꫒꫓꫔꫕꫖꫗꫘꫙꫚ꫛꫜê«ê«žê«Ÿê« ê«¡ê«¢ê«£ê«¤ê«¥ê«¦ê«§ê«¨ê«©ê«ªê««ê«¬ê«­ê«®ê«¯ê«°ê«±ê«²ê«³ê«´ê«µê«¶ê«·ê«¸ê«¹ê«ºê«»ê«¼ê«½ê«¾ê«¿ê¬€ê¬ê¬‚ꬃꬄꬅꬆ꬇꬈ꬉꬊꬋꬌê¬ê¬Žê¬ +ê¬ê¬‘ꬒꬓꬔꬕꬖ꬗꬘꬙꬚꬛꬜ê¬ê¬žê¬Ÿê¬ ê¬¡ê¬¢ê¬£ê¬¤ê¬¥ê¬¦ê¬§ê¬¨ê¬©ê¬ªê¬«ê¬¬ê¬­ê¬®ê¬¯ê¬°ê¬±ê¬²ê¬³ê¬´ê¬µê¬¶ê¬·ê¬¸ê¬¹ê¬ºê¬»ê¬¼ê¬½ê¬¾ê¬¿ê­€ê­ê­‚ꭃꭄꭅꭆꭇꭈꭉꭊꭋꭌê­ê­Žê­ +ê­ê­‘ꭒꭓꭔꭕꭖꭗꭘꭙꭚ꭛ꭜê­ê­žê­Ÿê­ ê­¡ê­¢ê­£ê­¤ê­¥ê­¦ê­§ê­¨ê­©ê­ªê­«ê­¬ê­­ê­®ê­¯ê­°ê­±ê­²ê­³ê­´ê­µê­¶ê­·ê­¸ê­¹ê­ºê­»ê­¼ê­½ê­¾ê­¿ê®€ê®ê®‚ꮃꮄꮅꮆꮇꮈꮉꮊꮋꮌê®ê®Žê® +ê®ê®‘ꮒꮓꮔꮕꮖꮗꮘꮙꮚꮛꮜê®ê®žê®Ÿê® ê®¡ê®¢ê®£ê®¤ê®¥ê®¦ê®§ê®¨ê®©ê®ªê®«ê®¬ê®­ê®®ê®¯ê®°ê®±ê®²ê®³ê®´ê®µê®¶ê®·ê®¸ê®¹ê®ºê®»ê®¼ê®½ê®¾ê®¿ê¯€ê¯ê¯‚ꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌê¯ê¯Žê¯ +ê¯ê¯‘ꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜê¯ê¯žê¯Ÿê¯ ê¯¡ê¯¢ê¯£ê¯¤ê¯¥ê¯¦ê¯§ê¯¨ê¯©ê¯ªê¯«ê¯¬ê¯­ê¯®ê¯¯ê¯°ê¯±ê¯²ê¯³ê¯´ê¯µê¯¶ê¯·ê¯¸ê¯¹ê¯ºê¯»ê¯¼ê¯½ê¯¾ê¯¿ + +Hangul Syllables (U+AC00-U+D7AF): + +ê°€ê°ê°‚갃간갅갆갇갈갉갊갋갌ê°ê°Žê°ê°ê°‘값갓갔강갖갗갘같갚갛개ê°ê°žê°Ÿ +갠갡갢갣갤갥갦갧갨갩갪갫갬갭갮갯갰갱갲갳갴갵갶갷갸갹갺갻갼갽갾갿 +ê±€ê±ê±‚걃걄걅걆걇걈걉걊걋걌ê±ê±Žê±ê±ê±‘걒걓걔걕걖걗걘걙걚걛걜ê±ê±žê±Ÿ +걠걡걢걣걤걥걦걧걨걩걪걫걬걭걮걯거걱걲걳건걵걶걷걸걹걺걻걼걽걾걿 +ê²€ê²ê²‚것겄겅겆겇겈겉겊겋게ê²ê²Žê²ê²ê²‘겒겓겔겕겖겗겘겙겚겛겜ê²ê²žê²Ÿ +겠겡겢겣겤겥겦겧겨격겪겫견겭겮겯결겱겲겳겴겵겶겷겸겹겺겻겼경겾겿 +ê³€ê³ê³‚곃계곅곆곇곈곉곊곋곌ê³ê³Žê³ê³ê³‘곒곓곔곕곖곗곘곙곚곛곜ê³ê³žê³Ÿ +고곡곢곣곤곥곦곧골곩곪곫곬곭곮곯곰곱곲곳곴공곶곷곸곹곺곻과곽곾곿 +ê´€ê´ê´‚괃괄괅괆괇괈괉괊괋괌ê´ê´Žê´ê´ê´‘괒괓괔괕괖괗괘괙괚괛괜ê´ê´žê´Ÿ +괠괡괢괣괤괥괦괧괨괩괪괫괬괭괮괯괰괱괲괳괴괵괶괷괸괹괺괻괼괽괾괿 +êµ€êµêµ‚굃굄굅굆굇굈굉굊굋굌êµêµŽêµêµêµ‘굒굓굔굕굖굗굘굙굚굛굜êµêµžêµŸ +굠굡굢굣굤굥굦굧굨굩굪굫구국굮굯군굱굲굳굴굵굶굷굸굹굺굻굼굽굾굿 +궀ê¶ê¶‚궃궄궅궆궇궈궉궊궋권ê¶ê¶Žê¶ê¶ê¶‘궒궓궔궕궖궗궘궙궚궛궜ê¶ê¶žê¶Ÿ +궠궡궢궣궤궥궦궧궨궩궪궫궬궭궮궯궰궱궲궳궴궵궶궷궸궹궺궻궼궽궾궿 +ê·€ê·ê·‚귃귄귅귆귇귈귉귊귋귌ê·ê·Žê·ê·ê·‘귒귓귔귕귖귗귘귙귚귛규ê·ê·žê·Ÿ +균귡귢귣귤귥귦귧귨귩귪귫귬귭귮귯귰귱귲귳귴귵귶귷그극귺귻근귽귾귿 +글ê¸ê¸‚긃긄긅긆긇금급긊긋긌ê¸ê¸Žê¸ê¸ê¸‘긒긓긔긕긖긗긘긙긚긛긜ê¸ê¸žê¸Ÿ +긠긡긢긣긤긥긦긧긨긩긪긫긬긭긮긯기긱긲긳긴긵긶긷길긹긺긻긼긽긾긿 +ê¹€ê¹ê¹‚깃깄깅깆깇깈깉깊깋까ê¹ê¹Žê¹ê¹ê¹‘깒깓깔깕깖깗깘깙깚깛깜ê¹ê¹žê¹Ÿ +깠깡깢깣깤깥깦깧깨깩깪깫깬깭깮깯깰깱깲깳깴깵깶깷깸깹깺깻깼깽깾깿 +꺀êºêº‚꺃꺄꺅꺆꺇꺈꺉꺊꺋꺌êºêºŽêºêºêº‘꺒꺓꺔꺕꺖꺗꺘꺙꺚꺛꺜êºêºžêºŸ +꺠꺡꺢꺣꺤꺥꺦꺧꺨꺩꺪꺫꺬꺭꺮꺯꺰꺱꺲꺳꺴꺵꺶꺷꺸꺹꺺꺻꺼꺽꺾꺿 +껀ê»ê»‚껃껄껅껆껇껈껉껊껋껌ê»ê»Žê»ê»ê»‘껒껓껔껕껖껗께껙껚껛껜ê»ê»žê»Ÿ +껠껡껢껣껤껥껦껧껨껩껪껫껬껭껮껯껰껱껲껳껴껵껶껷껸껹껺껻껼껽껾껿 +ê¼€ê¼ê¼‚꼃꼄꼅꼆꼇꼈꼉꼊꼋꼌ê¼ê¼Žê¼ê¼ê¼‘꼒꼓꼔꼕꼖꼗꼘꼙꼚꼛꼜ê¼ê¼žê¼Ÿ +꼠꼡꼢꼣꼤꼥꼦꼧꼨꼩꼪꼫꼬꼭꼮꼯꼰꼱꼲꼳꼴꼵꼶꼷꼸꼹꼺꼻꼼꼽꼾꼿 +ê½€ê½ê½‚꽃꽄꽅꽆꽇꽈꽉꽊꽋꽌ê½ê½Žê½ê½ê½‘꽒꽓꽔꽕꽖꽗꽘꽙꽚꽛꽜ê½ê½žê½Ÿ +꽠꽡꽢꽣꽤꽥꽦꽧꽨꽩꽪꽫꽬꽭꽮꽯꽰꽱꽲꽳꽴꽵꽶꽷꽸꽹꽺꽻꽼꽽꽾꽿 +ê¾€ê¾ê¾‚꾃꾄꾅꾆꾇꾈꾉꾊꾋꾌ê¾ê¾Žê¾ê¾ê¾‘꾒꾓꾔꾕꾖꾗꾘꾙꾚꾛꾜ê¾ê¾žê¾Ÿ +꾠꾡꾢꾣꾤꾥꾦꾧꾨꾩꾪꾫꾬꾭꾮꾯꾰꾱꾲꾳꾴꾵꾶꾷꾸꾹꾺꾻꾼꾽꾾꾿 +ê¿€ê¿ê¿‚꿃꿄꿅꿆꿇꿈꿉꿊꿋꿌ê¿ê¿Žê¿ê¿ê¿‘꿒꿓꿔꿕꿖꿗꿘꿙꿚꿛꿜ê¿ê¿žê¿Ÿ +꿠꿡꿢꿣꿤꿥꿦꿧꿨꿩꿪꿫꿬꿭꿮꿯꿰꿱꿲꿳꿴꿵꿶꿷꿸꿹꿺꿻꿼꿽꿾꿿 +뀀ë€ë€‚뀃뀄뀅뀆뀇뀈뀉뀊뀋뀌ë€ë€Žë€ë€ë€‘뀒뀓뀔뀕뀖뀗뀘뀙뀚뀛뀜ë€ë€žë€Ÿ +뀠뀡뀢뀣뀤뀥뀦뀧뀨뀩뀪뀫뀬뀭뀮뀯뀰뀱뀲뀳뀴뀵뀶뀷뀸뀹뀺뀻뀼뀽뀾뀿 +ë€ëë‚ëƒë„ë…ë†ë‡ëˆë‰ëŠë‹ëŒëëŽëëë‘ë’ë“ë”ë•ë–ë—ë˜ë™ëšë›ëœëëžëŸ +ë ë¡ë¢ë£ë¤ë¥ë¦ë§ë¨ë©ëªë«ë¬ë­ë®ë¯ë°ë±ë²ë³ë´ëµë¶ë·ë¸ë¹ëºë»ë¼ë½ë¾ë¿ +ë‚€ë‚낂낃낄낅낆낇낈낉낊낋낌ë‚ë‚Žë‚ë‚낑낒낓낔낕낖낗나낙낚낛난ë‚ë‚žë‚Ÿ +날낡낢낣낤낥낦낧남납낪낫났낭낮낯낰낱낲낳내낵낶낷낸낹낺낻낼낽낾낿 +냀ëƒëƒ‚냃냄냅냆냇냈냉냊냋냌ëƒëƒŽëƒëƒëƒ‘냒냓냔냕냖냗냘냙냚냛냜ëƒëƒžëƒŸ +냠냡냢냣냤냥냦냧냨냩냪냫냬냭냮냯냰냱냲냳냴냵냶냷냸냹냺냻냼냽냾냿 +ë„€ë„넂넃넄넅넆넇너넉넊넋넌ë„ë„Žë„ë„넑넒넓넔넕넖넗넘넙넚넛넜ë„ë„žë„Ÿ +넠넡넢넣네넥넦넧넨넩넪넫넬넭넮넯넰넱넲넳넴넵넶넷넸넹넺넻넼넽넾넿 +ë…€ë…녂녃년녅녆녇녈녉녊녋녌ë…ë…Žë…ë…녑녒녓녔녕녖녗녘녙녚녛녜ë…ë…žë…Ÿ +녠녡녢녣녤녥녦녧녨녩녪녫녬녭녮녯녰녱녲녳녴녵녶녷노녹녺녻논녽녾녿 +놀ë†ë†‚놃놄놅놆놇놈놉놊놋놌ë†ë†Žë†ë†ë†‘높놓놔놕놖놗놘놙놚놛놜ë†ë†žë†Ÿ +놠놡놢놣놤놥놦놧놨놩놪놫놬놭놮놯놰놱놲놳놴놵놶놷놸놹놺놻놼놽놾놿 +뇀ë‡ë‡‚뇃뇄뇅뇆뇇뇈뇉뇊뇋뇌ë‡ë‡Žë‡ë‡ë‡‘뇒뇓뇔뇕뇖뇗뇘뇙뇚뇛뇜ë‡ë‡žë‡Ÿ +뇠뇡뇢뇣뇤뇥뇦뇧뇨뇩뇪뇫뇬뇭뇮뇯뇰뇱뇲뇳뇴뇵뇶뇷뇸뇹뇺뇻뇼뇽뇾뇿 +눀ëˆëˆ‚눃누눅눆눇눈눉눊눋눌ëˆëˆŽëˆëˆëˆ‘눒눓눔눕눖눗눘눙눚눛눜ëˆëˆžëˆŸ +눠눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿 +뉀ë‰ë‰‚뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌ë‰ë‰Žë‰ë‰ë‰‘뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜ë‰ë‰žë‰Ÿ +뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿 +늀ëŠëŠ‚늃늄늅늆늇늈늉늊늋늌ëŠëŠŽëŠëŠëŠ‘늒늓는늕늖늗늘늙늚늛늜ëŠëŠžëŠŸ +늠늡늢늣늤능늦늧늨늩늪늫늬늭늮늯늰늱늲늳늴늵늶늷늸늹늺늻늼늽늾늿 +ë‹€ë‹ë‹‚닃닄닅닆닇니닉닊닋닌ë‹ë‹Žë‹ë‹ë‹‘닒닓닔닕닖닗님닙닚닛닜ë‹ë‹žë‹Ÿ +닠닡닢닣다닥닦닧단닩닪닫달닭닮닯닰닱닲닳담답닶닷닸당닺닻닼닽닾닿 +대ëŒëŒ‚댃댄댅댆댇댈댉댊댋댌ëŒëŒŽëŒëŒëŒ‘댒댓댔댕댖댗댘댙댚댛댜ëŒëŒžëŒŸ +댠댡댢댣댤댥댦댧댨댩댪댫댬댭댮댯댰댱댲댳댴댵댶댷댸댹댺댻댼댽댾댿 +ë€ëë‚ëƒë„ë…ë†ë‡ëˆë‰ëŠë‹ëŒëëŽëëë‘ë’ë“ë”ë•ë–ë—ë˜ë™ëšë›ëœëëžëŸ +ë ë¡ë¢ë£ë¤ë¥ë¦ë§ë¨ë©ëªë«ë¬ë­ë®ë¯ë°ë±ë²ë³ë´ëµë¶ë·ë¸ë¹ëºë»ë¼ë½ë¾ë¿ +뎀ëŽëŽ‚뎃뎄뎅뎆뎇뎈뎉뎊뎋뎌ëŽëŽŽëŽëŽëŽ‘뎒뎓뎔뎕뎖뎗뎘뎙뎚뎛뎜ëŽëŽžëŽŸ +뎠뎡뎢뎣뎤뎥뎦뎧뎨뎩뎪뎫뎬뎭뎮뎯뎰뎱뎲뎳뎴뎵뎶뎷뎸뎹뎺뎻뎼뎽뎾뎿 +ë€ëë‚ëƒë„ë…ë†ë‡ëˆë‰ëŠë‹ëŒëëŽëëë‘ë’ë“ë”ë•ë–ë—ë˜ë™ëšë›ëœëëžëŸ +ë ë¡ë¢ë£ë¤ë¥ë¦ë§ë¨ë©ëªë«ë¬ë­ë®ë¯ë°ë±ë²ë³ë´ëµë¶ë·ë¸ë¹ëºë»ë¼ë½ë¾ë¿ +ë€ëë‚ëƒë„ë…ë†ë‡ëˆë‰ëŠë‹ëŒëëŽëëë‘ë’ë“ë”ë•ë–ë—ë˜ë™ëšë›ëœëëžëŸ +ë ë¡ë¢ë£ë¤ë¥ë¦ë§ë¨ë©ëªë«ë¬ë­ë®ë¯ë°ë±ë²ë³ë´ëµë¶ë·ë¸ë¹ëºë»ë¼ë½ë¾ë¿ +ë‘€ë‘둂둃둄둅둆둇둈둉둊둋둌ë‘ë‘Žë‘ë‘둑둒둓둔둕둖둗둘둙둚둛둜ë‘ë‘žë‘Ÿ +둠둡둢둣둤둥둦둧둨둩둪둫둬둭둮둯둰둱둲둳둴둵둶둷둸둹둺둻둼둽둾둿 +ë’€ë’뒂뒃뒄뒅뒆뒇뒈뒉뒊뒋뒌ë’ë’Žë’ë’뒑뒒뒓뒔뒕뒖뒗뒘뒙뒚뒛뒜ë’ë’žë’Ÿ +뒠뒡뒢뒣뒤뒥뒦뒧뒨뒩뒪뒫뒬뒭뒮뒯뒰뒱뒲뒳뒴뒵뒶뒷뒸뒹뒺뒻뒼뒽뒾뒿 +ë“€ë“듂듃듄듅듆듇듈듉듊듋듌ë“ë“Žë“ë“듑듒듓듔듕듖듗듘듙듚듛드ë“ë“žë“Ÿ +든듡듢듣들듥듦듧듨듩듪듫듬듭듮듯듰등듲듳듴듵듶듷듸듹듺듻듼듽듾듿 +딀ë”딂딃딄딅딆딇딈딉딊딋딌ë”딎ë”ë”딑딒딓디딕딖딗딘딙딚딛딜ë”딞딟 +딠딡딢딣딤딥딦딧딨딩딪딫딬딭딮딯따딱딲딳딴딵딶딷딸딹딺딻딼딽딾딿 +ë•€ë•ë•‚땃땄땅땆땇땈땉땊땋때ë•ë•Žë•ë•ë•‘땒땓땔땕땖땗땘땙땚땛땜ë•ë•žë•Ÿ +땠땡땢땣땤땥땦땧땨땩땪땫땬땭땮땯땰땱땲땳땴땵땶땷땸땹땺땻땼땽땾땿 +ë–€ë–떂떃떄떅떆떇떈떉떊떋떌ë–ë–Žë–ë–떑떒떓떔떕떖떗떘떙떚떛떜ë–ë–žë–Ÿ +떠떡떢떣떤떥떦떧떨떩떪떫떬떭떮떯떰떱떲떳떴떵떶떷떸떹떺떻떼떽떾떿 +ë—€ë—뗂뗃뗄뗅뗆뗇뗈뗉뗊뗋뗌ë—ë—Žë—ë—뗑뗒뗓뗔뗕뗖뗗뗘뗙뗚뗛뗜ë—ë—žë—Ÿ +뗠뗡뗢뗣뗤뗥뗦뗧뗨뗩뗪뗫뗬뗭뗮뗯뗰뗱뗲뗳뗴뗵뗶뗷뗸뗹뗺뗻뗼뗽뗾뗿 +똀ë˜ë˜‚똃똄똅똆똇똈똉똊똋똌ë˜ë˜Žë˜ë˜ë˜‘똒똓똔똕똖똗똘똙똚똛똜ë˜ë˜žë˜Ÿ +똠똡똢똣똤똥똦똧똨똩똪똫똬똭똮똯똰똱똲똳똴똵똶똷똸똹똺똻똼똽똾똿 +뙀ë™ë™‚뙃뙄뙅뙆뙇뙈뙉뙊뙋뙌ë™ë™Žë™ë™ë™‘뙒뙓뙔뙕뙖뙗뙘뙙뙚뙛뙜ë™ë™žë™Ÿ +뙠뙡뙢뙣뙤뙥뙦뙧뙨뙩뙪뙫뙬뙭뙮뙯뙰뙱뙲뙳뙴뙵뙶뙷뙸뙹뙺뙻뙼뙽뙾뙿 +뚀ëšëš‚뚃뚄뚅뚆뚇뚈뚉뚊뚋뚌ëšëšŽëšëšëš‘뚒뚓뚔뚕뚖뚗뚘뚙뚚뚛뚜ëšëšžëšŸ +뚠뚡뚢뚣뚤뚥뚦뚧뚨뚩뚪뚫뚬뚭뚮뚯뚰뚱뚲뚳뚴뚵뚶뚷뚸뚹뚺뚻뚼뚽뚾뚿 +뛀ë›ë›‚뛃뛄뛅뛆뛇뛈뛉뛊뛋뛌ë›ë›Žë›ë›ë›‘뛒뛓뛔뛕뛖뛗뛘뛙뛚뛛뛜ë›ë›žë›Ÿ +뛠뛡뛢뛣뛤뛥뛦뛧뛨뛩뛪뛫뛬뛭뛮뛯뛰뛱뛲뛳뛴뛵뛶뛷뛸뛹뛺뛻뛼뛽뛾뛿 +뜀ëœëœ‚뜃뜄뜅뜆뜇뜈뜉뜊뜋뜌ëœëœŽëœëœëœ‘뜒뜓뜔뜕뜖뜗뜘뜙뜚뜛뜜ëœëœžëœŸ +뜠뜡뜢뜣뜤뜥뜦뜧뜨뜩뜪뜫뜬뜭뜮뜯뜰뜱뜲뜳뜴뜵뜶뜷뜸뜹뜺뜻뜼뜽뜾뜿 +ë€ëë‚ëƒë„ë…ë†ë‡ëˆë‰ëŠë‹ëŒëëŽëëë‘ë’ë“ë”ë•ë–ë—ë˜ë™ëšë›ëœëëžëŸ +ë ë¡ë¢ë£ë¤ë¥ë¦ë§ë¨ë©ëªë«ë¬ë­ë®ë¯ë°ë±ë²ë³ë´ëµë¶ë·ë¸ë¹ëºë»ë¼ë½ë¾ë¿ +란ëžëž‚랃랄랅랆랇랈랉랊랋람ëžëžŽëžëžëž‘랒랓랔랕랖랗래랙랚랛랜ëžëžžëžŸ +랠랡랢랣랤랥랦랧램랩랪랫랬랭랮랯랰랱랲랳랴략랶랷랸랹랺랻랼랽랾랿 +럀ëŸëŸ‚럃럄럅럆럇럈량럊럋럌ëŸëŸŽëŸëŸëŸ‘럒럓럔럕럖럗럘럙럚럛럜ëŸëŸžëŸŸ +럠럡럢럣럤럥럦럧럨럩럪럫러럭럮럯런럱럲럳럴럵럶럷럸럹럺럻럼럽럾럿 +ë €ë ë ‚렃렄렅렆렇레렉렊렋렌ë ë Žë ë ë ‘렒렓렔렕렖렗렘렙렚렛렜ë ë žë Ÿ +렠렡렢렣려력렦렧련렩렪렫렬렭렮렯렰렱렲렳렴렵렶렷렸령렺렻렼렽렾렿 +ë¡€ë¡ë¡‚롃롄롅롆롇롈롉롊롋롌ë¡ë¡Žë¡ë¡ë¡‘롒롓롔롕롖롗롘롙롚롛로ë¡ë¡žë¡Ÿ +론롡롢롣롤롥롦롧롨롩롪롫롬롭롮롯롰롱롲롳롴롵롶롷롸롹롺롻롼롽롾롿 +뢀ë¢ë¢‚뢃뢄뢅뢆뢇뢈뢉뢊뢋뢌ë¢ë¢Žë¢ë¢ë¢‘뢒뢓뢔뢕뢖뢗뢘뢙뢚뢛뢜ë¢ë¢žë¢Ÿ +뢠뢡뢢뢣뢤뢥뢦뢧뢨뢩뢪뢫뢬뢭뢮뢯뢰뢱뢲뢳뢴뢵뢶뢷뢸뢹뢺뢻뢼뢽뢾뢿 +룀ë£ë£‚룃룄룅룆룇룈룉룊룋료ë£ë£Žë£ë£ë£‘룒룓룔룕룖룗룘룙룚룛룜ë£ë£žë£Ÿ +룠룡룢룣룤룥룦룧루룩룪룫룬룭룮룯룰룱룲룳룴룵룶룷룸룹룺룻룼룽룾룿 +뤀ë¤ë¤‚뤃뤄뤅뤆뤇뤈뤉뤊뤋뤌ë¤ë¤Žë¤ë¤ë¤‘뤒뤓뤔뤕뤖뤗뤘뤙뤚뤛뤜ë¤ë¤žë¤Ÿ +뤠뤡뤢뤣뤤뤥뤦뤧뤨뤩뤪뤫뤬뤭뤮뤯뤰뤱뤲뤳뤴뤵뤶뤷뤸뤹뤺뤻뤼뤽뤾뤿 +륀ë¥ë¥‚륃륄륅륆륇륈륉륊륋륌ë¥ë¥Žë¥ë¥ë¥‘륒륓륔륕륖륗류륙륚륛륜ë¥ë¥žë¥Ÿ +률륡륢륣륤륥륦륧륨륩륪륫륬륭륮륯륰륱륲륳르륵륶륷른륹륺륻를륽륾륿 +릀ë¦ë¦‚릃름릅릆릇릈릉릊릋릌ë¦ë¦Žë¦ë¦ë¦‘릒릓릔릕릖릗릘릙릚릛릜ë¦ë¦žë¦Ÿ +릠릡릢릣릤릥릦릧릨릩릪릫리릭릮릯린릱릲릳릴릵릶릷릸릹릺릻림립릾릿 +맀ë§ë§‚맃맄맅맆맇마막맊맋만ë§ë§Žë§ë§ë§‘맒맓맔맕맖맗맘맙맚맛맜ë§ë§žë§Ÿ +맠맡맢맣매맥맦맧맨맩맪맫맬맭맮맯맰맱맲맳맴맵맶맷맸맹맺맻맼맽맾맿 +먀ë¨ë¨‚먃먄먅먆먇먈먉먊먋먌ë¨ë¨Žë¨ë¨ë¨‘먒먓먔먕먖먗먘먙먚먛먜ë¨ë¨žë¨Ÿ +먠먡먢먣먤먥먦먧먨먩먪먫먬먭먮먯먰먱먲먳먴먵먶먷머먹먺먻먼먽먾먿 +ë©€ë©ë©‚멃멄멅멆멇멈멉멊멋멌ë©ë©Žë©ë©ë©‘멒멓메멕멖멗멘멙멚멛멜ë©ë©žë©Ÿ +멠멡멢멣멤멥멦멧멨멩멪멫멬멭멮멯며멱멲멳면멵멶멷멸멹멺멻멼멽멾멿 +몀ëªëª‚몃몄명몆몇몈몉몊몋몌ëªëªŽëªëªëª‘몒몓몔몕몖몗몘몙몚몛몜ëªëªžëªŸ +몠몡몢몣몤몥몦몧모목몪몫몬몭몮몯몰몱몲몳몴몵몶몷몸몹몺못몼몽몾몿 +ë«€ë«ë«‚뫃뫄뫅뫆뫇뫈뫉뫊뫋뫌ë«ë«Žë«ë«ë«‘뫒뫓뫔뫕뫖뫗뫘뫙뫚뫛뫜ë«ë«žë«Ÿ +뫠뫡뫢뫣뫤뫥뫦뫧뫨뫩뫪뫫뫬뫭뫮뫯뫰뫱뫲뫳뫴뫵뫶뫷뫸뫹뫺뫻뫼뫽뫾뫿 +묀ë¬ë¬‚묃묄묅묆묇묈묉묊묋묌ë¬ë¬Žë¬ë¬ë¬‘묒묓묔묕묖묗묘묙묚묛묜ë¬ë¬žë¬Ÿ +묠묡묢묣묤묥묦묧묨묩묪묫묬묭묮묯묰묱묲묳무묵묶묷문묹묺묻물묽묾묿 +ë­€ë­ë­‚뭃뭄뭅뭆뭇뭈뭉뭊뭋뭌ë­ë­Žë­ë­ë­‘뭒뭓뭔뭕뭖뭗뭘뭙뭚뭛뭜ë­ë­žë­Ÿ +뭠뭡뭢뭣뭤뭥뭦뭧뭨뭩뭪뭫뭬뭭뭮뭯뭰뭱뭲뭳뭴뭵뭶뭷뭸뭹뭺뭻뭼뭽뭾뭿 +뮀ë®ë®‚뮃뮄뮅뮆뮇뮈뮉뮊뮋뮌ë®ë®Žë®ë®ë®‘뮒뮓뮔뮕뮖뮗뮘뮙뮚뮛뮜ë®ë®žë®Ÿ +뮠뮡뮢뮣뮤뮥뮦뮧뮨뮩뮪뮫뮬뮭뮮뮯뮰뮱뮲뮳뮴뮵뮶뮷뮸뮹뮺뮻뮼뮽뮾뮿 +므ë¯ë¯‚믃믄믅믆믇믈믉믊믋믌ë¯ë¯Žë¯ë¯ë¯‘믒믓믔믕믖믗믘믙믚믛믜ë¯ë¯žë¯Ÿ +믠믡믢믣믤믥믦믧믨믩믪믫믬믭믮믯믰믱믲믳믴믵믶믷미믹믺믻민믽믾믿 +ë°€ë°ë°‚밃밄밅밆밇밈밉밊밋밌ë°ë°Žë°ë°ë°‘밒밓바박밖밗반밙밚받발ë°ë°žë°Ÿ +밠밡밢밣밤밥밦밧밨방밪밫밬밭밮밯배백밲밳밴밵밶밷밸밹밺밻밼밽밾밿 +ë±€ë±ë±‚뱃뱄뱅뱆뱇뱈뱉뱊뱋뱌ë±ë±Žë±ë±ë±‘뱒뱓뱔뱕뱖뱗뱘뱙뱚뱛뱜ë±ë±žë±Ÿ +뱠뱡뱢뱣뱤뱥뱦뱧뱨뱩뱪뱫뱬뱭뱮뱯뱰뱱뱲뱳뱴뱵뱶뱷뱸뱹뱺뱻뱼뱽뱾뱿 +ë²€ë²ë²‚벃버벅벆벇번벉벊벋벌ë²ë²Žë²ë²ë²‘벒벓범법벖벗벘벙벚벛벜ë²ë²žë²Ÿ +베벡벢벣벤벥벦벧벨벩벪벫벬벭벮벯벰벱벲벳벴벵벶벷벸벹벺벻벼벽벾벿 +ë³€ë³ë³‚볃별볅볆볇볈볉볊볋볌ë³ë³Žë³ë³ë³‘볒볓볔볕볖볗볘볙볚볛볜ë³ë³žë³Ÿ +볠볡볢볣볤볥볦볧볨볩볪볫볬볭볮볯볰볱볲볳보복볶볷본볹볺볻볼볽볾볿 +ë´€ë´ë´‚봃봄봅봆봇봈봉봊봋봌ë´ë´Žë´ë´ë´‘봒봓봔봕봖봗봘봙봚봛봜ë´ë´žë´Ÿ +봠봡봢봣봤봥봦봧봨봩봪봫봬봭봮봯봰봱봲봳봴봵봶봷봸봹봺봻봼봽봾봿 +ëµ€ëµëµ‚뵃뵄뵅뵆뵇뵈뵉뵊뵋뵌ëµëµŽëµëµëµ‘뵒뵓뵔뵕뵖뵗뵘뵙뵚뵛뵜ëµëµžëµŸ +뵠뵡뵢뵣뵤뵥뵦뵧뵨뵩뵪뵫뵬뵭뵮뵯뵰뵱뵲뵳뵴뵵뵶뵷뵸뵹뵺뵻뵼뵽뵾뵿 +부ë¶ë¶‚붃분붅붆붇불붉붊붋붌ë¶ë¶Žë¶ë¶ë¶‘붒붓붔붕붖붗붘붙붚붛붜ë¶ë¶žë¶Ÿ +붠붡붢붣붤붥붦붧붨붩붪붫붬붭붮붯붰붱붲붳붴붵붶붷붸붹붺붻붼붽붾붿 +ë·€ë·ë·‚뷃뷄뷅뷆뷇뷈뷉뷊뷋뷌ë·ë·Žë·ë·ë·‘뷒뷓뷔뷕뷖뷗뷘뷙뷚뷛뷜ë·ë·žë·Ÿ +뷠뷡뷢뷣뷤뷥뷦뷧뷨뷩뷪뷫뷬뷭뷮뷯뷰뷱뷲뷳뷴뷵뷶뷷뷸뷹뷺뷻뷼뷽뷾뷿 +븀ë¸ë¸‚븃븄븅븆븇븈븉븊븋브ë¸ë¸Žë¸ë¸ë¸‘븒븓블븕븖븗븘븙븚븛븜ë¸ë¸žë¸Ÿ +븠븡븢븣븤븥븦븧븨븩븪븫븬븭븮븯븰븱븲븳븴븵븶븷븸븹븺븻븼븽븾븿 +ë¹€ë¹ë¹‚빃비빅빆빇빈빉빊빋빌ë¹ë¹Žë¹ë¹ë¹‘빒빓빔빕빖빗빘빙빚빛빜ë¹ë¹žë¹Ÿ +빠빡빢빣빤빥빦빧빨빩빪빫빬빭빮빯빰빱빲빳빴빵빶빷빸빹빺빻빼빽빾빿 +뺀ëºëº‚뺃뺄뺅뺆뺇뺈뺉뺊뺋뺌ëºëºŽëºëºëº‘뺒뺓뺔뺕뺖뺗뺘뺙뺚뺛뺜ëºëºžëºŸ +뺠뺡뺢뺣뺤뺥뺦뺧뺨뺩뺪뺫뺬뺭뺮뺯뺰뺱뺲뺳뺴뺵뺶뺷뺸뺹뺺뺻뺼뺽뺾뺿 +뻀ë»ë»‚뻃뻄뻅뻆뻇뻈뻉뻊뻋뻌ë»ë»Žë»ë»ë»‘뻒뻓뻔뻕뻖뻗뻘뻙뻚뻛뻜ë»ë»žë»Ÿ +뻠뻡뻢뻣뻤뻥뻦뻧뻨뻩뻪뻫뻬뻭뻮뻯뻰뻱뻲뻳뻴뻵뻶뻷뻸뻹뻺뻻뻼뻽뻾뻿 +ë¼€ë¼ë¼‚뼃뼄뼅뼆뼇뼈뼉뼊뼋뼌ë¼ë¼Žë¼ë¼ë¼‘뼒뼓뼔뼕뼖뼗뼘뼙뼚뼛뼜ë¼ë¼žë¼Ÿ +뼠뼡뼢뼣뼤뼥뼦뼧뼨뼩뼪뼫뼬뼭뼮뼯뼰뼱뼲뼳뼴뼵뼶뼷뼸뼹뼺뼻뼼뼽뼾뼿 +ë½€ë½ë½‚뽃뽄뽅뽆뽇뽈뽉뽊뽋뽌ë½ë½Žë½ë½ë½‘뽒뽓뽔뽕뽖뽗뽘뽙뽚뽛뽜ë½ë½žë½Ÿ +뽠뽡뽢뽣뽤뽥뽦뽧뽨뽩뽪뽫뽬뽭뽮뽯뽰뽱뽲뽳뽴뽵뽶뽷뽸뽹뽺뽻뽼뽽뽾뽿 +ë¾€ë¾ë¾‚뾃뾄뾅뾆뾇뾈뾉뾊뾋뾌ë¾ë¾Žë¾ë¾ë¾‘뾒뾓뾔뾕뾖뾗뾘뾙뾚뾛뾜ë¾ë¾žë¾Ÿ +뾠뾡뾢뾣뾤뾥뾦뾧뾨뾩뾪뾫뾬뾭뾮뾯뾰뾱뾲뾳뾴뾵뾶뾷뾸뾹뾺뾻뾼뾽뾾뾿 +ë¿€ë¿ë¿‚뿃뿄뿅뿆뿇뿈뿉뿊뿋뿌ë¿ë¿Žë¿ë¿ë¿‘뿒뿓뿔뿕뿖뿗뿘뿙뿚뿛뿜ë¿ë¿žë¿Ÿ +뿠뿡뿢뿣뿤뿥뿦뿧뿨뿩뿪뿫뿬뿭뿮뿯뿰뿱뿲뿳뿴뿵뿶뿷뿸뿹뿺뿻뿼뿽뿾뿿 +쀀ì€ì€‚쀃쀄쀅쀆쀇쀈쀉쀊쀋쀌ì€ì€Žì€ì€ì€‘쀒쀓쀔쀕쀖쀗쀘쀙쀚쀛쀜ì€ì€žì€Ÿ +쀠쀡쀢쀣쀤쀥쀦쀧쀨쀩쀪쀫쀬쀭쀮쀯쀰쀱쀲쀳쀴쀵쀶쀷쀸쀹쀺쀻쀼쀽쀾쀿 +ì€ìì‚ìƒì„ì…ì†ì‡ìˆì‰ìŠì‹ìŒììŽììì‘ì’ì“ì”ì•ì–ì—ì˜ì™ìšì›ìœììžìŸ +ì ì¡ì¢ì£ì¤ì¥ì¦ì§ì¨ì©ìªì«ì¬ì­ì®ì¯ì°ì±ì²ì³ì´ìµì¶ì·ì¸ì¹ìºì»ì¼ì½ì¾ì¿ +ì‚€ì‚삂삃삄삅삆삇삈삉삊삋삌ì‚ì‚Žì‚ì‚삑삒삓삔삕삖삗삘삙삚삛삜ì‚ì‚žì‚Ÿ +삠삡삢삣삤삥삦삧삨삩삪삫사삭삮삯산삱삲삳살삵삶삷삸삹삺삻삼삽삾삿 +샀ìƒìƒ‚샃샄샅샆샇새색샊샋샌ìƒìƒŽìƒìƒìƒ‘샒샓샔샕샖샗샘샙샚샛샜ìƒìƒžìƒŸ +샠샡샢샣샤샥샦샧샨샩샪샫샬샭샮샯샰샱샲샳샴샵샶샷샸샹샺샻샼샽샾샿 +ì„€ì„섂섃섄섅섆섇섈섉섊섋섌ì„ì„Žì„ì„섑섒섓섔섕섖섗섘섙섚섛서ì„ì„žì„Ÿ +선섡섢섣설섥섦섧섨섩섪섫섬섭섮섯섰성섲섳섴섵섶섷세섹섺섻센섽섾섿 +ì…€ì…셂셃셄셅셆셇셈셉셊셋셌ì…ì…Žì…ì…셑셒셓셔셕셖셗션셙셚셛셜ì…ì…žì…Ÿ +셠셡셢셣셤셥셦셧셨셩셪셫셬셭셮셯셰셱셲셳셴셵셶셷셸셹셺셻셼셽셾셿 +솀ì†ì†‚솃솄솅솆솇솈솉솊솋소ì†ì†Žì†ì†ì†‘솒솓솔솕솖솗솘솙솚솛솜ì†ì†žì†Ÿ +솠송솢솣솤솥솦솧솨솩솪솫솬솭솮솯솰솱솲솳솴솵솶솷솸솹솺솻솼솽솾솿 +쇀ì‡ì‡‚쇃쇄쇅쇆쇇쇈쇉쇊쇋쇌ì‡ì‡Žì‡ì‡ì‡‘쇒쇓쇔쇕쇖쇗쇘쇙쇚쇛쇜ì‡ì‡žì‡Ÿ +쇠쇡쇢쇣쇤쇥쇦쇧쇨쇩쇪쇫쇬쇭쇮쇯쇰쇱쇲쇳쇴쇵쇶쇷쇸쇹쇺쇻쇼쇽쇾쇿 +숀ìˆìˆ‚숃숄숅숆숇숈숉숊숋숌ìˆìˆŽìˆìˆìˆ‘숒숓숔숕숖숗수숙숚숛순ìˆìˆžìˆŸ +술숡숢숣숤숥숦숧숨숩숪숫숬숭숮숯숰숱숲숳숴숵숶숷숸숹숺숻숼숽숾숿 +쉀ì‰ì‰‚쉃쉄쉅쉆쉇쉈쉉쉊쉋쉌ì‰ì‰Žì‰ì‰ì‰‘쉒쉓쉔쉕쉖쉗쉘쉙쉚쉛쉜ì‰ì‰žì‰Ÿ +쉠쉡쉢쉣쉤쉥쉦쉧쉨쉩쉪쉫쉬쉭쉮쉯쉰쉱쉲쉳쉴쉵쉶쉷쉸쉹쉺쉻쉼쉽쉾쉿 +슀ìŠìŠ‚슃슄슅슆슇슈슉슊슋슌ìŠìŠŽìŠìŠìŠ‘슒슓슔슕슖슗슘슙슚슛슜ìŠìŠžìŠŸ +슠슡슢슣스슥슦슧슨슩슪슫슬슭슮슯슰슱슲슳슴습슶슷슸승슺슻슼슽슾슿 +ì‹€ì‹ì‹‚싃싄싅싆싇싈싉싊싋싌ì‹ì‹Žì‹ì‹ì‹‘싒싓싔싕싖싗싘싙싚싛시ì‹ì‹žì‹Ÿ +신싡싢싣실싥싦싧싨싩싪싫심십싮싯싰싱싲싳싴싵싶싷싸싹싺싻싼싽싾싿 +쌀ìŒìŒ‚쌃쌄쌅쌆쌇쌈쌉쌊쌋쌌ìŒìŒŽìŒìŒìŒ‘쌒쌓쌔쌕쌖쌗쌘쌙쌚쌛쌜ìŒìŒžìŒŸ +쌠쌡쌢쌣쌤쌥쌦쌧쌨쌩쌪쌫쌬쌭쌮쌯쌰쌱쌲쌳쌴쌵쌶쌷쌸쌹쌺쌻쌼쌽쌾쌿 +ì€ìì‚ìƒì„ì…ì†ì‡ìˆì‰ìŠì‹ìŒììŽììì‘ì’ì“ì”ì•ì–ì—ì˜ì™ìšì›ìœììžìŸ +ì ì¡ì¢ì£ì¤ì¥ì¦ì§ì¨ì©ìªì«ì¬ì­ì®ì¯ì°ì±ì²ì³ì´ìµì¶ì·ì¸ì¹ìºì»ì¼ì½ì¾ì¿ +쎀ìŽìŽ‚쎃쎄쎅쎆쎇쎈쎉쎊쎋쎌ìŽìŽŽìŽìŽìŽ‘쎒쎓쎔쎕쎖쎗쎘쎙쎚쎛쎜ìŽìŽžìŽŸ +쎠쎡쎢쎣쎤쎥쎦쎧쎨쎩쎪쎫쎬쎭쎮쎯쎰쎱쎲쎳쎴쎵쎶쎷쎸쎹쎺쎻쎼쎽쎾쎿 +ì€ìì‚ìƒì„ì…ì†ì‡ìˆì‰ìŠì‹ìŒììŽììì‘ì’ì“ì”ì•ì–ì—ì˜ì™ìšì›ìœììžìŸ +ì ì¡ì¢ì£ì¤ì¥ì¦ì§ì¨ì©ìªì«ì¬ì­ì®ì¯ì°ì±ì²ì³ì´ìµì¶ì·ì¸ì¹ìºì»ì¼ì½ì¾ì¿ +ì€ìì‚ìƒì„ì…ì†ì‡ìˆì‰ìŠì‹ìŒììŽììì‘ì’ì“ì”ì•ì–ì—ì˜ì™ìšì›ìœììžìŸ +ì ì¡ì¢ì£ì¤ì¥ì¦ì§ì¨ì©ìªì«ì¬ì­ì®ì¯ì°ì±ì²ì³ì´ìµì¶ì·ì¸ì¹ìºì»ì¼ì½ì¾ì¿ +ì‘€ì‘쑂쑃쑄쑅쑆쑇쑈쑉쑊쑋쑌ì‘ì‘Žì‘ì‘쑑쑒쑓쑔쑕쑖쑗쑘쑙쑚쑛쑜ì‘ì‘žì‘Ÿ +쑠쑡쑢쑣쑤쑥쑦쑧쑨쑩쑪쑫쑬쑭쑮쑯쑰쑱쑲쑳쑴쑵쑶쑷쑸쑹쑺쑻쑼쑽쑾쑿 +ì’€ì’쒂쒃쒄쒅쒆쒇쒈쒉쒊쒋쒌ì’ì’Žì’ì’쒑쒒쒓쒔쒕쒖쒗쒘쒙쒚쒛쒜ì’ì’žì’Ÿ +쒠쒡쒢쒣쒤쒥쒦쒧쒨쒩쒪쒫쒬쒭쒮쒯쒰쒱쒲쒳쒴쒵쒶쒷쒸쒹쒺쒻쒼쒽쒾쒿 +ì“€ì“쓂쓃쓄쓅쓆쓇쓈쓉쓊쓋쓌ì“ì“Žì“ì“쓑쓒쓓쓔쓕쓖쓗쓘쓙쓚쓛쓜ì“ì“žì“Ÿ +쓠쓡쓢쓣쓤쓥쓦쓧쓨쓩쓪쓫쓬쓭쓮쓯쓰쓱쓲쓳쓴쓵쓶쓷쓸쓹쓺쓻쓼쓽쓾쓿 +씀ì”씂씃씄씅씆씇씈씉씊씋씌ì”씎ì”ì”씑씒씓씔씕씖씗씘씙씚씛씜ì”씞씟 +씠씡씢씣씤씥씦씧씨씩씪씫씬씭씮씯씰씱씲씳씴씵씶씷씸씹씺씻씼씽씾씿 +ì•€ì•ì•‚앃아악앆앇안앉않앋알ì•ì•Žì•ì•ì•‘앒앓암압앖앗았앙앚앛앜ì•ì•žì•Ÿ +애액앢앣앤앥앦앧앨앩앪앫앬앭앮앯앰앱앲앳앴앵앶앷앸앹앺앻야약앾앿 +ì–€ì–얂얃얄얅얆얇얈얉얊얋얌ì–ì–Žì–ì–양얒얓얔얕얖얗얘얙얚얛얜ì–ì–žì–Ÿ +얠얡얢얣얤얥얦얧얨얩얪얫얬얭얮얯얰얱얲얳어억얶얷언얹얺얻얼얽얾얿 +ì—€ì—엂엃엄업없엇었엉엊엋엌ì—ì—Žì—ì—엑엒엓엔엕엖엗엘엙엚엛엜ì—ì—žì—Ÿ +엠엡엢엣엤엥엦엧엨엩엪엫여역엮엯연엱엲엳열엵엶엷엸엹엺엻염엽엾엿 +였ì˜ì˜‚옃옄옅옆옇예옉옊옋옌ì˜ì˜Žì˜ì˜ì˜‘옒옓옔옕옖옗옘옙옚옛옜ì˜ì˜žì˜Ÿ +옠옡옢옣오옥옦옧온옩옪옫올옭옮옯옰옱옲옳옴옵옶옷옸옹옺옻옼옽옾옿 +와ì™ì™‚왃완왅왆왇왈왉왊왋왌ì™ì™Žì™ì™ì™‘왒왓왔왕왖왗왘왙왚왛왜ì™ì™žì™Ÿ +왠왡왢왣왤왥왦왧왨왩왪왫왬왭왮왯왰왱왲왳왴왵왶왷외왹왺왻왼왽왾왿 +욀ìšìš‚욃욄욅욆욇욈욉욊욋욌ìšìšŽìšìšìš‘욒욓요욕욖욗욘욙욚욛욜ìšìšžìšŸ +욠욡욢욣욤욥욦욧욨용욪욫욬욭욮욯우욱욲욳운욵욶욷울욹욺욻욼욽욾욿 +움ì›ì›‚웃웄웅웆웇웈웉웊웋워ì›ì›Žì›ì›ì›‘웒웓월웕웖웗웘웙웚웛웜ì›ì›žì›Ÿ +웠웡웢웣웤웥웦웧웨웩웪웫웬웭웮웯웰웱웲웳웴웵웶웷웸웹웺웻웼웽웾웿 +윀ìœìœ‚윃위윅윆윇윈윉윊윋윌ìœìœŽìœìœìœ‘윒윓윔윕윖윗윘윙윚윛윜ìœìœžìœŸ +유육윢윣윤윥윦윧율윩윪윫윬윭윮윯윰윱윲윳윴융윶윷윸윹윺윻으윽윾윿 +ì€ìì‚ìƒì„ì…ì†ì‡ìˆì‰ìŠì‹ìŒììŽììì‘ì’ì“ì”ì•ì–ì—ì˜ì™ìšì›ìœììžìŸ +ì ì¡ì¢ì£ì¤ì¥ì¦ì§ì¨ì©ìªì«ì¬ì­ì®ì¯ì°ì±ì²ì³ì´ìµì¶ì·ì¸ì¹ìºì»ì¼ì½ì¾ì¿ +잀ìžìž‚잃임입잆잇있잉잊잋잌ìžìžŽìžìžìž‘잒잓잔잕잖잗잘잙잚잛잜ìžìžžìžŸ +잠잡잢잣잤장잦잧잨잩잪잫재잭잮잯잰잱잲잳잴잵잶잷잸잹잺잻잼잽잾잿 +쟀ìŸìŸ‚쟃쟄쟅쟆쟇쟈쟉쟊쟋쟌ìŸìŸŽìŸìŸìŸ‘쟒쟓쟔쟕쟖쟗쟘쟙쟚쟛쟜ìŸìŸžìŸŸ +쟠쟡쟢쟣쟤쟥쟦쟧쟨쟩쟪쟫쟬쟭쟮쟯쟰쟱쟲쟳쟴쟵쟶쟷쟸쟹쟺쟻쟼쟽쟾쟿 +ì €ì ì ‚젃전젅젆젇절젉젊젋젌ì ì Žì ì ì ‘젒젓젔정젖젗젘젙젚젛제ì ì žì Ÿ +젠젡젢젣젤젥젦젧젨젩젪젫젬젭젮젯젰젱젲젳젴젵젶젷져젹젺젻젼젽젾젿 +ì¡€ì¡ì¡‚졃졄졅졆졇졈졉졊졋졌ì¡ì¡Žì¡ì¡ì¡‘졒졓졔졕졖졗졘졙졚졛졜ì¡ì¡žì¡Ÿ +졠졡졢졣졤졥졦졧졨졩졪졫졬졭졮졯조족졲졳존졵졶졷졸졹졺졻졼졽졾졿 +좀ì¢ì¢‚좃좄종좆좇좈좉좊좋좌ì¢ì¢Žì¢ì¢ì¢‘좒좓좔좕좖좗좘좙좚좛좜ì¢ì¢žì¢Ÿ +좠좡좢좣좤좥좦좧좨좩좪좫좬좭좮좯좰좱좲좳좴좵좶좷좸좹좺좻좼좽좾좿 +죀ì£ì£‚죃죄죅죆죇죈죉죊죋죌ì£ì£Žì£ì£ì£‘죒죓죔죕죖죗죘죙죚죛죜ì£ì£žì£Ÿ +죠죡죢죣죤죥죦죧죨죩죪죫죬죭죮죯죰죱죲죳죴죵죶죷죸죹죺죻주죽죾죿 +준ì¤ì¤‚줃줄줅줆줇줈줉줊줋줌ì¤ì¤Žì¤ì¤ì¤‘줒줓줔줕줖줗줘줙줚줛줜ì¤ì¤žì¤Ÿ +줠줡줢줣줤줥줦줧줨줩줪줫줬줭줮줯줰줱줲줳줴줵줶줷줸줹줺줻줼줽줾줿 +쥀ì¥ì¥‚쥃쥄쥅쥆쥇쥈쥉쥊쥋쥌ì¥ì¥Žì¥ì¥ì¥‘쥒쥓쥔쥕쥖쥗쥘쥙쥚쥛쥜ì¥ì¥žì¥Ÿ +쥠쥡쥢쥣쥤쥥쥦쥧쥨쥩쥪쥫쥬쥭쥮쥯쥰쥱쥲쥳쥴쥵쥶쥷쥸쥹쥺쥻쥼쥽쥾쥿 +즀ì¦ì¦‚즃즄즅즆즇즈즉즊즋즌ì¦ì¦Žì¦ì¦ì¦‘즒즓즔즕즖즗즘즙즚즛즜ì¦ì¦žì¦Ÿ +즠즡즢즣즤즥즦즧즨즩즪즫즬즭즮즯즰즱즲즳즴즵즶즷즸즹즺즻즼즽즾즿 +지ì§ì§‚짃진짅짆짇질짉짊짋짌ì§ì§Žì§ì§ì§‘짒짓짔징짖짗짘짙짚짛짜ì§ì§žì§Ÿ +짠짡짢짣짤짥짦짧짨짩짪짫짬짭짮짯짰짱짲짳짴짵짶짷째짹짺짻짼짽짾짿 +쨀ì¨ì¨‚쨃쨄쨅쨆쨇쨈쨉쨊쨋쨌ì¨ì¨Žì¨ì¨ì¨‘쨒쨓쨔쨕쨖쨗쨘쨙쨚쨛쨜ì¨ì¨žì¨Ÿ +쨠쨡쨢쨣쨤쨥쨦쨧쨨쨩쨪쨫쨬쨭쨮쨯쨰쨱쨲쨳쨴쨵쨶쨷쨸쨹쨺쨻쨼쨽쨾쨿 +ì©€ì©ì©‚쩃쩄쩅쩆쩇쩈쩉쩊쩋쩌ì©ì©Žì©ì©ì©‘쩒쩓쩔쩕쩖쩗쩘쩙쩚쩛쩜ì©ì©žì©Ÿ +쩠쩡쩢쩣쩤쩥쩦쩧쩨쩩쩪쩫쩬쩭쩮쩯쩰쩱쩲쩳쩴쩵쩶쩷쩸쩹쩺쩻쩼쩽쩾쩿 +쪀ìªìª‚쪃쪄쪅쪆쪇쪈쪉쪊쪋쪌ìªìªŽìªìªìª‘쪒쪓쪔쪕쪖쪗쪘쪙쪚쪛쪜ìªìªžìªŸ +쪠쪡쪢쪣쪤쪥쪦쪧쪨쪩쪪쪫쪬쪭쪮쪯쪰쪱쪲쪳쪴쪵쪶쪷쪸쪹쪺쪻쪼쪽쪾쪿 +ì«€ì«ì«‚쫃쫄쫅쫆쫇쫈쫉쫊쫋쫌ì«ì«Žì«ì«ì«‘쫒쫓쫔쫕쫖쫗쫘쫙쫚쫛쫜ì«ì«žì«Ÿ +쫠쫡쫢쫣쫤쫥쫦쫧쫨쫩쫪쫫쫬쫭쫮쫯쫰쫱쫲쫳쫴쫵쫶쫷쫸쫹쫺쫻쫼쫽쫾쫿 +쬀ì¬ì¬‚쬃쬄쬅쬆쬇쬈쬉쬊쬋쬌ì¬ì¬Žì¬ì¬ì¬‘쬒쬓쬔쬕쬖쬗쬘쬙쬚쬛쬜ì¬ì¬žì¬Ÿ +쬠쬡쬢쬣쬤쬥쬦쬧쬨쬩쬪쬫쬬쬭쬮쬯쬰쬱쬲쬳쬴쬵쬶쬷쬸쬹쬺쬻쬼쬽쬾쬿 +ì­€ì­ì­‚쭃쭄쭅쭆쭇쭈쭉쭊쭋쭌ì­ì­Žì­ì­ì­‘쭒쭓쭔쭕쭖쭗쭘쭙쭚쭛쭜ì­ì­žì­Ÿ +쭠쭡쭢쭣쭤쭥쭦쭧쭨쭩쭪쭫쭬쭭쭮쭯쭰쭱쭲쭳쭴쭵쭶쭷쭸쭹쭺쭻쭼쭽쭾쭿 +쮀ì®ì®‚쮃쮄쮅쮆쮇쮈쮉쮊쮋쮌ì®ì®Žì®ì®ì®‘쮒쮓쮔쮕쮖쮗쮘쮙쮚쮛쮜ì®ì®žì®Ÿ +쮠쮡쮢쮣쮤쮥쮦쮧쮨쮩쮪쮫쮬쮭쮮쮯쮰쮱쮲쮳쮴쮵쮶쮷쮸쮹쮺쮻쮼쮽쮾쮿 +쯀ì¯ì¯‚쯃쯄쯅쯆쯇쯈쯉쯊쯋쯌ì¯ì¯Žì¯ì¯ì¯‘쯒쯓쯔쯕쯖쯗쯘쯙쯚쯛쯜ì¯ì¯žì¯Ÿ +쯠쯡쯢쯣쯤쯥쯦쯧쯨쯩쯪쯫쯬쯭쯮쯯쯰쯱쯲쯳쯴쯵쯶쯷쯸쯹쯺쯻쯼쯽쯾쯿 +ì°€ì°ì°‚찃찄찅찆찇찈찉찊찋찌ì°ì°Žì°ì°ì°‘찒찓찔찕찖찗찘찙찚찛찜ì°ì°žì°Ÿ +찠찡찢찣찤찥찦찧차착찪찫찬찭찮찯찰찱찲찳찴찵찶찷참찹찺찻찼창찾찿 +ì±€ì±ì±‚챃채책챆챇챈챉챊챋챌ì±ì±Žì±ì±ì±‘챒챓챔챕챖챗챘챙챚챛챜ì±ì±žì±Ÿ +챠챡챢챣챤챥챦챧챨챩챪챫챬챭챮챯챰챱챲챳챴챵챶챷챸챹챺챻챼챽챾챿 +ì²€ì²ì²‚첃첄첅첆첇첈첉첊첋첌ì²ì²Žì²ì²ì²‘첒첓첔첕첖첗처척첚첛천ì²ì²žì²Ÿ +철첡첢첣첤첥첦첧첨첩첪첫첬청첮첯첰첱첲첳체첵첶첷첸첹첺첻첼첽첾첿 +ì³€ì³ì³‚쳃쳄쳅쳆쳇쳈쳉쳊쳋쳌ì³ì³Žì³ì³ì³‘쳒쳓쳔쳕쳖쳗쳘쳙쳚쳛쳜ì³ì³žì³Ÿ +쳠쳡쳢쳣쳤쳥쳦쳧쳨쳩쳪쳫쳬쳭쳮쳯쳰쳱쳲쳳쳴쳵쳶쳷쳸쳹쳺쳻쳼쳽쳾쳿 +ì´€ì´ì´‚촃촄촅촆촇초촉촊촋촌ì´ì´Žì´ì´ì´‘촒촓촔촕촖촗촘촙촚촛촜ì´ì´žì´Ÿ +촠촡촢촣촤촥촦촧촨촩촪촫촬촭촮촯촰촱촲촳촴촵촶촷촸촹촺촻촼촽촾촿 +ìµ€ìµìµ‚쵃쵄쵅쵆쵇쵈쵉쵊쵋쵌ìµìµŽìµìµìµ‘쵒쵓쵔쵕쵖쵗쵘쵙쵚쵛최ìµìµžìµŸ +쵠쵡쵢쵣쵤쵥쵦쵧쵨쵩쵪쵫쵬쵭쵮쵯쵰쵱쵲쵳쵴쵵쵶쵷쵸쵹쵺쵻쵼쵽쵾쵿 +춀ì¶ì¶‚춃춄춅춆춇춈춉춊춋춌ì¶ì¶Žì¶ì¶ì¶‘춒춓추축춖춗춘춙춚춛출ì¶ì¶žì¶Ÿ +춠춡춢춣춤춥춦춧춨충춪춫춬춭춮춯춰춱춲춳춴춵춶춷춸춹춺춻춼춽춾춿 +ì·€ì·ì·‚췃췄췅췆췇췈췉췊췋췌ì·ì·Žì·ì·ì·‘췒췓췔췕췖췗췘췙췚췛췜ì·ì·žì·Ÿ +췠췡췢췣췤췥췦췧취췩췪췫췬췭췮췯췰췱췲췳췴췵췶췷췸췹췺췻췼췽췾췿 +츀ì¸ì¸‚츃츄츅츆츇츈츉츊츋츌ì¸ì¸Žì¸ì¸ì¸‘츒츓츔츕츖츗츘츙츚츛츜ì¸ì¸žì¸Ÿ +츠측츢츣츤츥츦츧츨츩츪츫츬츭츮츯츰츱츲츳츴층츶츷츸츹츺츻츼츽츾츿 +ì¹€ì¹ì¹‚칃칄칅칆칇칈칉칊칋칌ì¹ì¹Žì¹ì¹ì¹‘칒칓칔칕칖칗치칙칚칛친ì¹ì¹žì¹Ÿ +칠칡칢칣칤칥칦칧침칩칪칫칬칭칮칯칰칱칲칳카칵칶칷칸칹칺칻칼칽칾칿 +캀ìºìº‚캃캄캅캆캇캈캉캊캋캌ìºìºŽìºìºìº‘캒캓캔캕캖캗캘캙캚캛캜ìºìºžìºŸ +캠캡캢캣캤캥캦캧캨캩캪캫캬캭캮캯캰캱캲캳캴캵캶캷캸캹캺캻캼캽캾캿 +컀ì»ì»‚컃컄컅컆컇컈컉컊컋컌ì»ì»Žì»ì»ì»‘컒컓컔컕컖컗컘컙컚컛컜ì»ì»žì»Ÿ +컠컡컢컣커컥컦컧컨컩컪컫컬컭컮컯컰컱컲컳컴컵컶컷컸컹컺컻컼컽컾컿 +ì¼€ì¼ì¼‚켃켄켅켆켇켈켉켊켋켌ì¼ì¼Žì¼ì¼ì¼‘켒켓켔켕켖켗켘켙켚켛켜ì¼ì¼žì¼Ÿ +켠켡켢켣켤켥켦켧켨켩켪켫켬켭켮켯켰켱켲켳켴켵켶켷켸켹켺켻켼켽켾켿 +ì½€ì½ì½‚콃콄콅콆콇콈콉콊콋콌ì½ì½Žì½ì½ì½‘콒콓코콕콖콗콘콙콚콛콜ì½ì½žì½Ÿ +콠콡콢콣콤콥콦콧콨콩콪콫콬콭콮콯콰콱콲콳콴콵콶콷콸콹콺콻콼콽콾콿 +ì¾€ì¾ì¾‚쾃쾄쾅쾆쾇쾈쾉쾊쾋쾌ì¾ì¾Žì¾ì¾ì¾‘쾒쾓쾔쾕쾖쾗쾘쾙쾚쾛쾜ì¾ì¾žì¾Ÿ +쾠쾡쾢쾣쾤쾥쾦쾧쾨쾩쾪쾫쾬쾭쾮쾯쾰쾱쾲쾳쾴쾵쾶쾷쾸쾹쾺쾻쾼쾽쾾쾿 +ì¿€ì¿ì¿‚쿃쿄쿅쿆쿇쿈쿉쿊쿋쿌ì¿ì¿Žì¿ì¿ì¿‘쿒쿓쿔쿕쿖쿗쿘쿙쿚쿛쿜ì¿ì¿žì¿Ÿ +쿠쿡쿢쿣쿤쿥쿦쿧쿨쿩쿪쿫쿬쿭쿮쿯쿰쿱쿲쿳쿴쿵쿶쿷쿸쿹쿺쿻쿼쿽쿾쿿 +퀀í€í€‚퀃퀄퀅퀆퀇퀈퀉퀊퀋퀌í€í€Ží€í€í€‘퀒퀓퀔퀕퀖퀗퀘퀙퀚퀛퀜í€í€ží€Ÿ +퀠퀡퀢퀣퀤퀥퀦퀧퀨퀩퀪퀫퀬퀭퀮퀯퀰퀱퀲퀳퀴퀵퀶퀷퀸퀹퀺퀻퀼퀽퀾퀿 +í€íí‚íƒí„í…í†í‡íˆí‰íŠí‹íŒííŽííí‘í’í“í”í•í–í—í˜í™íší›íœíížíŸ +í í¡í¢í£í¤í¥í¦í§í¨í©íªí«í¬í­í®í¯í°í±í²í³í´íµí¶í·í¸í¹íºí»í¼í½í¾í¿ +í‚€í‚킂킃킄킅킆킇킈킉킊킋킌í‚í‚Ží‚í‚킑킒킓킔킕킖킗킘킙킚킛킜í‚í‚ží‚Ÿ +킠킡킢킣키킥킦킧킨킩킪킫킬킭킮킯킰킱킲킳킴킵킶킷킸킹킺킻킼킽킾킿 +타íƒíƒ‚탃탄탅탆탇탈탉탊탋탌íƒíƒŽíƒíƒíƒ‘탒탓탔탕탖탗탘탙탚탛태íƒíƒžíƒŸ +탠탡탢탣탤탥탦탧탨탩탪탫탬탭탮탯탰탱탲탳탴탵탶탷탸탹탺탻탼탽탾탿 +í„€í„턂턃턄턅턆턇턈턉턊턋턌í„í„Ží„í„턑턒턓턔턕턖턗턘턙턚턛턜í„í„ží„Ÿ +턠턡턢턣턤턥턦턧턨턩턪턫턬턭턮턯터턱턲턳턴턵턶턷털턹턺턻턼턽턾턿 +í…€í…텂텃텄텅텆텇텈텉텊텋테í…í…Ží…í…텑텒텓텔텕텖텗텘텙텚텛템í…í…ží…Ÿ +텠텡텢텣텤텥텦텧텨텩텪텫텬텭텮텯텰텱텲텳텴텵텶텷텸텹텺텻텼텽텾텿 +톀í†í†‚톃톄톅톆톇톈톉톊톋톌í†í†Ží†í†í†‘톒톓톔톕톖톗톘톙톚톛톜í†í†ží†Ÿ +토톡톢톣톤톥톦톧톨톩톪톫톬톭톮톯톰톱톲톳톴통톶톷톸톹톺톻톼톽톾톿 +퇀í‡í‡‚퇃퇄퇅퇆퇇퇈퇉퇊퇋퇌í‡í‡Ží‡í‡í‡‘퇒퇓퇔퇕퇖퇗퇘퇙퇚퇛퇜í‡í‡ží‡Ÿ +퇠퇡퇢퇣퇤퇥퇦퇧퇨퇩퇪퇫퇬퇭퇮퇯퇰퇱퇲퇳퇴퇵퇶퇷퇸퇹퇺퇻퇼퇽퇾퇿 +툀íˆíˆ‚툃툄툅툆툇툈툉툊툋툌íˆíˆŽíˆíˆíˆ‘툒툓툔툕툖툗툘툙툚툛툜íˆíˆžíˆŸ +툠툡툢툣툤툥툦툧툨툩툪툫투툭툮툯툰툱툲툳툴툵툶툷툸툹툺툻툼툽툾툿 +퉀í‰í‰‚퉃퉄퉅퉆퉇퉈퉉퉊퉋퉌í‰í‰Ží‰í‰í‰‘퉒퉓퉔퉕퉖퉗퉘퉙퉚퉛퉜í‰í‰ží‰Ÿ +퉠퉡퉢퉣퉤퉥퉦퉧퉨퉩퉪퉫퉬퉭퉮퉯퉰퉱퉲퉳퉴퉵퉶퉷퉸퉹퉺퉻퉼퉽퉾퉿 +튀íŠíŠ‚튃튄튅튆튇튈튉튊튋튌íŠíŠŽíŠíŠíŠ‘튒튓튔튕튖튗튘튙튚튛튜íŠíŠžíŠŸ +튠튡튢튣튤튥튦튧튨튩튪튫튬튭튮튯튰튱튲튳튴튵튶튷트특튺튻튼튽튾튿 +í‹€í‹í‹‚틃틄틅틆틇틈틉틊틋틌í‹í‹Ží‹í‹í‹‘틒틓틔틕틖틗틘틙틚틛틜í‹í‹ží‹Ÿ +틠틡틢틣틤틥틦틧틨틩틪틫틬틭틮틯티틱틲틳틴틵틶틷틸틹틺틻틼틽틾틿 +팀íŒíŒ‚팃팄팅팆팇팈팉팊팋파íŒíŒŽíŒíŒíŒ‘팒팓팔팕팖팗팘팙팚팛팜íŒíŒžíŒŸ +팠팡팢팣팤팥팦팧패팩팪팫팬팭팮팯팰팱팲팳팴팵팶팷팸팹팺팻팼팽팾팿 +í€íí‚íƒí„í…í†í‡íˆí‰íŠí‹íŒííŽííí‘í’í“í”í•í–í—í˜í™íší›íœíížíŸ +í í¡í¢í£í¤í¥í¦í§í¨í©íªí«í¬í­í®í¯í°í±í²í³í´íµí¶í·í¸í¹íºí»í¼í½í¾í¿ +펀íŽíŽ‚펃펄펅펆펇펈펉펊펋펌íŽíŽŽíŽíŽíŽ‘펒펓펔펕펖펗페펙펚펛펜íŽíŽžíŽŸ +펠펡펢펣펤펥펦펧펨펩펪펫펬펭펮펯펰펱펲펳펴펵펶펷편펹펺펻펼펽펾펿 +í€íí‚íƒí„í…í†í‡íˆí‰íŠí‹íŒííŽííí‘í’í“í”í•í–í—í˜í™íší›íœíížíŸ +í í¡í¢í£í¤í¥í¦í§í¨í©íªí«í¬í­í®í¯í°í±í²í³í´íµí¶í·í¸í¹íºí»í¼í½í¾í¿ +í€íí‚íƒí„í…í†í‡íˆí‰íŠí‹íŒííŽííí‘í’í“í”í•í–í—í˜í™íší›íœíížíŸ +í í¡í¢í£í¤í¥í¦í§í¨í©íªí«í¬í­í®í¯í°í±í²í³í´íµí¶í·í¸í¹íºí»í¼í½í¾í¿ +í‘€í‘푂푃푄푅푆푇푈푉푊푋푌í‘í‘Ží‘í‘푑푒푓푔푕푖푗푘푙푚푛표í‘í‘ží‘Ÿ +푠푡푢푣푤푥푦푧푨푩푪푫푬푭푮푯푰푱푲푳푴푵푶푷푸푹푺푻푼푽푾푿 +í’€í’풂풃풄풅풆풇품풉풊풋풌í’í’Ží’í’풑풒풓풔풕풖풗풘풙풚풛풜í’í’ží’Ÿ +풠풡풢풣풤풥풦풧풨풩풪풫풬풭풮풯풰풱풲풳풴풵풶풷풸풹풺풻풼풽풾풿 +í“€í“퓂퓃퓄퓅퓆퓇퓈퓉퓊퓋퓌í“í“Ží“í“퓑퓒퓓퓔퓕퓖퓗퓘퓙퓚퓛퓜í“í“ží“Ÿ +퓠퓡퓢퓣퓤퓥퓦퓧퓨퓩퓪퓫퓬퓭퓮퓯퓰퓱퓲퓳퓴퓵퓶퓷퓸퓹퓺퓻퓼퓽퓾퓿 +픀í”픂픃프픅픆픇픈픉픊픋플í”픎í”í”픑픒픓픔픕픖픗픘픙픚픛픜í”픞픟 +픠픡픢픣픤픥픦픧픨픩픪픫픬픭픮픯픰픱픲픳픴픵픶픷픸픹픺픻피픽픾픿 +í•€í•í•‚핃필핅핆핇핈핉핊핋핌í•í•Ží•í•í•‘핒핓핔핕핖핗하학핚핛한í•í•ží•Ÿ +할핡핢핣핤핥핦핧함합핪핫핬항핮핯핰핱핲핳해핵핶핷핸핹핺핻핼핽핾핿 +í–€í–햂햃햄햅햆햇했행햊햋햌í–í–Ží–í–햑햒햓햔햕햖햗햘햙햚햛햜í–í–ží–Ÿ +햠햡햢햣햤향햦햧햨햩햪햫햬햭햮햯햰햱햲햳햴햵햶햷햸햹햺햻햼햽햾햿 +í—€í—헂헃헄헅헆헇허헉헊헋헌í—í—Ží—í—헑헒헓헔헕헖헗험헙헚헛헜í—í—ží—Ÿ +헠헡헢헣헤헥헦헧헨헩헪헫헬헭헮헯헰헱헲헳헴헵헶헷헸헹헺헻헼헽헾헿 +혀í˜í˜‚혃현혅혆혇혈혉혊혋혌í˜í˜Ží˜í˜í˜‘혒혓혔형혖혗혘혙혚혛혜í˜í˜ží˜Ÿ +혠혡혢혣혤혥혦혧혨혩혪혫혬혭혮혯혰혱혲혳혴혵혶혷호혹혺혻혼혽혾혿 +홀í™í™‚홃홄홅홆홇홈홉홊홋홌í™í™Ží™í™í™‘홒홓화확홖홗환홙홚홛활í™í™ží™Ÿ +홠홡홢홣홤홥홦홧홨황홪홫홬홭홮홯홰홱홲홳홴홵홶홷홸홹홺홻홼홽홾홿 +횀íšíš‚횃횄횅횆횇횈횉횊횋회íšíšŽíšíšíš‘횒횓횔횕횖횗횘횙횚횛횜íšíšžíšŸ +횠횡횢횣횤횥횦횧효횩횪횫횬횭횮횯횰횱횲횳횴횵횶횷횸횹횺횻횼횽횾횿 +훀í›í›‚훃후훅훆훇훈훉훊훋훌í›í›Ží›í›í›‘훒훓훔훕훖훗훘훙훚훛훜í›í›ží›Ÿ +훠훡훢훣훤훥훦훧훨훩훪훫훬훭훮훯훰훱훲훳훴훵훶훷훸훹훺훻훼훽훾훿 +휀íœíœ‚휃휄휅휆휇휈휉휊휋휌íœíœŽíœíœíœ‘휒휓휔휕휖휗휘휙휚휛휜íœíœžíœŸ +휠휡휢휣휤휥휦휧휨휩휪휫휬휭휮휯휰휱휲휳휴휵휶휷휸휹휺휻휼휽휾휿 +í€íí‚íƒí„í…í†í‡íˆí‰íŠí‹íŒííŽííí‘í’í“í”í•í–í—í˜í™íší›íœíížíŸ +í í¡í¢í£í¤í¥í¦í§í¨í©íªí«í¬í­í®í¯í°í±í²í³í´íµí¶í·í¸í¹íºí»í¼í½í¾í¿ +힀ížíž‚힃힄힅힆힇히힉힊힋힌ížížŽížížíž‘힒힓힔힕힖힗힘힙힚힛힜ížížžížŸ +힠힡힢힣힤힥힦힧힨힩힪힫힬힭힮힯 + +Free block (U+D7B0-U+D7FF): + +ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀíŸíŸ‚ퟃퟄퟅퟆ퟇퟈퟉퟊ퟋퟌíŸíŸŽíŸíŸíŸ‘ퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜíŸíŸžíŸŸíŸ íŸ¡íŸ¢íŸ£íŸ¤íŸ¥íŸ¦íŸ§íŸ¨íŸ©íŸªíŸ«íŸ¬íŸ­íŸ®íŸ¯ +ퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ퟼퟽퟾퟿ + +High Surrogates (U+D800-U+DB7F): + +í €í í ‚í ƒí „í …í †í ‡í ˆí ‰í Ší ‹í Œí í Ží í í ‘í ’í “í ”í •í –í —í ˜í ™í ší ›í œí í ží Ÿí  í ¡í ¢í £í ¤í ¥í ¦í §í ¨í ©í ªí «í ¬í ­í ®í ¯í °í ±í ²í ³í ´í µí ¶í ·í ¸í ¹í ºí »í ¼í ½í ¾í ¿ +í¡€í¡í¡‚í¡ƒí¡„í¡…í¡†í¡‡í¡ˆí¡‰í¡Ší¡‹í¡Œí¡í¡Ží¡í¡í¡‘í¡’í¡“í¡”í¡•í¡–í¡—í¡˜í¡™í¡ší¡›í¡œí¡í¡ží¡Ÿí¡ í¡¡í¡¢í¡£í¡¤í¡¥í¡¦í¡§í¡¨í¡©í¡ªí¡«í¡¬í¡­í¡®í¡¯í¡°í¡±í¡²í¡³í¡´í¡µí¡¶í¡·í¡¸í¡¹í¡ºí¡»í¡¼í¡½í¡¾í¡¿ +í¢€í¢í¢‚í¢ƒí¢„í¢…í¢†í¢‡í¢ˆí¢‰í¢Ší¢‹í¢Œí¢í¢Ží¢í¢í¢‘í¢’í¢“í¢”í¢•í¢–í¢—í¢˜í¢™í¢ší¢›í¢œí¢í¢ží¢Ÿí¢ í¢¡í¢¢í¢£í¢¤í¢¥í¢¦í¢§í¢¨í¢©í¢ªí¢«í¢¬í¢­í¢®í¢¯í¢°í¢±í¢²í¢³í¢´í¢µí¢¶í¢·í¢¸í¢¹í¢ºí¢»í¢¼í¢½í¢¾í¢¿ +í£€í£í£‚í£ƒí£„í£…í£†í£‡í£ˆí£‰í£Ší£‹í£Œí£í£Ží£í£í£‘í£’í£“í£”í£•í£–í£—í£˜í£™í£ší£›í£œí£í£ží£Ÿí£ í£¡í£¢í££í£¤í£¥í£¦í£§í£¨í£©í£ªí£«í£¬í£­í£®í£¯í£°í£±í£²í£³í£´í£µí£¶í£·í£¸í£¹í£ºí£»í£¼í£½í£¾í£¿ +í¤€í¤í¤‚í¤ƒí¤„í¤…í¤†í¤‡í¤ˆí¤‰í¤Ší¤‹í¤Œí¤í¤Ží¤í¤í¤‘í¤’í¤“í¤”í¤•í¤–í¤—í¤˜í¤™í¤ší¤›í¤œí¤í¤ží¤Ÿí¤ í¤¡í¤¢í¤£í¤¤í¤¥í¤¦í¤§í¤¨í¤©í¤ªí¤«í¤¬í¤­í¤®í¤¯í¤°í¤±í¤²í¤³í¤´í¤µí¤¶í¤·í¤¸í¤¹í¤ºí¤»í¤¼í¤½í¤¾í¤¿ +í¥€í¥í¥‚í¥ƒí¥„í¥…í¥†í¥‡í¥ˆí¥‰í¥Ší¥‹í¥Œí¥í¥Ží¥í¥í¥‘í¥’í¥“í¥”í¥•í¥–í¥—í¥˜í¥™í¥ší¥›í¥œí¥í¥ží¥Ÿí¥ í¥¡í¥¢í¥£í¥¤í¥¥í¥¦í¥§í¥¨í¥©í¥ªí¥«í¥¬í¥­í¥®í¥¯í¥°í¥±í¥²í¥³í¥´í¥µí¥¶í¥·í¥¸í¥¹í¥ºí¥»í¥¼í¥½í¥¾í¥¿ +í¦€í¦í¦‚í¦ƒí¦„í¦…í¦†í¦‡í¦ˆí¦‰í¦Ší¦‹í¦Œí¦í¦Ží¦í¦í¦‘í¦’í¦“í¦”í¦•í¦–í¦—í¦˜í¦™í¦ší¦›í¦œí¦í¦ží¦Ÿí¦ í¦¡í¦¢í¦£í¦¤í¦¥í¦¦í¦§í¦¨í¦©í¦ªí¦«í¦¬í¦­í¦®í¦¯í¦°í¦±í¦²í¦³í¦´í¦µí¦¶í¦·í¦¸í¦¹í¦ºí¦»í¦¼í¦½í¦¾í¦¿ +í§€í§í§‚í§ƒí§„í§…í§†í§‡í§ˆí§‰í§Ší§‹í§Œí§í§Ží§í§í§‘í§’í§“í§”í§•í§–í§—í§˜í§™í§ší§›í§œí§í§ží§Ÿí§ í§¡í§¢í§£í§¤í§¥í§¦í§§í§¨í§©í§ªí§«í§¬í§­í§®í§¯í§°í§±í§²í§³í§´í§µí§¶í§·í§¸í§¹í§ºí§»í§¼í§½í§¾í§¿ +í¨€í¨í¨‚í¨ƒí¨„í¨…í¨†í¨‡í¨ˆí¨‰í¨Ší¨‹í¨Œí¨í¨Ží¨í¨í¨‘í¨’í¨“í¨”í¨•í¨–í¨—í¨˜í¨™í¨ší¨›í¨œí¨í¨ží¨Ÿí¨ í¨¡í¨¢í¨£í¨¤í¨¥í¨¦í¨§í¨¨í¨©í¨ªí¨«í¨¬í¨­í¨®í¨¯í¨°í¨±í¨²í¨³í¨´í¨µí¨¶í¨·í¨¸í¨¹í¨ºí¨»í¨¼í¨½í¨¾í¨¿ +í©€í©í©‚í©ƒí©„í©…í©†í©‡í©ˆí©‰í©Ší©‹í©Œí©í©Ží©í©í©‘í©’í©“í©”í©•í©–í©—í©˜í©™í©ší©›í©œí©í©ží©Ÿí© í©¡í©¢í©£í©¤í©¥í©¦í©§í©¨í©©í©ªí©«í©¬í©­í©®í©¯í©°í©±í©²í©³í©´í©µí©¶í©·í©¸í©¹í©ºí©»í©¼í©½í©¾í©¿ +íª€íªíª‚íªƒíª„íª…íª†íª‡íªˆíª‰íªŠíª‹íªŒíªíªŽíªíªíª‘íª’íª“íª”íª•íª–íª—íª˜íª™íªšíª›íªœíªíªžíªŸíª íª¡íª¢íª£íª¤íª¥íª¦íª§íª¨íª©íªªíª«íª¬íª­íª®íª¯íª°íª±íª²íª³íª´íªµíª¶íª·íª¸íª¹íªºíª»íª¼íª½íª¾íª¿ +í«€í«í«‚í«ƒí«„í«…í«†í«‡í«ˆí«‰í«Ší«‹í«Œí«í«Ží«í«í«‘í«’í«“í«”í«•í«–í«—í«˜í«™í«ší«›í«œí«í«ží«Ÿí« í«¡í«¢í«£í«¤í«¥í«¦í«§í«¨í«©í«ªí««í«¬í«­í«®í«¯í«°í«±í«²í«³í«´í«µí«¶í«·í«¸í«¹í«ºí«»í«¼í«½í«¾í«¿ +í¬€í¬í¬‚í¬ƒí¬„í¬…í¬†í¬‡í¬ˆí¬‰í¬Ší¬‹í¬Œí¬í¬Ží¬í¬í¬‘í¬’í¬“í¬”í¬•í¬–í¬—í¬˜í¬™í¬ší¬›í¬œí¬í¬ží¬Ÿí¬ í¬¡í¬¢í¬£í¬¤í¬¥í¬¦í¬§í¬¨í¬©í¬ªí¬«í¬¬í¬­í¬®í¬¯í¬°í¬±í¬²í¬³í¬´í¬µí¬¶í¬·í¬¸í¬¹í¬ºí¬»í¬¼í¬½í¬¾í¬¿ +í­€í­í­‚í­ƒí­„í­…í­†í­‡í­ˆí­‰í­Ší­‹í­Œí­í­Ží­í­í­‘í­’í­“í­”í­•í­–í­—í­˜í­™í­ší­›í­œí­í­ží­Ÿí­ í­¡í­¢í­£í­¤í­¥í­¦í­§í­¨í­©í­ªí­«í­¬í­­í­®í­¯í­°í­±í­²í­³í­´í­µí­¶í­·í­¸í­¹í­ºí­»í­¼í­½í­¾í­¿ + +High Private Use Surrogates (U+DB80-U+DBFF): + +í®€í®í®‚í®ƒí®„í®…í®†í®‡í®ˆí®‰í®Ší®‹í®Œí®í®Ží®í®í®‘í®’í®“í®”í®•í®–í®—í®˜í®™í®ší®›í®œí®í®ží®Ÿí® í®¡í®¢í®£í®¤í®¥í®¦í®§í®¨í®©í®ªí®«í®¬í®­í®®í®¯í®°í®±í®²í®³í®´í®µí®¶í®·í®¸í®¹í®ºí®»í®¼í®½í®¾í®¿ +í¯€í¯í¯‚í¯ƒí¯„í¯…í¯†í¯‡í¯ˆí¯‰í¯Ší¯‹í¯Œí¯í¯Ží¯í¯í¯‘í¯’í¯“í¯”í¯•í¯–í¯—í¯˜í¯™í¯ší¯›í¯œí¯í¯ží¯Ÿí¯ í¯¡í¯¢í¯£í¯¤í¯¥í¯¦í¯§í¯¨í¯©í¯ªí¯«í¯¬í¯­í¯®í¯¯í¯°í¯±í¯²í¯³í¯´í¯µí¯¶í¯·í¯¸í¯¹í¯ºí¯»í¯¼í¯½í¯¾í¯¿ + +Low Surrogates (U+DC00-U+DFFF): + +í°€í°í°‚í°ƒí°„í°…í°†í°‡í°ˆí°‰í°Ší°‹í°Œí°í°Ží°í°í°‘í°’í°“í°”í°•í°–í°—í°˜í°™í°ší°›í°œí°í°ží°Ÿí° í°¡í°¢í°£í°¤í°¥í°¦í°§í°¨í°©í°ªí°«í°¬í°­í°®í°¯í°°í°±í°²í°³í°´í°µí°¶í°·í°¸í°¹í°ºí°»í°¼í°½í°¾í°¿ +í±€í±í±‚í±ƒí±„í±…í±†í±‡í±ˆí±‰í±Ší±‹í±Œí±í±Ží±í±í±‘í±’í±“í±”í±•í±–í±—í±˜í±™í±ší±›í±œí±í±ží±Ÿí± í±¡í±¢í±£í±¤í±¥í±¦í±§í±¨í±©í±ªí±«í±¬í±­í±®í±¯í±°í±±í±²í±³í±´í±µí±¶í±·í±¸í±¹í±ºí±»í±¼í±½í±¾í±¿ +í²€í²í²‚í²ƒí²„í²…í²†í²‡í²ˆí²‰í²Ší²‹í²Œí²í²Ží²í²í²‘í²’í²“í²”í²•í²–í²—í²˜í²™í²ší²›í²œí²í²ží²Ÿí² í²¡í²¢í²£í²¤í²¥í²¦í²§í²¨í²©í²ªí²«í²¬í²­í²®í²¯í²°í²±í²²í²³í²´í²µí²¶í²·í²¸í²¹í²ºí²»í²¼í²½í²¾í²¿ +í³€í³í³‚í³ƒí³„í³…í³†í³‡í³ˆí³‰í³Ší³‹í³Œí³í³Ží³í³í³‘í³’í³“í³”í³•í³–í³—í³˜í³™í³ší³›í³œí³í³ží³Ÿí³ í³¡í³¢í³£í³¤í³¥í³¦í³§í³¨í³©í³ªí³«í³¬í³­í³®í³¯í³°í³±í³²í³³í³´í³µí³¶í³·í³¸í³¹í³ºí³»í³¼í³½í³¾í³¿ +í´€í´í´‚í´ƒí´„í´…í´†í´‡í´ˆí´‰í´Ší´‹í´Œí´í´Ží´í´í´‘í´’í´“í´”í´•í´–í´—í´˜í´™í´ší´›í´œí´í´ží´Ÿí´ í´¡í´¢í´£í´¤í´¥í´¦í´§í´¨í´©í´ªí´«í´¬í´­í´®í´¯í´°í´±í´²í´³í´´í´µí´¶í´·í´¸í´¹í´ºí´»í´¼í´½í´¾í´¿ +íµ€íµíµ‚íµƒíµ„íµ…íµ†íµ‡íµˆíµ‰íµŠíµ‹íµŒíµíµŽíµíµíµ‘íµ’íµ“íµ”íµ•íµ–íµ—íµ˜íµ™íµšíµ›íµœíµíµžíµŸíµ íµ¡íµ¢íµ£íµ¤íµ¥íµ¦íµ§íµ¨íµ©íµªíµ«íµ¬íµ­íµ®íµ¯íµ°íµ±íµ²íµ³íµ´íµµíµ¶íµ·íµ¸íµ¹íµºíµ»íµ¼íµ½íµ¾íµ¿ +í¶€í¶í¶‚í¶ƒí¶„í¶…í¶†í¶‡í¶ˆí¶‰í¶Ší¶‹í¶Œí¶í¶Ží¶í¶í¶‘í¶’í¶“í¶”í¶•í¶–í¶—í¶˜í¶™í¶ší¶›í¶œí¶í¶ží¶Ÿí¶ í¶¡í¶¢í¶£í¶¤í¶¥í¶¦í¶§í¶¨í¶©í¶ªí¶«í¶¬í¶­í¶®í¶¯í¶°í¶±í¶²í¶³í¶´í¶µí¶¶í¶·í¶¸í¶¹í¶ºí¶»í¶¼í¶½í¶¾í¶¿ +í·€í·í·‚í·ƒí·„í·…í·†í·‡í·ˆí·‰í·Ší·‹í·Œí·í·Ží·í·í·‘í·’í·“í·”í·•í·–í·—í·˜í·™í·ší·›í·œí·í·ží·Ÿí· í·¡í·¢í·£í·¤í·¥í·¦í·§í·¨í·©í·ªí·«í·¬í·­í·®í·¯í·°í·±í·²í·³í·´í·µí·¶í··í·¸í·¹í·ºí·»í·¼í·½í·¾í·¿ +í¸€í¸í¸‚í¸ƒí¸„í¸…í¸†í¸‡í¸ˆí¸‰í¸Ší¸‹í¸Œí¸í¸Ží¸í¸í¸‘í¸’í¸“í¸”í¸•í¸–í¸—í¸˜í¸™í¸ší¸›í¸œí¸í¸ží¸Ÿí¸ í¸¡í¸¢í¸£í¸¤í¸¥í¸¦í¸§í¸¨í¸©í¸ªí¸«í¸¬í¸­í¸®í¸¯í¸°í¸±í¸²í¸³í¸´í¸µí¸¶í¸·í¸¸í¸¹í¸ºí¸»í¸¼í¸½í¸¾í¸¿ +í¹€í¹í¹‚í¹ƒí¹„í¹…í¹†í¹‡í¹ˆí¹‰í¹Ší¹‹í¹Œí¹í¹Ží¹í¹í¹‘í¹’í¹“í¹”í¹•í¹–í¹—í¹˜í¹™í¹ší¹›í¹œí¹í¹ží¹Ÿí¹ í¹¡í¹¢í¹£í¹¤í¹¥í¹¦í¹§í¹¨í¹©í¹ªí¹«í¹¬í¹­í¹®í¹¯í¹°í¹±í¹²í¹³í¹´í¹µí¹¶í¹·í¹¸í¹¹í¹ºí¹»í¹¼í¹½í¹¾í¹¿ +íº€íºíº‚íºƒíº„íº…íº†íº‡íºˆíº‰íºŠíº‹íºŒíºíºŽíºíºíº‘íº’íº“íº”íº•íº–íº—íº˜íº™íºšíº›íºœíºíºžíºŸíº íº¡íº¢íº£íº¤íº¥íº¦íº§íº¨íº©íºªíº«íº¬íº­íº®íº¯íº°íº±íº²íº³íº´íºµíº¶íº·íº¸íº¹íººíº»íº¼íº½íº¾íº¿ +í»€í»í»‚í»ƒí»„í»…í»†í»‡í»ˆí»‰í»Ší»‹í»Œí»í»Ží»í»í»‘í»’í»“í»”í»•í»–í»—í»˜í»™í»ší»›í»œí»í»ží»Ÿí» í»¡í»¢í»£í»¤í»¥í»¦í»§í»¨í»©í»ªí»«í»¬í»­í»®í»¯í»°í»±í»²í»³í»´í»µí»¶í»·í»¸í»¹í»ºí»»í»¼í»½í»¾í»¿ +í¼€í¼í¼‚í¼ƒí¼„í¼…í¼†í¼‡í¼ˆí¼‰í¼Ší¼‹í¼Œí¼í¼Ží¼í¼í¼‘í¼’í¼“í¼”í¼•í¼–í¼—í¼˜í¼™í¼ší¼›í¼œí¼í¼ží¼Ÿí¼ í¼¡í¼¢í¼£í¼¤í¼¥í¼¦í¼§í¼¨í¼©í¼ªí¼«í¼¬í¼­í¼®í¼¯í¼°í¼±í¼²í¼³í¼´í¼µí¼¶í¼·í¼¸í¼¹í¼ºí¼»í¼¼í¼½í¼¾í¼¿ +í½€í½í½‚í½ƒí½„í½…í½†í½‡í½ˆí½‰í½Ší½‹í½Œí½í½Ží½í½í½‘í½’í½“í½”í½•í½–í½—í½˜í½™í½ší½›í½œí½í½ží½Ÿí½ í½¡í½¢í½£í½¤í½¥í½¦í½§í½¨í½©í½ªí½«í½¬í½­í½®í½¯í½°í½±í½²í½³í½´í½µí½¶í½·í½¸í½¹í½ºí½»í½¼í½½í½¾í½¿ +í¾€í¾í¾‚í¾ƒí¾„í¾…í¾†í¾‡í¾ˆí¾‰í¾Ší¾‹í¾Œí¾í¾Ží¾í¾í¾‘í¾’í¾“í¾”í¾•í¾–í¾—í¾˜í¾™í¾ší¾›í¾œí¾í¾ží¾Ÿí¾ í¾¡í¾¢í¾£í¾¤í¾¥í¾¦í¾§í¾¨í¾©í¾ªí¾«í¾¬í¾­í¾®í¾¯í¾°í¾±í¾²í¾³í¾´í¾µí¾¶í¾·í¾¸í¾¹í¾ºí¾»í¾¼í¾½í¾¾í¾¿ +í¿€í¿í¿‚í¿ƒí¿„í¿…í¿†í¿‡í¿ˆí¿‰í¿Ší¿‹í¿Œí¿í¿Ží¿í¿í¿‘í¿’í¿“í¿”í¿•í¿–í¿—í¿˜í¿™í¿ší¿›í¿œí¿í¿ží¿Ÿí¿ í¿¡í¿¢í¿£í¿¤í¿¥í¿¦í¿§í¿¨í¿©í¿ªí¿«í¿¬í¿­í¿®í¿¯í¿°í¿±í¿²í¿³í¿´í¿µí¿¶í¿·í¿¸í¿¹í¿ºí¿»í¿¼í¿½í¿¾í¿¿ + +Private Use Area (U+E000-U+F8FF): + +î€î€‚î€î€Žî€î€î€‘î€î€žî€Ÿî€ î€¡î€¢î€£î€¤î€¥î€¦î€§î€¨î€©î€ªî€«î€¬î€­î€®î€¯î€°î€±î€²î€³î€´î€µî€¶î€·î€¸î€¹î€ºî€»î€¼î€½î€¾î€¿ +î€îî‚îƒî„î…î†î‡îˆî‰îŠî‹îŒîîŽîîî‘î’î“î”î•î–î—î˜î™îšî›îœîîžîŸî î¡î¢î£î¤î¥î¦î§î¨î©îªî«î¬î­î®î¯î°î±î²î³î´îµî¶î·î¸î¹îºî»î¼î½î¾î¿ +î‚€î‚î‚î‚Žî‚î‚î‚ +îƒîƒ‚îƒîƒŽîƒîƒîƒ‘îƒîƒžîƒŸîƒ îƒ¡îƒ¢îƒ£îƒ¤îƒ¥îƒ¦îƒ§îƒ¨îƒ©îƒªîƒ«îƒ¬îƒ­îƒ®îƒ¯îƒ°îƒ±îƒ²îƒ³îƒ´îƒµîƒ¶îƒ·îƒ¸îƒ¹îƒºîƒ»îƒ¼îƒ½îƒ¾îƒ¿ +î„€î„î„î„Žî„î„î„ +î…€î…î…î…Žî…î…î… +î†î†‚î†î†Žî†î†î†‘î†î†žî†Ÿî† î†¡î†¢î†£î†¤î†¥î†¦î†§î†¨î†©î†ªî†«î†¬î†­î†®î†¯î†°î†±î†²î†³î†´î†µî†¶î†·î†¸î†¹î†ºî†»î†¼î†½î†¾î†¿ +î‡î‡‚î‡î‡Žî‡î‡î‡‘î‡î‡žî‡Ÿî‡ î‡¡î‡¢î‡£î‡¤î‡¥î‡¦î‡§î‡¨î‡©î‡ªî‡«î‡¬î‡­î‡®î‡¯î‡°î‡±î‡²î‡³î‡´î‡µî‡¶î‡·î‡¸î‡¹î‡ºî‡»î‡¼î‡½î‡¾î‡¿ +îˆîˆ‚îˆîˆŽîˆîˆîˆ‘îˆîˆžîˆŸîˆ îˆ¡îˆ¢îˆ£îˆ¤îˆ¥îˆ¦îˆ§îˆ¨îˆ©îˆªîˆ«îˆ¬îˆ­îˆ®îˆ¯îˆ°îˆ±îˆ²îˆ³îˆ´îˆµîˆ¶îˆ·îˆ¸îˆ¹îˆºîˆ»îˆ¼îˆ½îˆ¾îˆ¿ +î‰î‰‚î‰î‰Žî‰î‰î‰‘î‰î‰žî‰Ÿî‰ î‰¡î‰¢î‰£î‰¤î‰¥î‰¦î‰§î‰¨î‰©î‰ªî‰«î‰¬î‰­î‰®î‰¯î‰°î‰±î‰²î‰³î‰´î‰µî‰¶î‰·î‰¸î‰¹î‰ºî‰»î‰¼î‰½î‰¾î‰¿ +îŠîŠ‚îŠîŠŽîŠîŠîŠ‘îŠîŠžîŠŸîŠ îŠ¡îŠ¢îŠ£îŠ¤îŠ¥îŠ¦îŠ§îŠ¨îŠ©îŠªîŠ«îŠ¬îŠ­îŠ®îŠ¯îŠ°îŠ±îŠ²îŠ³îŠ´îŠµîŠ¶îŠ·îŠ¸îŠ¹îŠºîŠ»îŠ¼îŠ½îŠ¾îŠ¿ +î‹€î‹î‹‚î‹î‹Žî‹î‹î‹‘î‹î‹žî‹Ÿî‹ î‹¡î‹¢î‹£î‹¤î‹¥î‹¦î‹§î‹¨î‹©î‹ªî‹«î‹¬î‹­î‹®î‹¯î‹°î‹±î‹²î‹³î‹´î‹µî‹¶î‹·î‹¸î‹¹î‹ºî‹»î‹¼î‹½î‹¾î‹¿ +îŒîŒ‚îŒîŒŽîŒîŒîŒ‘îŒîŒžîŒŸîŒ îŒ¡îŒ¢îŒ£îŒ¤îŒ¥îŒ¦îŒ§îŒ¨îŒ©îŒªîŒ«îŒ¬îŒ­îŒ®îŒ¯îŒ°îŒ±îŒ²îŒ³îŒ´îŒµîŒ¶îŒ·îŒ¸îŒ¹îŒºîŒ»îŒ¼îŒ½îŒ¾îŒ¿ +î€îî‚îƒî„î…î†î‡îˆî‰îŠî‹îŒîîŽîîî‘î’î“î”î•î–î—î˜î™îšî›îœîîžîŸî î¡î¢î£î¤î¥î¦î§î¨î©îªî«î¬î­î®î¯î°î±î²î³î´îµî¶î·î¸î¹îºî»î¼î½î¾î¿ +îŽîŽ‚îŽîŽŽîŽîŽîŽ‘îŽîŽžîŽŸîŽ îŽ¡îŽ¢îŽ£îŽ¤îŽ¥îŽ¦îŽ§îŽ¨îŽ©îŽªîŽ«îŽ¬îŽ­îŽ®îŽ¯îŽ°îŽ±îŽ²îŽ³îŽ´îŽµîŽ¶îŽ·îŽ¸îŽ¹îŽºîŽ»îŽ¼îŽ½îŽ¾îŽ¿ +î€îî‚îƒî„î…î†î‡îˆî‰îŠî‹îŒîîŽîîî‘î’î“î”î•î–î—î˜î™îšî›îœîîžîŸî î¡î¢î£î¤î¥î¦î§î¨î©îªî«î¬î­î®î¯î°î±î²î³î´îµî¶î·î¸î¹îºî»î¼î½î¾î¿ +î€îî‚îƒî„î…î†î‡îˆî‰îŠî‹îŒîîŽîîî‘î’î“î”î•î–î—î˜î™îšî›îœîîžîŸî î¡î¢î£î¤î¥î¦î§î¨î©îªî«î¬î­î®î¯î°î±î²î³î´îµî¶î·î¸î¹îºî»î¼î½î¾î¿ +î‘€î‘î‘î‘Žî‘î‘î‘ +î’€î’î’î’Žî’î’î’ +î“€î“î“î“Žî“î“î“ +î”î”î”î”î” +î•€î•î•‚î•î•Žî•î•î•‘î•î•žî•Ÿî• î•¡î•¢î•£î•¤î•¥î•¦î•§î•¨î•©î•ªî•«î•¬î•­î•®î•¯î•°î•±î•²î•³î•´î•µî•¶î•·î•¸î•¹î•ºî•»î•¼î•½î•¾î•¿ +î–€î–î–î–Žî–î–î– +î—€î—î—î—Žî—î—î— +î˜î˜‚î˜î˜Žî˜î˜î˜‘î˜î˜žî˜Ÿî˜ î˜¡î˜¢î˜£î˜¤î˜¥î˜¦î˜§î˜¨î˜©î˜ªî˜«î˜¬î˜­î˜®î˜¯î˜°î˜±î˜²î˜³î˜´î˜µî˜¶î˜·î˜¸î˜¹î˜ºî˜»î˜¼î˜½î˜¾î˜¿ +î™î™‚î™î™Žî™î™î™‘î™î™žî™Ÿî™ î™¡î™¢î™£î™¤î™¥î™¦î™§î™¨î™©î™ªî™«î™¬î™­î™®î™¯î™°î™±î™²î™³î™´î™µî™¶î™·î™¸î™¹î™ºî™»î™¼î™½î™¾î™¿ +îšîš‚îšîšŽîšîšîš‘îšîšžîšŸîš îš¡îš¢îš£îš¤îš¥îš¦îš§îš¨îš©îšªîš«îš¬îš­îš®îš¯îš°îš±îš²îš³îš´îšµîš¶îš·îš¸îš¹îšºîš»îš¼îš½îš¾îš¿ +î›î›‚î›î›Žî›î›î›‘î›î›žî›Ÿî› î›¡î›¢î›£î›¤î›¥î›¦î›§î›¨î›©î›ªî›«î›¬î›­î›®î›¯î›°î›±î›²î›³î›´î›µî›¶î›·î›¸î›¹î›ºî›»î›¼î›½î›¾î›¿ +îœîœ‚îœîœŽîœîœîœ‘îœîœžîœŸîœ îœ¡îœ¢îœ£îœ¤îœ¥îœ¦îœ§îœ¨îœ©îœªîœ«îœ¬îœ­îœ®îœ¯îœ°îœ±îœ²îœ³îœ´îœµîœ¶îœ·îœ¸îœ¹îœºîœ»îœ¼îœ½îœ¾îœ¿ +î€îî‚îƒî„î…î†î‡îˆî‰îŠî‹îŒîîŽîîî‘î’î“î”î•î–î—î˜î™îšî›îœîîžîŸî î¡î¢î£î¤î¥î¦î§î¨î©îªî«î¬î­î®î¯î°î±î²î³î´îµî¶î·î¸î¹îºî»î¼î½î¾î¿ +îžîž‚îžîžŽîžîžîž‘îžîžžîžŸîž îž¡îž¢îž£îž¤îž¥îž¦îž§îž¨îž©îžªîž«îž¬îž­îž®îž¯îž°îž±îž²îž³îž´îžµîž¶îž·îž¸îž¹îžºîž»îž¼îž½îž¾îž¿ +îŸîŸ‚îŸîŸŽîŸîŸîŸ‘îŸîŸžîŸŸîŸ îŸ¡îŸ¢îŸ£îŸ¤îŸ¥îŸ¦îŸ§îŸ¨îŸ©îŸªîŸ«îŸ¬îŸ­îŸ®îŸ¯îŸ°îŸ±îŸ²îŸ³îŸ´îŸµîŸ¶îŸ·îŸ¸îŸ¹îŸºîŸ»îŸ¼îŸ½îŸ¾îŸ¿ +î €î î ‚î î Žî î î ‘î î žî Ÿî  î ¡î ¢î £î ¤î ¥î ¦î §î ¨î ©î ªî «î ¬î ­î ®î ¯î °î ±î ²î ³î ´î µî ¶î ·î ¸î ¹î ºî »î ¼î ½î ¾î ¿ +î¡€î¡î¡‚î¡î¡Žî¡î¡î¡‘î¡î¡žî¡Ÿî¡ î¡¡î¡¢î¡£î¡¤î¡¥î¡¦î¡§î¡¨î¡©î¡ªî¡«î¡¬î¡­î¡®î¡¯î¡°î¡±î¡²î¡³î¡´î¡µî¡¶î¡·î¡¸î¡¹î¡ºî¡»î¡¼î¡½î¡¾î¡¿ +î¢î¢‚î¢î¢Žî¢î¢î¢‘î¢î¢žî¢Ÿî¢ î¢¡î¢¢î¢£î¢¤î¢¥î¢¦î¢§î¢¨î¢©î¢ªî¢«î¢¬î¢­î¢®î¢¯î¢°î¢±î¢²î¢³î¢´î¢µî¢¶î¢·î¢¸î¢¹î¢ºî¢»î¢¼î¢½î¢¾î¢¿ +î£î£‚î£î£Žî£î£î£‘î£î£žî£Ÿî£ î£¡î£¢î££î£¤î£¥î£¦î£§î£¨î£©î£ªî£«î£¬î£­î£®î£¯î£°î£±î£²î£³î£´î£µî£¶î£·î£¸î£¹î£ºî£»î£¼î£½î£¾î£¿ +î¤î¤‚î¤î¤Žî¤î¤î¤‘î¤î¤žî¤Ÿî¤ î¤¡î¤¢î¤£î¤¤î¤¥î¤¦î¤§î¤¨î¤©î¤ªî¤«î¤¬î¤­î¤®î¤¯î¤°î¤±î¤²î¤³î¤´î¤µî¤¶î¤·î¤¸î¤¹î¤ºî¤»î¤¼î¤½î¤¾î¤¿ +î¥î¥‚î¥î¥Žî¥î¥î¥‘î¥î¥žî¥Ÿî¥ î¥¡î¥¢î¥£î¥¤î¥¥î¥¦î¥§î¥¨î¥©î¥ªî¥«î¥¬î¥­î¥®î¥¯î¥°î¥±î¥²î¥³î¥´î¥µî¥¶î¥·î¥¸î¥¹î¥ºî¥»î¥¼î¥½î¥¾î¥¿ +î¦î¦‚î¦î¦Žî¦î¦î¦‘î¦î¦žî¦Ÿî¦ î¦¡î¦¢î¦£î¦¤î¦¥î¦¦î¦§î¦¨î¦©î¦ªî¦«î¦¬î¦­î¦®î¦¯î¦°î¦±î¦²î¦³î¦´î¦µî¦¶î¦·î¦¸î¦¹î¦ºî¦»î¦¼î¦½î¦¾î¦¿ +î§î§‚î§î§Žî§î§î§‘î§î§žî§Ÿî§ î§¡î§¢î§£î§¤î§¥î§¦î§§î§¨î§©î§ªî§«î§¬î§­î§®î§¯î§°î§±î§²î§³î§´î§µî§¶î§·î§¸î§¹î§ºî§»î§¼î§½î§¾î§¿ +î¨î¨‚î¨î¨Žî¨î¨î¨‘î¨î¨žî¨Ÿî¨ î¨¡î¨¢î¨£î¨¤î¨¥î¨¦î¨§î¨¨î¨©î¨ªî¨«î¨¬î¨­î¨®î¨¯î¨°î¨±î¨²î¨³î¨´î¨µî¨¶î¨·î¨¸î¨¹î¨ºî¨»î¨¼î¨½î¨¾î¨¿ +î©€î©î©‚î©î©Žî©î©î©‘î©î©žî©Ÿî© î©¡î©¢î©£î©¤î©¥î©¦î©§î©¨î©©î©ªî©«î©¬î©­î©®î©¯î©°î©±î©²î©³î©´î©µî©¶î©·î©¸î©¹î©ºî©»î©¼î©½î©¾î©¿ +îªîª‚îªîªŽîªîªîª‘îªîªžîªŸîª îª¡îª¢îª£îª¤îª¥îª¦îª§îª¨îª©îªªîª«îª¬îª­îª®îª¯îª°îª±îª²îª³îª´îªµîª¶îª·îª¸îª¹îªºîª»îª¼îª½îª¾îª¿ +î«€î«î«‚î«î«Žî«î«î«‘î«î«žî«Ÿî« î«¡î«¢î«£î«¤î«¥î«¦î«§î«¨î«©î«ªî««î«¬î«­î«®î«¯î«°î«±î«²î«³î«´î«µî«¶î«·î«¸î«¹î«ºî«»î«¼î«½î«¾î«¿ +î¬î¬‚î¬î¬Žî¬î¬î¬‘î¬î¬žî¬Ÿî¬ î¬¡î¬¢î¬£î¬¤î¬¥î¬¦î¬§î¬¨î¬©î¬ªî¬«î¬¬î¬­î¬®î¬¯î¬°î¬±î¬²î¬³î¬´î¬µî¬¶î¬·î¬¸î¬¹î¬ºî¬»î¬¼î¬½î¬¾î¬¿ +î­€î­î­‚î­î­Žî­î­î­‘î­î­žî­Ÿî­ î­¡î­¢î­£î­¤î­¥î­¦î­§î­¨î­©î­ªî­«î­¬î­­î­®î­¯î­°î­±î­²î­³î­´î­µî­¶î­·î­¸î­¹î­ºî­»î­¼î­½î­¾î­¿ +î®î®‚î®î®Žî®î®î®‘î®î®žî®Ÿî® î®¡î®¢î®£î®¤î®¥î®¦î®§î®¨î®©î®ªî®«î®¬î®­î®®î®¯î®°î®±î®²î®³î®´î®µî®¶î®·î®¸î®¹î®ºî®»î®¼î®½î®¾î®¿ +î¯î¯‚î¯î¯Žî¯î¯î¯‘î¯î¯žî¯Ÿî¯ î¯¡î¯¢î¯£î¯¤î¯¥î¯¦î¯§î¯¨î¯©î¯ªî¯«î¯¬î¯­î¯®î¯¯î¯°î¯±î¯²î¯³î¯´î¯µî¯¶î¯·î¯¸î¯¹î¯ºî¯»î¯¼î¯½î¯¾î¯¿ +î°€î°î°‚î°î°Žî°î°î°‘î°î°žî°Ÿî° î°¡î°¢î°£î°¤î°¥î°¦î°§î°¨î°©î°ªî°«î°¬î°­î°®î°¯î°°î°±î°²î°³î°´î°µî°¶î°·î°¸î°¹î°ºî°»î°¼î°½î°¾î°¿ +î±€î±î±‚î±î±Žî±î±î±‘î±î±žî±Ÿî± î±¡î±¢î±£î±¤î±¥î±¦î±§î±¨î±©î±ªî±«î±¬î±­î±®î±¯î±°î±±î±²î±³î±´î±µî±¶î±·î±¸î±¹î±ºî±»î±¼î±½î±¾î±¿ +î²€î²î²‚î²î²Žî²î²î²‘î²î²žî²Ÿî² î²¡î²¢î²£î²¤î²¥î²¦î²§î²¨î²©î²ªî²«î²¬î²­î²®î²¯î²°î²±î²²î²³î²´î²µî²¶î²·î²¸î²¹î²ºî²»î²¼î²½î²¾î²¿ +î³€î³î³‚î³î³Žî³î³î³‘î³î³žî³Ÿî³ î³¡î³¢î³£î³¤î³¥î³¦î³§î³¨î³©î³ªî³«î³¬î³­î³®î³¯î³°î³±î³²î³³î³´î³µî³¶î³·î³¸î³¹î³ºî³»î³¼î³½î³¾î³¿ +î´€î´î´‚î´î´Žî´î´î´‘î´î´žî´Ÿî´ î´¡î´¢î´£î´¤î´¥î´¦î´§î´¨î´©î´ªî´«î´¬î´­î´®î´¯î´°î´±î´²î´³î´´î´µî´¶î´·î´¸î´¹î´ºî´»î´¼î´½î´¾î´¿ +îµ€îµîµ‚îµîµŽîµîµîµ‘îµîµžîµŸîµ îµ¡îµ¢îµ£îµ¤îµ¥îµ¦îµ§îµ¨îµ©îµªîµ«îµ¬îµ­îµ®îµ¯îµ°îµ±îµ²îµ³îµ´îµµîµ¶îµ·îµ¸îµ¹îµºîµ»îµ¼îµ½îµ¾îµ¿ +î¶î¶‚î¶î¶Žî¶î¶î¶‘î¶î¶žî¶Ÿî¶ î¶¡î¶¢î¶£î¶¤î¶¥î¶¦î¶§î¶¨î¶©î¶ªî¶«î¶¬î¶­î¶®î¶¯î¶°î¶±î¶²î¶³î¶´î¶µî¶¶î¶·î¶¸î¶¹î¶ºî¶»î¶¼î¶½î¶¾î¶¿ +î·€î·î·‚î·î·Žî·î·î·‘î·î·žî·Ÿî· î·¡î·¢î·£î·¤î·¥î·¦î·§î·¨î·©î·ªî·«î·¬î·­î·®î·¯î·°î·±î·²î·³î·´î·µî·¶î··î·¸î·¹î·ºî·»î·¼î·½î·¾î·¿ +î¸î¸‚î¸î¸Žî¸î¸î¸‘î¸î¸žî¸Ÿî¸ î¸¡î¸¢î¸£î¸¤î¸¥î¸¦î¸§î¸¨î¸©î¸ªî¸«î¸¬î¸­î¸®î¸¯î¸°î¸±î¸²î¸³î¸´î¸µî¸¶î¸·î¸¸î¸¹î¸ºî¸»î¸¼î¸½î¸¾î¸¿ +î¹€î¹î¹‚î¹î¹Žî¹î¹î¹‘î¹î¹žî¹Ÿî¹ î¹¡î¹¢î¹£î¹¤î¹¥î¹¦î¹§î¹¨î¹©î¹ªî¹«î¹¬î¹­î¹®î¹¯î¹°î¹±î¹²î¹³î¹´î¹µî¹¶î¹·î¹¸î¹¹î¹ºî¹»î¹¼î¹½î¹¾î¹¿ +îºîº‚îºîºŽîºîºîº‘îºîºžîºŸîº îº¡îº¢îº£îº¤îº¥îº¦îº§îº¨îº©îºªîº«îº¬îº­îº®îº¯îº°îº±îº²îº³îº´îºµîº¶îº·îº¸îº¹îººîº»îº¼îº½îº¾îº¿ +î»î»‚î»î»Žî»î»î»‘î»î»žî»Ÿî» î»¡î»¢î»£î»¤î»¥î»¦î»§î»¨î»©î»ªî»«î»¬î»­î»®î»¯î»°î»±î»²î»³î»´î»µî»¶î»·î»¸î»¹î»ºî»»î»¼î»½î»¾î»¿ +î¼€î¼î¼‚î¼î¼Žî¼î¼î¼‘î¼î¼žî¼Ÿî¼ î¼¡î¼¢î¼£î¼¤î¼¥î¼¦î¼§î¼¨î¼©î¼ªî¼«î¼¬î¼­î¼®î¼¯î¼°î¼±î¼²î¼³î¼´î¼µî¼¶î¼·î¼¸î¼¹î¼ºî¼»î¼¼î¼½î¼¾î¼¿ +î½€î½î½‚î½î½Žî½î½î½‘î½î½žî½Ÿî½ î½¡î½¢î½£î½¤î½¥î½¦î½§î½¨î½©î½ªî½«î½¬î½­î½®î½¯î½°î½±î½²î½³î½´î½µî½¶î½·î½¸î½¹î½ºî½»î½¼î½½î½¾î½¿ +î¾€î¾î¾‚î¾î¾Žî¾î¾î¾‘î¾î¾žî¾Ÿî¾ î¾¡î¾¢î¾£î¾¤î¾¥î¾¦î¾§î¾¨î¾©î¾ªî¾«î¾¬î¾­î¾®î¾¯î¾°î¾±î¾²î¾³î¾´î¾µî¾¶î¾·î¾¸î¾¹î¾ºî¾»î¾¼î¾½î¾¾î¾¿ +î¿€î¿î¿‚î¿î¿Žî¿î¿î¿‘î¿î¿žî¿Ÿî¿ î¿¡î¿¢î¿£î¿¤î¿¥î¿¦î¿§î¿¨î¿©î¿ªî¿«î¿¬î¿­î¿®î¿¯î¿°î¿±î¿²î¿³î¿´î¿µî¿¶î¿·î¿¸î¿¹î¿ºî¿»î¿¼î¿½î¿¾î¿¿ +ï€ï€‚ï€ï€Žï€ï€ï€‘ï€ï€žï€Ÿï€ ï€¡ï€¢ï€£ï€¤ï€¥ï€¦ï€§ï€¨ï€©ï€ªï€«ï€¬ï€­ï€®ï€¯ï€°ï€±ï€²ï€³ï€´ï€µï€¶ï€·ï€¸ï€¹ï€ºï€»ï€¼ï€½ï€¾ï€¿ +ï€ïï‚ïƒï„ï…ï†ï‡ïˆï‰ïŠï‹ïŒïïŽïïï‘ï’ï“ï”ï•ï–ï—ï˜ï™ïšï›ïœïïžïŸï ï¡ï¢ï£ï¤ï¥ï¦ï§ï¨ï©ïªï«ï¬ï­ï®ï¯ï°ï±ï²ï³ï´ïµï¶ï·ï¸ï¹ïºï»ï¼ï½ï¾ï¿ +ï‚€ï‚ï‚ï‚Žï‚ï‚ï‚ +ïƒïƒ‚ïƒïƒŽïƒïƒïƒ‘ïƒïƒžïƒŸïƒ ïƒ¡ïƒ¢ïƒ£ïƒ¤ïƒ¥ïƒ¦ïƒ§ïƒ¨ïƒ©ïƒªïƒ«ïƒ¬ïƒ­ïƒ®ïƒ¯ïƒ°ïƒ±ïƒ²ïƒ³ïƒ´ïƒµïƒ¶ïƒ·ïƒ¸ïƒ¹ïƒºïƒ»ïƒ¼ïƒ½ïƒ¾ïƒ¿ +ï„€ï„ï„ï„Žï„ï„ï„ +ï…€ï…ï…ï…Žï…ï…ï… +ï†ï†‚ï†ï†Žï†ï†ï†‘ï†ï†žï†Ÿï† ï†¡ï†¢ï†£ï†¤ï†¥ï†¦ï†§ï†¨ï†©ï†ªï†«ï†¬ï†­ï†®ï†¯ï†°ï†±ï†²ï†³ï†´ï†µï†¶ï†·ï†¸ï†¹ï†ºï†»ï†¼ï†½ï†¾ï†¿ +ï‡ï‡‚ï‡ï‡Žï‡ï‡ï‡‘ï‡ï‡žï‡Ÿï‡ ï‡¡ï‡¢ï‡£ï‡¤ï‡¥ï‡¦ï‡§ï‡¨ï‡©ï‡ªï‡«ï‡¬ï‡­ï‡®ï‡¯ï‡°ï‡±ï‡²ï‡³ï‡´ï‡µï‡¶ï‡·ï‡¸ï‡¹ï‡ºï‡»ï‡¼ï‡½ï‡¾ï‡¿ +ïˆïˆ‚ïˆïˆŽïˆïˆïˆ‘ïˆïˆžïˆŸïˆ ïˆ¡ïˆ¢ïˆ£ïˆ¤ïˆ¥ïˆ¦ïˆ§ïˆ¨ïˆ©ïˆªïˆ«ïˆ¬ïˆ­ïˆ®ïˆ¯ïˆ°ïˆ±ïˆ²ïˆ³ïˆ´ïˆµïˆ¶ïˆ·ïˆ¸ïˆ¹ïˆºïˆ»ïˆ¼ïˆ½ïˆ¾ïˆ¿ +ï‰ï‰‚ï‰ï‰Žï‰ï‰ï‰‘ï‰ï‰žï‰Ÿï‰ ï‰¡ï‰¢ï‰£ï‰¤ï‰¥ï‰¦ï‰§ï‰¨ï‰©ï‰ªï‰«ï‰¬ï‰­ï‰®ï‰¯ï‰°ï‰±ï‰²ï‰³ï‰´ï‰µï‰¶ï‰·ï‰¸ï‰¹ï‰ºï‰»ï‰¼ï‰½ï‰¾ï‰¿ +ïŠïŠ‚ïŠïŠŽïŠïŠïŠ‘ïŠïŠžïŠŸïŠ ïŠ¡ïŠ¢ïŠ£ïŠ¤ïŠ¥ïŠ¦ïŠ§ïŠ¨ïŠ©ïŠªïŠ«ïŠ¬ïŠ­ïŠ®ïŠ¯ïŠ°ïŠ±ïŠ²ïŠ³ïŠ´ïŠµïŠ¶ïŠ·ïŠ¸ïŠ¹ïŠºïŠ»ïŠ¼ïŠ½ïŠ¾ïŠ¿ +ï‹€ï‹ï‹‚ï‹ï‹Žï‹ï‹ï‹‘ï‹ï‹žï‹Ÿï‹ ï‹¡ï‹¢ï‹£ï‹¤ï‹¥ï‹¦ï‹§ï‹¨ï‹©ï‹ªï‹«ï‹¬ï‹­ï‹®ï‹¯ï‹°ï‹±ï‹²ï‹³ï‹´ï‹µï‹¶ï‹·ï‹¸ï‹¹ï‹ºï‹»ï‹¼ï‹½ï‹¾ï‹¿ +ïŒïŒ‚ïŒïŒŽïŒïŒïŒ‘ïŒïŒžïŒŸïŒ ïŒ¡ïŒ¢ïŒ£ïŒ¤ïŒ¥ïŒ¦ïŒ§ïŒ¨ïŒ©ïŒªïŒ«ïŒ¬ïŒ­ïŒ®ïŒ¯ïŒ°ïŒ±ïŒ²ïŒ³ïŒ´ïŒµïŒ¶ïŒ·ïŒ¸ïŒ¹ïŒºïŒ»ïŒ¼ïŒ½ïŒ¾ïŒ¿ +ï€ïï‚ïƒï„ï…ï†ï‡ïˆï‰ïŠï‹ïŒïïŽïïï‘ï’ï“ï”ï•ï–ï—ï˜ï™ïšï›ïœïïžïŸï ï¡ï¢ï£ï¤ï¥ï¦ï§ï¨ï©ïªï«ï¬ï­ï®ï¯ï°ï±ï²ï³ï´ïµï¶ï·ï¸ï¹ïºï»ï¼ï½ï¾ï¿ +ïŽïŽ‚ïŽïŽŽïŽïŽïŽ‘ïŽïŽžïŽŸïŽ ïŽ¡ïŽ¢ïŽ£ïŽ¤ïŽ¥ïŽ¦ïŽ§ïŽ¨ïŽ©ïŽªïŽ«ïŽ¬ïŽ­ïŽ®ïŽ¯ïŽ°ïŽ±ïŽ²ïŽ³ïŽ´ïŽµïŽ¶ïŽ·ïŽ¸ïŽ¹ïŽºïŽ»ïŽ¼ïŽ½ïŽ¾ïŽ¿ +ï€ïï‚ïƒï„ï…ï†ï‡ïˆï‰ïŠï‹ïŒïïŽïïï‘ï’ï“ï”ï•ï–ï—ï˜ï™ïšï›ïœïïžïŸï ï¡ï¢ï£ï¤ï¥ï¦ï§ï¨ï©ïªï«ï¬ï­ï®ï¯ï°ï±ï²ï³ï´ïµï¶ï·ï¸ï¹ïºï»ï¼ï½ï¾ï¿ +ï€ïï‚ïƒï„ï…ï†ï‡ïˆï‰ïŠï‹ïŒïïŽïïï‘ï’ï“ï”ï•ï–ï—ï˜ï™ïšï›ïœïïžïŸï ï¡ï¢ï£ï¤ï¥ï¦ï§ï¨ï©ïªï«ï¬ï­ï®ï¯ï°ï±ï²ï³ï´ïµï¶ï·ï¸ï¹ïºï»ï¼ï½ï¾ï¿ +ï‘€ï‘ï‘ï‘Žï‘ï‘ï‘ +ï’€ï’ï’ï’Žï’ï’ï’ +ï“€ï“ï“ï“Žï“ï“ï“ +ï”ï”ï”ï”ï” +ï•€ï•ï•‚ï•ï•Žï•ï•ï•‘ï•ï•žï•Ÿï• ï•¡ï•¢ï•£ï•¤ï•¥ï•¦ï•§ï•¨ï•©ï•ªï•«ï•¬ï•­ï•®ï•¯ï•°ï•±ï•²ï•³ï•´ï•µï•¶ï•·ï•¸ï•¹ï•ºï•»ï•¼ï•½ï•¾ï•¿ +ï–€ï–ï–ï–Žï–ï–ï– +ï—€ï—ï—ï—Žï—ï—ï— +ï˜ï˜‚ï˜ï˜Žï˜ï˜ï˜‘ï˜ï˜žï˜Ÿï˜ ï˜¡ï˜¢ï˜£ï˜¤ï˜¥ï˜¦ï˜§ï˜¨ï˜©ï˜ªï˜«ï˜¬ï˜­ï˜®ï˜¯ï˜°ï˜±ï˜²ï˜³ï˜´ï˜µï˜¶ï˜·ï˜¸ï˜¹ï˜ºï˜»ï˜¼ï˜½ï˜¾ï˜¿ +ï™ï™‚ï™ï™Žï™ï™ï™‘ï™ï™žï™Ÿï™ ï™¡ï™¢ï™£ï™¤ï™¥ï™¦ï™§ï™¨ï™©ï™ªï™«ï™¬ï™­ï™®ï™¯ï™°ï™±ï™²ï™³ï™´ï™µï™¶ï™·ï™¸ï™¹ï™ºï™»ï™¼ï™½ï™¾ï™¿ +ïšïš‚ïšïšŽïšïšïš‘ïšïšžïšŸïš ïš¡ïš¢ïš£ïš¤ïš¥ïš¦ïš§ïš¨ïš©ïšªïš«ïš¬ïš­ïš®ïš¯ïš°ïš±ïš²ïš³ïš´ïšµïš¶ïš·ïš¸ïš¹ïšºïš»ïš¼ïš½ïš¾ïš¿ +ï›ï›‚ï›ï›Žï›ï›ï›‘ï›ï›žï›Ÿï› ï›¡ï›¢ï›£ï›¤ï›¥ï›¦ï›§ï›¨ï›©ï›ªï›«ï›¬ï›­ï›®ï›¯ï›°ï›±ï›²ï›³ï›´ï›µï›¶ï›·ï›¸ï›¹ï›ºï›»ï›¼ï›½ï›¾ï›¿ +ïœïœ‚ïœïœŽïœïœïœ‘ïœïœžïœŸïœ ïœ¡ïœ¢ïœ£ïœ¤ïœ¥ïœ¦ïœ§ïœ¨ïœ©ïœªïœ«ïœ¬ïœ­ïœ®ïœ¯ïœ°ïœ±ïœ²ïœ³ïœ´ïœµïœ¶ïœ·ïœ¸ïœ¹ïœºïœ»ïœ¼ïœ½ïœ¾ïœ¿ +ï€ïï‚ïƒï„ï…ï†ï‡ïˆï‰ïŠï‹ïŒïïŽïïï‘ï’ï“ï”ï•ï–ï—ï˜ï™ïšï›ïœïïžïŸï ï¡ï¢ï£ï¤ï¥ï¦ï§ï¨ï©ïªï«ï¬ï­ï®ï¯ï°ï±ï²ï³ï´ïµï¶ï·ï¸ï¹ïºï»ï¼ï½ï¾ï¿ +ïžïž‚ïžïžŽïžïžïž‘ïžïžžïžŸïž ïž¡ïž¢ïž£ïž¤ïž¥ïž¦ïž§ïž¨ïž©ïžªïž«ïž¬ïž­ïž®ïž¯ïž°ïž±ïž²ïž³ïž´ïžµïž¶ïž·ïž¸ïž¹ïžºïž»ïž¼ïž½ïž¾ïž¿ +ïŸïŸ‚ïŸïŸŽïŸïŸïŸ‘ïŸïŸžïŸŸïŸ ïŸ¡ïŸ¢ïŸ£ïŸ¤ïŸ¥ïŸ¦ïŸ§ïŸ¨ïŸ©ïŸªïŸ«ïŸ¬ïŸ­ïŸ®ïŸ¯ïŸ°ïŸ±ïŸ²ïŸ³ïŸ´ïŸµïŸ¶ïŸ·ïŸ¸ïŸ¹ïŸºïŸ»ïŸ¼ïŸ½ïŸ¾ïŸ¿ +ï €ï ï ‚ï ï Žï ï ï ‘ï ï žï Ÿï  ï ¡ï ¢ï £ï ¤ï ¥ï ¦ï §ï ¨ï ©ï ªï «ï ¬ï ­ï ®ï ¯ï °ï ±ï ²ï ³ï ´ï µï ¶ï ·ï ¸ï ¹ï ºï »ï ¼ï ½ï ¾ï ¿ +ï¡€ï¡ï¡‚ï¡ï¡Žï¡ï¡ï¡‘ï¡ï¡žï¡Ÿï¡ ï¡¡ï¡¢ï¡£ï¡¤ï¡¥ï¡¦ï¡§ï¡¨ï¡©ï¡ªï¡«ï¡¬ï¡­ï¡®ï¡¯ï¡°ï¡±ï¡²ï¡³ï¡´ï¡µï¡¶ï¡·ï¡¸ï¡¹ï¡ºï¡»ï¡¼ï¡½ï¡¾ï¡¿ +ï¢ï¢‚ï¢ï¢Žï¢ï¢ï¢‘ï¢ï¢žï¢Ÿï¢ ï¢¡ï¢¢ï¢£ï¢¤ï¢¥ï¢¦ï¢§ï¢¨ï¢©ï¢ªï¢«ï¢¬ï¢­ï¢®ï¢¯ï¢°ï¢±ï¢²ï¢³ï¢´ï¢µï¢¶ï¢·ï¢¸ï¢¹ï¢ºï¢»ï¢¼ï¢½ï¢¾ï¢¿ +ï£ï£‚ï£ï£Žï£ï£ï£‘ï£ï£žï£Ÿï£ ï£¡ï£¢ï££ï£¤ï£¥ï£¦ï£§ï£¨ï£©ï£ªï£«ï£¬ï£­ï£®ï£¯ï£°ï£±ï£²ï£³ï£´ï£µï£¶ï£·ï£¸ï£¹ï£ºï£»ï£¼ï£½ï£¾ï£¿ + +CJK Compatibility Ideographs (U+F900-U+FAFF): + +豈ï¤ï¤‚賈滑串句龜龜契金喇奈ï¤ï¤Žï¤ï¤ï¤‘裸邏樂洛烙珞落酪駱亂卵ï¤ï¤žï¤Ÿ +鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄 +鹿ï¥ï¥‚弄籠聾牢磊賂雷壘屢樓ï¥ï¥Žï¥ï¥ï¥‘勒肋凜凌稜綾菱陵讀拏樂ï¥ï¥žï¥Ÿ +怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵 +呂ï¦ï¦‚旅濾礪閭驪麗黎力曆歷ï¦ï¦Žï¦ï¦ï¦‘漣煉璉秊練聯輦蓮連鍊列ï¦ï¦žï¦Ÿ +裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂 +燎ï§ï§‚遼龍暈阮劉杻柳流溜琉ï§ï§Žï§ï§ï§‘戮陸倫崙淪輪律慄栗率隆ï§ï§žï§Ÿ +易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺 +切ï¨ï¨‚糖宅洞暴輻行降見廓兀ï¨ï¨Žï¨ï¨ï¨‘晴﨓﨔凞猪益礼神祥福靖ï¨ï¨žï¨Ÿ +蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴郞隷侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎 +ï©€ï©ï©‚暑梅海渚漢煮爫琢碑社ï©ï©Žï©ï©ï©‘禍禎穀突節練縉繁署者臭ï©ï©žï©Ÿ +褐視謁謹賓贈辶逸難響頻恵𤋮舘﩮﩯並况全侀充冀勇勺喝啕喙嗢塚墳奄奔 +婢ïªïª‚廙彩徭惘慎愈憎慠懲戴ïªïªŽïªïªïª‘朗望杖歹殺流滛滋漢瀞煮ïªïªžïªŸ +猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹 +ï«€ï«ï«‚遲醙鉶陼難靖韛響頋頻ï«ï«Žï«ï«ï«‘㮝䀘䀹𥉉𥳐𧻓齃龎﫚﫛﫜ï«ï«žï«Ÿ +﫠﫡﫢﫣﫤﫥﫦﫧﫨﫩﫪﫫﫬﫭﫮﫯﫰﫱﫲﫳﫴﫵﫶﫷﫸﫹﫺﫻﫼﫽﫾﫿 + +Alphabetic Presentation Forms (U+FB00-U+FB4F): + +ffï¬ï¬‚ffifflſtst﬇﬈﬉﬊﬋﬌ï¬ï¬Žï¬ï¬ï¬‘﬒ﬓﬔﬕﬖﬗ﬘﬙﬚﬛﬜ï¬â—Œï¬žï¬Ÿï¬ ï¬¡ï¬¢ï¬£ï¬¤ï¬¥ï¬¦ï¬§ï¬¨ï¬©ï¬ªï¬«ï¬¬ï¬­ï¬®ï¬¯ï¬°ï¬±ï¬²ï¬³ï¬´ï¬µï¬¶ï¬·ï¬¸ï¬¹ï¬ºï¬»ï¬¼ï¬½ï¬¾ï¬¿ +ï­€ï­ï­‚ףּפּ﭅צּקּרּשּתּוֹבֿï­ï­Žï­ + +Arabic Presentation Forms-A (U+FB50-U+FDFF): + +ï­ï­‘ﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜï­ï­žï­Ÿï­ ï­¡ï­¢ï­£ï­¤ï­¥ï­¦ï­§ï­¨ï­©ï­ªï­«ï­¬ï­­ï­®ï­¯ï­°ï­±ï­²ï­³ï­´ï­µï­¶ï­·ï­¸ï­¹ï­ºï­»ï­¼ï­½ï­¾ï­¿ï®€ï®ï®‚ﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌï®ï®Žï® +ï®ï®‘ﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜï®ï®žï®Ÿï® ï®¡ï®¢ï®£ï®¤ï®¥ï®¦ï®§ï®¨ï®©ï®ªï®«ï®¬ï®­ï®®ï®¯ï®°ï®±ï®²ï®³ï®´ï®µï®¶ï®·ï®¸ï®¹ï®ºï®»ï®¼ï®½ï®¾ï®¿ï¯€ï¯ï¯‚﯃﯄﯅﯆﯇﯈﯉﯊﯋﯌ï¯ï¯Žï¯ +ï¯ï¯‘﯒ﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜï¯ï¯žï¯Ÿï¯ ï¯¡ï¯¢ï¯£ï¯¤ï¯¥ï¯¦ï¯§ï¯¨ï¯©ï¯ªï¯«ï¯¬ï¯­ï¯®ï¯¯ï¯°ï¯±ï¯²ï¯³ï¯´ï¯µï¯¶ï¯·ï¯¸ï¯¹ï¯ºï¯»ï¯¼ï¯½ï¯¾ï¯¿ï°€ï°ï°‚ﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌï°ï°Žï° +ï°ï°‘ﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜï°ï°žï°Ÿï° ï°¡ï°¢ï°£ï°¤ï°¥ï°¦ï°§ï°¨ï°©ï°ªï°«ï°¬ï°­ï°®ï°¯ï°°ï°±ï°²ï°³ï°´ï°µï°¶ï°·ï°¸ï°¹ï°ºï°»ï°¼ï°½ï°¾ï°¿ï±€ï±ï±‚ﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌï±ï±Žï± +ï±ï±‘ﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜï±ï±žï±Ÿï± ï±¡ï±¢ï±£ï±¤ï±¥ï±¦ï±§ï±¨ï±©ï±ªï±«ï±¬ï±­ï±®ï±¯ï±°ï±±ï±²ï±³ï±´ï±µï±¶ï±·ï±¸ï±¹ï±ºï±»ï±¼ï±½ï±¾ï±¿ï²€ï²ï²‚ﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌï²ï²Žï² +ï²ï²‘ﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜï²ï²žï²Ÿï² ï²¡ï²¢ï²£ï²¤ï²¥ï²¦ï²§ï²¨ï²©ï²ªï²«ï²¬ï²­ï²®ï²¯ï²°ï²±ï²²ï²³ï²´ï²µï²¶ï²·ï²¸ï²¹ï²ºï²»ï²¼ï²½ï²¾ï²¿ï³€ï³ï³‚ﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌï³ï³Žï³ +ï³ï³‘ﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜï³ï³žï³Ÿï³ ï³¡ï³¢ï³£ï³¤ï³¥ï³¦ï³§ï³¨ï³©ï³ªï³«ï³¬ï³­ï³®ï³¯ï³°ï³±ï³²ï³³ï³´ï³µï³¶ï³·ï³¸ï³¹ï³ºï³»ï³¼ï³½ï³¾ï³¿ï´€ï´ï´‚ﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌï´ï´Žï´ +ï´ï´‘ﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜï´ï´žï´Ÿï´ ï´¡ï´¢ï´£ï´¤ï´¥ï´¦ï´§ï´¨ï´©ï´ªï´«ï´¬ï´­ï´®ï´¯ï´°ï´±ï´²ï´³ï´´ï´µï´¶ï´·ï´¸ï´¹ï´ºï´»ï´¼ï´½ï´¾ï´¿ïµ€ïµïµ‚﵃﵄﵅﵆﵇﵈﵉﵊﵋﵌ïµïµŽïµ +ïµïµ‘ﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜïµïµžïµŸïµ ïµ¡ïµ¢ïµ£ïµ¤ïµ¥ïµ¦ïµ§ïµ¨ïµ©ïµªïµ«ïµ¬ïµ­ïµ®ïµ¯ïµ°ïµ±ïµ²ïµ³ïµ´ïµµïµ¶ïµ·ïµ¸ïµ¹ïµºïµ»ïµ¼ïµ½ïµ¾ïµ¿ï¶€ï¶ï¶‚ﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌï¶ï¶Žï¶ +ï¶ï¶‘ﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜï¶ï¶žï¶Ÿï¶ ï¶¡ï¶¢ï¶£ï¶¤ï¶¥ï¶¦ï¶§ï¶¨ï¶©ï¶ªï¶«ï¶¬ï¶­ï¶®ï¶¯ï¶°ï¶±ï¶²ï¶³ï¶´ï¶µï¶¶ï¶·ï¶¸ï¶¹ï¶ºï¶»ï¶¼ï¶½ï¶¾ï¶¿ï·€ï·ï·‚ﷃﷄﷅﷆﷇ﷈﷉﷊﷋﷌ï·ï·Žï· +ï·ï·‘﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜ï·ï·žï·Ÿï· ï·¡ï·¢ï·£ï·¤ï·¥ï·¦ï·§ï·¨ï·©ï·ªï·«ï·¬ï·­ï·®ï·¯ï·°ï·±ï·²ï·³ï·´ï·µï·¶ï··ï·¸ï·¹ï·ºï·»ï·¼ï·½ï·¾ï·¿ + +Variation Selectors (U+FE00-U+FE0F): + +◌︀◌ï¸â—Œï¸‚◌︃◌︄◌︅◌︆◌︇◌︈◌︉◌︊◌︋◌︌◌ï¸â—Œï¸Žâ—Œï¸ + +Free block (U+FE10-U+FE1F): + +ï¸ï¸‘︒︓︔︕︖︗︘︙︚︛︜ï¸ï¸žï¸Ÿ + +Combining Half Marks (U+FE20-U+FE2F): + +◌︠◌︡◌︢◌︧︨︩︪︫︬︭︣︤︥︦︮︯ + +CJK Compatibility Forms (U+FE30-U+FE4F): + +︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀ï¹ï¹‚﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌ï¹ï¹Žï¹ + +Small Form Variants (U+FE50-U+FE6F): + +ï¹ï¹‘﹒﹓﹔﹕﹖﹗﹘﹙﹚﹛﹜ï¹ï¹žï¹Ÿï¹ ï¹¡ï¹¢ï¹£ï¹¤ï¹¥ï¹¦ï¹§ï¹¨ï¹©ï¹ªï¹«ï¹¬ï¹­ï¹®ï¹¯ + +Arabic Presentation Forms-B (U+FE70-U+FEFF): + +ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀïºïº‚ﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌïºïºŽïºïºïº‘ﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜïºïºžïºŸïº ïº¡ïº¢ïº£ïº¤ïº¥ïº¦ïº§ïº¨ïº©ïºªïº«ïº¬ïº­ïº®ïº¯ +ﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀï»ï»‚ﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌï»ï»Žï»ï»ï»‘ﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜï»ï»žï»Ÿï» ï»¡ï»¢ï»£ï»¤ï»¥ï»¦ï»§ï»¨ï»©ï»ªï»«ï»¬ï»­ï»®ï»¯ +ﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ﻽﻾ + +Halfwidth and Fullwidth Forms (U+FF00-U+FFEF): + +ï¼€ï¼ï¼‚#$%&'()*+,ï¼ï¼Žï¼ï¼ï¼‘23456789:;<ï¼ï¼žï¼Ÿ +@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +ï½€ï½ï½‚cdefghijklï½ï½Žï½ï½ï½‘rstuvwxyz{|ï½ï½žï½Ÿ +⦆。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタï¾ï¾‚テトナニヌネノハヒフï¾ï¾Žï¾ï¾ï¾‘メモヤユヨラリルレロワï¾ï¾ž +゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒ﾿￀ï¿ï¿‚ᅢᅣᅤᅥᅦ￈￉ᅧᅨᅩï¿ï¿Žï¿ï¿ï¿‘ᅭᅮᅯᅰᅱᅲ￘￙ᅳᅴᅵï¿ï¿ž +￟¢£¬ ̄¦¥₩￧│←↑→↓■○￯ + +Specials (U+FFF0-U+FFFF): + +￰￱￲￳￴￵￶￷￸�￾￿ diff --git a/e2fsprogs/Config.src b/e2fsprogs/Config.src new file mode 100644 index 000000000..743e1e11f --- /dev/null +++ b/e2fsprogs/Config.src @@ -0,0 +1,71 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Linux Ext2 FS Progs" + +INSERT + +config CHATTR + bool "chattr" + default y + help + chattr changes the file attributes on a second extended file system. + +### config E2FSCK +### bool "e2fsck" +### default y +### help +### e2fsck is used to check Linux second extended file systems (ext2fs). +### e2fsck also supports ext2 filesystems countaining a journal (ext3). +### The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also +### provided. + +config FSCK + bool "fsck" + default y + help + fsck is used to check and optionally repair one or more filesystems. + In actuality, fsck is simply a front-end for the various file system + checkers (fsck.fstype) available under Linux. + +config LSATTR + bool "lsattr" + default y + select PLATFORM_LINUX + help + lsattr lists the file attributes on a second extended file system. + +### config MKE2FS +### bool "mke2fs" +### default y +### help +### mke2fs is used to create an ext2/ext3 filesystem. The normal compat +### symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided. + +config TUNE2FS + bool "tune2fs" + default n # off: it is too limited compared to upstream version + help + tune2fs allows the system administrator to adjust various tunable + filesystem parameters on Linux ext2/ext3 filesystems. + +### config E2LABEL +### bool "e2label" +### default y +### depends on TUNE2FS +### help +### e2label will display or change the filesystem label on the ext2 +### filesystem located on device. + +### NB: this one is now provided by util-linux/volume_id/* +### config FINDFS +### bool "findfs" +### default y +### depends on TUNE2FS +### help +### findfs will search the disks in the system looking for a filesystem +### which has a label matching label or a UUID equal to uuid. + +endmenu diff --git a/e2fsprogs/Kbuild.src b/e2fsprogs/Kbuild.src new file mode 100644 index 000000000..b7a14c381 --- /dev/null +++ b/e2fsprogs/Kbuild.src @@ -0,0 +1,15 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT + +lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o +lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o + +lib-$(CONFIG_FSCK) += fsck.o +lib-$(CONFIG_TUNE2FS) += tune2fs.o diff --git a/e2fsprogs/README b/e2fsprogs/README new file mode 100644 index 000000000..eb158e588 --- /dev/null +++ b/e2fsprogs/README @@ -0,0 +1,12 @@ +Authors and contributors of original e2fsprogs: + +Remy Card +Theodore Ts'o +Stephen C. Tweedie +Andreas Gruenbacher, +Kaz Kylheku +F.W. ten Wolde +Jeremy Fitzhardinge +M.J.E. Mol +Miquel van Smoorenburg +Uwe Ohse diff --git a/e2fsprogs/chattr.c b/e2fsprogs/chattr.c new file mode 100644 index 000000000..f1cc8389f --- /dev/null +++ b/e2fsprogs/chattr.c @@ -0,0 +1,195 @@ +/* vi: set sw=4 ts=4: */ +/* + * chattr.c - Change file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + * 93/11/13 - Replace stat() calls by lstat() to avoid loops + * 94/02/27 - Integrated in Ted's distribution + * 98/12/29 - Ignore symlinks when working recursively (G M Sipe) + * 98/12/29 - Display version info only when -V specified (G M Sipe) + */ + +//usage:#define chattr_trivial_usage +//usage: "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..." +//usage:#define chattr_full_usage "\n\n" +//usage: "Change file attributes on an ext2 fs\n" +//usage: "\nModifiers:" +//usage: "\n - Remove attributes" +//usage: "\n + Add attributes" +//usage: "\n = Set attributes" +//usage: "\nAttributes:" +//usage: "\n A Don't track atime" +//usage: "\n a Append mode only" +//usage: "\n c Enable compress" +//usage: "\n D Write dir contents synchronously" +//usage: "\n d Don't backup with dump" +//usage: "\n i Cannot be modified (immutable)" +//usage: "\n j Write all data to journal first" +//usage: "\n s Zero disk storage when deleted" +//usage: "\n S Write file contents synchronously" +//usage: "\n t Disable tail-merging of partial blocks with other files" +//usage: "\n u Allow file to be undeleted" +//usage: "\n -R Recurse" +//usage: "\n -v Set the file's version/generation number" + +#include "libbb.h" +#include "e2fs_lib.h" + +#define OPT_ADD 1 +#define OPT_REM 2 +#define OPT_SET 4 +#define OPT_SET_VER 8 + +struct globals { + unsigned long version; + unsigned long af; + unsigned long rf; + smallint flags; + smallint recursive; +}; + +static unsigned long get_flag(char c) +{ + const char *fp = strchr(e2attr_flags_sname_chattr, c); + if (fp) + return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr]; + bb_show_usage(); +} + +static int decode_arg(const char *arg, struct globals *gp) +{ + unsigned long *fl; + char opt = *arg++; + + fl = &gp->af; + if (opt == '-') { + gp->flags |= OPT_REM; + fl = &gp->rf; + } else if (opt == '+') { + gp->flags |= OPT_ADD; + } else if (opt == '=') { + gp->flags |= OPT_SET; + } else + return 0; + + while (*arg) + *fl |= get_flag(*arg++); + + return 1; +} + +static void change_attributes(const char *name, struct globals *gp); + +static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp) +{ + char *path = concat_subpath_file(dir_name, de->d_name); + /* path is NULL if de->d_name is "." or "..", else... */ + if (path) { + change_attributes(path, gp); + free(path); + } + return 0; +} + +static void change_attributes(const char *name, struct globals *gp) +{ + unsigned long fsflags; + struct stat st; + + if (lstat(name, &st) != 0) { + bb_perror_msg("stat %s", name); + return; + } + if (S_ISLNK(st.st_mode) && gp->recursive) + return; + + /* Don't try to open device files, fifos etc. We probably + * ought to display an error if the file was explicitly given + * on the command line (whether or not recursive was + * requested). */ + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) + return; + + if (gp->flags & OPT_SET_VER) + if (fsetversion(name, gp->version) != 0) + bb_perror_msg("setting version on %s", name); + + if (gp->flags & OPT_SET) { + fsflags = gp->af; + } else { + if (fgetflags(name, &fsflags) != 0) { + bb_perror_msg("reading flags on %s", name); + goto skip_setflags; + } + /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */ + fsflags &= ~gp->rf; + /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */ + fsflags |= gp->af; + /* What is this? And why it's not done for SET case? */ + if (!S_ISDIR(st.st_mode)) + fsflags &= ~EXT2_DIRSYNC_FL; + } + if (fsetflags(name, fsflags) != 0) + bb_perror_msg("setting flags on %s", name); + + skip_setflags: + if (gp->recursive && S_ISDIR(st.st_mode)) + iterate_on_dir(name, chattr_dir_proc, gp); +} + +int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chattr_main(int argc UNUSED_PARAM, char **argv) +{ + struct globals g; + char *arg; + + memset(&g, 0, sizeof(g)); + + /* parse the args */ + while ((arg = *++argv)) { + /* take care of -R and -v */ + if (arg[0] == '-' + && (arg[1] == 'R' || arg[1] == 'v') + && !arg[2] + ) { + if (arg[1] == 'R') { + g.recursive = 1; + continue; + } + /* arg[1] == 'v' */ + if (!*++argv) + bb_show_usage(); + g.version = xatoul(*argv); + g.flags |= OPT_SET_VER; + continue; + } + + if (!decode_arg(arg, &g)) + break; + } + + /* run sanity checks on all the arguments given us */ + if (!*argv) + bb_show_usage(); + if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM))) + bb_error_msg_and_die("= is incompatible with - and +"); + if (g.rf & g.af) + bb_error_msg_and_die("can't set and unset a flag"); + if (!g.flags) + bb_error_msg_and_die("must use '-v', =, - or +"); + + /* now run chattr on all the files passed to us */ + do change_attributes(*argv, &g); while (*++argv); + + return EXIT_SUCCESS; +} diff --git a/e2fsprogs/e2fs_defs.h b/e2fsprogs/e2fs_defs.h new file mode 100644 index 000000000..379640eb8 --- /dev/null +++ b/e2fsprogs/e2fs_defs.h @@ -0,0 +1,561 @@ +/* vi: set sw=4 ts=4: */ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef LINUX_EXT2_FS_H +#define LINUX_EXT2_FS_H 1 + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT2_JOURNAL_INO 8 /* Journal inode */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 32000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(uint32_t)) + +/* + * Macro-instructions used to manage fragments + */ +#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE +#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) +#define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) + +/* + * ACL structures + */ +struct ext2_acl_header { /* Header of Access Control Lists */ + uint32_t aclh_size; + uint32_t aclh_file_count; + uint32_t aclh_acle_count; + uint32_t aclh_first_acle; +}; + +struct ext2_acl_entry { /* Access Control List Entry */ + uint32_t acle_size; + uint16_t acle_perms; /* Access permissions */ + uint16_t acle_type; /* Type of entry */ + uint16_t acle_tag; /* User or group identity */ + uint16_t acle_pad1; + uint32_t acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc { + uint32_t bg_block_bitmap; /* Blocks bitmap block */ + uint32_t bg_inode_bitmap; /* Inodes bitmap block */ + uint32_t bg_inode_table; /* Inodes table block */ + uint16_t bg_free_blocks_count; /* Free blocks count */ + uint16_t bg_free_inodes_count; /* Free inodes count */ + uint16_t bg_used_dirs_count; /* Directories count */ + uint16_t bg_pad; + uint32_t bg_reserved[3]; +}; + +/* + * Data structures used by the directory indexing feature + * + * Note: all of the multibyte integer fields are little endian. + */ + +/* + * Note: dx_root_info is laid out so that if it should somehow get + * overlaid by a dirent the two low bits of the hash version will be + * zero. Therefore, the hash version mod 4 should never be 0. + * Sincerely, the paranoia department. + */ +struct ext2_dx_root_info { + uint32_t reserved_zero; + uint8_t hash_version; /* 0 now, 1 at release */ + uint8_t info_length; /* 8 */ + uint8_t indirect_levels; + uint8_t unused_flags; +}; + +#define EXT2_HASH_LEGACY 0 +#define EXT2_HASH_HALF_MD4 1 +#define EXT2_HASH_TEA 2 + +#define EXT2_HASH_FLAG_INCOMPAT 0x1 + +struct ext2_dx_entry { + uint32_t hash; + uint32_t block; +}; + +struct ext2_dx_countlimit { + uint16_t limit; + uint16_t count; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) +/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ +#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) +#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 +#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ + +/* + * ioctl commands + */ +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + uint16_t i_mode; /* File mode */ + uint16_t i_uid; /* Low 16 bits of Owner Uid */ + uint32_t i_size; /* Size in bytes */ + uint32_t i_atime; /* Access time */ + uint32_t i_ctime; /* Creation time */ + uint32_t i_mtime; /* Modification time */ + uint32_t i_dtime; /* Deletion Time */ + uint16_t i_gid; /* Low 16 bits of Group Id */ + uint16_t i_links_count; /* Links count */ + uint32_t i_blocks; /* Blocks count */ + uint32_t i_flags; /* File flags */ + union { + struct { + uint32_t l_i_reserved1; + } linux1; + struct { + uint32_t h_i_translator; + } hurd1; + struct { + uint32_t m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + uint32_t i_generation; /* File version (for NFS) */ + uint32_t i_file_acl; /* File ACL */ + uint32_t i_dir_acl; /* Directory ACL */ + uint32_t i_faddr; /* Fragment address */ + union { + struct { + uint8_t l_i_frag; /* Fragment number */ + uint8_t l_i_fsize; /* Fragment size */ + uint16_t i_pad1; + uint16_t l_i_uid_high; /* these 2 fields */ + uint16_t l_i_gid_high; /* were reserved2[0] */ + uint32_t l_i_reserved2; + } linux2; + struct { + uint8_t h_i_frag; /* Fragment number */ + uint8_t h_i_fsize; /* Fragment size */ + uint16_t h_i_mode_high; + uint16_t h_i_uid_high; + uint16_t h_i_gid_high; + uint32_t h_i_author; + } hurd2; + struct { + uint8_t m_i_frag; /* Fragment number */ + uint8_t m_i_fsize; /* Fragment size */ + uint16_t m_pad1; + uint32_t m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +/* + * Permanent part of an large inode on the disk + */ +struct ext2_inode_large { + uint16_t i_mode; /* File mode */ + uint16_t i_uid; /* Low 16 bits of Owner Uid */ + uint32_t i_size; /* Size in bytes */ + uint32_t i_atime; /* Access time */ + uint32_t i_ctime; /* Creation time */ + uint32_t i_mtime; /* Modification time */ + uint32_t i_dtime; /* Deletion Time */ + uint16_t i_gid; /* Low 16 bits of Group Id */ + uint16_t i_links_count; /* Links count */ + uint32_t i_blocks; /* Blocks count */ + uint32_t i_flags; /* File flags */ + union { + struct { + uint32_t l_i_reserved1; + } linux1; + struct { + uint32_t h_i_translator; + } hurd1; + struct { + uint32_t m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + uint32_t i_generation; /* File version (for NFS) */ + uint32_t i_file_acl; /* File ACL */ + uint32_t i_dir_acl; /* Directory ACL */ + uint32_t i_faddr; /* Fragment address */ + union { + struct { + uint8_t l_i_frag; /* Fragment number */ + uint8_t l_i_fsize; /* Fragment size */ + uint16_t i_pad1; + uint16_t l_i_uid_high; /* these 2 fields */ + uint16_t l_i_gid_high; /* were reserved2[0] */ + uint32_t l_i_reserved2; + } linux2; + struct { + uint8_t h_i_frag; /* Fragment number */ + uint8_t h_i_fsize; /* Fragment size */ + uint16_t h_i_mode_high; + uint16_t h_i_uid_high; + uint16_t h_i_gid_high; + uint32_t h_i_author; + } hurd2; + struct { + uint8_t m_i_frag; /* Fragment number */ + uint8_t m_i_fsize; /* Fragment size */ + uint16_t m_pad1; + uint32_t m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ + uint16_t i_extra_isize; + uint16_t i_pad1; +}; + +#define i_size_high i_dir_acl + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +/* + * Structure of the super block + */ +struct ext2_super_block { + uint32_t s_inodes_count; /* Inodes count */ + uint32_t s_blocks_count; /* Blocks count */ + uint32_t s_r_blocks_count; /* Reserved blocks count */ + uint32_t s_free_blocks_count; /* Free blocks count */ + uint32_t s_free_inodes_count; /* Free inodes count */ + uint32_t s_first_data_block; /* First Data Block */ + uint32_t s_log_block_size; /* Block size */ + int32_t s_log_frag_size; /* Fragment size */ + uint32_t s_blocks_per_group; /* # Blocks per group */ + uint32_t s_frags_per_group; /* # Fragments per group */ + uint32_t s_inodes_per_group; /* # Inodes per group */ + uint32_t s_mtime; /* Mount time */ + uint32_t s_wtime; /* Write time */ + uint16_t s_mnt_count; /* Mount count */ + int16_t s_max_mnt_count; /* Maximal mount count */ + uint16_t s_magic; /* Magic signature */ + uint16_t s_state; /* File system state */ + uint16_t s_errors; /* Behaviour when detecting errors */ + uint16_t s_minor_rev_level; /* minor revision level */ + uint32_t s_lastcheck; /* time of last check */ + uint32_t s_checkinterval; /* max. time between checks */ + uint32_t s_creator_os; /* OS */ + uint32_t s_rev_level; /* Revision level */ + uint16_t s_def_resuid; /* Default uid for reserved blocks */ + uint16_t s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + uint32_t s_first_ino; /* First non-reserved inode */ + uint16_t s_inode_size; /* size of inode structure */ + uint16_t s_block_group_nr; /* block group # of this superblock */ + uint32_t s_feature_compat; /* compatible feature set */ + uint32_t s_feature_incompat; /* incompatible feature set */ + uint32_t s_feature_ro_compat; /* readonly-compatible feature set */ + uint8_t s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + uint32_t s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + uint8_t s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + uint16_t s_reserved_gdt_blocks; /* Per group table for online growth */ + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + uint8_t s_journal_uuid[16]; /* uuid of journal superblock */ + uint32_t s_journal_inum; /* inode number of journal file */ + uint32_t s_journal_dev; /* device number of journal file */ + uint32_t s_last_orphan; /* start of list of inodes to delete */ + uint32_t s_hash_seed[4]; /* HTREE hash seed */ + uint8_t s_def_hash_version; /* Default hash version to use */ + uint8_t s_jnl_backup_type; /* Default type of journal backup */ + uint16_t s_reserved_word_pad; + uint32_t s_default_mount_opts; + uint32_t s_first_meta_bg; /* First metablock group */ + uint32_t s_mkfs_time; /* When the filesystem was created */ + uint32_t s_jnl_blocks[17]; /* Backup of the journal inode */ + uint32_t s_reserved[172]; /* Padding to the end of the block */ +}; + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Journal inode backup types + */ +#define EXT3_JNL_BACKUP_BLOCKS 1 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 + + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + uint32_t inode; /* Inode number */ + uint16_t rec_len; /* Directory entry length */ + uint16_t name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + uint32_t inode; /* Inode number */ + uint16_t rec_len; /* Directory entry length */ + uint8_t name_len; /* Name length */ + uint8_t file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + +#endif diff --git a/e2fsprogs/e2fs_lib.c b/e2fsprogs/e2fs_lib.c new file mode 100644 index 000000000..a6aec9484 --- /dev/null +++ b/e2fsprogs/e2fs_lib.c @@ -0,0 +1,213 @@ +/* vi: set sw=4 ts=4: */ +/* + * See README for additional information + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "e2fs_lib.h" + +#define HAVE_EXT2_IOCTLS 1 + +#if INT_MAX == LONG_MAX +#define IF_LONG_IS_SAME(...) __VA_ARGS__ +#define IF_LONG_IS_WIDER(...) +#else +#define IF_LONG_IS_SAME(...) +#define IF_LONG_IS_WIDER(...) __VA_ARGS__ +#endif + +static void close_silently(int fd) +{ + int e = errno; + close(fd); + errno = e; +} + + +/* Iterate a function on each entry of a directory */ +int iterate_on_dir(const char *dir_name, + int FAST_FUNC (*func)(const char *, struct dirent *, void *), + void *private) +{ + DIR *dir; + struct dirent *de; + + dir = opendir(dir_name); + if (dir == NULL) { + return -1; + } + while ((de = readdir(dir)) != NULL) { + func(dir_name, de, private); + } + closedir(dir); + return 0; +} + + +/* Get/set a file version on an ext2 file system */ +int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version) +{ +#if HAVE_EXT2_IOCTLS + int fd, r; + IF_LONG_IS_WIDER(int ver;) + + fd = open(name, O_RDONLY | O_NONBLOCK); + if (fd == -1) + return -1; + if (!get_version) { + IF_LONG_IS_WIDER( + ver = (int) set_version; + r = ioctl(fd, EXT2_IOC_SETVERSION, &ver); + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_SETVERSION, (void*)&set_version); + ) + } else { + IF_LONG_IS_WIDER( + r = ioctl(fd, EXT2_IOC_GETVERSION, &ver); + *get_version = ver; + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_GETVERSION, (void*)get_version); + ) + } + close_silently(fd); + return r; +#else /* ! HAVE_EXT2_IOCTLS */ + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} + + +/* Get/set a file flags on an ext2 file system */ +int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags) +{ +#if HAVE_EXT2_IOCTLS + struct stat buf; + int fd, r; + IF_LONG_IS_WIDER(int f;) + + if (stat(name, &buf) == 0 /* stat is ok */ + && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode) + ) { + goto notsupp; + } + fd = open(name, O_RDONLY | O_NONBLOCK); /* neither read nor write asked for */ + if (fd == -1) + return -1; + + if (!get_flags) { + IF_LONG_IS_WIDER( + f = (int) set_flags; + r = ioctl(fd, EXT2_IOC_SETFLAGS, &f); + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&set_flags); + ) + } else { + IF_LONG_IS_WIDER( + r = ioctl(fd, EXT2_IOC_GETFLAGS, &f); + *get_flags = f; + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)get_flags); + ) + } + + close_silently(fd); + return r; + notsupp: +#endif /* HAVE_EXT2_IOCTLS */ + errno = EOPNOTSUPP; + return -1; +} + + +/* Print file attributes on an ext2 file system */ +const uint32_t e2attr_flags_value[] = { +#ifdef ENABLE_COMPRESSION + EXT2_COMPRBLK_FL, + EXT2_DIRTY_FL, + EXT2_NOCOMPR_FL, + EXT2_ECOMPR_FL, +#endif + EXT2_INDEX_FL, + EXT2_SECRM_FL, + EXT2_UNRM_FL, + EXT2_SYNC_FL, + EXT2_DIRSYNC_FL, + EXT2_IMMUTABLE_FL, + EXT2_APPEND_FL, + EXT2_NODUMP_FL, + EXT2_NOATIME_FL, + EXT2_COMPR_FL, + EXT3_JOURNAL_DATA_FL, + EXT2_NOTAIL_FL, + EXT2_TOPDIR_FL +}; + +const char e2attr_flags_sname[] = +#ifdef ENABLE_COMPRESSION + "BZXE" +#endif + "I" + "suSDiadAcjtT"; + +static const char e2attr_flags_lname[] = +#ifdef ENABLE_COMPRESSION + "Compressed_File" "\0" + "Compressed_Dirty_File" "\0" + "Compression_Raw_Access" "\0" + "Compression_Error" "\0" +#endif + "Indexed_directory" "\0" + "Secure_Deletion" "\0" + "Undelete" "\0" + "Synchronous_Updates" "\0" + "Synchronous_Directory_Updates" "\0" + "Immutable" "\0" + "Append_Only" "\0" + "No_Dump" "\0" + "No_Atime" "\0" + "Compression_Requested" "\0" + "Journaled_Data" "\0" + "No_Tailmerging" "\0" + "Top_of_Directory_Hierarchies" "\0" + /* Another trailing NUL is added by compiler */; + +void print_e2flags(FILE *f, unsigned long flags, unsigned options) +{ + const uint32_t *fv; + const char *fn; + + fv = e2attr_flags_value; + if (options & PFOPT_LONG) { + int first = 1; + fn = e2attr_flags_lname; + do { + if (flags & *fv) { + if (!first) + fputs(", ", f); + fputs(fn, f); + first = 0; + } + fv++; + fn += strlen(fn) + 1; + } while (*fn); + if (first) + fputs("---", f); + } else { + fn = e2attr_flags_sname; + do { + char c = '-'; + if (flags & *fv) + c = *fn; + fputc(c, f); + fv++; + fn++; + } while (*fn); + } +} diff --git a/e2fsprogs/e2fs_lib.h b/e2fsprogs/e2fs_lib.h new file mode 100644 index 000000000..3905ee71d --- /dev/null +++ b/e2fsprogs/e2fs_lib.h @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* + * See README for additional information + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* Constants and structures */ +#include "e2fs_defs.h" + +PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN + +/* Iterate a function on each entry of a directory */ +int iterate_on_dir(const char *dir_name, + int FAST_FUNC (*func)(const char *, struct dirent *, void *), + void *private); + +/* Get/set a file version on an ext2 file system */ +int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version); +#define fgetversion(name, version) fgetsetversion(name, version, 0) +#define fsetversion(name, version) fgetsetversion(name, NULL, version) + +/* Get/set a file flags on an ext2 file system */ +int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags); +#define fgetflags(name, flags) fgetsetflags(name, flags, 0) +#define fsetflags(name, flags) fgetsetflags(name, NULL, flags) + +/* Must be 1 for compatibility with `int long_format'. */ +#define PFOPT_LONG 1 +/* Print file attributes on an ext2 file system */ +void print_e2flags(FILE *f, unsigned long flags, unsigned options); + +extern const uint32_t e2attr_flags_value[]; +extern const char e2attr_flags_sname[]; + +/* If you plan to ENABLE_COMPRESSION, see e2fs_lib.c and chattr.c - */ +/* make sure that chattr doesn't accept bad options! */ +#ifdef ENABLE_COMPRESSION +#define e2attr_flags_value_chattr (&e2attr_flags_value[5]) +#define e2attr_flags_sname_chattr (&e2attr_flags_sname[5]) +#else +#define e2attr_flags_value_chattr (&e2attr_flags_value[1]) +#define e2attr_flags_sname_chattr (&e2attr_flags_sname[1]) +#endif + +POP_SAVED_FUNCTION_VISIBILITY diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c new file mode 100644 index 000000000..4b2f774f5 --- /dev/null +++ b/e2fsprogs/fsck.c @@ -0,0 +1,1096 @@ +/* vi: set sw=4 ts=4: */ +/* + * fsck --- A generic, parallelizing front-end for the fsck program. + * It will automatically try to run fsck programs in parallel if the + * devices are on separate spindles. It is based on the same ideas as + * the generic front end for fsck by David Engel and Fred van Kempen, + * but it has been completely rewritten from scratch to support + * parallel execution. + * + * Written by Theodore Ts'o, + * + * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: + * o Changed -t fstype to behave like with mount when -A (all file + * systems) or -M (like mount) is specified. + * o fsck looks if it can find the fsck.type program to decide + * if it should ignore the fs type. This way more fsck programs + * can be added without changing this front-end. + * o -R flag skip root file system. + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +/* All filesystem specific hooks have been removed. + * If filesystem cannot be determined, we will execute + * "fsck.auto". Currently this also happens if you specify + * UUID=xxx or LABEL=xxx as an object to check. + * Detection code for that is also probably has to be in fsck.auto. + * + * In other words, this is _really_ is just a driver program which + * spawns actual fsck.something for each filesystem to check. + * It doesn't guess filesystem types from on-disk format. + */ + +//usage:#define fsck_trivial_usage +//usage: "[-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]..." +//usage:#define fsck_full_usage "\n\n" +//usage: "Check and repair filesystems\n" +//usage: "\n -A Walk /etc/fstab and check all filesystems" +//usage: "\n -N Don't execute, just show what would be done" +//usage: "\n -P With -A, check filesystems in parallel" +//usage: "\n -R With -A, skip the root filesystem" +//usage: "\n -T Don't show title on startup" +//usage: "\n -V Verbose" +//usage: "\n -C n Write status information to specified filedescriptor" +//usage: "\n -t TYPE List of filesystem types to check" + +#include "libbb.h" + +/* "progress indicator" code is somewhat buggy and ext[23] specific. + * We should be filesystem agnostic. IOW: there should be a well-defined + * API for fsck.something, NOT ad-hoc hacks in generic fsck. */ +#define DO_PROGRESS_INDICATOR 0 + +/* fsck 1.41.4 (27-Jan-2009) manpage says: + * 0 - No errors + * 1 - File system errors corrected + * 2 - System should be rebooted + * 4 - File system errors left uncorrected + * 8 - Operational error + * 16 - Usage or syntax error + * 32 - Fsck canceled by user request + * 128 - Shared library error + */ +#define EXIT_OK 0 +#define EXIT_NONDESTRUCT 1 +#define EXIT_DESTRUCT 2 +#define EXIT_UNCORRECTED 4 +#define EXIT_ERROR 8 +#define EXIT_USAGE 16 +#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ + +/* + * Internal structure for mount table entries. + */ +struct fs_info { + struct fs_info *next; + char *device; + char *mountpt; + char *type; + char *opts; + int passno; + int flags; +}; + +#define FLAG_DONE 1 +#define FLAG_PROGRESS 2 +/* + * Structure to allow exit codes to be stored + */ +struct fsck_instance { + struct fsck_instance *next; + int pid; + int flags; +#if DO_PROGRESS_INDICATOR + time_t start_time; +#endif + char *prog; + char *device; + char *base_device; /* /dev/hda for /dev/hdaN etc */ +}; + +static const char ignored_types[] ALIGN1 = + "ignore\0" + "iso9660\0" + "nfs\0" + "proc\0" + "sw\0" + "swap\0" + "tmpfs\0" + "devpts\0"; + +#if 0 +static const char really_wanted[] ALIGN1 = + "minix\0" + "ext2\0" + "ext3\0" + "jfs\0" + "reiserfs\0" + "xiafs\0" + "xfs\0"; +#endif + +#define BASE_MD "/dev/md" + +static char **args; +static int num_args; +static int verbose; + +#define FS_TYPE_FLAG_NORMAL 0 +#define FS_TYPE_FLAG_OPT 1 +#define FS_TYPE_FLAG_NEGOPT 2 +static char **fs_type_list; +static uint8_t *fs_type_flag; +static smallint fs_type_negated; + +static smallint noexecute; +static smallint serialize; +static smallint skip_root; +/* static smallint like_mount; */ +static smallint parallel_root; +static smallint force_all_parallel; + +#if DO_PROGRESS_INDICATOR +static smallint progress; +static int progress_fd; +#endif + +static int num_running; +static int max_running; +static char *fstype; +static struct fs_info *filesys_info; +static struct fs_info *filesys_last; +static struct fsck_instance *instance_list; + +/* + * Return the "base device" given a particular device; this is used to + * assure that we only fsck one partition on a particular drive at any + * one time. Otherwise, the disk heads will be seeking all over the + * place. If the base device cannot be determined, return NULL. + * + * The base_device() function returns an allocated string which must + * be freed. + */ +#if ENABLE_FEATURE_DEVFS +/* + * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3 + * pathames. + */ +static const char *const devfs_hier[] = { + "host", "bus", "target", "lun", NULL +}; +#endif + +static char *base_device(const char *device) +{ + char *str, *cp; +#if ENABLE_FEATURE_DEVFS + const char *const *hier; + const char *disk; + int len; +#endif + str = xstrdup(device); + + /* Skip over "/dev/"; if it's not present, give up */ + cp = skip_dev_pfx(str); + if (cp == str) + goto errout; + + /* + * For md devices, we treat them all as if they were all + * on one disk, since we don't know how to parallelize them. + */ + if (cp[0] == 'm' && cp[1] == 'd') { + cp[2] = 0; + return str; + } + + /* Handle DAC 960 devices */ + if (strncmp(cp, "rd/", 3) == 0) { + cp += 3; + if (cp[0] != 'c' || !isdigit(cp[1]) + || cp[2] != 'd' || !isdigit(cp[3])) + goto errout; + cp[4] = 0; + return str; + } + + /* Now let's handle /dev/hd* and /dev/sd* devices.... */ + if ((cp[0] == 'h' || cp[0] == 's') && cp[1] == 'd') { + cp += 2; + /* If there's a single number after /dev/hd, skip it */ + if (isdigit(*cp)) + cp++; + /* What follows must be an alpha char, or give up */ + if (!isalpha(*cp)) + goto errout; + cp[1] = 0; + return str; + } + +#if ENABLE_FEATURE_DEVFS + /* Now let's handle devfs (ugh) names */ + len = 0; + if (strncmp(cp, "ide/", 4) == 0) + len = 4; + if (strncmp(cp, "scsi/", 5) == 0) + len = 5; + if (len) { + cp += len; + /* + * Now we proceed down the expected devfs hierarchy. + * i.e., .../host1/bus2/target3/lun4/... + * If we don't find the expected token, followed by + * some number of digits at each level, abort. + */ + for (hier = devfs_hier; *hier; hier++) { + len = strlen(*hier); + if (strncmp(cp, *hier, len) != 0) + goto errout; + cp += len; + while (*cp != '/' && *cp != 0) { + if (!isdigit(*cp)) + goto errout; + cp++; + } + cp++; + } + cp[-1] = 0; + return str; + } + + /* Now handle devfs /dev/disc or /dev/disk names */ + disk = 0; + if (strncmp(cp, "discs/", 6) == 0) + disk = "disc"; + else if (strncmp(cp, "disks/", 6) == 0) + disk = "disk"; + if (disk) { + cp += 6; + if (strncmp(cp, disk, 4) != 0) + goto errout; + cp += 4; + while (*cp != '/' && *cp != 0) { + if (!isdigit(*cp)) + goto errout; + cp++; + } + *cp = 0; + return str; + } +#endif + errout: + free(str); + return NULL; +} + +static void free_instance(struct fsck_instance *p) +{ + free(p->prog); + free(p->device); + free(p->base_device); + free(p); +} + +static struct fs_info *create_fs_device(const char *device, const char *mntpnt, + const char *type, const char *opts, + int passno) +{ + struct fs_info *fs; + + fs = xzalloc(sizeof(*fs)); + fs->device = xstrdup(device); + fs->mountpt = xstrdup(mntpnt); + if (strchr(type, ',')) + type = (char *)"auto"; + fs->type = xstrdup(type); + fs->opts = xstrdup(opts ? opts : ""); + fs->passno = passno < 0 ? 1 : passno; + /*fs->flags = 0; */ + /*fs->next = NULL; */ + + if (!filesys_info) + filesys_info = fs; + else + filesys_last->next = fs; + filesys_last = fs; + + return fs; +} + +/* Load the filesystem database from /etc/fstab */ +static void load_fs_info(const char *filename) +{ + FILE *fstab; + struct mntent mte; + + fstab = setmntent(filename, "r"); + if (!fstab) { + bb_perror_msg("can't read '%s'", filename); + return; + } + + // Loop through entries + while (getmntent_r(fstab, &mte, bb_common_bufsiz1, COMMON_BUFSIZE)) { + //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir, + // mte.mnt_type, mte.mnt_opts, + // mte.mnt_passno); + create_fs_device(mte.mnt_fsname, mte.mnt_dir, + mte.mnt_type, mte.mnt_opts, + mte.mnt_passno); + } + endmntent(fstab); +} + +/* Lookup filesys in /etc/fstab and return the corresponding entry. */ +static struct fs_info *lookup(char *filesys) +{ + struct fs_info *fs; + + for (fs = filesys_info; fs; fs = fs->next) { + if (strcmp(filesys, fs->device) == 0 + || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0) + ) + break; + } + + return fs; +} + +#if DO_PROGRESS_INDICATOR +static int progress_active(void) +{ + struct fsck_instance *inst; + + for (inst = instance_list; inst; inst = inst->next) { + if (inst->flags & FLAG_DONE) + continue; + if (inst->flags & FLAG_PROGRESS) + return 1; + } + return 0; +} +#endif + + +/* + * Send a signal to all outstanding fsck child processes + */ +static void kill_all_if_got_signal(void) +{ + static smallint kill_sent; + + struct fsck_instance *inst; + + if (!bb_got_signal || kill_sent) + return; + + for (inst = instance_list; inst; inst = inst->next) { + if (inst->flags & FLAG_DONE) + continue; + kill(inst->pid, SIGTERM); + } + kill_sent = 1; +} + +/* + * Wait for one child process to exit; when it does, unlink it from + * the list of executing child processes, free, and return its exit status. + * If there is no exited child, return -1. + */ +static int wait_one(int flags) +{ + int status; + int sig; + struct fsck_instance *inst, *prev; + pid_t pid; + + if (!instance_list) + return -1; + /* if (noexecute) { already returned -1; } */ + + while (1) { + pid = waitpid(-1, &status, flags); + kill_all_if_got_signal(); + if (pid == 0) /* flags == WNOHANG and no children exited */ + return -1; + if (pid < 0) { + if (errno == EINTR) + continue; + if (errno == ECHILD) { /* paranoia */ + bb_error_msg("wait: no more children"); + return -1; + } + bb_perror_msg("wait"); + continue; + } + prev = NULL; + inst = instance_list; + do { + if (inst->pid == pid) + goto child_died; + prev = inst; + inst = inst->next; + } while (inst); + } + child_died: + + if (WIFEXITED(status)) + status = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) { + sig = WTERMSIG(status); + status = EXIT_UNCORRECTED; + if (sig != SIGINT) { + printf("Warning: %s %s terminated " + "by signal %d\n", + inst->prog, inst->device, sig); + status = EXIT_ERROR; + } + } else { + printf("%s %s: status is %x, should never happen\n", + inst->prog, inst->device, status); + status = EXIT_ERROR; + } + +#if DO_PROGRESS_INDICATOR + if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) { + struct fsck_instance *inst2; + for (inst2 = instance_list; inst2; inst2 = inst2->next) { + if (inst2->flags & FLAG_DONE) + continue; + if (strcmp(inst2->type, "ext2") != 0 + && strcmp(inst2->type, "ext3") != 0 + ) { + continue; + } + /* ext[23], we will send USR1 + * (request to start displaying progress bar) + * + * If we've just started the fsck, wait a tiny + * bit before sending the kill, to give it + * time to set up the signal handler + */ + if (inst2->start_time >= time(NULL) - 1) + sleep(1); + kill(inst2->pid, SIGUSR1); + inst2->flags |= FLAG_PROGRESS; + break; + } + } +#endif + + if (prev) + prev->next = inst->next; + else + instance_list = inst->next; + if (verbose > 1) + printf("Finished with %s (exit status %d)\n", + inst->device, status); + num_running--; + free_instance(inst); + + return status; +} + +/* + * Wait until all executing child processes have exited; return the + * logical OR of all of their exit code values. + */ +#define FLAG_WAIT_ALL 0 +#define FLAG_WAIT_ATLEAST_ONE WNOHANG +static int wait_many(int flags) +{ + int exit_status; + int global_status = 0; + int wait_flags = 0; + + while ((exit_status = wait_one(wait_flags)) != -1) { + global_status |= exit_status; + wait_flags |= flags; + } + return global_status; +} + +/* + * Execute a particular fsck program, and link it into the list of + * child processes we are waiting for. + */ +static void execute(const char *type, const char *device, + const char *mntpt /*, int interactive */) +{ + int i; + struct fsck_instance *inst; + pid_t pid; + + args[0] = xasprintf("fsck.%s", type); + +#if DO_PROGRESS_INDICATOR + if (progress && !progress_active()) { + if (strcmp(type, "ext2") == 0 + || strcmp(type, "ext3") == 0 + ) { + args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */ + inst->flags |= FLAG_PROGRESS; + } + } +#endif + + args[num_args - 2] = (char*)device; + /* args[num_args - 1] = NULL; - already is */ + + if (verbose || noexecute) { + printf("[%s (%d) -- %s]", args[0], num_running, + mntpt ? mntpt : device); + for (i = 0; args[i]; i++) + printf(" %s", args[i]); + bb_putchar('\n'); + } + + /* Fork and execute the correct program. */ + pid = -1; + if (!noexecute) { + pid = spawn(args); + if (pid < 0) + bb_simple_perror_msg(args[0]); + } + +#if DO_PROGRESS_INDICATOR + free(args[XXX]); +#endif + + /* No child, so don't record an instance */ + if (pid <= 0) { + free(args[0]); + return; + } + + inst = xzalloc(sizeof(*inst)); + inst->pid = pid; + inst->prog = args[0]; + inst->device = xstrdup(device); + inst->base_device = base_device(device); +#if DO_PROGRESS_INDICATOR + inst->start_time = time(NULL); +#endif + + /* Add to the list of running fsck's. + * (was adding to the end, but adding to the front is simpler...) */ + inst->next = instance_list; + instance_list = inst; +} + +/* + * Run the fsck program on a particular device + * + * If the type is specified using -t, and it isn't prefixed with "no" + * (as in "noext2") and only one filesystem type is specified, then + * use that type regardless of what is specified in /etc/fstab. + * + * If the type isn't specified by the user, then use either the type + * specified in /etc/fstab, or "auto". + */ +static void fsck_device(struct fs_info *fs /*, int interactive */) +{ + const char *type; + + if (strcmp(fs->type, "auto") != 0) { + type = fs->type; + if (verbose > 2) + bb_info_msg("using filesystem type '%s' %s", + type, "from fstab"); + } else if (fstype + && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */ + && strncmp(fstype, "opts=", 5) != 0 + && strncmp(fstype, "loop", 4) != 0 + && !strchr(fstype, ',') + ) { + type = fstype; + if (verbose > 2) + bb_info_msg("using filesystem type '%s' %s", + type, "from -t"); + } else { + type = "auto"; + if (verbose > 2) + bb_info_msg("using filesystem type '%s' %s", + type, "(default)"); + } + + num_running++; + execute(type, fs->device, fs->mountpt /*, interactive */); +} + +/* + * Returns TRUE if a partition on the same disk is already being + * checked. + */ +static int device_already_active(char *device) +{ + struct fsck_instance *inst; + char *base; + + if (force_all_parallel) + return 0; + +#ifdef BASE_MD + /* Don't check a soft raid disk with any other disk */ + if (instance_list + && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) + || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)) + ) { + return 1; + } +#endif + + base = base_device(device); + /* + * If we don't know the base device, assume that the device is + * already active if there are any fsck instances running. + */ + if (!base) + return (instance_list != NULL); + + for (inst = instance_list; inst; inst = inst->next) { + if (!inst->base_device || !strcmp(base, inst->base_device)) { + free(base); + return 1; + } + } + + free(base); + return 0; +} + +/* + * This function returns true if a particular option appears in a + * comma-delimited options list + */ +static int opt_in_list(char *opt, char *optlist) +{ + char *s; + int len; + + if (!optlist) + return 0; + + len = strlen(opt); + s = optlist - 1; + while (1) { + s = strstr(s + 1, opt); + if (!s) + return 0; + /* neither "opt.." nor "xxx,opt.."? */ + if (s != optlist && s[-1] != ',') + continue; + /* neither "..opt" nor "..opt,xxx"? */ + if (s[len] != '\0' && s[len] != ',') + continue; + return 1; + } +} + +/* See if the filesystem matches the criteria given by the -t option */ +static int fs_match(struct fs_info *fs) +{ + int n, ret, checked_type; + char *cp; + + if (!fs_type_list) + return 1; + + ret = 0; + checked_type = 0; + n = 0; + while (1) { + cp = fs_type_list[n]; + if (!cp) + break; + switch (fs_type_flag[n]) { + case FS_TYPE_FLAG_NORMAL: + checked_type++; + if (strcmp(cp, fs->type) == 0) + ret = 1; + break; + case FS_TYPE_FLAG_NEGOPT: + if (opt_in_list(cp, fs->opts)) + return 0; + break; + case FS_TYPE_FLAG_OPT: + if (!opt_in_list(cp, fs->opts)) + return 0; + break; + } + n++; + } + if (checked_type == 0) + return 1; + + return (fs_type_negated ? !ret : ret); +} + +/* Check if we should ignore this filesystem. */ +static int ignore(struct fs_info *fs) +{ + /* + * If the pass number is 0, ignore it. + */ + if (fs->passno == 0) + return 1; + + /* + * If a specific fstype is specified, and it doesn't match, + * ignore it. + */ + if (!fs_match(fs)) + return 1; + + /* Are we ignoring this type? */ + if (index_in_strings(ignored_types, fs->type) >= 0) + return 1; + + /* We can and want to check this file system type. */ + return 0; +} + +/* Check all file systems, using the /etc/fstab table. */ +static int check_all(void) +{ + struct fs_info *fs; + int status = EXIT_OK; + smallint not_done_yet; + smallint pass_done; + int passno; + + if (verbose) + puts("Checking all filesystems"); + + /* + * Do an initial scan over the filesystem; mark filesystems + * which should be ignored as done, and resolve any "auto" + * filesystem types (done as a side-effect of calling ignore()). + */ + for (fs = filesys_info; fs; fs = fs->next) + if (ignore(fs)) + fs->flags |= FLAG_DONE; + + /* + * Find and check the root filesystem. + */ + if (!parallel_root) { + for (fs = filesys_info; fs; fs = fs->next) { + if (LONE_CHAR(fs->mountpt, '/')) { + if (!skip_root && !ignore(fs)) { + fsck_device(fs /*, 1*/); + status |= wait_many(FLAG_WAIT_ALL); + if (status > EXIT_NONDESTRUCT) + return status; + } + fs->flags |= FLAG_DONE; + break; + } + } + } + /* + * This is for the bone-headed user who has root + * filesystem listed twice. + * "Skip root" will skip _all_ root entries. + */ + if (skip_root) + for (fs = filesys_info; fs; fs = fs->next) + if (LONE_CHAR(fs->mountpt, '/')) + fs->flags |= FLAG_DONE; + + not_done_yet = 1; + passno = 1; + while (not_done_yet) { + not_done_yet = 0; + pass_done = 1; + + for (fs = filesys_info; fs; fs = fs->next) { + if (bb_got_signal) + break; + if (fs->flags & FLAG_DONE) + continue; + /* + * If the filesystem's pass number is higher + * than the current pass number, then we didn't + * do it yet. + */ + if (fs->passno > passno) { + not_done_yet = 1; + continue; + } + /* + * If a filesystem on a particular device has + * already been spawned, then we need to defer + * this to another pass. + */ + if (device_already_active(fs->device)) { + pass_done = 0; + continue; + } + /* + * Spawn off the fsck process + */ + fsck_device(fs /*, serialize*/); + fs->flags |= FLAG_DONE; + + /* + * Only do one filesystem at a time, or if we + * have a limit on the number of fsck's extant + * at one time, apply that limit. + */ + if (serialize + || (max_running && (num_running >= max_running)) + ) { + pass_done = 0; + break; + } + } + if (bb_got_signal) + break; + if (verbose > 1) + printf("--waiting-- (pass %d)\n", passno); + status |= wait_many(pass_done ? FLAG_WAIT_ALL : + FLAG_WAIT_ATLEAST_ONE); + if (pass_done) { + if (verbose > 1) + puts("----------------------------------"); + passno++; + } else + not_done_yet = 1; + } + kill_all_if_got_signal(); + status |= wait_many(FLAG_WAIT_ATLEAST_ONE); + return status; +} + +/* + * Deal with the fsck -t argument. + * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"! + * Why here we require "-t novfat,nonfs" ?? + */ +static void compile_fs_type(char *fs_type) +{ + char *s; + int num = 2; + smallint negate; + + s = fs_type; + while ((s = strchr(s, ','))) { + num++; + s++; + } + + fs_type_list = xzalloc(num * sizeof(fs_type_list[0])); + fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0])); + fs_type_negated = -1; /* not yet known is it negated or not */ + + num = 0; + s = fs_type; + while (1) { + char *comma; + + negate = 0; + if (s[0] == 'n' && s[1] == 'o') { /* "no.." */ + s += 2; + negate = 1; + } else if (s[0] == '!') { + s++; + negate = 1; + } + + if (strcmp(s, "loop") == 0) + /* loop is really short-hand for opts=loop */ + goto loop_special_case; + if (strncmp(s, "opts=", 5) == 0) { + s += 5; + loop_special_case: + fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT; + } else { + if (fs_type_negated == -1) + fs_type_negated = negate; + if (fs_type_negated != negate) + bb_error_msg_and_die( +"either all or none of the filesystem types passed to -t must be prefixed " +"with 'no' or '!'"); + } + comma = strchr(s, ','); + fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s); + if (!comma) + break; + s = comma + 1; + } +} + +static char **new_args(void) +{ + args = xrealloc_vector(args, 2, num_args); + return &args[num_args++]; +} + +int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fsck_main(int argc UNUSED_PARAM, char **argv) +{ + int i, status; + /*int interactive;*/ + struct fs_info *fs; + const char *fstab; + char *tmp; + char **devices; + int num_devices; + smallint opts_for_fsck; + smallint doall; + smallint notitle; + + /* we want wait() to be interruptible */ + signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); + signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo); + + setbuf(stdout, NULL); + + opts_for_fsck = doall = notitle = 0; + devices = NULL; + num_devices = 0; + new_args(); /* args[0] = NULL, will be replaced by fsck. */ + /* instance_list = NULL; - in bss, so already zeroed */ + + while (*++argv) { + int j; + int optpos; + char *options; + char *arg = *argv; + + /* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */ + if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) { +// FIXME: must check that arg is a blkdev, or resolve +// "/path", "UUID=xxx" or "LABEL=xxx" into block device name +// ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties) + devices = xrealloc_vector(devices, 2, num_devices); + devices[num_devices++] = arg; + continue; + } + + if (arg[0] != '-' || opts_for_fsck) { + *new_args() = arg; + continue; + } + + if (LONE_CHAR(arg + 1, '-')) { /* "--" ? */ + opts_for_fsck = 1; + continue; + } + + optpos = 0; + options = NULL; + for (j = 1; arg[j]; j++) { + switch (arg[j]) { + case 'A': + doall = 1; + break; +#if DO_PROGRESS_INDICATOR + case 'C': + progress = 1; + if (arg[++j]) { /* -Cn */ + progress_fd = xatoi_positive(&arg[j]); + goto next_arg; + } + /* -C n */ + if (!*++argv) + bb_show_usage(); + progress_fd = xatoi_positive(*argv); + goto next_arg; +#endif + case 'V': + verbose++; + break; + case 'N': + noexecute = 1; + break; + case 'R': + skip_root = 1; + break; + case 'T': + notitle = 1; + break; +/* case 'M': + like_mount = 1; + break; */ + case 'P': + parallel_root = 1; + break; + case 's': + serialize = 1; + break; + case 't': + if (fstype) + bb_show_usage(); + if (arg[++j]) + tmp = &arg[j]; + else if (*++argv) + tmp = *argv; + else + bb_show_usage(); + fstype = xstrdup(tmp); + compile_fs_type(fstype); + goto next_arg; + case '?': + bb_show_usage(); + break; + default: + optpos++; + /* one extra for '\0' */ + options = xrealloc(options, optpos + 2); + options[optpos] = arg[j]; + break; + } + } + next_arg: + if (optpos) { + options[0] = '-'; + options[optpos + 1] = '\0'; + *new_args() = options; + } + } + if (getenv("FSCK_FORCE_ALL_PARALLEL")) + force_all_parallel = 1; + tmp = getenv("FSCK_MAX_INST"); + if (tmp) + max_running = xatoi(tmp); + new_args(); /* args[num_args - 2] will be replaced by */ + new_args(); /* args[num_args - 1] is the last, NULL element */ + + if (!notitle) + puts("fsck (busybox "BB_VER", "BB_BT")"); + + /* Even plain "fsck /dev/hda1" needs fstab to get fs type, + * so we are scanning it anyway */ + fstab = getenv("FSTAB_FILE"); + if (!fstab) + fstab = "/etc/fstab"; + load_fs_info(fstab); + + /*interactive = (num_devices == 1) | serialize;*/ + + if (num_devices == 0) + /*interactive =*/ serialize = doall = 1; + if (doall) + return check_all(); + + status = 0; + for (i = 0; i < num_devices; i++) { + if (bb_got_signal) { + kill_all_if_got_signal(); + break; + } + + fs = lookup(devices[i]); + if (!fs) + fs = create_fs_device(devices[i], "", "auto", NULL, -1); + fsck_device(fs /*, interactive */); + + if (serialize + || (max_running && (num_running >= max_running)) + ) { + int exit_status = wait_one(0); + if (exit_status >= 0) + status |= exit_status; + if (verbose > 1) + puts("----------------------------------"); + } + } + status |= wait_many(FLAG_WAIT_ALL); + return status; +} diff --git a/e2fsprogs/lsattr.c b/e2fsprogs/lsattr.c new file mode 100644 index 000000000..1312fe754 --- /dev/null +++ b/e2fsprogs/lsattr.c @@ -0,0 +1,120 @@ +/* vi: set sw=4 ts=4: */ +/* + * lsattr.c - List file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + * 93/11/13 - Replace stat() calls by lstat() to avoid loops + * 94/02/27 - Integrated in Ted's distribution + * 98/12/29 - Display version info only when -V specified (G M Sipe) + */ + +//usage:#define lsattr_trivial_usage +//usage: "[-Radlv] [FILE]..." +//usage:#define lsattr_full_usage "\n\n" +//usage: "List file attributes on an ext2 fs\n" +//usage: "\n -R Recurse" +//usage: "\n -a Don't hide entries starting with ." +//usage: "\n -d List directory entries instead of contents" +//usage: "\n -l List long flag names" +//usage: "\n -v List the file's version/generation number" + +#include "libbb.h" +#include "e2fs_lib.h" + +enum { + OPT_RECUR = 0x1, + OPT_ALL = 0x2, + OPT_DIRS_OPT = 0x4, + OPT_PF_LONG = 0x8, + OPT_GENERATION = 0x10, +}; + +static void list_attributes(const char *name) +{ + unsigned long fsflags; + unsigned long generation; + + if (fgetflags(name, &fsflags) != 0) + goto read_err; + + if (option_mask32 & OPT_GENERATION) { + if (fgetversion(name, &generation) != 0) + goto read_err; + printf("%5lu ", generation); + } + + if (option_mask32 & OPT_PF_LONG) { + printf("%-28s ", name); + print_e2flags(stdout, fsflags, PFOPT_LONG); + bb_putchar('\n'); + } else { + print_e2flags(stdout, fsflags, 0); + printf(" %s\n", name); + } + + return; + read_err: + bb_perror_msg("reading %s", name); +} + +static int FAST_FUNC lsattr_dir_proc(const char *dir_name, + struct dirent *de, + void *private UNUSED_PARAM) +{ + struct stat st; + char *path; + + path = concat_path_file(dir_name, de->d_name); + + if (lstat(path, &st) != 0) + bb_perror_msg("stat %s", path); + else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) { + list_attributes(path); + if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR) + && !DOT_OR_DOTDOT(de->d_name) + ) { + printf("\n%s:\n", path); + iterate_on_dir(path, lsattr_dir_proc, NULL); + bb_putchar('\n'); + } + } + + free(path); + return 0; +} + +static void lsattr_args(const char *name) +{ + struct stat st; + + if (lstat(name, &st) == -1) { + bb_perror_msg("stat %s", name); + } else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) { + iterate_on_dir(name, lsattr_dir_proc, NULL); + } else { + list_attributes(name); + } +} + +int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lsattr_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "Radlv"); + argv += optind; + + if (!*argv) + *--argv = (char*)"."; + do lsattr_args(*argv++); while (*argv); + + return EXIT_SUCCESS; +} diff --git a/e2fsprogs/old_e2fsprogs/Config.src b/e2fsprogs/old_e2fsprogs/Config.src new file mode 100644 index 000000000..bbec08e7e --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/Config.src @@ -0,0 +1,69 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Linux Ext2 FS Progs" + +INSERT + +config CHATTR + bool "chattr" + default n + help + chattr changes the file attributes on a second extended file system. + +config E2FSCK + bool "e2fsck" + default n + help + e2fsck is used to check Linux second extended file systems (ext2fs). + e2fsck also supports ext2 filesystems countaining a journal (ext3). + The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also + provided. + +config FSCK + bool "fsck" + default n + help + fsck is used to check and optionally repair one or more filesystems. + In actuality, fsck is simply a front-end for the various file system + checkers (fsck.fstype) available under Linux. + +config LSATTR + bool "lsattr" + default n + help + lsattr lists the file attributes on a second extended file system. + +config MKE2FS + bool "mke2fs" + default n + help + mke2fs is used to create an ext2/ext3 filesystem. The normal compat + symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided. + +config TUNE2FS + bool "tune2fs" + default n + help + tune2fs allows the system administrator to adjust various tunable + filesystem parameters on Linux ext2/ext3 filesystems. + +config E2LABEL + bool "e2label" + default n + depends on TUNE2FS + help + e2label will display or change the filesystem label on the ext2 + filesystem located on device. + +config FINDFS + bool "findfs" + default n + depends on TUNE2FS + help + findfs will search the disks in the system looking for a filesystem + which has a label matching label or a UUID equal to uuid. + +endmenu diff --git a/e2fsprogs/old_e2fsprogs/Kbuild.src b/e2fsprogs/old_e2fsprogs/Kbuild.src new file mode 100644 index 000000000..fff1a0d6f --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/Kbuild.src @@ -0,0 +1,18 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT + +lib-$(CONFIG_CHATTR) += chattr.o +lib-$(CONFIG_E2FSCK) += e2fsck.o util.o +lib-$(CONFIG_FSCK) += fsck.o util.o +lib-$(CONFIG_LSATTR) += lsattr.o +lib-$(CONFIG_MKE2FS) += mke2fs.o util.o +lib-$(CONFIG_TUNE2FS) += tune2fs.o util.o + +CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h diff --git a/e2fsprogs/old_e2fsprogs/README b/e2fsprogs/old_e2fsprogs/README new file mode 100644 index 000000000..fac090193 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/README @@ -0,0 +1,3 @@ +This is a pretty straight rip from the e2fsprogs pkg. + +See README's in subdirs for specific info. diff --git a/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src b/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src new file mode 100644 index 000000000..02b4d2496 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src @@ -0,0 +1,26 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +NEEDED-$(CONFIG_E2FSCK) = y +NEEDED-$(CONFIG_FSCK) = y +NEEDED-$(CONFIG_MKE2FS) = y +NEEDED-$(CONFIG_TUNE2FS) = y + +lib-y:= + +INSERT + +lib-$(NEEDED-y) += cache.o dev.o devname.o devno.o blkid_getsize.o \ + probe.o read.o resolve.o save.o tag.o list.o + +CFLAGS_dev.o := -include $(srctree)/include/busybox.h +CFLAGS_devname.o := -include $(srctree)/include/busybox.h +CFLAGS_devno.o := -include $(srctree)/include/busybox.h +CFLAGS_blkid_getsize.o := -include $(srctree)/include/busybox.h +CFLAGS_probe.o := -include $(srctree)/include/busybox.h +CFLAGS_save.o := -include $(srctree)/include/busybox.h +CFLAGS_tag.o := -include $(srctree)/include/busybox.h +CFLAGS_list.o := -include $(srctree)/include/busybox.h diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid.h b/e2fsprogs/old_e2fsprogs/blkid/blkid.h new file mode 100644 index 000000000..9a3c2afdb --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/blkid.h @@ -0,0 +1,104 @@ +/* vi: set sw=4 ts=4: */ +/* + * blkid.h - Interface for libblkid, a library to identify block devices + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ +#ifndef BLKID_BLKID_H +#define BLKID_BLKID_H 1 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLKID_VERSION "1.0.0" +#define BLKID_DATE "12-Feb-2003" + +typedef struct blkid_struct_dev *blkid_dev; +typedef struct blkid_struct_cache *blkid_cache; +typedef __s64 blkid_loff_t; + +typedef struct blkid_struct_tag_iterate *blkid_tag_iterate; +typedef struct blkid_struct_dev_iterate *blkid_dev_iterate; + +/* + * Flags for blkid_get_dev + * + * BLKID_DEV_CREATE Create an empty device structure if not found + * in the cache. + * BLKID_DEV_VERIFY Make sure the device structure corresponds + * with reality. + * BLKID_DEV_FIND Just look up a device entry, and return NULL + * if it is not found. + * BLKID_DEV_NORMAL Get a valid device structure, either from the + * cache or by probing the device. + */ +#define BLKID_DEV_FIND 0x0000 +#define BLKID_DEV_CREATE 0x0001 +#define BLKID_DEV_VERIFY 0x0002 +#define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY) + +/* cache.c */ +extern void blkid_put_cache(blkid_cache cache); +extern int blkid_get_cache(blkid_cache *cache, const char *filename); + +/* dev.c */ +extern const char *blkid_dev_devname(blkid_dev dev); + +extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache); +extern int blkid_dev_set_search(blkid_dev_iterate iter, + char *search_type, char *search_value); +extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev); +extern void blkid_dev_iterate_end(blkid_dev_iterate iterate); + +/* devno.c */ +extern char *blkid_devno_to_devname(dev_t devno); + +/* devname.c */ +extern int blkid_probe_all(blkid_cache cache); +extern int blkid_probe_all_new(blkid_cache cache); +extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, + int flags); + +/* getsize.c */ +extern blkid_loff_t blkid_get_dev_size(int fd); + +/* probe.c */ +int blkid_known_fstype(const char *fstype); +extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev); + +/* read.c */ + +/* resolve.c */ +extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname, + const char *devname); +extern char *blkid_get_devname(blkid_cache cache, const char *token, + const char *value); + +/* tag.c */ +extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev); +extern int blkid_tag_next(blkid_tag_iterate iterate, + const char **type, const char **value); +extern void blkid_tag_iterate_end(blkid_tag_iterate iterate); +extern int blkid_dev_has_tag(blkid_dev dev, const char *type, + const char *value); +extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, + const char *type, + const char *value); +extern int blkid_parse_tag_string(const char *token, char **ret_type, + char **ret_val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkidP.h b/e2fsprogs/old_e2fsprogs/blkid/blkidP.h new file mode 100644 index 000000000..d6b2b42cc --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/blkidP.h @@ -0,0 +1,186 @@ +/* vi: set sw=4 ts=4: */ +/* + * blkidP.h - Internal interfaces for libblkid + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ +#ifndef BLKID_BLKIDP_H +#define BLKID_BLKIDP_H 1 + +#include +#include + +#include "blkid.h" +#include "list.h" + +#ifdef __GNUC__ +#define __BLKID_ATTR(x) __attribute__(x) +#else +#define __BLKID_ATTR(x) +#endif + + +/* + * This describes the attributes of a specific device. + * We can traverse all of the tags by bid_tags (linking to the tag bit_names). + * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag + * values, if they exist. + */ +struct blkid_struct_dev +{ + struct list_head bid_devs; /* All devices in the cache */ + struct list_head bid_tags; /* All tags for this device */ + blkid_cache bid_cache; /* Dev belongs to this cache */ + char *bid_name; /* Device inode pathname */ + char *bid_type; /* Preferred device TYPE */ + int bid_pri; /* Device priority */ + dev_t bid_devno; /* Device major/minor number */ + time_t bid_time; /* Last update time of device */ + unsigned int bid_flags; /* Device status bitflags */ + char *bid_label; /* Shortcut to device LABEL */ + char *bid_uuid; /* Shortcut to binary UUID */ +}; + +#define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ +#define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */ + +/* + * Each tag defines a NAME=value pair for a particular device. The tags + * are linked via bit_names for a single device, so that traversing the + * names list will get you a list of all tags associated with a device. + * They are also linked via bit_values for all devices, so one can easily + * search all tags with a given NAME for a specific value. + */ +struct blkid_struct_tag +{ + struct list_head bit_tags; /* All tags for this device */ + struct list_head bit_names; /* All tags with given NAME */ + char *bit_name; /* NAME of tag (shared) */ + char *bit_val; /* value of tag */ + blkid_dev bit_dev; /* pointer to device */ +}; +typedef struct blkid_struct_tag *blkid_tag; + +/* + * Minimum number of seconds between device probes, even when reading + * from the cache. This is to avoid re-probing all devices which were + * just probed by another program that does not share the cache. + */ +#define BLKID_PROBE_MIN 2 + +/* + * Time in seconds an entry remains verified in the in-memory cache + * before being reverified (in case of long-running processes that + * keep a cache in memory and continue to use it for a long time). + */ +#define BLKID_PROBE_INTERVAL 200 + +/* This describes an entire blkid cache file and probed devices. + * We can traverse all of the found devices via bic_list. + * We can traverse all of the tag types by bic_tags, which hold empty tags + * for each tag type. Those tags can be used as list_heads for iterating + * through all devices with a specific tag type (e.g. LABEL). + */ +struct blkid_struct_cache +{ + struct list_head bic_devs; /* List head of all devices */ + struct list_head bic_tags; /* List head of all tag types */ + time_t bic_time; /* Last probe time */ + time_t bic_ftime; /* Mod time of the cachefile */ + unsigned int bic_flags; /* Status flags of the cache */ + char *bic_filename; /* filename of cache */ +}; + +#define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */ +#define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */ + +extern char *blkid_strdup(const char *s); +extern char *blkid_strndup(const char *s, const int length); + +#define BLKID_CACHE_FILE "/etc/blkid.tab" +extern const char *blkid_devdirs[]; + +#define BLKID_ERR_IO 5 +#define BLKID_ERR_PROC 9 +#define BLKID_ERR_MEM 12 +#define BLKID_ERR_CACHE 14 +#define BLKID_ERR_DEV 19 +#define BLKID_ERR_PARAM 22 +#define BLKID_ERR_BIG 27 + +/* + * Priority settings for different types of devices + */ +#define BLKID_PRI_EVMS 30 +#define BLKID_PRI_LVM 20 +#define BLKID_PRI_MD 10 + +#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG) +#define CONFIG_BLKID_DEBUG +#endif + +#define DEBUG_CACHE 0x0001 +#define DEBUG_DUMP 0x0002 +#define DEBUG_DEV 0x0004 +#define DEBUG_DEVNAME 0x0008 +#define DEBUG_DEVNO 0x0010 +#define DEBUG_PROBE 0x0020 +#define DEBUG_READ 0x0040 +#define DEBUG_RESOLVE 0x0080 +#define DEBUG_SAVE 0x0100 +#define DEBUG_TAG 0x0200 +#define DEBUG_INIT 0x8000 +#define DEBUG_ALL 0xFFFF + +#ifdef CONFIG_BLKID_DEBUG +#include +extern int blkid_debug_mask; +#define DBG(m,x) if ((m) & blkid_debug_mask) x; +#else +#define DBG(m,x) +#endif + +#ifdef CONFIG_BLKID_DEBUG +extern void blkid_debug_dump_dev(blkid_dev dev); +extern void blkid_debug_dump_tag(blkid_tag tag); +#endif + +/* lseek.c */ +/* extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); */ +#ifdef CONFIG_LFS +# define blkid_llseek lseek64 +#else +# define blkid_llseek lseek +#endif + +/* read.c */ +extern void blkid_read_cache(blkid_cache cache); + +/* save.c */ +extern int blkid_flush_cache(blkid_cache cache); + +/* + * Functions to create and find a specific tag type: tag.c + */ +extern void blkid_free_tag(blkid_tag tag); +extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type); +extern int blkid_set_tag(blkid_dev dev, const char *name, + const char *value, const int vlength); + +/* + * Functions to create and find a specific tag type: dev.c + */ +extern blkid_dev blkid_new_dev(void); +extern void blkid_free_dev(blkid_dev dev); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c new file mode 100644 index 000000000..e1f6ba6d2 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c @@ -0,0 +1,179 @@ +/* vi: set sw=4 ts=4: */ +/* + * getsize.c --- get the size of a partition. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +/* include this before sys/queues.h! */ +#include "blkidP.h" + +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_SYS_DISKLABEL_H +#include +#include +#endif +#ifdef HAVE_SYS_DISK_H +#ifdef HAVE_SYS_QUEUE_H +#include /* for LIST_HEAD */ +#endif +#include +#endif +#ifdef __linux__ +#include +#endif + +#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif + +#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +#endif + +#ifdef APPLE_DARWIN +#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif /* APPLE_DARWIN */ + +static int valid_offset(int fd, blkid_loff_t offset) +{ + char ch; + + if (blkid_llseek(fd, offset, 0) < 0) + return 0; + if (read(fd, &ch, 1) < 1) + return 0; + return 1; +} + +/* + * Returns the number of blocks in a partition + */ +blkid_loff_t blkid_get_dev_size(int fd) +{ + int valid_blkgetsize64 = 1; +#ifdef __linux__ + struct utsname ut; +#endif + unsigned long long size64; + unsigned long size; + blkid_loff_t high, low; +#ifdef FDGETPRM + struct floppy_struct this_floppy; +#endif +#ifdef HAVE_SYS_DISKLABEL_H + int part = -1; + struct disklabel lab; + struct partition *pp; + char ch; + struct stat st; +#endif /* HAVE_SYS_DISKLABEL_H */ + +#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { + if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) + && (size64 << 9 > 0xFFFFFFFF)) + return 0; /* EFBIG */ + return (blkid_loff_t) size64 << 9; + } +#endif + +#ifdef BLKGETSIZE64 +#ifdef __linux__ + uname(&ut); + if ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.')) + valid_blkgetsize64 = 0; +#endif + if (valid_blkgetsize64 && + ioctl(fd, BLKGETSIZE64, &size64) >= 0) { + if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) + && ((size64) > 0xFFFFFFFF)) + return 0; /* EFBIG */ + return size64; + } +#endif + +#ifdef BLKGETSIZE + if (ioctl(fd, BLKGETSIZE, &size) >= 0) + return (blkid_loff_t)size << 9; +#endif + +#ifdef FDGETPRM + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) + return (blkid_loff_t)this_floppy.size << 9; +#endif +#ifdef HAVE_SYS_DISKLABEL_H +#if 0 + /* + * This should work in theory but I haven't tested it. Anyone + * on a BSD system want to test this for me? In the meantime, + * binary search mechanism should work just fine. + */ + if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode)) + part = st.st_rdev & 7; + if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { + pp = &lab.d_partitions[part]; + if (pp->p_size) + return pp->p_size << 9; + } +#endif +#endif /* HAVE_SYS_DISKLABEL_H */ + + /* + * OK, we couldn't figure it out by using a specialized ioctl, + * which is generally the best way. So do binary search to + * find the size of the partition. + */ + low = 0; + for (high = 1024; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const blkid_loff_t mid = (low + high) / 2; + + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + return low + 1; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_loff_t bytes; + int fd; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n" + "Determine the size of a device\n", argv[0]); + return 1; + } + + if ((fd = open(argv[1], O_RDONLY)) < 0) + perror(argv[0]); + + bytes = blkid_get_dev_size(fd); + printf("Device %s has %lld 1k blocks.\n", argv[1], bytes >> 10); + + return 0; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/cache.c b/e2fsprogs/old_e2fsprogs/blkid/cache.c new file mode 100644 index 000000000..d1d29146b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/cache.c @@ -0,0 +1,125 @@ +/* vi: set sw=4 ts=4: */ +/* + * cache.c - allocation/initialization/free routines for cache + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#include +#include "blkidP.h" + +int blkid_debug_mask = 0; + +int blkid_get_cache(blkid_cache *ret_cache, const char *filename) +{ + blkid_cache cache; + +#ifdef CONFIG_BLKID_DEBUG + if (!(blkid_debug_mask & DEBUG_INIT)) { + char *dstr = getenv("BLKID_DEBUG"); + + if (dstr) + blkid_debug_mask = strtoul(dstr, 0, 0); + blkid_debug_mask |= DEBUG_INIT; + } +#endif + + DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n", + filename ? filename : "default cache")); + + cache = xzalloc(sizeof(struct blkid_struct_cache)); + + INIT_LIST_HEAD(&cache->bic_devs); + INIT_LIST_HEAD(&cache->bic_tags); + + if (filename && !strlen(filename)) + filename = 0; + if (!filename && (getuid() == geteuid())) + filename = getenv("BLKID_FILE"); + if (!filename) + filename = BLKID_CACHE_FILE; + cache->bic_filename = blkid_strdup(filename); + + blkid_read_cache(cache); + + *ret_cache = cache; + return 0; +} + +void blkid_put_cache(blkid_cache cache) +{ + if (!cache) + return; + + (void) blkid_flush_cache(cache); + + DBG(DEBUG_CACHE, printf("freeing cache struct\n")); + + /* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */ + + while (!list_empty(&cache->bic_devs)) { + blkid_dev dev = list_entry(cache->bic_devs.next, + struct blkid_struct_dev, + bid_devs); + blkid_free_dev(dev); + } + + while (!list_empty(&cache->bic_tags)) { + blkid_tag tag = list_entry(cache->bic_tags.next, + struct blkid_struct_tag, + bit_tags); + + while (!list_empty(&tag->bit_names)) { + blkid_tag bad = list_entry(tag->bit_names.next, + struct blkid_struct_tag, + bit_names); + + DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n", + bad->bit_name, bad->bit_val)); + blkid_free_tag(bad); + } + blkid_free_tag(tag); + } + free(cache->bic_filename); + + free(cache); +} + +#ifdef TEST_PROGRAM +int main(int argc, char** argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_debug_mask = DEBUG_ALL; + if ((argc > 2)) { + fprintf(stderr, "Usage: %s [filename]\n", argv[0]); + exit(1); + } + + if ((ret = blkid_get_cache(&cache, argv[1])) < 0) { + fprintf(stderr, "error %d parsing cache file %s\n", ret, + argv[1] ? argv[1] : BLKID_CACHE_FILE); + exit(1); + } + if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + if ((ret = blkid_probe_all(cache) < 0)) + fprintf(stderr, "error probing devices\n"); + + blkid_put_cache(cache); + + return ret; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/dev.c b/e2fsprogs/old_e2fsprogs/blkid/dev.c new file mode 100644 index 000000000..bb0cc914a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/dev.c @@ -0,0 +1,213 @@ +/* vi: set sw=4 ts=4: */ +/* + * dev.c - allocation/initialization/free routines for dev + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include + +#include "blkidP.h" + +blkid_dev blkid_new_dev(void) +{ + blkid_dev dev; + + dev = xzalloc(sizeof(struct blkid_struct_dev)); + + INIT_LIST_HEAD(&dev->bid_devs); + INIT_LIST_HEAD(&dev->bid_tags); + + return dev; +} + +void blkid_free_dev(blkid_dev dev) +{ + if (!dev) + return; + + DBG(DEBUG_DEV, + printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type)); + DBG(DEBUG_DEV, blkid_debug_dump_dev(dev)); + + list_del(&dev->bid_devs); + while (!list_empty(&dev->bid_tags)) { + blkid_tag tag = list_entry(dev->bid_tags.next, + struct blkid_struct_tag, + bit_tags); + blkid_free_tag(tag); + } + if (dev->bid_name) + free(dev->bid_name); + free(dev); +} + +/* + * Given a blkid device, return its name + */ +const char *blkid_dev_devname(blkid_dev dev) +{ + return dev->bid_name; +} + +#ifdef CONFIG_BLKID_DEBUG +void blkid_debug_dump_dev(blkid_dev dev) +{ + struct list_head *p; + + if (!dev) { + printf(" dev: NULL\n"); + return; + } + + printf(" dev: name = %s\n", dev->bid_name); + printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno); + printf(" dev: TIME=\"%lu\"\n", dev->bid_time); + printf(" dev: PRI=\"%d\"\n", dev->bid_pri); + printf(" dev: flags = 0x%08X\n", dev->bid_flags); + + list_for_each(p, &dev->bid_tags) { + blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); + if (tag) + printf(" tag: %s=\"%s\"\n", tag->bit_name, + tag->bit_val); + else + printf(" tag: NULL\n"); + } + bb_putchar('\n'); +} +#endif + +/* + * dev iteration routines for the public libblkid interface. + * + * These routines do not expose the list.h implementation, which are a + * contamination of the namespace, and which force us to reveal far, far + * too much of our internal implemenation. I'm not convinced I want + * to keep list.h in the long term, anyway. It's fine for kernel + * programming, but performance is not the #1 priority for this + * library, and I really don't like the tradeoff of type-safety for + * performance for this application. [tytso:20030125.2007EST] + */ + +/* + * This series of functions iterate over all devices in a blkid cache + */ +#define DEV_ITERATE_MAGIC 0x01a5284c + +struct blkid_struct_dev_iterate { + int magic; + blkid_cache cache; + struct list_head *p; +}; + +blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache) +{ + blkid_dev_iterate iter; + + iter = xmalloc(sizeof(struct blkid_struct_dev_iterate)); + iter->magic = DEV_ITERATE_MAGIC; + iter->cache = cache; + iter->p = cache->bic_devs.next; + return iter; +} + +/* + * Return 0 on success, -1 on error + */ +extern int blkid_dev_next(blkid_dev_iterate iter, + blkid_dev *dev) +{ + *dev = 0; + if (!iter || iter->magic != DEV_ITERATE_MAGIC || + iter->p == &iter->cache->bic_devs) + return -1; + *dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs); + iter->p = iter->p->next; + return 0; +} + +void blkid_dev_iterate_end(blkid_dev_iterate iter) +{ + if (!iter || iter->magic != DEV_ITERATE_MAGIC) + return; + iter->magic = 0; + free(iter); +} + +#ifdef TEST_PROGRAM +#ifdef HAVE_GETOPT_H +#include +#else +extern char *optarg; +extern int optind; +#endif + +void usage(char *prog) +{ + fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog); + fprintf(stderr, "\tList all devices and exit\n", prog); + exit(1); +} + +int main(int argc, char **argv) +{ + blkid_dev_iterate iter; + blkid_cache cache = NULL; + blkid_dev dev; + int c, ret; + char *tmp; + char *file = NULL; + char *search_type = NULL; + char *search_value = NULL; + + while ((c = getopt (argc, argv, "m:f:")) != EOF) + switch (c) { + case 'f': + file = optarg; + break; + case 'm': + blkid_debug_mask = strtoul (optarg, &tmp, 0); + if (*tmp) { + fprintf(stderr, "Invalid debug mask: %d\n", + optarg); + exit(1); + } + break; + case '?': + usage(argv[0]); + } + if (argc >= optind+2) { + search_type = argv[optind]; + search_value = argv[optind+1]; + optind += 2; + } + if (argc != optind) + usage(argv[0]); + + if ((ret = blkid_get_cache(&cache, file)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + + iter = blkid_dev_iterate_begin(cache); + if (search_type) + blkid_dev_set_search(iter, search_type, search_value); + while (blkid_dev_next(iter, &dev) == 0) { + printf("Device: %s\n", blkid_dev_devname(dev)); + } + blkid_dev_iterate_end(iter); + + + blkid_put_cache(cache); + return 0; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/devname.c b/e2fsprogs/old_e2fsprogs/blkid/devname.c new file mode 100644 index 000000000..fad92cb31 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/devname.c @@ -0,0 +1,367 @@ +/* vi: set sw=4 ts=4: */ +/* + * devname.c - get a dev by its device inode name + * + * Copyright (C) Andries Brouwer + * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif +#include + +#include "blkidP.h" + +/* + * Find a dev struct in the cache by device name, if available. + * + * If there is no entry with the specified device name, and the create + * flag is set, then create an empty device entry. + */ +blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) +{ + blkid_dev dev = NULL, tmp; + struct list_head *p; + + if (!cache || !devname) + return NULL; + + list_for_each(p, &cache->bic_devs) { + tmp = list_entry(p, struct blkid_struct_dev, bid_devs); + if (strcmp(tmp->bid_name, devname)) + continue; + + DBG(DEBUG_DEVNAME, + printf("found devname %s in cache\n", tmp->bid_name)); + dev = tmp; + break; + } + + if (!dev && (flags & BLKID_DEV_CREATE)) { + dev = blkid_new_dev(); + if (!dev) + return NULL; + dev->bid_name = blkid_strdup(devname); + dev->bid_cache = cache; + list_add_tail(&dev->bid_devs, &cache->bic_devs); + cache->bic_flags |= BLKID_BIC_FL_CHANGED; + } + + if (flags & BLKID_DEV_VERIFY) + dev = blkid_verify(cache, dev); + return dev; +} + +/* + * Probe a single block device to add to the device cache. + */ +static void probe_one(blkid_cache cache, const char *ptname, + dev_t devno, int pri) +{ + blkid_dev dev = NULL; + struct list_head *p; + const char **dir; + char *devname = NULL; + + /* See if we already have this device number in the cache. */ + list_for_each(p, &cache->bic_devs) { + blkid_dev tmp = list_entry(p, struct blkid_struct_dev, + bid_devs); + if (tmp->bid_devno == devno) { + dev = blkid_verify(cache, tmp); + break; + } + } + if (dev && dev->bid_devno == devno) + goto set_pri; + + /* + * Take a quick look at /dev/ptname for the device number. We check + * all of the likely device directories. If we don't find it, or if + * the stat information doesn't check out, use blkid_devno_to_devname() + * to find it via an exhaustive search for the device major/minor. + */ + for (dir = blkid_devdirs; *dir; dir++) { + struct stat st; + char device[256]; + + sprintf(device, "%s/%s", *dir, ptname); + if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) && + dev->bid_devno == devno) + goto set_pri; + + if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) && + st.st_rdev == devno) { + devname = blkid_strdup(device); + break; + } + } + if (!devname) { + devname = blkid_devno_to_devname(devno); + if (!devname) + return; + } + dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL); + free(devname); + +set_pri: + if (!pri && !strncmp(ptname, "md", 2)) + pri = BLKID_PRI_MD; + if (dev) + dev->bid_pri = pri; +} + +#define PROC_PARTITIONS "/proc/partitions" +#define VG_DIR "/proc/lvm/VGs" + +/* + * This function initializes the UUID cache with devices from the LVM + * proc hierarchy. We currently depend on the names of the LVM + * hierarchy giving us the device structure in /dev. (XXX is this a + * safe thing to do?) + */ +#ifdef VG_DIR +#include +static dev_t lvm_get_devno(const char *lvm_device) +{ + FILE *lvf; + char buf[1024]; + int ma, mi; + dev_t ret = 0; + + DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device)); + if ((lvf = fopen_for_read(lvm_device)) == NULL) { + DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno, + strerror(errno))); + return 0; + } + + while (fgets(buf, sizeof(buf), lvf)) { + if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) { + ret = makedev(ma, mi); + break; + } + } + fclose(lvf); + + return ret; +} + +static void lvm_probe_all(blkid_cache cache) +{ + DIR *vg_list; + struct dirent *vg_iter; + int vg_len = strlen(VG_DIR); + dev_t dev; + + if ((vg_list = opendir(VG_DIR)) == NULL) + return; + + DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR)); + + while ((vg_iter = readdir(vg_list)) != NULL) { + DIR *lv_list; + char *vdirname; + char *vg_name; + struct dirent *lv_iter; + + vg_name = vg_iter->d_name; + if (LONE_CHAR(vg_name, '.') || !strcmp(vg_name, "..")) + continue; + vdirname = xmalloc(vg_len + strlen(vg_name) + 8); + sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name); + + lv_list = opendir(vdirname); + free(vdirname); + if (lv_list == NULL) + continue; + + while ((lv_iter = readdir(lv_list)) != NULL) { + char *lv_name, *lvm_device; + + lv_name = lv_iter->d_name; + if (LONE_CHAR(lv_name, '.') || !strcmp(lv_name, "..")) + continue; + + lvm_device = xmalloc(vg_len + strlen(vg_name) + + strlen(lv_name) + 8); + sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name, + lv_name); + dev = lvm_get_devno(lvm_device); + sprintf(lvm_device, "%s/%s", vg_name, lv_name); + DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n", + lvm_device, + (unsigned int) dev)); + probe_one(cache, lvm_device, dev, BLKID_PRI_LVM); + free(lvm_device); + } + closedir(lv_list); + } + closedir(vg_list); +} +#endif + +#define PROC_EVMS_VOLUMES "/proc/evms/volumes" + +static int +evms_probe_all(blkid_cache cache) +{ + char line[100]; + int ma, mi, sz, num = 0; + FILE *procpt; + char device[110]; + + procpt = fopen_for_read(PROC_EVMS_VOLUMES); + if (!procpt) + return 0; + while (fgets(line, sizeof(line), procpt)) { + if (sscanf(line, " %d %d %d %*s %*s %[^\n ]", + &ma, &mi, &sz, device) != 4) + continue; + + DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n", + device, ma, mi)); + + probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS); + num++; + } + fclose(procpt); + return num; +} + +/* + * Read the device data for all available block devices in the system. + */ +int blkid_probe_all(blkid_cache cache) +{ + FILE *proc; + char line[1024]; + char ptname0[128], ptname1[128], *ptname = NULL; + char *ptnames[2]; + dev_t devs[2]; + int ma, mi; + unsigned long long sz; + int lens[2] = { 0, 0 }; + int which = 0, last = 0; + + ptnames[0] = ptname0; + ptnames[1] = ptname1; + + if (!cache) + return -BLKID_ERR_PARAM; + + if (cache->bic_flags & BLKID_BIC_FL_PROBED && + time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL) + return 0; + + blkid_read_cache(cache); + evms_probe_all(cache); +#ifdef VG_DIR + lvm_probe_all(cache); +#endif + + proc = fopen_for_read(PROC_PARTITIONS); + if (!proc) + return -BLKID_ERR_PROC; + + while (fgets(line, sizeof(line), proc)) { + last = which; + which ^= 1; + ptname = ptnames[which]; + + if (sscanf(line, " %d %d %llu %128[^\n ]", + &ma, &mi, &sz, ptname) != 4) + continue; + devs[which] = makedev(ma, mi); + + DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname)); + + /* Skip whole disk devs unless they have no partitions + * If we don't have a partition on this dev, also + * check previous dev to see if it didn't have a partn. + * heuristic: partition name ends in a digit. + * + * Skip extended partitions. + * heuristic: size is 1 + * + * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs + */ + + lens[which] = strlen(ptname); + if (isdigit(ptname[lens[which] - 1])) { + DBG(DEBUG_DEVNAME, + printf("partition dev %s, devno 0x%04X\n", + ptname, (unsigned int) devs[which])); + + if (sz > 1) + probe_one(cache, ptname, devs[which], 0); + lens[which] = 0; + lens[last] = 0; + } else if (lens[last] && strncmp(ptnames[last], ptname, + lens[last])) { + DBG(DEBUG_DEVNAME, + printf("whole dev %s, devno 0x%04X\n", + ptnames[last], (unsigned int) devs[last])); + probe_one(cache, ptnames[last], devs[last], 0); + lens[last] = 0; + } + } + + /* Handle the last device if it wasn't partitioned */ + if (lens[which]) + probe_one(cache, ptname, devs[which], 0); + + fclose(proc); + + cache->bic_time = time(NULL); + cache->bic_flags |= BLKID_BIC_FL_PROBED; + blkid_flush_cache(cache); + return 0; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_debug_mask = DEBUG_ALL; + if (argc != 1) { + fprintf(stderr, "Usage: %s\n" + "Probe all devices and exit\n", argv[0]); + exit(1); + } + if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + if (blkid_probe_all(cache) < 0) + printf("%s: error probing devices\n", argv[0]); + + blkid_put_cache(cache); + return 0; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/devno.c b/e2fsprogs/old_e2fsprogs/blkid/devno.c new file mode 100644 index 000000000..ae326f81a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/devno.c @@ -0,0 +1,222 @@ +/* vi: set sw=4 ts=4: */ +/* + * devno.c - find a particular device by its device number (major/minor) + * + * Copyright (C) 2000, 2001, 2003 Theodore Ts'o + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif + +#include "blkidP.h" + +struct dir_list { + char *name; + struct dir_list *next; +}; + +char *blkid_strndup(const char *s, int length) +{ + char *ret; + + if (!s) + return NULL; + + if (!length) + length = strlen(s); + + ret = xmalloc(length + 1); + strncpy(ret, s, length); + ret[length] = '\0'; + return ret; +} + +char *blkid_strdup(const char *s) +{ + return blkid_strndup(s, 0); +} + +/* + * This function adds an entry to the directory list + */ +static void add_to_dirlist(const char *name, struct dir_list **list) +{ + struct dir_list *dp; + + dp = xmalloc(sizeof(struct dir_list)); + dp->name = blkid_strdup(name); + dp->next = *list; + *list = dp; +} + +/* + * This function frees a directory list + */ +static void free_dirlist(struct dir_list **list) +{ + struct dir_list *dp, *next; + + for (dp = *list; dp; dp = next) { + next = dp->next; + free(dp->name); + free(dp); + } + *list = NULL; +} + +static void scan_dir(char *dir_name, dev_t devno, struct dir_list **list, + char **devname) +{ + DIR *dir; + struct dirent *dp; + char path[1024]; + int dirlen; + struct stat st; + + if ((dir = opendir(dir_name)) == NULL) + return; + dirlen = strlen(dir_name) + 2; + while ((dp = readdir(dir)) != 0) { + if (dirlen + strlen(dp->d_name) >= sizeof(path)) + continue; + + if (dp->d_name[0] == '.' && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) + continue; + + sprintf(path, "%s/%s", dir_name, dp->d_name); + if (stat(path, &st) < 0) + continue; + + if (S_ISDIR(st.st_mode)) + add_to_dirlist(path, list); + else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) { + *devname = blkid_strdup(path); + DBG(DEBUG_DEVNO, + printf("found 0x%llx at %s (%p)\n", devno, + path, *devname)); + break; + } + } + closedir(dir); +} + +/* Directories where we will try to search for device numbers */ +const char *blkid_devdirs[] = { "/devices", "/devfs", "/dev", NULL }; + +/* + * This function finds the pathname to a block device with a given + * device number. It returns a pointer to allocated memory to the + * pathname on success, and NULL on failure. + */ +char *blkid_devno_to_devname(dev_t devno) +{ + struct dir_list *list = NULL, *new_list = NULL; + char *devname = NULL; + const char **dir; + + /* + * Add the starting directories to search in reverse order of + * importance, since we are using a stack... + */ + for (dir = blkid_devdirs; *dir; dir++) + add_to_dirlist(*dir, &list); + + while (list) { + struct dir_list *current = list; + + list = list->next; + DBG(DEBUG_DEVNO, printf("directory %s\n", current->name)); + scan_dir(current->name, devno, &new_list, &devname); + free(current->name); + free(current); + if (devname) + break; + /* + * If we're done checking at this level, descend to + * the next level of subdirectories. (breadth-first) + */ + if (list == NULL) { + list = new_list; + new_list = NULL; + } + } + free_dirlist(&list); + free_dirlist(&new_list); + + if (!devname) { + DBG(DEBUG_DEVNO, + printf("blkid: cannot find devno 0x%04lx\n", + (unsigned long) devno)); + } else { + DBG(DEBUG_DEVNO, + printf("found devno 0x%04llx as %s\n", devno, devname)); + } + + + return devname; +} + +#ifdef TEST_PROGRAM +int main(int argc, char** argv) +{ + char *devname, *tmp; + int major, minor; + dev_t devno; + const char *errmsg = "Cannot parse %s: %s\n"; + + blkid_debug_mask = DEBUG_ALL; + if ((argc != 2) && (argc != 3)) { + fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n" + "Resolve a device number to a device name\n", + argv[0], argv[0]); + exit(1); + } + if (argc == 2) { + devno = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "device number", argv[1]); + exit(1); + } + } else { + major = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "major number", argv[1]); + exit(1); + } + minor = strtoul(argv[2], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "minor number", argv[2]); + exit(1); + } + devno = makedev(major, minor); + } + printf("Looking for device 0x%04Lx\n", devno); + devname = blkid_devno_to_devname(devno); + free(devname); + return 0; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/list.c b/e2fsprogs/old_e2fsprogs/blkid/list.c new file mode 100644 index 000000000..04d61a19b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/list.c @@ -0,0 +1,110 @@ +/* vi: set sw=4 ts=4: */ + +#include "list.h" + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +void __list_add(struct list_head * add, + struct list_head * prev, + struct list_head * next) +{ + next->prev = add; + add->next = next; + add->prev = prev; + prev->next = add; +} + +/* + * list_add - add a new entry + * @add: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +void list_add(struct list_head *add, struct list_head *head) +{ + __list_add(add, head, head->next); +} + +/* + * list_add_tail - add a new entry + * @add: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +void list_add_tail(struct list_head *add, struct list_head *head) +{ + __list_add(add, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/* + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * + * list_empty() on @entry does not return true after this, @entry is + * in an undefined state. + */ +void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/* + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/* + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} diff --git a/e2fsprogs/old_e2fsprogs/blkid/list.h b/e2fsprogs/old_e2fsprogs/blkid/list.h new file mode 100644 index 000000000..a24baaa91 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/list.h @@ -0,0 +1,73 @@ +/* vi: set sw=4 ts=4: */ +#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD) +#define BLKID_LIST_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next); +void list_add(struct list_head *add, struct list_head *head); +void list_add_tail(struct list_head *add, struct list_head *head); +void __list_del(struct list_head * prev, struct list_head * next); +void list_del(struct list_head *entry); +void list_del_init(struct list_head *entry); +int list_empty(struct list_head *head); +void list_splice(struct list_head *list, struct list_head *head); + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over elements in a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over elements in a list, but don't dereference + * pos after the body is done (in case it is freed) + * @pos: the &struct list_head to use as a loop counter. + * @pnext: the &struct list_head to use as a pointer to the next item. + * @head: the head for your list (not included in iteration). + */ +#define list_for_each_safe(pos, pnext, head) \ + for (pos = (head)->next, pnext = pos->next; pos != (head); \ + pos = pnext, pnext = pos->next) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/probe.c b/e2fsprogs/old_e2fsprogs/blkid/probe.c new file mode 100644 index 000000000..77bfc737d --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/probe.c @@ -0,0 +1,721 @@ +/* vi: set sw=4 ts=4: */ +/* + * probe.c - identify a block device by its contents, and return a dev + * struct with the details + * + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#include "blkidP.h" +#include "../uuid/uuid.h" +#include "probe.h" + +/* + * This is a special case code to check for an MDRAID device. We do + * this special since it requires checking for a superblock at the end + * of the device. + */ +static int check_mdraid(int fd, unsigned char *ret_uuid) +{ + struct mdp_superblock_s *md; + blkid_loff_t offset; + char buf[4096]; + + if (fd < 0) + return -BLKID_ERR_PARAM; + + offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536; + + if (blkid_llseek(fd, offset, 0) < 0 || + read(fd, buf, 4096) != 4096) + return -BLKID_ERR_IO; + + /* Check for magic number */ + if (memcmp("\251+N\374", buf, 4)) + return -BLKID_ERR_PARAM; + + if (!ret_uuid) + return 0; + *ret_uuid = 0; + + /* The MD UUID is not contiguous in the superblock, make it so */ + md = (struct mdp_superblock_s *)buf; + if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) { + memcpy(ret_uuid, &md->set_uuid0, 4); + memcpy(ret_uuid, &md->set_uuid1, 12); + } + return 0; +} + +static void set_uuid(blkid_dev dev, uuid_t uuid) +{ + char str[37]; + + if (!uuid_is_null(uuid)) { + uuid_unparse(uuid, str); + blkid_set_tag(dev, "UUID", str, sizeof(str)); + } +} + +static void get_ext2_info(blkid_dev dev, unsigned char *buf) +{ + struct ext2_super_block *es = (struct ext2_super_block *) buf; + const char *label = NULL; + + DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n", + blkid_le32(es->s_feature_compat), + blkid_le32(es->s_feature_incompat), + blkid_le32(es->s_feature_ro_compat))); + + if (strlen(es->s_volume_name)) + label = es->s_volume_name; + blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name)); + + set_uuid(dev, es->s_uuid); +} + +static int probe_ext3(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct ext2_super_block *es; + + es = (struct ext2_super_block *)buf; + + /* Distinguish between jbd and ext2/3 fs */ + if (blkid_le32(es->s_feature_incompat) & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + + /* Distinguish between ext3 and ext2 */ + if (!(blkid_le32(es->s_feature_compat) & + EXT3_FEATURE_COMPAT_HAS_JOURNAL)) + return -BLKID_ERR_PARAM; + + get_ext2_info(dev, buf); + + blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2")); + + return 0; +} + +static int probe_ext2(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct ext2_super_block *es; + + es = (struct ext2_super_block *)buf; + + /* Distinguish between jbd and ext2/3 fs */ + if (blkid_le32(es->s_feature_incompat) & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + + get_ext2_info(dev, buf); + + return 0; +} + +static int probe_jbd(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct ext2_super_block *es = (struct ext2_super_block *) buf; + + if (!(blkid_le32(es->s_feature_incompat) & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) + return -BLKID_ERR_PARAM; + + get_ext2_info(dev, buf); + + return 0; +} + +static int probe_vfat(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct vfat_super_block *vs; + char serno[10]; + const char *label = NULL; + int label_len = 0; + + vs = (struct vfat_super_block *)buf; + + if (strncmp(vs->vs_label, "NO NAME", 7)) { + char *end = vs->vs_label + sizeof(vs->vs_label) - 1; + + while (*end == ' ' && end >= vs->vs_label) + --end; + if (end >= vs->vs_label) { + label = vs->vs_label; + label_len = end - vs->vs_label + 1; + } + } + + /* We can't just print them as %04X, because they are unaligned */ + sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2], + vs->vs_serno[1], vs->vs_serno[0]); + blkid_set_tag(dev, "LABEL", label, label_len); + blkid_set_tag(dev, "UUID", serno, sizeof(serno)); + + return 0; +} + +static int probe_msdos(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct msdos_super_block *ms = (struct msdos_super_block *) buf; + char serno[10]; + const char *label = NULL; + int label_len = 0; + + if (strncmp(ms->ms_label, "NO NAME", 7)) { + char *end = ms->ms_label + sizeof(ms->ms_label) - 1; + + while (*end == ' ' && end >= ms->ms_label) + --end; + if (end >= ms->ms_label) { + label = ms->ms_label; + label_len = end - ms->ms_label + 1; + } + } + + /* We can't just print them as %04X, because they are unaligned */ + sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2], + ms->ms_serno[1], ms->ms_serno[0]); + blkid_set_tag(dev, "UUID", serno, 0); + blkid_set_tag(dev, "LABEL", label, label_len); + blkid_set_tag(dev, "SEC_TYPE", "msdos", sizeof("msdos")); + + return 0; +} + +static int probe_xfs(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct xfs_super_block *xs; + const char *label = NULL; + + xs = (struct xfs_super_block *)buf; + + if (strlen(xs->xs_fname)) + label = xs->xs_fname; + blkid_set_tag(dev, "LABEL", label, sizeof(xs->xs_fname)); + set_uuid(dev, xs->xs_uuid); + return 0; +} + +static int probe_reiserfs(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id, unsigned char *buf) +{ + struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf; + unsigned int blocksize; + const char *label = NULL; + + blocksize = blkid_le16(rs->rs_blocksize); + + /* If the superblock is inside the journal, we have the wrong one */ + if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block)) + return -BLKID_ERR_BIG; + + /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */ + if (!strcmp(id->bim_magic, "ReIsEr2Fs") || + !strcmp(id->bim_magic, "ReIsEr3Fs")) { + if (strlen(rs->rs_label)) + label = rs->rs_label; + set_uuid(dev, rs->rs_uuid); + } + blkid_set_tag(dev, "LABEL", label, sizeof(rs->rs_label)); + + return 0; +} + +static int probe_jfs(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct jfs_super_block *js; + const char *label = NULL; + + js = (struct jfs_super_block *)buf; + + if (strlen((char *) js->js_label)) + label = (char *) js->js_label; + blkid_set_tag(dev, "LABEL", label, sizeof(js->js_label)); + set_uuid(dev, js->js_uuid); + return 0; +} + +static int probe_romfs(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct romfs_super_block *ros; + const char *label = NULL; + + ros = (struct romfs_super_block *)buf; + + if (strlen((char *) ros->ros_volume)) + label = (char *) ros->ros_volume; + blkid_set_tag(dev, "LABEL", label, 0); + return 0; +} + +static int probe_cramfs(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct cramfs_super_block *csb; + const char *label = NULL; + + csb = (struct cramfs_super_block *)buf; + + if (strlen((char *) csb->name)) + label = (char *) csb->name; + blkid_set_tag(dev, "LABEL", label, 0); + return 0; +} + +static int probe_swap0(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf __BLKID_ATTR((unused))) +{ + blkid_set_tag(dev, "UUID", 0, 0); + blkid_set_tag(dev, "LABEL", 0, 0); + return 0; +} + +static int probe_swap1(int fd, + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf __BLKID_ATTR((unused))) +{ + struct swap_id_block *sws; + + probe_swap0(fd, cache, dev, id, buf); + /* + * Version 1 swap headers are always located at offset of 1024 + * bytes, although the swap signature itself is located at the + * end of the page (which may vary depending on hardware + * pagesize). + */ + if (lseek(fd, 1024, SEEK_SET) < 0) return 1; + sws = xmalloc(1024); + if (read(fd, sws, 1024) != 1024) { + free(sws); + return 1; + } + + /* arbitrary sanity check.. is there any garbage down there? */ + if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) { + if (sws->sws_volume[0]) + blkid_set_tag(dev, "LABEL", (const char*)sws->sws_volume, + sizeof(sws->sws_volume)); + if (sws->sws_uuid[0]) + set_uuid(dev, sws->sws_uuid); + } + free(sws); + + return 0; +} + +static const char +* const udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02", + "NSR03", "TEA01", 0 }; + +static int probe_udf(int fd, blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev __BLKID_ATTR((unused)), + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf __BLKID_ATTR((unused))) +{ + int j, bs; + struct iso_volume_descriptor isosb; + const char *const *m; + + /* determine the block size by scanning in 2K increments + (block sizes larger than 2K will be null padded) */ + for (bs = 1; bs < 16; bs++) { + lseek(fd, bs*2048+32768, SEEK_SET); + if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb)) + return 1; + if (isosb.id[0]) + break; + } + + /* Scan up to another 64 blocks looking for additional VSD's */ + for (j = 1; j < 64; j++) { + if (j > 1) { + lseek(fd, j*bs*2048+32768, SEEK_SET); + if (read(fd, (char *)&isosb, sizeof(isosb)) + != sizeof(isosb)) + return 1; + } + /* If we find NSR0x then call it udf: + NSR01 for UDF 1.00 + NSR02 for UDF 1.50 + NSR03 for UDF 2.00 */ + if (!strncmp(isosb.id, "NSR0", 4)) + return 0; + for (m = udf_magic; *m; m++) + if (!strncmp(*m, isosb.id, 5)) + break; + if (*m == 0) + return 1; + } + return 1; +} + +static int probe_ocfs(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct ocfs_volume_header ovh; + struct ocfs_volume_label ovl; + __u32 major; + + memcpy(&ovh, buf, sizeof(ovh)); + memcpy(&ovl, buf+512, sizeof(ovl)); + + major = ocfsmajor(ovh); + if (major == 1) + blkid_set_tag(dev, "SEC_TYPE", "ocfs1", sizeof("ocfs1")); + else if (major >= 9) + blkid_set_tag(dev, "SEC_TYPE", "ntocfs", sizeof("ntocfs")); + + blkid_set_tag(dev, "LABEL", (const char*)ovl.label, ocfslabellen(ovl)); + blkid_set_tag(dev, "MOUNT", (const char*)ovh.mount, ocfsmountlen(ovh)); + set_uuid(dev, ovl.vol_id); + return 0; +} + +static int probe_ocfs2(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct ocfs2_super_block *osb; + + osb = (struct ocfs2_super_block *)buf; + + blkid_set_tag(dev, "LABEL", (const char*)osb->s_label, sizeof(osb->s_label)); + set_uuid(dev, osb->s_uuid); + return 0; +} + +static int probe_oracleasm(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + const struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct oracle_asm_disk_label *dl; + + dl = (struct oracle_asm_disk_label *)buf; + + blkid_set_tag(dev, "LABEL", dl->dl_id, sizeof(dl->dl_id)); + return 0; +} + +/* + * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined + * in the type_array table below + bim_kbalign. + * + * When probing for a lot of magics, we handle everything in 1kB buffers so + * that we don't have to worry about reading each combination of block sizes. + */ +#define BLKID_BLK_OFFS 64 /* currently reiserfs */ + +/* + * Various filesystem magics that we can check for. Note that kboff and + * sboff are in kilobytes and bytes respectively. All magics are in + * byte strings so we don't worry about endian issues. + */ +static const struct blkid_magic type_array[] = { +/* type kboff sboff len magic probe */ + { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm }, + { "ntfs", 0, 3, 8, "NTFS ", 0 }, + { "jbd", 1, 0x38, 2, "\123\357", probe_jbd }, + { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 }, + { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 }, + { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs }, + { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs }, + { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs }, + { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs }, + { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs }, + { "vfat", 0, 0x52, 5, "MSWIN", probe_vfat }, + { "vfat", 0, 0x52, 8, "FAT32 ", probe_vfat }, + { "vfat", 0, 0x36, 5, "MSDOS", probe_msdos }, + { "vfat", 0, 0x36, 8, "FAT16 ", probe_msdos }, + { "vfat", 0, 0x36, 8, "FAT12 ", probe_msdos }, + { "minix", 1, 0x10, 2, "\177\023", 0 }, + { "minix", 1, 0x10, 2, "\217\023", 0 }, + { "minix", 1, 0x10, 2, "\150\044", 0 }, + { "minix", 1, 0x10, 2, "\170\044", 0 }, + { "vxfs", 1, 0, 4, "\365\374\001\245", 0 }, + { "xfs", 0, 0, 4, "XFSB", probe_xfs }, + { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs }, + { "bfs", 0, 0, 4, "\316\372\173\033", 0 }, + { "cramfs", 0, 0, 4, "E=\315\050", probe_cramfs }, + { "qnx4", 0, 4, 6, "QNX4FS", 0 }, + { "udf", 32, 1, 5, "BEA01", probe_udf }, + { "udf", 32, 1, 5, "BOOT2", probe_udf }, + { "udf", 32, 1, 5, "CD001", probe_udf }, + { "udf", 32, 1, 5, "CDW02", probe_udf }, + { "udf", 32, 1, 5, "NSR02", probe_udf }, + { "udf", 32, 1, 5, "NSR03", probe_udf }, + { "udf", 32, 1, 5, "TEA01", probe_udf }, + { "iso9660", 32, 1, 5, "CD001", 0 }, + { "iso9660", 32, 9, 5, "CDROM", 0 }, + { "jfs", 32, 0, 4, "JFS1", probe_jfs }, + { "hfs", 1, 0, 2, "BD", 0 }, + { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 }, + { "hpfs", 8, 0, 4, "I\350\225\371", 0 }, + { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 }, + { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 }, + { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 }, + { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 }, + { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 }, + { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 }, + { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 }, + { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 }, + { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 }, + { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 }, + { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 }, + { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs }, + { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 }, + { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 }, + { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 }, + { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +/* + * Verify that the data in dev is consistent with what is on the actual + * block device (using the devname field only). Normally this will be + * called when finding items in the cache, but for long running processes + * is also desirable to revalidate an item before use. + * + * If we are unable to revalidate the data, we return the old data and + * do not set the BLKID_BID_FL_VERIFIED flag on it. + */ +blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev) +{ + const struct blkid_magic *id; + unsigned char *bufs[BLKID_BLK_OFFS + 1], *buf; + const char *type; + struct stat st; + time_t diff, now; + int fd, idx; + + if (!dev) + return NULL; + + now = time(NULL); + diff = now - dev->bid_time; + + if ((now < dev->bid_time) || + (diff < BLKID_PROBE_MIN) || + (dev->bid_flags & BLKID_BID_FL_VERIFIED && + diff < BLKID_PROBE_INTERVAL)) + return dev; + + DBG(DEBUG_PROBE, + printf("need to revalidate %s (time since last check %lu)\n", + dev->bid_name, diff)); + + if (((fd = open(dev->bid_name, O_RDONLY)) < 0) || + (fstat(fd, &st) < 0)) { + if (errno == ENXIO || errno == ENODEV || errno == ENOENT) { + blkid_free_dev(dev); + return NULL; + } + /* We don't have read permission, just return cache data. */ + DBG(DEBUG_PROBE, + printf("returning unverified data for %s\n", + dev->bid_name)); + return dev; + } + + memset(bufs, 0, sizeof(bufs)); + + /* + * Iterate over the type array. If we already know the type, + * then try that first. If it doesn't work, then blow away + * the type information, and try again. + * + */ +try_again: + type = 0; + if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) { + uuid_t uuid; + + if (check_mdraid(fd, uuid) == 0) { + set_uuid(dev, uuid); + type = "mdraid"; + goto found_type; + } + } + for (id = type_array; id->bim_type; id++) { + if (dev->bid_type && + strcmp(id->bim_type, dev->bid_type)) + continue; + + idx = id->bim_kboff + (id->bim_sboff >> 10); + if (idx > BLKID_BLK_OFFS || idx < 0) + continue; + buf = bufs[idx]; + if (!buf) { + if (lseek(fd, idx << 10, SEEK_SET) < 0) + continue; + + buf = xmalloc(1024); + + if (read(fd, buf, 1024) != 1024) { + free(buf); + continue; + } + bufs[idx] = buf; + } + + if (memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ff), + id->bim_len)) + continue; + + if ((id->bim_probe == NULL) || + (id->bim_probe(fd, cache, dev, id, buf) == 0)) { + type = id->bim_type; + goto found_type; + } + } + + if (!id->bim_type && dev->bid_type) { + /* + * Zap the device filesystem type and try again + */ + blkid_set_tag(dev, "TYPE", 0, 0); + blkid_set_tag(dev, "SEC_TYPE", 0, 0); + blkid_set_tag(dev, "LABEL", 0, 0); + blkid_set_tag(dev, "UUID", 0, 0); + goto try_again; + } + + if (!dev->bid_type) { + blkid_free_dev(dev); + return NULL; + } + +found_type: + if (dev && type) { + dev->bid_devno = st.st_rdev; + dev->bid_time = time(NULL); + dev->bid_flags |= BLKID_BID_FL_VERIFIED; + cache->bic_flags |= BLKID_BIC_FL_CHANGED; + + blkid_set_tag(dev, "TYPE", type, 0); + + DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n", + dev->bid_name, st.st_rdev, type)); + } + + close(fd); + + return dev; +} + +int blkid_known_fstype(const char *fstype) +{ + const struct blkid_magic *id; + + for (id = type_array; id->bim_type; id++) { + if (strcmp(fstype, id->bim_type) == 0) + return 1; + } + return 0; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_dev dev; + blkid_cache cache; + int ret; + + blkid_debug_mask = DEBUG_ALL; + if (argc != 2) { + fprintf(stderr, "Usage: %s device\n" + "Probe a single device to determine type\n", argv[0]); + exit(1); + } + if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL); + if (!dev) { + printf("%s: %s has an unsupported type\n", argv[0], argv[1]); + return 1; + } + printf("%s is type %s\n", argv[1], dev->bid_type ? + dev->bid_type : "(null)"); + if (dev->bid_label) + printf("\tlabel is '%s'\n", dev->bid_label); + if (dev->bid_uuid) + printf("\tuuid is %s\n", dev->bid_uuid); + + blkid_free_dev(dev); + return 0; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/probe.h b/e2fsprogs/old_e2fsprogs/blkid/probe.h new file mode 100644 index 000000000..b6d8f8e7d --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/probe.h @@ -0,0 +1,374 @@ +/* vi: set sw=4 ts=4: */ +/* + * probe.h - constants and on-disk structures for extracting device data + * + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ +#ifndef BLKID_PROBE_H +#define BLKID_PROBE_H 1 + +#include + +struct blkid_magic; + +typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev, + const struct blkid_magic *id, unsigned char *buf); + +struct blkid_magic { + const char *bim_type; /* type name for this magic */ + long bim_kboff; /* kilobyte offset of superblock */ + unsigned bim_sboff; /* byte offset within superblock */ + unsigned bim_len; /* length of magic */ + const char *bim_magic; /* magic string */ + blkid_probe_t bim_probe; /* probe function */ +}; + +/* + * Structures for each of the content types we want to extract information + * from. We do not necessarily need the magic field here, because we have + * already identified the content type before we get this far. It may still + * be useful if there are probe functions which handle multiple content types. + */ +struct ext2_super_block { + __u32 s_inodes_count; + __u32 s_blocks_count; + __u32 s_r_blocks_count; + __u32 s_free_blocks_count; + __u32 s_free_inodes_count; + __u32 s_first_data_block; + __u32 s_log_block_size; + __u32 s_dummy3[7]; + unsigned char s_magic[2]; + __u16 s_state; + __u32 s_dummy5[8]; + __u32 s_feature_compat; + __u32 s_feature_incompat; + __u32 s_feature_ro_compat; + unsigned char s_uuid[16]; + char s_volume_name[16]; +}; +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x00000004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008 + +struct xfs_super_block { + unsigned char xs_magic[4]; + __u32 xs_blocksize; + __u64 xs_dblocks; + __u64 xs_rblocks; + __u32 xs_dummy1[2]; + unsigned char xs_uuid[16]; + __u32 xs_dummy2[15]; + char xs_fname[12]; + __u32 xs_dummy3[2]; + __u64 xs_icount; + __u64 xs_ifree; + __u64 xs_fdblocks; +}; + +struct reiserfs_super_block { + __u32 rs_blocks_count; + __u32 rs_free_blocks; + __u32 rs_root_block; + __u32 rs_journal_block; + __u32 rs_journal_dev; + __u32 rs_orig_journal_size; + __u32 rs_dummy2[5]; + __u16 rs_blocksize; + __u16 rs_dummy3[3]; + unsigned char rs_magic[12]; + __u32 rs_dummy4[5]; + unsigned char rs_uuid[16]; + char rs_label[16]; +}; + +struct jfs_super_block { + unsigned char js_magic[4]; + __u32 js_version; + __u64 js_size; + __u32 js_bsize; + __u32 js_dummy1; + __u32 js_pbsize; + __u32 js_dummy2[27]; + unsigned char js_uuid[16]; + unsigned char js_label[16]; + unsigned char js_loguuid[16]; +}; + +struct romfs_super_block { + unsigned char ros_magic[8]; + __u32 ros_dummy1[2]; + unsigned char ros_volume[16]; +}; + +struct cramfs_super_block { + __u8 magic[4]; + __u32 size; + __u32 flags; + __u32 future; + __u8 signature[16]; + struct cramfs_info { + __u32 crc; + __u32 edition; + __u32 blocks; + __u32 files; + } info; + __u8 name[16]; +}; + +struct swap_id_block { +/* unsigned char sws_boot[1024]; */ + __u32 sws_version; + __u32 sws_lastpage; + __u32 sws_nrbad; + unsigned char sws_uuid[16]; + char sws_volume[16]; + unsigned char sws_pad[117]; + __u32 sws_badpg; +}; + +/* Yucky misaligned values */ +struct vfat_super_block { +/* 00*/ unsigned char vs_ignored[3]; +/* 03*/ unsigned char vs_sysid[8]; +/* 0b*/ unsigned char vs_sector_size[2]; +/* 0d*/ __u8 vs_cluster_size; +/* 0e*/ __u16 vs_reserved; +/* 10*/ __u8 vs_fats; +/* 11*/ unsigned char vs_dir_entries[2]; +/* 13*/ unsigned char vs_sectors[2]; +/* 15*/ unsigned char vs_media; +/* 16*/ __u16 vs_fat_length; +/* 18*/ __u16 vs_secs_track; +/* 1a*/ __u16 vs_heads; +/* 1c*/ __u32 vs_hidden; +/* 20*/ __u32 vs_total_sect; +/* 24*/ __u32 vs_fat32_length; +/* 28*/ __u16 vs_flags; +/* 2a*/ __u8 vs_version[2]; +/* 2c*/ __u32 vs_root_cluster; +/* 30*/ __u16 vs_insfo_sector; +/* 32*/ __u16 vs_backup_boot; +/* 34*/ __u16 vs_reserved2[6]; +/* 40*/ unsigned char vs_unknown[3]; +/* 43*/ unsigned char vs_serno[4]; +/* 47*/ char vs_label[11]; +/* 52*/ unsigned char vs_magic[8]; +/* 5a*/ unsigned char vs_dummy2[164]; +/*1fe*/ unsigned char vs_pmagic[2]; +}; + +/* Yucky misaligned values */ +struct msdos_super_block { +/* 00*/ unsigned char ms_ignored[3]; +/* 03*/ unsigned char ms_sysid[8]; +/* 0b*/ unsigned char ms_sector_size[2]; +/* 0d*/ __u8 ms_cluster_size; +/* 0e*/ __u16 ms_reserved; +/* 10*/ __u8 ms_fats; +/* 11*/ unsigned char ms_dir_entries[2]; +/* 13*/ unsigned char ms_sectors[2]; +/* 15*/ unsigned char ms_media; +/* 16*/ __u16 ms_fat_length; +/* 18*/ __u16 ms_secs_track; +/* 1a*/ __u16 ms_heads; +/* 1c*/ __u32 ms_hidden; +/* 20*/ __u32 ms_total_sect; +/* 24*/ unsigned char ms_unknown[3]; +/* 27*/ unsigned char ms_serno[4]; +/* 2b*/ char ms_label[11]; +/* 36*/ unsigned char ms_magic[8]; +/* 3d*/ unsigned char ms_dummy2[192]; +/*1fe*/ unsigned char ms_pmagic[2]; +}; + +struct minix_super_block { + __u16 ms_ninodes; + __u16 ms_nzones; + __u16 ms_imap_blocks; + __u16 ms_zmap_blocks; + __u16 ms_firstdatazone; + __u16 ms_log_zone_size; + __u32 ms_max_size; + unsigned char ms_magic[2]; + __u16 ms_state; + __u32 ms_zones; +}; + +struct mdp_superblock_s { + __u32 md_magic; + __u32 major_version; + __u32 minor_version; + __u32 patch_version; + __u32 gvalid_words; + __u32 set_uuid0; + __u32 ctime; + __u32 level; + __u32 size; + __u32 nr_disks; + __u32 raid_disks; + __u32 md_minor; + __u32 not_persistent; + __u32 set_uuid1; + __u32 set_uuid2; + __u32 set_uuid3; +}; + +struct hfs_super_block { + char h_magic[2]; + char h_dummy[18]; + __u32 h_blksize; +}; + +struct ocfs_volume_header { + unsigned char minor_version[4]; + unsigned char major_version[4]; + unsigned char signature[128]; + char mount[128]; + unsigned char mount_len[2]; +}; + +struct ocfs_volume_label { + unsigned char disk_lock[48]; + char label[64]; + unsigned char label_len[2]; + unsigned char vol_id[16]; + unsigned char vol_id_len[2]; +}; + +#define ocfsmajor(o) ((__u32)o.major_version[0] \ + + (((__u32) o.major_version[1]) << 8) \ + + (((__u32) o.major_version[2]) << 16) \ + + (((__u32) o.major_version[3]) << 24)) +#define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8)) +#define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8)) + +#define OCFS_MAGIC "OracleCFS" + +struct ocfs2_super_block { + unsigned char signature[8]; + unsigned char s_dummy1[184]; + unsigned char s_dummy2[80]; + char s_label[64]; + unsigned char s_uuid[16]; +}; + +#define OCFS2_MIN_BLOCKSIZE 512 +#define OCFS2_MAX_BLOCKSIZE 4096 + +#define OCFS2_SUPER_BLOCK_BLKNO 2 + +#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" + +struct oracle_asm_disk_label { + char dummy[32]; + char dl_tag[8]; + char dl_id[24]; +}; + +#define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK" +#define ORACLE_ASM_DISK_LABEL_OFFSET 32 + +#define ISODCL(from, to) (to - from + 1) +struct iso_volume_descriptor { + char type[ISODCL(1,1)]; /* 711 */ + char id[ISODCL(2,6)]; + char version[ISODCL(7,7)]; + char data[ISODCL(8,2048)]; +}; + +/* + * Byte swap functions + */ +#ifdef __GNUC__ +#define _INLINE_ static __inline__ +#else /* For Watcom C */ +#define _INLINE_ static inline +#endif + +static __u16 blkid_swab16(__u16 val); +static __u32 blkid_swab32(__u32 val); +static __u64 blkid_swab64(__u64 val); + +#if ((defined __GNUC__) && \ + (defined(__i386__) || defined(__i486__) || defined(__i586__))) + +#define _BLKID_HAVE_ASM_BITOPS_ + +_INLINE_ __u32 blkid_swab32(__u32 val) +{ +#ifdef EXT2FS_REQUIRE_486 + __asm__("bswap %0" : "=r" (val) : "0" (val)); +#else + __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ + "rorl $16,%0\n\t" /* swap words */ + "xchgb %b0,%h0" /* swap higher bytes */ + :"=q" (val) + : "0" (val)); +#endif + return val; +} + +_INLINE_ __u16 blkid_swab16(__u16 val) +{ + __asm__("xchgb %b0,%h0" /* swap bytes */ + : "=q" (val) + : "0" (val)); + return val; +} + +_INLINE_ __u64 blkid_swab64(__u64 val) +{ + return blkid_swab32(val >> 32) | + ( ((__u64)blkid_swab32((__u32)val)) << 32 ); +} +#endif + +#if !defined(_BLKID_HAVE_ASM_BITOPS_) + +_INLINE_ __u16 blkid_swab16(__u16 val) +{ + return (val >> 8) | (val << 8); +} + +_INLINE_ __u32 blkid_swab32(__u32 val) +{ + return (val>>24) | ((val>>8) & 0xFF00) | + ((val<<8) & 0xFF0000) | (val<<24); +} + +_INLINE_ __u64 blkid_swab64(__u64 val) +{ + return blkid_swab32(val >> 32) | + ( ((__u64)blkid_swab32((__u32)val)) << 32 ); +} +#endif + + + +#if __BYTE_ORDER == __BIG_ENDIAN +#define blkid_le16(x) blkid_swab16(x) +#define blkid_le32(x) blkid_swab32(x) +#define blkid_le64(x) blkid_swab64(x) +#define blkid_be16(x) (x) +#define blkid_be32(x) (x) +#define blkid_be64(x) (x) +#else +#define blkid_le16(x) (x) +#define blkid_le32(x) (x) +#define blkid_le64(x) (x) +#define blkid_be16(x) blkid_swab16(x) +#define blkid_be32(x) blkid_swab32(x) +#define blkid_be64(x) blkid_swab64(x) +#endif + +#undef _INLINE_ + +#endif /* _BLKID_PROBE_H */ diff --git a/e2fsprogs/old_e2fsprogs/blkid/read.c b/e2fsprogs/old_e2fsprogs/blkid/read.c new file mode 100644 index 000000000..f795a5d14 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/read.c @@ -0,0 +1,459 @@ +/* vi: set sw=4 ts=4: */ +/* + * read.c - read the blkid cache from disk, to avoid scanning all devices + * + * Copyright (C) 2001, 2003 Theodore Y. Ts'o + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "blkidP.h" +#include "../uuid/uuid.h" + +#ifdef HAVE_STRTOULL +#define __USE_ISOC9X +#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */ +#else +/* FIXME: need to support real strtoull here */ +#define STRTOULL strtoul +#endif + +#include + +#ifdef TEST_PROGRAM +#define blkid_debug_dump_dev(dev) (debug_dump_dev(dev)) +static void debug_dump_dev(blkid_dev dev); +#endif + +/* + * File format: + * + * ...]>device_name + * + * The following tags are required for each entry: + * unique (within this file) ID number of this device + * (ascii time_t) time this entry was last read from disk + * (detected) type of filesystem/data for this partition + * + * The following tags may be present, depending on the device contents + * (user supplied) label (volume name, etc) + * (generated) universally unique identifier (serial no) + */ + +static char *skip_over_blank(char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + char ch; + + while ((ch = *cp)) { + /* If we see a backslash, skip the next character */ + if (ch == '\\') { + cp++; + if (*cp == '\0') + break; + cp++; + continue; + } + if (isspace(ch) || ch == '<' || ch == '>') + break; + cp++; + } + return cp; +} + +static char *strip_line(char *line) +{ + char *p; + + line = skip_over_blank(line); + + p = line + strlen(line) - 1; + + while (*line) { + if (isspace(*p)) + *p-- = '\0'; + else + break; + } + + return line; +} + +/* + * Start parsing a new line from the cache. + * + * line starts with " continue parsing line + * line starts with " skip line + * line starts with other, return -BLKID_ERR_CACHE -> error + */ +static int parse_start(char **cp) +{ + char *p; + + p = strip_line(*cp); + + /* Skip comment or blank lines. We can't just NUL the first '#' char, + * in case it is inside quotes, or escaped. + */ + if (*p == '\0' || *p == '#') + return 0; + + if (!strncmp(p, "", 9)) { + DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp)); + *cp += 9; + return 0; + } + + return -BLKID_ERR_CACHE; +} + +/* + * Allocate a new device struct with device name filled in. Will handle + * finding the device on lines of the form: + * devname + * devnamebar + */ +static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp) +{ + char *start, *tmp, *end, *name; + int ret; + + if ((ret = parse_start(cp)) <= 0) + return ret; + + start = tmp = strchr(*cp, '>'); + if (!start) { + DBG(DEBUG_READ, + printf("blkid: short line parsing dev: %s\n", *cp)); + return -BLKID_ERR_CACHE; + } + start = skip_over_blank(start + 1); + end = skip_over_word(start); + + DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start)); + + if (**cp == '>') + *cp = end; + else + (*cp)++; + + *tmp = '\0'; + + if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) { + DBG(DEBUG_READ, + printf("blkid: missing ending: %s\n", end)); + } else if (tmp) + *tmp = '\0'; + + if (end - start <= 1) { + DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp)); + return -BLKID_ERR_CACHE; + } + + name = blkid_strndup(start, end-start); + if (name == NULL) + return -BLKID_ERR_MEM; + + DBG(DEBUG_READ, printf("found dev %s\n", name)); + + if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) + return -BLKID_ERR_MEM; + + free(name); + return 1; +} + +/* + * Extract a tag of the form NAME="value" from the line. + */ +static int parse_token(char **name, char **value, char **cp) +{ + char *end; + + if (!name || !value || !cp) + return -BLKID_ERR_PARAM; + + if (!(*value = strchr(*cp, '='))) + return 0; + + **value = '\0'; + *name = strip_line(*cp); + *value = skip_over_blank(*value + 1); + + if (**value == '"') { + end = strchr(*value + 1, '"'); + if (!end) { + DBG(DEBUG_READ, + printf("unbalanced quotes at: %s\n", *value)); + *cp = *value; + return -BLKID_ERR_CACHE; + } + (*value)++; + *end = '\0'; + end++; + } else { + end = skip_over_word(*value); + if (*end) { + *end = '\0'; + end++; + } + } + *cp = end; + + return 1; +} + +/* + * Extract a tag of the form value from the line. + */ +/* +static int parse_xml(char **name, char **value, char **cp) +{ + char *end; + + if (!name || !value || !cp) + return -BLKID_ERR_PARAM; + + *name = strip_line(*cp); + + if ((*name)[0] != '<' || (*name)[1] == '/') + return 0; + + FIXME: finish this. +} +*/ + +/* + * Extract a tag from the line. + * + * Return 1 if a valid tag was found. + * Return 0 if no tag found. + * Return -ve error code. + */ +static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp) +{ + char *name; + char *value; + int ret; + + if (!cache || !dev) + return -BLKID_ERR_PARAM; + + if ((ret = parse_token(&name, &value, cp)) <= 0 /* && + (ret = parse_xml(&name, &value, cp)) <= 0 */) + return ret; + + /* Some tags are stored directly in the device struct */ + if (!strcmp(name, "DEVNO")) + dev->bid_devno = STRTOULL(value, 0, 0); + else if (!strcmp(name, "PRI")) + dev->bid_pri = strtol(value, 0, 0); + else if (!strcmp(name, "TIME")) + /* FIXME: need to parse a long long eventually */ + dev->bid_time = strtol(value, 0, 0); + else + ret = blkid_set_tag(dev, name, value, strlen(value)); + + DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value)); + + return ret < 0 ? ret : 1; +} + +/* + * Parse a single line of data, and return a newly allocated dev struct. + * Add the new device to the cache struct, if one was read. + * + * Lines are of the form /dev/foo + * + * Returns -ve value on error. + * Returns 0 otherwise. + * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL + * (e.g. comment lines, unknown XML content, etc). + */ +static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp) +{ + blkid_dev dev; + int ret; + + if (!cache || !dev_p) + return -BLKID_ERR_PARAM; + + *dev_p = NULL; + + DBG(DEBUG_READ, printf("line: %s\n", cp)); + + if ((ret = parse_dev(cache, dev_p, &cp)) <= 0) + return ret; + + dev = *dev_p; + + while ((ret = parse_tag(cache, dev, &cp)) > 0) { + ; + } + + if (dev->bid_type == NULL) { + DBG(DEBUG_READ, + printf("blkid: device %s has no TYPE\n",dev->bid_name)); + blkid_free_dev(dev); + } + + DBG(DEBUG_READ, blkid_debug_dump_dev(dev)); + + return ret; +} + +/* + * Parse the specified filename, and return the data in the supplied or + * a newly allocated cache struct. If the file doesn't exist, return a + * new empty cache struct. + */ +void blkid_read_cache(blkid_cache cache) +{ + FILE *file; + char buf[4096]; + int fd, lineno = 0; + struct stat st; + + if (!cache) + return; + + /* + * If the file doesn't exist, then we just return an empty + * struct so that the cache can be populated. + */ + if ((fd = open(cache->bic_filename, O_RDONLY)) < 0) + return; + if (fstat(fd, &st) < 0) + goto errout; + if ((st.st_mtime == cache->bic_ftime) || + (cache->bic_flags & BLKID_BIC_FL_CHANGED)) { + DBG(DEBUG_CACHE, printf("skipping re-read of %s\n", + cache->bic_filename)); + goto errout; + } + + DBG(DEBUG_CACHE, printf("reading cache file %s\n", + cache->bic_filename)); + + file = xfdopen_for_read(fd); + + while (fgets(buf, sizeof(buf), file)) { + blkid_dev dev; + unsigned int end; + + lineno++; + if (buf[0] == 0) + continue; + end = strlen(buf) - 1; + /* Continue reading next line if it ends with a backslash */ + while (buf[end] == '\\' && end < sizeof(buf) - 2 && + fgets(buf + end, sizeof(buf) - end, file)) { + end = strlen(buf) - 1; + lineno++; + } + + if (blkid_parse_line(cache, &dev, buf) < 0) { + DBG(DEBUG_READ, + printf("blkid: bad format on line %d\n", lineno)); + continue; + } + } + fclose(file); + + /* + * Initially we do not need to write out the cache file. + */ + cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; + cache->bic_ftime = st.st_mtime; + + return; +errout: + close(fd); +} + +#ifdef TEST_PROGRAM +static void debug_dump_dev(blkid_dev dev) +{ + struct list_head *p; + + if (!dev) { + printf(" dev: NULL\n"); + return; + } + + printf(" dev: name = %s\n", dev->bid_name); + printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno); + printf(" dev: TIME=\"%lu\"\n", dev->bid_time); + printf(" dev: PRI=\"%d\"\n", dev->bid_pri); + printf(" dev: flags = 0x%08X\n", dev->bid_flags); + + list_for_each(p, &dev->bid_tags) { + blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); + if (tag) + printf(" tag: %s=\"%s\"\n", tag->bit_name, + tag->bit_val); + else + printf(" tag: NULL\n"); + } + bb_putchar('\n'); +} + +int main(int argc, char**argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_debug_mask = DEBUG_ALL; + if (argc > 2) { + fprintf(stderr, "Usage: %s [filename]\n" + "Test parsing of the cache (filename)\n", argv[0]); + exit(1); + } + if ((ret = blkid_get_cache(&cache, argv[1])) < 0) + fprintf(stderr, "error %d reading cache file %s\n", ret, + argv[1] ? argv[1] : BLKID_CACHE_FILE); + + blkid_put_cache(cache); + + return ret; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/resolve.c b/e2fsprogs/old_e2fsprogs/blkid/resolve.c new file mode 100644 index 000000000..295ca61bf --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/resolve.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * resolve.c - resolve names and tags into specific devices + * + * Copyright (C) 2001, 2003 Theodore Ts'o. + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include "blkidP.h" +#include "probe.h" + +/* + * Find a tagname (e.g. LABEL or UUID) on a specific device. + */ +char *blkid_get_tag_value(blkid_cache cache, const char *tagname, + const char *devname) +{ + blkid_tag found; + blkid_dev dev; + blkid_cache c = cache; + char *ret = NULL; + + DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname)); + + if (!devname) + return NULL; + + if (!cache) { + if (blkid_get_cache(&c, NULL) < 0) + return NULL; + } + + if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) && + (found = blkid_find_tag_dev(dev, tagname))) + ret = blkid_strdup(found->bit_val); + + if (!cache) + blkid_put_cache(c); + + return ret; +} + +/* + * Locate a device name from a token (NAME=value string), or (name, value) + * pair. In the case of a token, value is ignored. If the "token" is not + * of the form "NAME=value" and there is no value given, then it is assumed + * to be the actual devname and a copy is returned. + */ +char *blkid_get_devname(blkid_cache cache, const char *token, + const char *value) +{ + blkid_dev dev; + blkid_cache c = cache; + char *t = NULL, *v = NULL; + char *ret = NULL; + + if (!token) + return NULL; + + if (!cache) { + if (blkid_get_cache(&c, NULL) < 0) + return NULL; + } + + DBG(DEBUG_RESOLVE, + printf("looking for %s%s%s %s\n", token, value ? "=" : "", + value ? value : "", cache ? "in cache" : "from disk")); + + if (!value) { + if (!strchr(token, '=')) + return blkid_strdup(token); + blkid_parse_tag_string(token, &t, &v); + if (!t || !v) + goto errout; + token = t; + value = v; + } + + dev = blkid_find_dev_with_tag(c, token, value); + if (!dev) + goto errout; + + ret = blkid_strdup(blkid_dev_devname(dev)); + +errout: + free(t); + free(v); + if (!cache) { + blkid_put_cache(c); + } + return ret; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + char *value; + blkid_cache cache; + + blkid_debug_mask = DEBUG_ALL; + if (argc != 2 && argc != 3) { + fprintf(stderr, "Usage:\t%s tagname=value\n" + "\t%s tagname devname\n" + "Find which device holds a given token or\n" + "Find what the value of a tag is in a device\n", + argv[0], argv[0]); + exit(1); + } + if (blkid_get_cache(&cache, bb_dev_null) < 0) { + fprintf(stderr, "Can't get blkid cache\n"); + exit(1); + } + + if (argv[2]) { + value = blkid_get_tag_value(cache, argv[1], argv[2]); + printf("%s has tag %s=%s\n", argv[2], argv[1], + value ? value : ""); + } else { + value = blkid_get_devname(cache, argv[1], NULL); + printf("%s has tag %s\n", value ? value : "", argv[1]); + } + blkid_put_cache(cache); + return value ? 0 : 1; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/save.c b/e2fsprogs/old_e2fsprogs/blkid/save.c new file mode 100644 index 000000000..e60cca445 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/save.c @@ -0,0 +1,189 @@ +/* vi: set sw=4 ts=4: */ +/* + * save.c - write the cache struct to disk + * + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#include "blkidP.h" + +static int save_dev(blkid_dev dev, FILE *file) +{ + struct list_head *p; + + if (!dev || dev->bid_name[0] != '/') + return 0; + + DBG(DEBUG_SAVE, + printf("device %s, type %s\n", dev->bid_name, dev->bid_type)); + + fprintf(file, + "bid_devno, dev->bid_time); + if (dev->bid_pri) + fprintf(file, " PRI=\"%d\"", dev->bid_pri); + list_for_each(p, &dev->bid_tags) { + blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); + fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val); + } + fprintf(file, ">%s\n", dev->bid_name); + + return 0; +} + +/* + * Write out the cache struct to the cache file on disk. + */ +int blkid_flush_cache(blkid_cache cache) +{ + struct list_head *p; + char *tmp = NULL; + const char *opened = NULL; + const char *filename; + FILE *file = NULL; + int fd, ret = 0; + struct stat st; + + if (!cache) + return -BLKID_ERR_PARAM; + + if (list_empty(&cache->bic_devs) || + !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) { + DBG(DEBUG_SAVE, printf("skipping cache file write\n")); + return 0; + } + + filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE; + + /* If we can't write to the cache file, then don't even try */ + if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) || + (ret == 0 && access(filename, W_OK) < 0)) { + DBG(DEBUG_SAVE, + printf("can't write to cache file %s\n", filename)); + return 0; + } + + /* + * Try and create a temporary file in the same directory so + * that in case of error we don't overwrite the cache file. + * If the cache file doesn't yet exist, it isn't a regular + * file (e.g. /dev/null or a socket), or we couldn't create + * a temporary file then we open it directly. + */ + if (ret == 0 && S_ISREG(st.st_mode)) { + tmp = xmalloc(strlen(filename) + 8); + sprintf(tmp, "%s-XXXXXX", filename); + fd = mkstemp(tmp); + if (fd >= 0) { + file = xfdopen_for_write(fd); + opened = tmp; + } + fchmod(fd, 0644); + } + + if (!file) { + file = fopen_for_write(filename); + opened = filename; + } + + DBG(DEBUG_SAVE, + printf("writing cache file %s (really %s)\n", + filename, opened)); + + if (!file) { + ret = errno; + goto errout; + } + + list_for_each(p, &cache->bic_devs) { + blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); + if (!dev->bid_type) + continue; + if ((ret = save_dev(dev, file)) < 0) + break; + } + + if (ret >= 0) { + cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; + ret = 1; + } + + fclose(file); + if (opened != filename) { + if (ret < 0) { + unlink(opened); + DBG(DEBUG_SAVE, + printf("unlinked temp cache %s\n", opened)); + } else { + char *backup; + + backup = xmalloc(strlen(filename) + 5); + sprintf(backup, "%s.old", filename); + unlink(backup); + link(filename, backup); + free(backup); + rename(opened, filename); + DBG(DEBUG_SAVE, + printf("moved temp cache %s\n", opened)); + } + } + +errout: + free(tmp); + return ret; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_debug_mask = DEBUG_ALL; + if (argc > 2) { + fprintf(stderr, "Usage: %s [filename]\n" + "Test loading/saving a cache (filename)\n", argv[0]); + exit(1); + } + + if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + if ((ret = blkid_probe_all(cache)) < 0) { + fprintf(stderr, "error (%d) probing devices\n", ret); + exit(1); + } + cache->bic_filename = blkid_strdup(argv[1]); + + if ((ret = blkid_flush_cache(cache)) < 0) { + fprintf(stderr, "error (%d) saving cache\n", ret); + exit(1); + } + + blkid_put_cache(cache); + + return ret; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/blkid/tag.c b/e2fsprogs/old_e2fsprogs/blkid/tag.c new file mode 100644 index 000000000..8337b46b6 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/blkid/tag.c @@ -0,0 +1,431 @@ +/* vi: set sw=4 ts=4: */ +/* + * tag.c - allocation/initialization/free routines for tag structs + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include +#include +#include + +#include "blkidP.h" + +static blkid_tag blkid_new_tag(void) +{ + blkid_tag tag; + + tag = xzalloc(sizeof(struct blkid_struct_tag)); + + INIT_LIST_HEAD(&tag->bit_tags); + INIT_LIST_HEAD(&tag->bit_names); + + return tag; +} + +#ifdef CONFIG_BLKID_DEBUG +void blkid_debug_dump_tag(blkid_tag tag) +{ + if (!tag) { + printf(" tag: NULL\n"); + return; + } + + printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); +} +#endif + +void blkid_free_tag(blkid_tag tag) +{ + if (!tag) + return; + + DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name, + tag->bit_val ? tag->bit_val : "(NULL)")); + DBG(DEBUG_TAG, blkid_debug_dump_tag(tag)); + + list_del(&tag->bit_tags); /* list of tags for this device */ + list_del(&tag->bit_names); /* list of tags with this type */ + + free(tag->bit_name); + free(tag->bit_val); + free(tag); +} + +/* + * Find the desired tag on a device. If value is NULL, then the + * first such tag is returned, otherwise return only exact tag if found. + */ +blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) +{ + struct list_head *p; + + if (!dev || !type) + return NULL; + + list_for_each(p, &dev->bid_tags) { + blkid_tag tmp = list_entry(p, struct blkid_struct_tag, + bit_tags); + + if (!strcmp(tmp->bit_name, type)) + return tmp; + } + return NULL; +} + +/* + * Find the desired tag type in the cache. + * We return the head tag for this tag type. + */ +static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) +{ + blkid_tag head = NULL, tmp; + struct list_head *p; + + if (!cache || !type) + return NULL; + + list_for_each(p, &cache->bic_tags) { + tmp = list_entry(p, struct blkid_struct_tag, bit_tags); + if (!strcmp(tmp->bit_name, type)) { + DBG(DEBUG_TAG, + printf(" found cache tag head %s\n", type)); + head = tmp; + break; + } + } + return head; +} + +/* + * Set a tag on an existing device. + * + * If value is NULL, then delete the tagsfrom the device. + */ +int blkid_set_tag(blkid_dev dev, const char *name, + const char *value, const int vlength) +{ + blkid_tag t = 0, head = 0; + char *val = NULL; + + if (!dev || !name) + return -BLKID_ERR_PARAM; + + if (!(val = blkid_strndup(value, vlength)) && value) + return -BLKID_ERR_MEM; + t = blkid_find_tag_dev(dev, name); + if (!value) { + blkid_free_tag(t); + } else if (t) { + if (!strcmp(t->bit_val, val)) { + /* Same thing, exit */ + free(val); + return 0; + } + free(t->bit_val); + t->bit_val = val; + } else { + /* Existing tag not present, add to device */ + if (!(t = blkid_new_tag())) + goto errout; + t->bit_name = blkid_strdup(name); + t->bit_val = val; + t->bit_dev = dev; + + list_add_tail(&t->bit_tags, &dev->bid_tags); + + if (dev->bid_cache) { + head = blkid_find_head_cache(dev->bid_cache, + t->bit_name); + if (!head) { + head = blkid_new_tag(); + if (!head) + goto errout; + + DBG(DEBUG_TAG, + printf(" creating new cache tag head %s\n", name)); + head->bit_name = blkid_strdup(name); + if (!head->bit_name) + goto errout; + list_add_tail(&head->bit_tags, + &dev->bid_cache->bic_tags); + } + list_add_tail(&t->bit_names, &head->bit_names); + } + } + + /* Link common tags directly to the device struct */ + if (!strcmp(name, "TYPE")) + dev->bid_type = val; + else if (!strcmp(name, "LABEL")) + dev->bid_label = val; + else if (!strcmp(name, "UUID")) + dev->bid_uuid = val; + + if (dev->bid_cache) + dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; + return 0; + +errout: + blkid_free_tag(t); + if (!t) + free(val); + blkid_free_tag(head); + return -BLKID_ERR_MEM; +} + + +/* + * Parse a "NAME=value" string. This is slightly different than + * parse_token, because that will end an unquoted value at a space, while + * this will assume that an unquoted value is the rest of the token (e.g. + * if we are passed an already quoted string from the command-line we don't + * have to both quote and escape quote so that the quotes make it to + * us). + * + * Returns 0 on success, and -1 on failure. + */ +int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) +{ + char *name, *value, *cp; + + DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token)); + + if (!token || !(cp = strchr(token, '='))) + return -1; + + name = blkid_strdup(token); + if (!name) + return -1; + value = name + (cp - token); + *value++ = '\0'; + if (*value == '"' || *value == '\'') { + char c = *value++; + if (!(cp = strrchr(value, c))) + goto errout; /* missing closing quote */ + *cp = '\0'; + } + value = blkid_strdup(value); + if (!value) + goto errout; + + *ret_type = name; + *ret_val = value; + + return 0; + +errout: + free(name); + return -1; +} + +/* + * Tag iteration routines for the public libblkid interface. + * + * These routines do not expose the list.h implementation, which are a + * contamination of the namespace, and which force us to reveal far, far + * too much of our internal implemenation. I'm not convinced I want + * to keep list.h in the long term, anyway. It's fine for kernel + * programming, but performance is not the #1 priority for this + * library, and I really don't like the tradeoff of type-safety for + * performance for this application. [tytso:20030125.2007EST] + */ + +/* + * This series of functions iterate over all tags in a device + */ +#define TAG_ITERATE_MAGIC 0x01a5284c + +struct blkid_struct_tag_iterate { + int magic; + blkid_dev dev; + struct list_head *p; +}; + +blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) +{ + blkid_tag_iterate iter; + + iter = xmalloc(sizeof(struct blkid_struct_tag_iterate)); + iter->magic = TAG_ITERATE_MAGIC; + iter->dev = dev; + iter->p = dev->bid_tags.next; + return iter; +} + +/* + * Return 0 on success, -1 on error + */ +extern int blkid_tag_next(blkid_tag_iterate iter, + const char **type, const char **value) +{ + blkid_tag tag; + + *type = 0; + *value = 0; + if (!iter || iter->magic != TAG_ITERATE_MAGIC || + iter->p == &iter->dev->bid_tags) + return -1; + tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); + *type = tag->bit_name; + *value = tag->bit_val; + iter->p = iter->p->next; + return 0; +} + +void blkid_tag_iterate_end(blkid_tag_iterate iter) +{ + if (!iter || iter->magic != TAG_ITERATE_MAGIC) + return; + iter->magic = 0; + free(iter); +} + +/* + * This function returns a device which matches a particular + * type/value pair. If there is more than one device that matches the + * search specification, it returns the one with the highest priority + * value. This allows us to give preference to EVMS or LVM devices. + * + * XXX there should also be an interface which uses an iterator so we + * can get all of the devices which match a type/value search parameter. + */ +extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, + const char *type, + const char *value) +{ + blkid_tag head; + blkid_dev dev; + int pri; + struct list_head *p; + + if (!cache || !type || !value) + return NULL; + + blkid_read_cache(cache); + + DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value)); + +try_again: + pri = -1; + dev = 0; + head = blkid_find_head_cache(cache, type); + + if (head) { + list_for_each(p, &head->bit_names) { + blkid_tag tmp = list_entry(p, struct blkid_struct_tag, + bit_names); + + if (!strcmp(tmp->bit_val, value) && + tmp->bit_dev->bid_pri > pri) { + dev = tmp->bit_dev; + pri = dev->bid_pri; + } + } + } + if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { + dev = blkid_verify(cache, dev); + if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)) + goto try_again; + } + + if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { + if (blkid_probe_all(cache) < 0) + return NULL; + goto try_again; + } + return dev; +} + +#ifdef TEST_PROGRAM +#ifdef HAVE_GETOPT_H +#include +#else +extern char *optarg; +extern int optind; +#endif + +void usage(char *prog) +{ + fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device " + "[type value]\n", + prog); + fprintf(stderr, "\tList all tags for a device and exit\n", prog); + exit(1); +} + +int main(int argc, char **argv) +{ + blkid_tag_iterate iter; + blkid_cache cache = NULL; + blkid_dev dev; + int c, ret, found; + int flags = BLKID_DEV_FIND; + char *tmp; + char *file = NULL; + char *devname = NULL; + char *search_type = NULL; + char *search_value = NULL; + const char *type, *value; + + while ((c = getopt (argc, argv, "m:f:")) != EOF) + switch (c) { + case 'f': + file = optarg; + break; + case 'm': + blkid_debug_mask = strtoul (optarg, &tmp, 0); + if (*tmp) { + fprintf(stderr, "Invalid debug mask: %d\n", + optarg); + exit(1); + } + break; + case '?': + usage(argv[0]); + } + if (argc > optind) + devname = argv[optind++]; + if (argc > optind) + search_type = argv[optind++]; + if (argc > optind) + search_value = argv[optind++]; + if (!devname || (argc != optind)) + usage(argv[0]); + + if ((ret = blkid_get_cache(&cache, file)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + + dev = blkid_get_dev(cache, devname, flags); + if (!dev) { + fprintf(stderr, "%s: cannot find device in blkid cache\n"); + exit(1); + } + if (search_type) { + found = blkid_dev_has_tag(dev, search_type, search_value); + printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev), + search_type, search_value ? search_value : "NULL", + found ? "FOUND" : "NOT FOUND"); + return !found; + } + printf("Device %s...\n", blkid_dev_devname(dev)); + + iter = blkid_tag_iterate_begin(dev); + while (blkid_tag_next(iter, &type, &value) == 0) { + printf("\tTag %s has value %s\n", type, value); + } + blkid_tag_iterate_end(iter); + + blkid_put_cache(cache); + return 0; +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/chattr.c b/e2fsprogs/old_e2fsprogs/chattr.c new file mode 100644 index 000000000..ae39d9224 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/chattr.c @@ -0,0 +1,220 @@ +/* vi: set sw=4 ts=4: */ +/* + * chattr.c - Change file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + * 93/11/13 - Replace stat() calls by lstat() to avoid loops + * 94/02/27 - Integrated in Ted's distribution + * 98/12/29 - Ignore symlinks when working recursively (G M Sipe) + * 98/12/29 - Display version info only when -V specified (G M Sipe) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ext2fs/ext2_fs.h" + +#ifdef __GNUC__ +# define EXT2FS_ATTR(x) __attribute__(x) +#else +# define EXT2FS_ATTR(x) +#endif + +#include "e2fsbb.h" +#include "e2p/e2p.h" + +#define OPT_ADD 1 +#define OPT_REM 2 +#define OPT_SET 4 +#define OPT_SET_VER 8 +static int flags; +static int recursive; + +static unsigned long version; + +static unsigned long af; +static unsigned long rf; +static unsigned long sf; + +struct flags_char { + unsigned long flag; + char optchar; +}; + +static const struct flags_char flags_array[] = { + { EXT2_NOATIME_FL, 'A' }, + { EXT2_SYNC_FL, 'S' }, + { EXT2_DIRSYNC_FL, 'D' }, + { EXT2_APPEND_FL, 'a' }, + { EXT2_COMPR_FL, 'c' }, + { EXT2_NODUMP_FL, 'd' }, + { EXT2_IMMUTABLE_FL, 'i' }, + { EXT3_JOURNAL_DATA_FL, 'j' }, + { EXT2_SECRM_FL, 's' }, + { EXT2_UNRM_FL, 'u' }, + { EXT2_NOTAIL_FL, 't' }, + { EXT2_TOPDIR_FL, 'T' }, + { 0, 0 } +}; + +static unsigned long get_flag(char c) +{ + const struct flags_char *fp; + for (fp = flags_array; fp->flag; fp++) + if (fp->optchar == c) + return fp->flag; + bb_show_usage(); + return 0; +} + +static int decode_arg(char *arg) +{ + unsigned long *fl; + char opt = *arg++; + + if (opt == '-') { + flags |= OPT_REM; + fl = &rf; + } else if (opt == '+') { + flags |= OPT_ADD; + fl = ⁡ + } else if (opt == '=') { + flags |= OPT_SET; + fl = &sf; + } else + return EOF; + + for (; *arg; ++arg) + (*fl) |= get_flag(*arg); + + return 1; +} + +static int chattr_dir_proc(const char *, struct dirent *, void *); + +static void change_attributes(const char * name) +{ + unsigned long fsflags; + struct stat st; + + if (lstat(name, &st) == -1) { + bb_error_msg("stat %s failed", name); + return; + } + if (S_ISLNK(st.st_mode) && recursive) + return; + + /* Don't try to open device files, fifos etc. We probably + * ought to display an error if the file was explicitly given + * on the command line (whether or not recursive was + * requested). */ + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) + return; + + if (flags & OPT_SET_VER) + if (fsetversion(name, version) == -1) + bb_error_msg("setting version on %s", name); + + if (flags & OPT_SET) { + fsflags = sf; + } else { + if (fgetflags(name, &fsflags) == -1) { + bb_error_msg("reading flags on %s", name); + goto skip_setflags; + } + if (flags & OPT_REM) + fsflags &= ~rf; + if (flags & OPT_ADD) + fsflags |= af; + if (!S_ISDIR(st.st_mode)) + fsflags &= ~EXT2_DIRSYNC_FL; + } + if (fsetflags(name, fsflags) == -1) + bb_error_msg("setting flags on %s", name); + +skip_setflags: + if (S_ISDIR(st.st_mode) && recursive) + iterate_on_dir(name, chattr_dir_proc, NULL); +} + +static int chattr_dir_proc(const char *dir_name, struct dirent *de, + void *private EXT2FS_ATTR((unused))) +{ + /*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/ + if (de->d_name[0] == '.' + && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])) + ) { + char *path = concat_subpath_file(dir_name, de->d_name); + if (path) { + change_attributes(path); + free(path); + } + } + return 0; +} + +int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chattr_main(int argc, char **argv) +{ + int i; + char *arg; + + /* parse the args */ + for (i = 1; i < argc; ++i) { + arg = argv[i]; + + /* take care of -R and -v */ + if (arg[0] == '-') { + if (arg[1] == 'R' && arg[2] == '\0') { + recursive = 1; + continue; + } else if (arg[1] == 'v' && arg[2] == '\0') { + char *tmp; + ++i; + if (i >= argc) + bb_show_usage(); + version = strtol(argv[i], &tmp, 0); + if (*tmp) + bb_error_msg_and_die("bad version '%s'", arg); + flags |= OPT_SET_VER; + continue; + } + } + + if (decode_arg(arg) == EOF) + break; + } + + /* run sanity checks on all the arguments given us */ + if (i >= argc) + bb_show_usage(); + if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM))) + bb_error_msg_and_die("= is incompatible with - and +"); + if ((rf & af) != 0) + bb_error_msg_and_die("Can't set and unset a flag"); + if (!flags) + bb_error_msg_and_die("Must use '-v', =, - or +"); + + /* now run chattr on all the files passed to us */ + while (i < argc) + change_attributes(argv[i++]); + + return EXIT_SUCCESS; +} diff --git a/e2fsprogs/old_e2fsprogs/e2fsbb.h b/e2fsprogs/old_e2fsprogs/e2fsbb.h new file mode 100644 index 000000000..d31c31955 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2fsbb.h @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * File: e2fsbb.h + * + * Redefine a bunch of e2fsprogs stuff to use busybox routines + * instead. This makes upgrade between e2fsprogs versions easy. + */ + +#ifndef E2FSBB_H +#define E2FSBB_H 1 + +#include "libbb.h" + +/* version we've last synced against */ +#define E2FSPROGS_VERSION "1.38" +#define E2FSPROGS_DATE "30-Jun-2005" + +typedef long errcode_t; +#define ERRCODE_RANGE 8 +#define error_message(code) strerror((int) (code & ((1< + * Free Software License: + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * linux/fs/recovery and linux/fs/revoke + * Written by Stephen C. Tweedie , 1999 + * + * Copyright 1999-2000 Red Hat Software --- All Rights Reserved + * + * Journal recovery routines for the generic filesystem journaling code; + * part of the ext2fs journaling system. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* +//usage:#define e2fsck_trivial_usage +//usage: "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] " +//usage: "[-I inode_buffer_blocks] [-P process_inode_size] " +//usage: "[-l|-L bad_blocks_file] [-C fd] [-j external_journal] " +//usage: "[-E extended-options] device" +//usage:#define e2fsck_full_usage "\n\n" +//usage: "Check ext2/ext3 file system\n" +//usage: "\n -p Automatic repair (no questions)" +//usage: "\n -n Make no changes to the filesystem" +//usage: "\n -y Assume 'yes' to all questions" +//usage: "\n -c Check for bad blocks and add them to the badblock list" +//usage: "\n -f Force checking even if filesystem is marked clean" +//usage: "\n -v Verbose" +//usage: "\n -b superblock Use alternative superblock" +//usage: "\n -B blocksize Force blocksize when looking for superblock" +//usage: "\n -j journal Set location of the external journal" +//usage: "\n -l file Add to badblocks list" +//usage: "\n -L file Set badblocks list" +*/ + +#include "e2fsck.h" /*Put all of our defines here to clean things up*/ + +#define _(x) x +#define N_(x) x + +/* + * Procedure declarations + */ + +static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf); + +/* pass1.c */ +static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool); + +/* pass2.c */ +static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, + ext2_ino_t ino, char *buf); + +/* pass3.c */ +static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode); +static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir, + int num, int gauranteed_size); +static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix); +static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, + int adj); + +/* rehash.c */ +static void e2fsck_rehash_directories(e2fsck_t ctx); + +/* util.c */ +static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, + const char *description); +static int ask(e2fsck_t ctx, const char * string, int def); +static void e2fsck_read_bitmaps(e2fsck_t ctx); +static void preenhalt(e2fsck_t ctx); +static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino, + struct ext2_inode * inode, const char * proc); +static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino, + struct ext2_inode * inode, const char * proc); +static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, + const char *name, io_manager manager); + +/* unix.c */ +static void e2fsck_clear_progbar(e2fsck_t ctx); +static int e2fsck_simple_progress(e2fsck_t ctx, const char *label, + float percent, unsigned int dpynum); + + +/* + * problem.h --- e2fsck problem error codes + */ + +typedef __u32 problem_t; + +struct problem_context { + errcode_t errcode; + ext2_ino_t ino, ino2, dir; + struct ext2_inode *inode; + struct ext2_dir_entry *dirent; + blk_t blk, blk2; + e2_blkcnt_t blkcount; + int group; + __u64 num; + const char *str; +}; + + +/* + * Function declarations + */ +static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx); +static int end_problem_latch(e2fsck_t ctx, int mask); +static int set_latch_flags(int mask, int setflags, int clearflags); +static void clear_problem_context(struct problem_context *ctx); + +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz + * kazlib_1_20 + */ + +#ifndef DICT_H +#define DICT_H + +/* + * Blurb for inclusion into C++ translation units + */ + +typedef unsigned long dictcount_t; +#define DICTCOUNT_T_MAX ULONG_MAX + +/* + * The dictionary is implemented as a red-black tree + */ + +typedef enum { dnode_red, dnode_black } dnode_color_t; + +typedef struct dnode_t { + struct dnode_t *dict_left; + struct dnode_t *dict_right; + struct dnode_t *dict_parent; + dnode_color_t dict_color; + const void *dict_key; + void *dict_data; +} dnode_t; + +typedef int (*dict_comp_t)(const void *, const void *); +typedef void (*dnode_free_t)(dnode_t *); + +typedef struct dict_t { + dnode_t dict_nilnode; + dictcount_t dict_nodecount; + dictcount_t dict_maxcount; + dict_comp_t dict_compare; + dnode_free_t dict_freenode; + int dict_dupes; +} dict_t; + +typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); + +typedef struct dict_load_t { + dict_t *dict_dictptr; + dnode_t dict_nilnode; +} dict_load_t; + +#define dict_count(D) ((D)->dict_nodecount) +#define dnode_get(N) ((N)->dict_data) +#define dnode_getkey(N) ((N)->dict_key) + +#endif + +/* + * Compatibility header file for e2fsck which should be included + * instead of linux/jfs.h + * + * Copyright (C) 2000 Stephen C. Tweedie + */ + +/* + * Pull in the definition of the e2fsck context structure + */ + +struct buffer_head { + char b_data[8192]; + e2fsck_t b_ctx; + io_channel b_io; + int b_size; + blk_t b_blocknr; + int b_dirty; + int b_uptodate; + int b_err; +}; + + +#define K_DEV_FS 1 +#define K_DEV_JOURNAL 2 + +#define lock_buffer(bh) do {} while (0) +#define unlock_buffer(bh) do {} while (0) +#define buffer_req(bh) 1 +#define do_readahead(journal, start) do {} while (0) + +static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */ + +typedef struct { + int object_length; +} kmem_cache_t; + +#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length) + +/* + * We use the standard libext2fs portability tricks for inline + * functions. + */ + +static kmem_cache_t * do_cache_create(int len) +{ + kmem_cache_t *new_cache; + + new_cache = xmalloc(sizeof(*new_cache)); + new_cache->object_length = len; + return new_cache; +} + +static void do_cache_destroy(kmem_cache_t *cache) +{ + free(cache); +} + + +/* + * Dictionary Abstract Data Type + */ + + +/* + * These macros provide short convenient names for structure members, + * which are embellished with dict_ prefixes so that they are + * properly confined to the documented namespace. It's legal for a + * program which uses dict to define, for instance, a macro called ``parent''. + * Such a macro would interfere with the dnode_t struct definition. + * In general, highly portable and reusable C modules which expose their + * structures need to confine structure member names to well-defined spaces. + * The resulting identifiers aren't necessarily convenient to use, nor + * readable, in the implementation, however! + */ + +#define left dict_left +#define right dict_right +#define parent dict_parent +#define color dict_color +#define key dict_key +#define data dict_data + +#define nilnode dict_nilnode +#define maxcount dict_maxcount +#define compare dict_compare +#define dupes dict_dupes + +#define dict_root(D) ((D)->nilnode.left) +#define dict_nil(D) (&(D)->nilnode) + +static void dnode_free(dnode_t *node); + +/* + * Perform a ``left rotation'' adjustment on the tree. The given node P and + * its right child C are rearranged so that the P instead becomes the left + * child of C. The left subtree of C is inherited as the new right subtree + * for P. The ordering of the keys within the tree is thus preserved. + */ + +static void rotate_left(dnode_t *upper) +{ + dnode_t *lower, *lowleft, *upparent; + + lower = upper->right; + upper->right = lowleft = lower->left; + lowleft->parent = upper; + + lower->parent = upparent = upper->parent; + + /* don't need to check for root node here because root->parent is + the sentinel nil node, and root->parent->left points back to root */ + + if (upper == upparent->left) { + upparent->left = lower; + } else { + assert (upper == upparent->right); + upparent->right = lower; + } + + lower->left = upper; + upper->parent = lower; +} + +/* + * This operation is the ``mirror'' image of rotate_left. It is + * the same procedure, but with left and right interchanged. + */ + +static void rotate_right(dnode_t *upper) +{ + dnode_t *lower, *lowright, *upparent; + + lower = upper->left; + upper->left = lowright = lower->right; + lowright->parent = upper; + + lower->parent = upparent = upper->parent; + + if (upper == upparent->right) { + upparent->right = lower; + } else { + assert (upper == upparent->left); + upparent->left = lower; + } + + lower->right = upper; + upper->parent = lower; +} + +/* + * Do a postorder traversal of the tree rooted at the specified + * node and free everything under it. Used by dict_free(). + */ + +static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) +{ + if (node == nil) + return; + free_nodes(dict, node->left, nil); + free_nodes(dict, node->right, nil); + dict->dict_freenode(node); +} + +/* + * Verify that the tree contains the given node. This is done by + * traversing all of the nodes and comparing their pointers to the + * given pointer. Returns 1 if the node is found, otherwise + * returns zero. It is intended for debugging purposes. + */ + +static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) +{ + if (root != nil) { + return root == node + || verify_dict_has_node(nil, root->left, node) + || verify_dict_has_node(nil, root->right, node); + } + return 0; +} + + +/* + * Select a different set of node allocator routines. + */ + +static void dict_set_allocator(dict_t *dict, dnode_free_t fr) +{ + assert(dict_count(dict) == 0); + dict->dict_freenode = fr; +} + +/* + * Free all the nodes in the dictionary by using the dictionary's + * installed free routine. The dictionary is emptied. + */ + +static void dict_free_nodes(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict); + free_nodes(dict, root, nil); + dict->dict_nodecount = 0; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; +} + +/* + * Initialize a user-supplied dictionary object. + */ + +static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) +{ + dict->compare = comp; + dict->dict_freenode = dnode_free; + dict->dict_nodecount = 0; + dict->maxcount = maxcount; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + dict->nilnode.color = dnode_black; + dict->dupes = 0; + return dict; +} + +/* + * Locate a node in the dictionary having the given key. + * If the node is not found, a null a pointer is returned (rather than + * a pointer that dictionary's nil sentinel node), otherwise a pointer to the + * located node is returned. + */ + +static dnode_t *dict_lookup(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *saved; + int result; + + /* simple binary search adapted for trees that contain duplicate keys */ + + while (root != nil) { + result = dict->compare(key, root->key); + if (result < 0) + root = root->left; + else if (result > 0) + root = root->right; + else { + if (!dict->dupes) { /* no duplicates, return match */ + return root; + } else { /* could be dupes, find leftmost one */ + do { + saved = root; + root = root->left; + while (root != nil && dict->compare(key, root->key)) + root = root->right; + } while (root != nil); + return saved; + } + } + } + + return NULL; +} + +/* + * Insert a node into the dictionary. The node should have been + * initialized with a data field. All other fields are ignored. + * The behavior is undefined if the user attempts to insert into + * a dictionary that is already full (for which the dict_isfull() + * function returns true). + */ + +static void dict_insert(dict_t *dict, dnode_t *node, const void *key) +{ + dnode_t *where = dict_root(dict), *nil = dict_nil(dict); + dnode_t *parent = nil, *uncle, *grandpa; + int result = -1; + + node->key = key; + + /* basic binary tree insert */ + + while (where != nil) { + parent = where; + result = dict->compare(key, where->key); + /* trap attempts at duplicate key insertion unless it's explicitly allowed */ + assert(dict->dupes || result != 0); + if (result < 0) + where = where->left; + else + where = where->right; + } + + assert(where == nil); + + if (result < 0) + parent->left = node; + else + parent->right = node; + + node->parent = parent; + node->left = nil; + node->right = nil; + + dict->dict_nodecount++; + + /* red black adjustments */ + + node->color = dnode_red; + + while (parent->color == dnode_red) { + grandpa = parent->parent; + if (parent == grandpa->left) { + uncle = grandpa->right; + if (uncle->color == dnode_red) { /* red parent, red uncle */ + parent->color = dnode_black; + uncle->color = dnode_black; + grandpa->color = dnode_red; + node = grandpa; + parent = grandpa->parent; + } else { /* red parent, black uncle */ + if (node == parent->right) { + rotate_left(parent); + parent = node; + assert (grandpa == parent->parent); + /* rotation between parent and child preserves grandpa */ + } + parent->color = dnode_black; + grandpa->color = dnode_red; + rotate_right(grandpa); + break; + } + } else { /* symmetric cases: parent == parent->parent->right */ + uncle = grandpa->left; + if (uncle->color == dnode_red) { + parent->color = dnode_black; + uncle->color = dnode_black; + grandpa->color = dnode_red; + node = grandpa; + parent = grandpa->parent; + } else { + if (node == parent->left) { + rotate_right(parent); + parent = node; + assert (grandpa == parent->parent); + } + parent->color = dnode_black; + grandpa->color = dnode_red; + rotate_left(grandpa); + break; + } + } + } + + dict_root(dict)->color = dnode_black; +} + +/* + * Allocate a node using the dictionary's allocator routine, give it + * the data item. + */ + +static dnode_t *dnode_init(dnode_t *dnode, void *data) +{ + dnode->data = data; + dnode->parent = NULL; + dnode->left = NULL; + dnode->right = NULL; + return dnode; +} + +static int dict_alloc_insert(dict_t *dict, const void *key, void *data) +{ + dnode_t *node = xmalloc(sizeof(dnode_t)); + + dnode_init(node, data); + dict_insert(dict, node, key); + return 1; +} + +/* + * Return the node with the lowest (leftmost) key. If the dictionary is empty + * (that is, dict_isempty(dict) returns 1) a null pointer is returned. + */ + +static dnode_t *dict_first(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; + + if (root != nil) + while ((left = root->left) != nil) + root = left; + + return (root == nil) ? NULL : root; +} + +/* + * Return the given node's successor node---the node which has the + * next key in the left to right ordering. If the node has + * no successor, a null pointer is returned rather than a pointer to + * the nil node. + */ + +static dnode_t *dict_next(dict_t *dict, dnode_t *curr) +{ + dnode_t *nil = dict_nil(dict), *parent, *left; + + if (curr->right != nil) { + curr = curr->right; + while ((left = curr->left) != nil) + curr = left; + return curr; + } + + parent = curr->parent; + + while (parent != nil && curr == parent->right) { + curr = parent; + parent = curr->parent; + } + + return (parent == nil) ? NULL : parent; +} + + +static void dnode_free(dnode_t *node) +{ + free(node); +} + + +#undef left +#undef right +#undef parent +#undef color +#undef key +#undef data + +#undef nilnode +#undef maxcount +#undef compare +#undef dupes + + +/* + * dirinfo.c --- maintains the directory information table for e2fsck. + */ + +/* + * This subroutine is called during pass1 to create a directory info + * entry. During pass1, the passed-in parent is 0; it will get filled + * in during pass2. + */ +static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent) +{ + struct dir_info *dir; + int i, j; + ext2_ino_t num_dirs; + errcode_t retval; + unsigned long old_size; + + if (!ctx->dir_info) { + ctx->dir_info_count = 0; + retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs); + if (retval) + num_dirs = 1024; /* Guess */ + ctx->dir_info_size = num_dirs + 10; + ctx->dir_info = (struct dir_info *) + e2fsck_allocate_memory(ctx, ctx->dir_info_size + * sizeof (struct dir_info), + "directory map"); + } + + if (ctx->dir_info_count >= ctx->dir_info_size) { + old_size = ctx->dir_info_size * sizeof(struct dir_info); + ctx->dir_info_size += 10; + retval = ext2fs_resize_mem(old_size, ctx->dir_info_size * + sizeof(struct dir_info), + &ctx->dir_info); + if (retval) { + ctx->dir_info_size -= 10; + return; + } + } + + /* + * Normally, add_dir_info is called with each inode in + * sequential order; but once in a while (like when pass 3 + * needs to recreate the root directory or lost+found + * directory) it is called out of order. In those cases, we + * need to move the dir_info entries down to make room, since + * the dir_info array needs to be sorted by inode number for + * get_dir_info()'s sake. + */ + if (ctx->dir_info_count && + ctx->dir_info[ctx->dir_info_count-1].ino >= ino) { + for (i = ctx->dir_info_count-1; i > 0; i--) + if (ctx->dir_info[i-1].ino < ino) + break; + dir = &ctx->dir_info[i]; + if (dir->ino != ino) + for (j = ctx->dir_info_count++; j > i; j--) + ctx->dir_info[j] = ctx->dir_info[j-1]; + } else + dir = &ctx->dir_info[ctx->dir_info_count++]; + + dir->ino = ino; + dir->dotdot = parent; + dir->parent = parent; +} + +/* + * get_dir_info() --- given an inode number, try to find the directory + * information entry for it. + */ +static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino) +{ + int low, high, mid; + + low = 0; + high = ctx->dir_info_count-1; + if (!ctx->dir_info) + return 0; + if (ino == ctx->dir_info[low].ino) + return &ctx->dir_info[low]; + if (ino == ctx->dir_info[high].ino) + return &ctx->dir_info[high]; + + while (low < high) { + mid = (low+high)/2; + if (mid == low || mid == high) + break; + if (ino == ctx->dir_info[mid].ino) + return &ctx->dir_info[mid]; + if (ino < ctx->dir_info[mid].ino) + high = mid; + else + low = mid; + } + return 0; +} + +/* + * Free the dir_info structure when it isn't needed any more. + */ +static void e2fsck_free_dir_info(e2fsck_t ctx) +{ + ext2fs_free_mem(&ctx->dir_info); + ctx->dir_info_size = 0; + ctx->dir_info_count = 0; +} + +/* + * Return the count of number of directories in the dir_info structure + */ +static int e2fsck_get_num_dirinfo(e2fsck_t ctx) +{ + return ctx->dir_info_count; +} + +/* + * A simple interator function + */ +static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control) +{ + if (*control >= ctx->dir_info_count) + return 0; + + return ctx->dir_info + (*control)++; +} + +/* + * dirinfo.c --- maintains the directory information table for e2fsck. + * + */ + +#ifdef ENABLE_HTREE + +/* + * This subroutine is called during pass1 to create a directory info + * entry. During pass1, the passed-in parent is 0; it will get filled + * in during pass2. + */ +static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks) +{ + struct dx_dir_info *dir; + int i, j; + errcode_t retval; + unsigned long old_size; + + if (!ctx->dx_dir_info) { + ctx->dx_dir_info_count = 0; + ctx->dx_dir_info_size = 100; /* Guess */ + ctx->dx_dir_info = (struct dx_dir_info *) + e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size + * sizeof (struct dx_dir_info), + "directory map"); + } + + if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) { + old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info); + ctx->dx_dir_info_size += 10; + retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size * + sizeof(struct dx_dir_info), + &ctx->dx_dir_info); + if (retval) { + ctx->dx_dir_info_size -= 10; + return; + } + } + + /* + * Normally, add_dx_dir_info is called with each inode in + * sequential order; but once in a while (like when pass 3 + * needs to recreate the root directory or lost+found + * directory) it is called out of order. In those cases, we + * need to move the dx_dir_info entries down to make room, since + * the dx_dir_info array needs to be sorted by inode number for + * get_dx_dir_info()'s sake. + */ + if (ctx->dx_dir_info_count && + ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) { + for (i = ctx->dx_dir_info_count-1; i > 0; i--) + if (ctx->dx_dir_info[i-1].ino < ino) + break; + dir = &ctx->dx_dir_info[i]; + if (dir->ino != ino) + for (j = ctx->dx_dir_info_count++; j > i; j--) + ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1]; + } else + dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++]; + + dir->ino = ino; + dir->numblocks = num_blocks; + dir->hashversion = 0; + dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks + * sizeof (struct dx_dirblock_info), + "dx_block info array"); +} + +/* + * get_dx_dir_info() --- given an inode number, try to find the directory + * information entry for it. + */ +static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino) +{ + int low, high, mid; + + low = 0; + high = ctx->dx_dir_info_count-1; + if (!ctx->dx_dir_info) + return 0; + if (ino == ctx->dx_dir_info[low].ino) + return &ctx->dx_dir_info[low]; + if (ino == ctx->dx_dir_info[high].ino) + return &ctx->dx_dir_info[high]; + + while (low < high) { + mid = (low+high)/2; + if (mid == low || mid == high) + break; + if (ino == ctx->dx_dir_info[mid].ino) + return &ctx->dx_dir_info[mid]; + if (ino < ctx->dx_dir_info[mid].ino) + high = mid; + else + low = mid; + } + return 0; +} + +/* + * Free the dx_dir_info structure when it isn't needed any more. + */ +static void e2fsck_free_dx_dir_info(e2fsck_t ctx) +{ + int i; + struct dx_dir_info *dir; + + if (ctx->dx_dir_info) { + dir = ctx->dx_dir_info; + for (i=0; i < ctx->dx_dir_info_count; i++) { + ext2fs_free_mem(&dir->dx_block); + } + ext2fs_free_mem(&ctx->dx_dir_info); + } + ctx->dx_dir_info_size = 0; + ctx->dx_dir_info_count = 0; +} + +/* + * A simple interator function + */ +static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control) +{ + if (*control >= ctx->dx_dir_info_count) + return 0; + + return ctx->dx_dir_info + (*control)++; +} + +#endif /* ENABLE_HTREE */ +/* + * e2fsck.c - a consistency checker for the new extended file system. + * + */ + +/* + * This function allocates an e2fsck context + */ +static errcode_t e2fsck_allocate_context(e2fsck_t *ret) +{ + e2fsck_t context; + errcode_t retval; + + retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context); + if (retval) + return retval; + + memset(context, 0, sizeof(struct e2fsck_struct)); + + context->process_inode_size = 256; + context->ext_attr_ver = 2; + + *ret = context; + return 0; +} + +struct ea_refcount_el { + blk_t ea_blk; + int ea_count; +}; + +struct ea_refcount { + blk_t count; + blk_t size; + blk_t cursor; + struct ea_refcount_el *list; +}; + +static void ea_refcount_free(ext2_refcount_t refcount) +{ + if (!refcount) + return; + + ext2fs_free_mem(&refcount->list); + ext2fs_free_mem(&refcount); +} + +/* + * This function resets an e2fsck context; it is called when e2fsck + * needs to be restarted. + */ +static errcode_t e2fsck_reset_context(e2fsck_t ctx) +{ + ctx->flags = 0; + ctx->lost_and_found = 0; + ctx->bad_lost_and_found = 0; + ext2fs_free_inode_bitmap(ctx->inode_used_map); + ctx->inode_used_map = 0; + ext2fs_free_inode_bitmap(ctx->inode_dir_map); + ctx->inode_dir_map = 0; + ext2fs_free_inode_bitmap(ctx->inode_reg_map); + ctx->inode_reg_map = 0; + ext2fs_free_block_bitmap(ctx->block_found_map); + ctx->block_found_map = 0; + ext2fs_free_icount(ctx->inode_link_info); + ctx->inode_link_info = 0; + if (ctx->journal_io) { + if (ctx->fs && ctx->fs->io != ctx->journal_io) + io_channel_close(ctx->journal_io); + ctx->journal_io = 0; + } + if (ctx->fs) { + ext2fs_free_dblist(ctx->fs->dblist); + ctx->fs->dblist = 0; + } + e2fsck_free_dir_info(ctx); +#ifdef ENABLE_HTREE + e2fsck_free_dx_dir_info(ctx); +#endif + ea_refcount_free(ctx->refcount); + ctx->refcount = 0; + ea_refcount_free(ctx->refcount_extra); + ctx->refcount_extra = 0; + ext2fs_free_block_bitmap(ctx->block_dup_map); + ctx->block_dup_map = 0; + ext2fs_free_block_bitmap(ctx->block_ea_map); + ctx->block_ea_map = 0; + ext2fs_free_inode_bitmap(ctx->inode_bad_map); + ctx->inode_bad_map = 0; + ext2fs_free_inode_bitmap(ctx->inode_imagic_map); + ctx->inode_imagic_map = 0; + ext2fs_u32_list_free(ctx->dirs_to_hash); + ctx->dirs_to_hash = 0; + + /* + * Clear the array of invalid meta-data flags + */ + ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag); + ext2fs_free_mem(&ctx->invalid_block_bitmap_flag); + ext2fs_free_mem(&ctx->invalid_inode_table_flag); + + /* Clear statistic counters */ + ctx->fs_directory_count = 0; + ctx->fs_regular_count = 0; + ctx->fs_blockdev_count = 0; + ctx->fs_chardev_count = 0; + ctx->fs_links_count = 0; + ctx->fs_symlinks_count = 0; + ctx->fs_fast_symlinks_count = 0; + ctx->fs_fifo_count = 0; + ctx->fs_total_count = 0; + ctx->fs_sockets_count = 0; + ctx->fs_ind_count = 0; + ctx->fs_dind_count = 0; + ctx->fs_tind_count = 0; + ctx->fs_fragmented = 0; + ctx->large_files = 0; + + /* Reset the superblock to the user's requested value */ + ctx->superblock = ctx->use_superblock; + + return 0; +} + +static void e2fsck_free_context(e2fsck_t ctx) +{ + if (!ctx) + return; + + e2fsck_reset_context(ctx); + if (ctx->blkid) + blkid_put_cache(ctx->blkid); + + ext2fs_free_mem(&ctx); +} + +/* + * ea_refcount.c + */ + +/* + * The strategy we use for keeping track of EA refcounts is as + * follows. We keep a sorted array of first EA blocks and its + * reference counts. Once the refcount has dropped to zero, it is + * removed from the array to save memory space. Once the EA block is + * checked, its bit is set in the block_ea_map bitmap. + */ + + +static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret) +{ + ext2_refcount_t refcount; + errcode_t retval; + size_t bytes; + + retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount); + if (retval) + return retval; + memset(refcount, 0, sizeof(struct ea_refcount)); + + if (!size) + size = 500; + refcount->size = size; + bytes = (size_t) (size * sizeof(struct ea_refcount_el)); +#ifdef DEBUG + printf("Refcount allocated %d entries, %d bytes.\n", + refcount->size, bytes); +#endif + retval = ext2fs_get_mem(bytes, &refcount->list); + if (retval) + goto errout; + memset(refcount->list, 0, bytes); + + refcount->count = 0; + refcount->cursor = 0; + + *ret = refcount; + return 0; + +errout: + ea_refcount_free(refcount); + return retval; +} + +/* + * collapse_refcount() --- go through the refcount array, and get rid + * of any count == zero entries + */ +static void refcount_collapse(ext2_refcount_t refcount) +{ + unsigned int i, j; + struct ea_refcount_el *list; + + list = refcount->list; + for (i = 0, j = 0; i < refcount->count; i++) { + if (list[i].ea_count) { + if (i != j) + list[j] = list[i]; + j++; + } + } +#if defined(DEBUG) || defined(TEST_PROGRAM) + printf("Refcount_collapse: size was %d, now %d\n", + refcount->count, j); +#endif + refcount->count = j; +} + + +/* + * insert_refcount_el() --- Insert a new entry into the sorted list at a + * specified position. + */ +static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount, + blk_t blk, int pos) +{ + struct ea_refcount_el *el; + errcode_t retval; + blk_t new_size = 0; + int num; + + if (refcount->count >= refcount->size) { + new_size = refcount->size + 100; +#ifdef DEBUG + printf("Reallocating refcount %d entries...\n", new_size); +#endif + retval = ext2fs_resize_mem((size_t) refcount->size * + sizeof(struct ea_refcount_el), + (size_t) new_size * + sizeof(struct ea_refcount_el), + &refcount->list); + if (retval) + return 0; + refcount->size = new_size; + } + num = (int) refcount->count - pos; + if (num < 0) + return 0; /* should never happen */ + if (num) { + memmove(&refcount->list[pos+1], &refcount->list[pos], + sizeof(struct ea_refcount_el) * num); + } + refcount->count++; + el = &refcount->list[pos]; + el->ea_count = 0; + el->ea_blk = blk; + return el; +} + + +/* + * get_refcount_el() --- given an block number, try to find refcount + * information in the sorted list. If the create flag is set, + * and we can't find an entry, create one in the sorted list. + */ +static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount, + blk_t blk, int create) +{ + float range; + int low, high, mid; + blk_t lowval, highval; + + if (!refcount || !refcount->list) + return 0; +retry: + low = 0; + high = (int) refcount->count-1; + if (create && ((refcount->count == 0) || + (blk > refcount->list[high].ea_blk))) { + if (refcount->count >= refcount->size) + refcount_collapse(refcount); + + return insert_refcount_el(refcount, blk, + (unsigned) refcount->count); + } + if (refcount->count == 0) + return 0; + + if (refcount->cursor >= refcount->count) + refcount->cursor = 0; + if (blk == refcount->list[refcount->cursor].ea_blk) + return &refcount->list[refcount->cursor++]; +#ifdef DEBUG + printf("Non-cursor get_refcount_el: %u\n", blk); +#endif + while (low <= high) { + if (low == high) + mid = low; + else { + /* Interpolate for efficiency */ + lowval = refcount->list[low].ea_blk; + highval = refcount->list[high].ea_blk; + + if (blk < lowval) + range = 0; + else if (blk > highval) + range = 1; + else + range = ((float) (blk - lowval)) / + (highval - lowval); + mid = low + ((int) (range * (high-low))); + } + + if (blk == refcount->list[mid].ea_blk) { + refcount->cursor = mid+1; + return &refcount->list[mid]; + } + if (blk < refcount->list[mid].ea_blk) + high = mid-1; + else + low = mid+1; + } + /* + * If we need to create a new entry, it should be right at + * low (where high will be left at low-1). + */ + if (create) { + if (refcount->count >= refcount->size) { + refcount_collapse(refcount); + if (refcount->count < refcount->size) + goto retry; + } + return insert_refcount_el(refcount, blk, low); + } + return 0; +} + +static errcode_t +ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret) +{ + struct ea_refcount_el *el; + + el = get_refcount_el(refcount, blk, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + el->ea_count++; + + if (ret) + *ret = el->ea_count; + return 0; +} + +static errcode_t +ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret) +{ + struct ea_refcount_el *el; + + el = get_refcount_el(refcount, blk, 0); + if (!el || el->ea_count == 0) + return EXT2_ET_INVALID_ARGUMENT; + + el->ea_count--; + + if (ret) + *ret = el->ea_count; + return 0; +} + +static errcode_t +ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count) +{ + struct ea_refcount_el *el; + + /* + * Get the refcount element + */ + el = get_refcount_el(refcount, blk, count ? 1 : 0); + if (!el) + return count ? EXT2_ET_NO_MEMORY : 0; + el->ea_count = count; + return 0; +} + +static inline void ea_refcount_intr_begin(ext2_refcount_t refcount) +{ + refcount->cursor = 0; +} + + +static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret) +{ + struct ea_refcount_el *list; + + while (1) { + if (refcount->cursor >= refcount->count) + return 0; + list = refcount->list; + if (list[refcount->cursor].ea_count) { + if (ret) + *ret = list[refcount->cursor].ea_count; + return list[refcount->cursor++].ea_blk; + } + refcount->cursor++; + } +} + + +/* + * ehandler.c --- handle bad block errors which come up during the + * course of an e2fsck session. + */ + + +static const char *operation; + +static errcode_t +e2fsck_handle_read_error(io_channel channel, unsigned long block, int count, + void *data, size_t size FSCK_ATTR((unused)), + int actual FSCK_ATTR((unused)), errcode_t error) +{ + int i; + char *p; + ext2_filsys fs = (ext2_filsys) channel->app_data; + e2fsck_t ctx; + + ctx = (e2fsck_t) fs->priv_data; + + /* + * If more than one block was read, try reading each block + * separately. We could use the actual bytes read to figure + * out where to start, but we don't bother. + */ + if (count > 1) { + p = (char *) data; + for (i=0; i < count; i++, p += channel->block_size, block++) { + error = io_channel_read_blk(channel, block, + 1, p); + if (error) + return error; + } + return 0; + } + if (operation) + printf(_("Error reading block %lu (%s) while %s. "), block, + error_message(error), operation); + else + printf(_("Error reading block %lu (%s). "), block, + error_message(error)); + preenhalt(ctx); + if (ask(ctx, _("Ignore error"), 1)) { + if (ask(ctx, _("Force rewrite"), 1)) + io_channel_write_blk(channel, block, 1, data); + return 0; + } + + return error; +} + +static errcode_t +e2fsck_handle_write_error(io_channel channel, unsigned long block, int count, + const void *data, size_t size FSCK_ATTR((unused)), + int actual FSCK_ATTR((unused)), errcode_t error) +{ + int i; + const char *p; + ext2_filsys fs = (ext2_filsys) channel->app_data; + e2fsck_t ctx; + + ctx = (e2fsck_t) fs->priv_data; + + /* + * If more than one block was written, try writing each block + * separately. We could use the actual bytes read to figure + * out where to start, but we don't bother. + */ + if (count > 1) { + p = (const char *) data; + for (i=0; i < count; i++, p += channel->block_size, block++) { + error = io_channel_write_blk(channel, block, + 1, p); + if (error) + return error; + } + return 0; + } + + if (operation) + printf(_("Error writing block %lu (%s) while %s. "), block, + error_message(error), operation); + else + printf(_("Error writing block %lu (%s). "), block, + error_message(error)); + preenhalt(ctx); + if (ask(ctx, _("Ignore error"), 1)) + return 0; + + return error; +} + +static const char *ehandler_operation(const char *op) +{ + const char *ret = operation; + + operation = op; + return ret; +} + +static void ehandler_init(io_channel channel) +{ + channel->read_error = e2fsck_handle_read_error; + channel->write_error = e2fsck_handle_write_error; +} + +/* + * journal.c --- code for handling the "ext3" journal + * + * Copyright (C) 2000 Andreas Dilger + * Copyright (C) 2000 Theodore Ts'o + * + * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie + * Copyright (C) 1999 Red Hat Software + * + * This file may be redistributed under the terms of the + * GNU General Public License version 2 or at your discretion + * any later version. + */ + +/* + * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths. + * This creates a larger static binary, and a smaller binary using + * shared libraries. It's also probably slightly less CPU-efficient, + * which is why it's not on by default. But, it's a good way of + * testing the functions in inode_io.c and fileio.c. + */ +#undef USE_INODE_IO + +/* Kernel compatibility functions for handling the journal. These allow us + * to use the recovery.c file virtually unchanged from the kernel, so we + * don't have to do much to keep kernel and user recovery in sync. + */ +static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys) +{ +#ifdef USE_INODE_IO + *phys = block; + return 0; +#else + struct inode *inode = journal->j_inode; + errcode_t retval; + blk_t pblk; + + if (!inode) { + *phys = block; + return 0; + } + + retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino, + &inode->i_ext2, NULL, 0, block, &pblk); + *phys = pblk; + return retval; +#endif +} + +static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize) +{ + struct buffer_head *bh; + + bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer"); + if (!bh) + return NULL; + + bh->b_ctx = kdev->k_ctx; + if (kdev->k_dev == K_DEV_FS) + bh->b_io = kdev->k_ctx->fs->io; + else + bh->b_io = kdev->k_ctx->journal_io; + bh->b_size = blocksize; + bh->b_blocknr = blocknr; + + return bh; +} + +static void sync_blockdev(kdev_t kdev) +{ + io_channel io; + + if (kdev->k_dev == K_DEV_FS) + io = kdev->k_ctx->fs->io; + else + io = kdev->k_ctx->journal_io; + + io_channel_flush(io); +} + +static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[]) +{ + int retval; + struct buffer_head *bh; + + for (; nr > 0; --nr) { + bh = *bhp++; + if (rw == READ && !bh->b_uptodate) { + retval = io_channel_read_blk(bh->b_io, + bh->b_blocknr, + 1, bh->b_data); + if (retval) { + bb_error_msg("while reading block %lu", + (unsigned long) bh->b_blocknr); + bh->b_err = retval; + continue; + } + bh->b_uptodate = 1; + } else if (rw == WRITE && bh->b_dirty) { + retval = io_channel_write_blk(bh->b_io, + bh->b_blocknr, + 1, bh->b_data); + if (retval) { + bb_error_msg("while writing block %lu", + (unsigned long) bh->b_blocknr); + bh->b_err = retval; + continue; + } + bh->b_dirty = 0; + bh->b_uptodate = 1; + } + } +} + +static void mark_buffer_dirty(struct buffer_head *bh) +{ + bh->b_dirty = 1; +} + +static inline void mark_buffer_clean(struct buffer_head * bh) +{ + bh->b_dirty = 0; +} + +static void brelse(struct buffer_head *bh) +{ + if (bh->b_dirty) + ll_rw_block(WRITE, 1, &bh); + ext2fs_free_mem(&bh); +} + +static int buffer_uptodate(struct buffer_head *bh) +{ + return bh->b_uptodate; +} + +static inline void mark_buffer_uptodate(struct buffer_head *bh, int val) +{ + bh->b_uptodate = val; +} + +static void wait_on_buffer(struct buffer_head *bh) +{ + if (!bh->b_uptodate) + ll_rw_block(READ, 1, &bh); +} + + +static void e2fsck_clear_recover(e2fsck_t ctx, int error) +{ + ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; + + /* if we had an error doing journal recovery, we need a full fsck */ + if (error) + ctx->fs->super->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(ctx->fs); +} + +static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct ext2_super_block jsuper; + struct problem_context pctx; + struct buffer_head *bh; + struct inode *j_inode = NULL; + struct kdev_s *dev_fs = NULL, *dev_journal; + const char *journal_name = NULL; + journal_t *journal = NULL; + errcode_t retval = 0; + io_manager io_ptr = 0; + unsigned long start = 0; + blk_t blk; + int ext_journal = 0; + int tried_backup_jnl = 0; + int i; + + clear_problem_context(&pctx); + + journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); + if (!journal) { + return EXT2_ET_NO_MEMORY; + } + + dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); + if (!dev_fs) { + retval = EXT2_ET_NO_MEMORY; + goto errout; + } + dev_journal = dev_fs+1; + + dev_fs->k_ctx = dev_journal->k_ctx = ctx; + dev_fs->k_dev = K_DEV_FS; + dev_journal->k_dev = K_DEV_JOURNAL; + + journal->j_dev = dev_journal; + journal->j_fs_dev = dev_fs; + journal->j_inode = NULL; + journal->j_blocksize = ctx->fs->blocksize; + + if (uuid_is_null(sb->s_journal_uuid)) { + if (!sb->s_journal_inum) + return EXT2_ET_BAD_INODE_NUM; + j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), + "journal inode"); + if (!j_inode) { + retval = EXT2_ET_NO_MEMORY; + goto errout; + } + + j_inode->i_ctx = ctx; + j_inode->i_ino = sb->s_journal_inum; + + if ((retval = ext2fs_read_inode(ctx->fs, + sb->s_journal_inum, + &j_inode->i_ext2))) { + try_backup_journal: + if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || + tried_backup_jnl) + goto errout; + memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); + memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, + EXT2_N_BLOCKS*4); + j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; + j_inode->i_ext2.i_links_count = 1; + j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; + tried_backup_jnl++; + } + if (!j_inode->i_ext2.i_links_count || + !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { + retval = EXT2_ET_NO_JOURNAL; + goto try_backup_journal; + } + if (j_inode->i_ext2.i_size / journal->j_blocksize < + JFS_MIN_JOURNAL_BLOCKS) { + retval = EXT2_ET_JOURNAL_TOO_SMALL; + goto try_backup_journal; + } + for (i=0; i < EXT2_N_BLOCKS; i++) { + blk = j_inode->i_ext2.i_block[i]; + if (!blk) { + if (i < EXT2_NDIR_BLOCKS) { + retval = EXT2_ET_JOURNAL_TOO_SMALL; + goto try_backup_journal; + } + continue; + } + if (blk < sb->s_first_data_block || + blk >= sb->s_blocks_count) { + retval = EXT2_ET_BAD_BLOCK_NUM; + goto try_backup_journal; + } + } + journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize; + +#ifdef USE_INODE_IO + retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, + &j_inode->i_ext2, + &journal_name); + if (retval) + goto errout; + + io_ptr = inode_io_manager; +#else + journal->j_inode = j_inode; + ctx->journal_io = ctx->fs->io; + if ((retval = journal_bmap(journal, 0, &start)) != 0) + goto errout; +#endif + } else { + ext_journal = 1; + if (!ctx->journal_name) { + char uuid[37]; + + uuid_unparse(sb->s_journal_uuid, uuid); + ctx->journal_name = blkid_get_devname(ctx->blkid, + "UUID", uuid); + if (!ctx->journal_name) + ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); + } + journal_name = ctx->journal_name; + + if (!journal_name) { + fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); + return EXT2_ET_LOAD_EXT_JOURNAL; + } + + io_ptr = unix_io_manager; + } + +#ifndef USE_INODE_IO + if (ext_journal) +#endif + retval = io_ptr->open(journal_name, IO_FLAG_RW, + &ctx->journal_io); + if (retval) + goto errout; + + io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); + + if (ext_journal) { + if (ctx->fs->blocksize == 1024) + start = 1; + bh = getblk(dev_journal, start, ctx->fs->blocksize); + if (!bh) { + retval = EXT2_ET_NO_MEMORY; + goto errout; + } + ll_rw_block(READ, 1, &bh); + if ((retval = bh->b_err) != 0) + goto errout; + memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024, + sizeof(jsuper)); + brelse(bh); +#if BB_BIG_ENDIAN + if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) + ext2fs_swap_super(&jsuper); +#endif + if (jsuper.s_magic != EXT2_SUPER_MAGIC || + !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); + retval = EXT2_ET_LOAD_EXT_JOURNAL; + goto errout; + } + /* Make sure the journal UUID is correct */ + if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, + sizeof(jsuper.s_uuid))) { + fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); + retval = EXT2_ET_LOAD_EXT_JOURNAL; + goto errout; + } + + journal->j_maxlen = jsuper.s_blocks_count; + start++; + } + + if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { + retval = EXT2_ET_NO_MEMORY; + goto errout; + } + + journal->j_sb_buffer = bh; + journal->j_superblock = (journal_superblock_t *)bh->b_data; + +#ifdef USE_INODE_IO + ext2fs_free_mem(&j_inode); +#endif + + *ret_journal = journal; + return 0; + +errout: + ext2fs_free_mem(&dev_fs); + ext2fs_free_mem(&j_inode); + ext2fs_free_mem(&journal); + return retval; +} + +static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx, + struct problem_context *pctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + int recover = ctx->fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER; + int has_journal = ctx->fs->super->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL; + + if (has_journal || sb->s_journal_inum) { + /* The journal inode is bogus, remove and force full fsck */ + pctx->ino = sb->s_journal_inum; + if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) { + if (has_journal && sb->s_journal_inum) + printf("*** ext3 journal has been deleted - " + "filesystem is now ext2 only ***\n\n"); + sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; + sb->s_journal_inum = 0; + ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */ + e2fsck_clear_recover(ctx, 1); + return 0; + } + return EXT2_ET_BAD_INODE_NUM; + } else if (recover) { + if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) { + e2fsck_clear_recover(ctx, 1); + return 0; + } + return EXT2_ET_UNSUPP_FEATURE; + } + return 0; +} + +#define V1_SB_SIZE 0x0024 +static void clear_v2_journal_fields(journal_t *journal) +{ + e2fsck_t ctx = journal->j_dev->k_ctx; + struct problem_context pctx; + + clear_problem_context(&pctx); + + if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx)) + return; + + memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0, + ctx->fs->blocksize-V1_SB_SIZE); + mark_buffer_dirty(journal->j_sb_buffer); +} + + +static errcode_t e2fsck_journal_load(journal_t *journal) +{ + e2fsck_t ctx = journal->j_dev->k_ctx; + journal_superblock_t *jsb; + struct buffer_head *jbh = journal->j_sb_buffer; + struct problem_context pctx; + + clear_problem_context(&pctx); + + ll_rw_block(READ, 1, &jbh); + if (jbh->b_err) { + bb_error_msg(_("reading journal superblock")); + return jbh->b_err; + } + + jsb = journal->j_superblock; + /* If we don't even have JFS_MAGIC, we probably have a wrong inode */ + if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER)) + return e2fsck_journal_fix_bad_inode(ctx, &pctx); + + switch (ntohl(jsb->s_header.h_blocktype)) { + case JFS_SUPERBLOCK_V1: + journal->j_format_version = 1; + if (jsb->s_feature_compat || + jsb->s_feature_incompat || + jsb->s_feature_ro_compat || + jsb->s_nr_users) + clear_v2_journal_fields(journal); + break; + + case JFS_SUPERBLOCK_V2: + journal->j_format_version = 2; + if (ntohl(jsb->s_nr_users) > 1 && + uuid_is_null(ctx->fs->super->s_journal_uuid)) + clear_v2_journal_fields(journal); + if (ntohl(jsb->s_nr_users) > 1) { + fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx); + return EXT2_ET_JOURNAL_UNSUPP_VERSION; + } + break; + + /* + * These should never appear in a journal super block, so if + * they do, the journal is badly corrupted. + */ + case JFS_DESCRIPTOR_BLOCK: + case JFS_COMMIT_BLOCK: + case JFS_REVOKE_BLOCK: + return EXT2_ET_CORRUPT_SUPERBLOCK; + + /* If we don't understand the superblock major type, but there + * is a magic number, then it is likely to be a new format we + * just don't understand, so leave it alone. */ + default: + return EXT2_ET_JOURNAL_UNSUPP_VERSION; + } + + if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES)) + return EXT2_ET_UNSUPP_FEATURE; + + if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES)) + return EXT2_ET_RO_UNSUPP_FEATURE; + + /* We have now checked whether we know enough about the journal + * format to be able to proceed safely, so any other checks that + * fail we should attempt to recover from. */ + if (jsb->s_blocksize != htonl(journal->j_blocksize)) { + bb_error_msg(_("%s: no valid journal superblock found"), + ctx->device_name); + return EXT2_ET_CORRUPT_SUPERBLOCK; + } + + if (ntohl(jsb->s_maxlen) < journal->j_maxlen) + journal->j_maxlen = ntohl(jsb->s_maxlen); + else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) { + bb_error_msg(_("%s: journal too short"), + ctx->device_name); + return EXT2_ET_CORRUPT_SUPERBLOCK; + } + + journal->j_tail_sequence = ntohl(jsb->s_sequence); + journal->j_transaction_sequence = journal->j_tail_sequence; + journal->j_tail = ntohl(jsb->s_start); + journal->j_first = ntohl(jsb->s_first); + journal->j_last = ntohl(jsb->s_maxlen); + + return 0; +} + +static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb, + journal_t *journal) +{ + char *p; + union { + uuid_t uuid; + __u32 val[4]; + } u; + __u32 new_seq = 0; + int i; + + /* Leave a valid existing V1 superblock signature alone. + * Anything unrecognizable we overwrite with a new V2 + * signature. */ + + if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) || + jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) { + jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); + } + + /* Zero out everything else beyond the superblock header */ + + p = ((char *) jsb) + sizeof(journal_header_t); + memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t)); + + jsb->s_blocksize = htonl(ctx->fs->blocksize); + jsb->s_maxlen = htonl(journal->j_maxlen); + jsb->s_first = htonl(1); + + /* Initialize the journal sequence number so that there is "no" + * chance we will find old "valid" transactions in the journal. + * This avoids the need to zero the whole journal (slow to do, + * and risky when we are just recovering the filesystem). + */ + uuid_generate(u.uuid); + for (i = 0; i < 4; i ++) + new_seq ^= u.val[i]; + jsb->s_sequence = htonl(new_seq); + + mark_buffer_dirty(journal->j_sb_buffer); + ll_rw_block(WRITE, 1, &journal->j_sb_buffer); +} + +static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, + journal_t *journal, + struct problem_context *pctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + int recover = ctx->fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER; + + if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) { + if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) { + e2fsck_journal_reset_super(ctx, journal->j_superblock, + journal); + journal->j_transaction_sequence = 1; + e2fsck_clear_recover(ctx, recover); + return 0; + } + return EXT2_ET_CORRUPT_SUPERBLOCK; + } else if (e2fsck_journal_fix_bad_inode(ctx, pctx)) + return EXT2_ET_CORRUPT_SUPERBLOCK; + + return 0; +} + +static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal, + int reset, int drop) +{ + journal_superblock_t *jsb; + + if (drop) + mark_buffer_clean(journal->j_sb_buffer); + else if (!(ctx->options & E2F_OPT_READONLY)) { + jsb = journal->j_superblock; + jsb->s_sequence = htonl(journal->j_transaction_sequence); + if (reset) + jsb->s_start = 0; /* this marks the journal as empty */ + mark_buffer_dirty(journal->j_sb_buffer); + } + brelse(journal->j_sb_buffer); + + if (ctx->journal_io) { + if (ctx->fs && ctx->fs->io != ctx->journal_io) + io_channel_close(ctx->journal_io); + ctx->journal_io = 0; + } + +#ifndef USE_INODE_IO + ext2fs_free_mem(&journal->j_inode); +#endif + ext2fs_free_mem(&journal->j_fs_dev); + ext2fs_free_mem(&journal); +} + +/* + * This function makes sure that the superblock fields regarding the + * journal are consistent. + */ +static int e2fsck_check_ext3_journal(e2fsck_t ctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + journal_t *journal; + int recover = ctx->fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER; + struct problem_context pctx; + problem_t problem; + int reset = 0, force_fsck = 0; + int retval; + + /* If we don't have any journal features, don't do anything more */ + if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 && + uuid_is_null(sb->s_journal_uuid)) + return 0; + + clear_problem_context(&pctx); + pctx.num = sb->s_journal_inum; + + retval = e2fsck_get_journal(ctx, &journal); + if (retval) { + if ((retval == EXT2_ET_BAD_INODE_NUM) || + (retval == EXT2_ET_BAD_BLOCK_NUM) || + (retval == EXT2_ET_JOURNAL_TOO_SMALL) || + (retval == EXT2_ET_NO_JOURNAL)) + return e2fsck_journal_fix_bad_inode(ctx, &pctx); + return retval; + } + + retval = e2fsck_journal_load(journal); + if (retval) { + if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) || + ((retval == EXT2_ET_UNSUPP_FEATURE) && + (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT, + &pctx))) || + ((retval == EXT2_ET_RO_UNSUPP_FEATURE) && + (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT, + &pctx))) || + ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) && + (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx)))) + retval = e2fsck_journal_fix_corrupt_super(ctx, journal, + &pctx); + e2fsck_journal_release(ctx, journal, 0, 1); + return retval; + } + + /* + * We want to make the flags consistent here. We will not leave with + * needs_recovery set but has_journal clear. We can't get in a loop + * with -y, -n, or -p, only if a user isn't making up their mind. + */ +no_has_journal: + if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { + recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; + pctx.str = "inode"; + if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) { + if (recover && + !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx)) + goto no_has_journal; + /* + * Need a full fsck if we are releasing a + * journal stored on a reserved inode. + */ + force_fsck = recover || + (sb->s_journal_inum < EXT2_FIRST_INODE(sb)); + /* Clear all of the journal fields */ + sb->s_journal_inum = 0; + sb->s_journal_dev = 0; + memset(sb->s_journal_uuid, 0, + sizeof(sb->s_journal_uuid)); + e2fsck_clear_recover(ctx, force_fsck); + } else if (!(ctx->options & E2F_OPT_READONLY)) { + sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + ext2fs_mark_super_dirty(ctx->fs); + } + } + + if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL && + !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) && + journal->j_superblock->s_start != 0) { + /* Print status information */ + fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx); + if (ctx->superblock) + problem = PR_0_JOURNAL_RUN_DEFAULT; + else + problem = PR_0_JOURNAL_RUN; + if (fix_problem(ctx, problem, &pctx)) { + ctx->options |= E2F_OPT_FORCE; + sb->s_feature_incompat |= + EXT3_FEATURE_INCOMPAT_RECOVER; + ext2fs_mark_super_dirty(ctx->fs); + } else if (fix_problem(ctx, + PR_0_JOURNAL_RESET_JOURNAL, &pctx)) { + reset = 1; + sb->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(ctx->fs); + } + /* + * If the user answers no to the above question, we + * ignore the fact that journal apparently has data; + * accidentally replaying over valid data would be far + * worse than skipping a questionable recovery. + * + * XXX should we abort with a fatal error here? What + * will the ext3 kernel code do if a filesystem with + * !NEEDS_RECOVERY but with a non-zero + * journal->j_superblock->s_start is mounted? + */ + } + + e2fsck_journal_release(ctx, journal, reset, 0); + return retval; +} + +static errcode_t recover_ext3_journal(e2fsck_t ctx) +{ + journal_t *journal; + int retval; + + journal_init_revoke_caches(); + retval = e2fsck_get_journal(ctx, &journal); + if (retval) + return retval; + + retval = e2fsck_journal_load(journal); + if (retval) + goto errout; + + retval = journal_init_revoke(journal, 1024); + if (retval) + goto errout; + + retval = -journal_recover(journal); + if (retval) + goto errout; + + if (journal->j_superblock->s_errno) { + ctx->fs->super->s_state |= EXT2_ERROR_FS; + ext2fs_mark_super_dirty(ctx->fs); + journal->j_superblock->s_errno = 0; + mark_buffer_dirty(journal->j_sb_buffer); + } + +errout: + journal_destroy_revoke(journal); + journal_destroy_revoke_caches(); + e2fsck_journal_release(ctx, journal, 1, 0); + return retval; +} + +static int e2fsck_run_ext3_journal(e2fsck_t ctx) +{ + io_manager io_ptr = ctx->fs->io->manager; + int blocksize = ctx->fs->blocksize; + errcode_t retval, recover_retval; + + printf(_("%s: recovering journal\n"), ctx->device_name); + if (ctx->options & E2F_OPT_READONLY) { + printf(_("%s: won't do journal recovery while read-only\n"), + ctx->device_name); + return EXT2_ET_FILE_RO; + } + + if (ctx->fs->flags & EXT2_FLAG_DIRTY) + ext2fs_flush(ctx->fs); /* Force out any modifications */ + + recover_retval = recover_ext3_journal(ctx); + + /* + * Reload the filesystem context to get up-to-date data from disk + * because journal recovery will change the filesystem under us. + */ + ext2fs_close(ctx->fs); + retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW, + ctx->superblock, blocksize, io_ptr, + &ctx->fs); + + if (retval) { + bb_error_msg(_("while trying to re-open %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } + ctx->fs->priv_data = ctx; + + /* Set the superblock flags */ + e2fsck_clear_recover(ctx, recover_retval); + return recover_retval; +} + +/* + * This function will move the journal inode from a visible file in + * the filesystem directory hierarchy to the reserved inode if necessary. + */ +static const char *const journal_names[] = { + ".journal", "journal", ".journal.dat", "journal.dat", 0 }; + +static void e2fsck_move_ext3_journal(e2fsck_t ctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct problem_context pctx; + struct ext2_inode inode; + ext2_filsys fs = ctx->fs; + ext2_ino_t ino; + errcode_t retval; + const char *const * cpp; + int group, mount_flags; + + clear_problem_context(&pctx); + + /* + * If the filesystem is opened read-only, or there is no + * journal, then do nothing. + */ + if ((ctx->options & E2F_OPT_READONLY) || + (sb->s_journal_inum == 0) || + !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) + return; + + /* + * Read in the journal inode + */ + if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0) + return; + + /* + * If it's necessary to backup the journal inode, do so. + */ + if ((sb->s_jnl_backup_type == 0) || + ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) && + memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) { + if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) { + memcpy(sb->s_jnl_blocks, inode.i_block, + EXT2_N_BLOCKS*4); + sb->s_jnl_blocks[16] = inode.i_size; + sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; + ext2fs_mark_super_dirty(fs); + fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; + } + } + + /* + * If the journal is already the hidden inode, then do nothing + */ + if (sb->s_journal_inum == EXT2_JOURNAL_INO) + return; + + /* + * The journal inode had better have only one link and not be readable. + */ + if (inode.i_links_count != 1) + return; + + /* + * If the filesystem is mounted, or we can't tell whether + * or not it's mounted, do nothing. + */ + retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags); + if (retval || (mount_flags & EXT2_MF_MOUNTED)) + return; + + /* + * If we can't find the name of the journal inode, then do + * nothing. + */ + for (cpp = journal_names; *cpp; cpp++) { + retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp, + strlen(*cpp), 0, &ino); + if ((retval == 0) && (ino == sb->s_journal_inum)) + break; + } + if (*cpp == 0) + return; + + /* We need the inode bitmap to be loaded */ + retval = ext2fs_read_bitmaps(fs); + if (retval) + return; + + pctx.str = *cpp; + if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx)) + return; + + /* + * OK, we've done all the checks, let's actually move the + * journal inode. Errors at this point mean we need to force + * an ext2 filesystem check. + */ + if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0) + goto err_out; + if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0) + goto err_out; + sb->s_journal_inum = EXT2_JOURNAL_INO; + ext2fs_mark_super_dirty(fs); + fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; + inode.i_links_count = 0; + inode.i_dtime = time(NULL); + if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0) + goto err_out; + + group = ext2fs_group_of_ino(fs, ino); + ext2fs_unmark_inode_bitmap(fs->inode_map, ino); + ext2fs_mark_ib_dirty(fs); + fs->group_desc[group].bg_free_inodes_count++; + fs->super->s_free_inodes_count++; + return; + +err_out: + pctx.errcode = retval; + fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx); + fs->super->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); +} + +/* + * message.c --- print e2fsck messages (with compression) + * + * print_e2fsck_message() prints a message to the user, using + * compression techniques and expansions of abbreviations. + * + * The following % expansions are supported: + * + * %b block number + * %B integer + * %c block number + * %Di ->ino inode number + * %Dn ->name string + * %Dr ->rec_len + * %Dl ->name_len + * %Dt ->filetype + * %d inode number + * %g integer + * %i inode number + * %Is -> i_size + * %IS -> i_extra_isize + * %Ib -> i_blocks + * %Il -> i_links_count + * %Im -> i_mode + * %IM -> i_mtime + * %IF -> i_faddr + * %If -> i_file_acl + * %Id -> i_dir_acl + * %Iu -> i_uid + * %Ig -> i_gid + * %j inode number + * %m + * %N + * %p ext2fs_get_pathname of directory + * %P ext2fs_get_pathname of ->ino with as + * the containing directory. (If dirent is NULL + * then return the pathname of directory ) + * %q ext2fs_get_pathname of directory + * %Q ext2fs_get_pathname of directory with as + * the containing directory. + * %s miscellaneous string + * %S backup superblock + * %X hexadecimal format + * + * The following '@' expansions are supported: + * + * @a extended attribute + * @A error allocating + * @b block + * @B bitmap + * @c compress + * @C conflicts with some other fs block + * @D deleted + * @d directory + * @e entry + * @E Entry '%Dn' in %p (%i) + * @f filesystem + * @F for @i %i (%Q) is + * @g group + * @h HTREE directory inode + * @i inode + * @I illegal + * @j journal + * @l lost+found + * @L is a link + * @m multiply-claimed + * @n invalid + * @o orphaned + * @p problem in + * @r root inode + * @s should be + * @S superblock + * @u unattached + * @v device + * @z zero-length + */ + + +/* + * This structure defines the abbreviations used by the text strings + * below. The first character in the string is the index letter. An + * abbreviation of the form '@' is expanded by looking up the index + * letter in the table below. + */ +static const char *const abbrevs[] = { + N_("aextended attribute"), + N_("Aerror allocating"), + N_("bblock"), + N_("Bbitmap"), + N_("ccompress"), + N_("Cconflicts with some other fs @b"), + N_("iinode"), + N_("Iillegal"), + N_("jjournal"), + N_("Ddeleted"), + N_("ddirectory"), + N_("eentry"), + N_("E@e '%Dn' in %p (%i)"), + N_("ffilesystem"), + N_("Ffor @i %i (%Q) is"), + N_("ggroup"), + N_("hHTREE @d @i"), + N_("llost+found"), + N_("Lis a link"), + N_("mmultiply-claimed"), + N_("ninvalid"), + N_("oorphaned"), + N_("pproblem in"), + N_("rroot @i"), + N_("sshould be"), + N_("Ssuper@b"), + N_("uunattached"), + N_("vdevice"), + N_("zzero-length"), + "@@", + 0 + }; + +/* + * Give more user friendly names to the "special" inodes. + */ +#define num_special_inodes 11 +static const char *const special_inode_name[] = +{ + N_(""), /* 0 */ + N_(""), /* 1 */ + "/", /* 2 */ + N_(""), /* 3 */ + N_(""), /* 4 */ + N_(""), /* 5 */ + N_(""), /* 6 */ + N_(""), /* 7 */ + N_(""), /* 8 */ + N_(""), /* 9 */ + N_(""), /* 10 */ +}; + +/* + * This function does "safe" printing. It will convert non-printable + * ASCII characters using '^' and M- notation. + */ +static void safe_print(const char *cp, int len) +{ + unsigned char ch; + + if (len < 0) + len = strlen(cp); + + while (len--) { + ch = *cp++; + if (ch > 128) { + fputs("M-", stdout); + ch -= 128; + } + if ((ch < 32) || (ch == 0x7f)) { + bb_putchar('^'); + ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */ + } + bb_putchar(ch); + } +} + + +/* + * This function prints a pathname, using the ext2fs_get_pathname + * function + */ +static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino) +{ + errcode_t retval; + char *path; + + if (!dir && (ino < num_special_inodes)) { + fputs(_(special_inode_name[ino]), stdout); + return; + } + + retval = ext2fs_get_pathname(fs, dir, ino, &path); + if (retval) + fputs("???", stdout); + else { + safe_print(path, -1); + ext2fs_free_mem(&path); + } +} + +static void print_e2fsck_message(e2fsck_t ctx, const char *msg, + struct problem_context *pctx, int first); +/* + * This function handles the '@' expansion. We allow recursive + * expansion; an @ expression can contain further '@' and '%' + * expressions. + */ +static void expand_at_expression(e2fsck_t ctx, char ch, + struct problem_context *pctx, + int *first) +{ + const char *const *cpp; + const char *str; + + /* Search for the abbreviation */ + for (cpp = abbrevs; *cpp; cpp++) { + if (ch == *cpp[0]) + break; + } + if (*cpp) { + str = _(*cpp) + 1; + if (*first && islower(*str)) { + *first = 0; + bb_putchar(toupper(*str++)); + } + print_e2fsck_message(ctx, str, pctx, *first); + } else + printf("@%c", ch); +} + +/* + * This function expands '%IX' expressions + */ +static void expand_inode_expression(char ch, + struct problem_context *ctx) +{ + struct ext2_inode *inode; + struct ext2_inode_large *large_inode; + char * time_str; + time_t t; + int do_gmt = -1; + + if (!ctx || !ctx->inode) + goto no_inode; + + inode = ctx->inode; + large_inode = (struct ext2_inode_large *) inode; + + switch (ch) { + case 's': + if (LINUX_S_ISDIR(inode->i_mode)) + printf("%u", inode->i_size); + else { + printf("%"PRIu64, (inode->i_size | + ((uint64_t) inode->i_size_high << 32))); + } + break; + case 'S': + printf("%u", large_inode->i_extra_isize); + break; + case 'b': + printf("%u", inode->i_blocks); + break; + case 'l': + printf("%d", inode->i_links_count); + break; + case 'm': + printf("0%o", inode->i_mode); + break; + case 'M': + /* The diet libc doesn't respect the TZ environemnt variable */ + if (do_gmt == -1) { + time_str = getenv("TZ"); + if (!time_str) + time_str = ""; + do_gmt = !strcmp(time_str, "GMT"); + } + t = inode->i_mtime; + time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t)); + printf("%.24s", time_str); + break; + case 'F': + printf("%u", inode->i_faddr); + break; + case 'f': + printf("%u", inode->i_file_acl); + break; + case 'd': + printf("%u", (LINUX_S_ISDIR(inode->i_mode) ? + inode->i_dir_acl : 0)); + break; + case 'u': + printf("%d", (inode->i_uid | + (inode->osd2.linux2.l_i_uid_high << 16))); + break; + case 'g': + printf("%d", (inode->i_gid | + (inode->osd2.linux2.l_i_gid_high << 16))); + break; + default: + no_inode: + printf("%%I%c", ch); + break; + } +} + +/* + * This function expands '%dX' expressions + */ +static void expand_dirent_expression(char ch, + struct problem_context *ctx) +{ + struct ext2_dir_entry *dirent; + int len; + + if (!ctx || !ctx->dirent) + goto no_dirent; + + dirent = ctx->dirent; + + switch (ch) { + case 'i': + printf("%u", dirent->inode); + break; + case 'n': + len = dirent->name_len & 0xFF; + if (len > EXT2_NAME_LEN) + len = EXT2_NAME_LEN; + if (len > dirent->rec_len) + len = dirent->rec_len; + safe_print(dirent->name, len); + break; + case 'r': + printf("%u", dirent->rec_len); + break; + case 'l': + printf("%u", dirent->name_len & 0xFF); + break; + case 't': + printf("%u", dirent->name_len >> 8); + break; + default: + no_dirent: + printf("%%D%c", ch); + break; + } +} + +static void expand_percent_expression(ext2_filsys fs, char ch, + struct problem_context *ctx) +{ + if (!ctx) + goto no_context; + + switch (ch) { + case '%': + bb_putchar('%'); + break; + case 'b': + printf("%u", ctx->blk); + break; + case 'B': + printf("%"PRIi64, ctx->blkcount); + break; + case 'c': + printf("%u", ctx->blk2); + break; + case 'd': + printf("%u", ctx->dir); + break; + case 'g': + printf("%d", ctx->group); + break; + case 'i': + printf("%u", ctx->ino); + break; + case 'j': + printf("%u", ctx->ino2); + break; + case 'm': + fputs(error_message(ctx->errcode), stdout); + break; + case 'N': + printf("%"PRIi64, ctx->num); + break; + case 'p': + print_pathname(fs, ctx->ino, 0); + break; + case 'P': + print_pathname(fs, ctx->ino2, + ctx->dirent ? ctx->dirent->inode : 0); + break; + case 'q': + print_pathname(fs, ctx->dir, 0); + break; + case 'Q': + print_pathname(fs, ctx->dir, ctx->ino); + break; + case 'S': + printf("%d", get_backup_sb(NULL, fs, NULL, NULL)); + break; + case 's': + fputs((ctx->str ? ctx->str : "NULL"), stdout); + break; + case 'X': + printf("0x%"PRIi64, ctx->num); + break; + default: + no_context: + printf("%%%c", ch); + break; + } +} + + +static void print_e2fsck_message(e2fsck_t ctx, const char *msg, + struct problem_context *pctx, int first) +{ + ext2_filsys fs = ctx->fs; + const char * cp; + int i; + + e2fsck_clear_progbar(ctx); + for (cp = msg; *cp; cp++) { + if (cp[0] == '@') { + cp++; + expand_at_expression(ctx, *cp, pctx, &first); + } else if (cp[0] == '%' && cp[1] == 'I') { + cp += 2; + expand_inode_expression(*cp, pctx); + } else if (cp[0] == '%' && cp[1] == 'D') { + cp += 2; + expand_dirent_expression(*cp, pctx); + } else if ((cp[0] == '%')) { + cp++; + expand_percent_expression(fs, *cp, pctx); + } else { + for (i=0; cp[i]; i++) + if ((cp[i] == '@') || cp[i] == '%') + break; + printf("%.*s", i, cp); + cp += i-1; + } + first = 0; + } +} + + +/* + * region.c --- code which manages allocations within a region. + */ + +struct region_el { + region_addr_t start; + region_addr_t end; + struct region_el *next; +}; + +struct region_struct { + region_addr_t min; + region_addr_t max; + struct region_el *allocated; +}; + +static region_t region_create(region_addr_t min, region_addr_t max) +{ + region_t region; + + region = xzalloc(sizeof(struct region_struct)); + region->min = min; + region->max = max; + return region; +} + +static void region_free(region_t region) +{ + struct region_el *r, *next; + + for (r = region->allocated; r; r = next) { + next = r->next; + free(r); + } + memset(region, 0, sizeof(struct region_struct)); + free(region); +} + +static int region_allocate(region_t region, region_addr_t start, int n) +{ + struct region_el *r, *new_region, *prev, *next; + region_addr_t end; + + end = start+n; + if ((start < region->min) || (end > region->max)) + return -1; + if (n == 0) + return 1; + + /* + * Search through the linked list. If we find that it + * conflicts witih something that's already allocated, return + * 1; if we can find an existing region which we can grow, do + * so. Otherwise, stop when we find the appropriate place + * insert a new region element into the linked list. + */ + for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) { + if (((start >= r->start) && (start < r->end)) || + ((end > r->start) && (end <= r->end)) || + ((start <= r->start) && (end >= r->end))) + return 1; + if (end == r->start) { + r->start = start; + return 0; + } + if (start == r->end) { + if ((next = r->next)) { + if (end > next->start) + return 1; + if (end == next->start) { + r->end = next->end; + r->next = next->next; + free(next); + return 0; + } + } + r->end = end; + return 0; + } + if (start < r->start) + break; + } + /* + * Insert a new region element structure into the linked list + */ + new_region = xmalloc(sizeof(struct region_el)); + new_region->start = start; + new_region->end = start + n; + new_region->next = r; + if (prev) + prev->next = new_region; + else + region->allocated = new_region; + return 0; +} + +/* + * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table + * + * Pass 1 of e2fsck iterates over all the inodes in the filesystems, + * and applies the following tests to each inode: + * + * - The mode field of the inode must be legal. + * - The size and block count fields of the inode are correct. + * - A data block must not be used by another inode + * + * Pass 1 also gathers the collects the following information: + * + * - A bitmap of which inodes are in use. (inode_used_map) + * - A bitmap of which inodes are directories. (inode_dir_map) + * - A bitmap of which inodes are regular files. (inode_reg_map) + * - A bitmap of which inodes have bad fields. (inode_bad_map) + * - A bitmap of which inodes are imagic inodes. (inode_imagic_map) + * - A bitmap of which blocks are in use. (block_found_map) + * - A bitmap of which blocks are in use by two inodes (block_dup_map) + * - The data blocks of the directory inodes. (dir_map) + * + * Pass 1 is designed to stash away enough information so that the + * other passes should not need to read in the inode information + * during the normal course of a filesystem check. (Althogh if an + * inconsistency is detected, other passes may need to read in an + * inode to fix it.) + * + * Note that pass 1B will be invoked if there are any duplicate blocks + * found. + */ + + +static int process_block(ext2_filsys fs, blk_t *blocknr, + e2_blkcnt_t blockcnt, blk_t ref_blk, + int ref_offset, void *priv_data); +static int process_bad_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, blk_t ref_blk, + int ref_offset, void *priv_data); +static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, + char *block_buf); +static void mark_table_blocks(e2fsck_t ctx); +static void alloc_imagic_map(e2fsck_t ctx); +static void mark_inode_bad(e2fsck_t ctx, ino_t ino); +static void handle_fs_bad_blocks(e2fsck_t ctx); +static void process_inodes(e2fsck_t ctx, char *block_buf); +static int process_inode_cmp(const void *a, const void *b); +static errcode_t scan_callback(ext2_filsys fs, + dgrp_t group, void * priv_data); +static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, + char *block_buf, int adjust_sign); +/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */ + +static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino, + struct ext2_inode * inode, int bufsize, + const char *proc); + +struct process_block_struct_1 { + ext2_ino_t ino; + unsigned is_dir:1, is_reg:1, clear:1, suppress:1, + fragmented:1, compressed:1, bbcheck:1; + blk_t num_blocks; + blk_t max_blocks; + e2_blkcnt_t last_block; + int num_illegal_blocks; + blk_t previous_block; + struct ext2_inode *inode; + struct problem_context *pctx; + ext2fs_block_bitmap fs_meta_blocks; + e2fsck_t ctx; +}; + +struct process_inode_block { + ext2_ino_t ino; + struct ext2_inode inode; +}; + +struct scan_callback_struct { + e2fsck_t ctx; + char *block_buf; +}; + +/* + * For the inodes to process list. + */ +static struct process_inode_block *inodes_to_process; +static int process_inode_count; + +static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE - + EXT2_MIN_BLOCK_LOG_SIZE + 1]; + +/* + * Free all memory allocated by pass1 in preparation for restarting + * things. + */ +static void unwind_pass1(void) +{ + ext2fs_free_mem(&inodes_to_process); +} + +/* + * Check to make sure a device inode is real. Returns 1 if the device + * checks out, 0 if not. + * + * Note: this routine is now also used to check FIFO's and Sockets, + * since they have the same requirement; the i_block fields should be + * zero. + */ +static int +e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode) +{ + int i; + + /* + * If i_blocks is non-zero, or the index flag is set, then + * this is a bogus device/fifo/socket + */ + if ((ext2fs_inode_data_blocks(fs, inode) != 0) || + (inode->i_flags & EXT2_INDEX_FL)) + return 0; + + /* + * We should be able to do the test below all the time, but + * because the kernel doesn't forcibly clear the device + * inode's additional i_block fields, there are some rare + * occasions when a legitimate device inode will have non-zero + * additional i_block fields. So for now, we only complain + * when the immutable flag is set, which should never happen + * for devices. (And that's when the problem is caused, since + * you can't set or clear immutable flags for devices.) Once + * the kernel has been fixed we can change this... + */ + if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) { + for (i=4; i < EXT2_N_BLOCKS; i++) + if (inode->i_block[i]) + return 0; + } + return 1; +} + +/* + * Check to make sure a symlink inode is real. Returns 1 if the symlink + * checks out, 0 if not. + */ +static int +e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf) +{ + unsigned int len; + int i; + blk_t blocks; + + if ((inode->i_size_high || inode->i_size == 0) || + (inode->i_flags & EXT2_INDEX_FL)) + return 0; + + blocks = ext2fs_inode_data_blocks(fs, inode); + if (blocks) { + if ((inode->i_size >= fs->blocksize) || + (blocks != fs->blocksize >> 9) || + (inode->i_block[0] < fs->super->s_first_data_block) || + (inode->i_block[0] >= fs->super->s_blocks_count)) + return 0; + + for (i = 1; i < EXT2_N_BLOCKS; i++) + if (inode->i_block[i]) + return 0; + + if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf)) + return 0; + + len = strnlen(buf, fs->blocksize); + if (len == fs->blocksize) + return 0; + } else { + if (inode->i_size >= sizeof(inode->i_block)) + return 0; + + len = strnlen((char *)inode->i_block, sizeof(inode->i_block)); + if (len == sizeof(inode->i_block)) + return 0; + } + if (len != inode->i_size) + return 0; + return 1; +} + +/* + * If the immutable (or append-only) flag is set on the inode, offer + * to clear it. + */ +#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL) +static void check_immutable(e2fsck_t ctx, struct problem_context *pctx) +{ + if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS)) + return; + + if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx)) + return; + + pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS; + e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); +} + +/* + * If device, fifo or socket, check size is zero -- if not offer to + * clear it + */ +static void check_size(e2fsck_t ctx, struct problem_context *pctx) +{ + struct ext2_inode *inode = pctx->inode; + + if ((inode->i_size == 0) && (inode->i_size_high == 0)) + return; + + if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx)) + return; + + inode->i_size = 0; + inode->i_size_high = 0; + e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1"); +} + +static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct ext2_inode_large *inode; + struct ext2_ext_attr_entry *entry; + char *start, *end; + int storage_size, remain, offs; + int problem = 0; + + inode = (struct ext2_inode_large *) pctx->inode; + storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE - + inode->i_extra_isize; + start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize + sizeof(__u32); + end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super); + entry = (struct ext2_ext_attr_entry *) start; + + /* scan all entry's headers first */ + + /* take finish entry 0UL into account */ + remain = storage_size - sizeof(__u32); + offs = end - start; + + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { + + /* header eats this space */ + remain -= sizeof(struct ext2_ext_attr_entry); + + /* is attribute name valid? */ + if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) { + pctx->num = entry->e_name_len; + problem = PR_1_ATTR_NAME_LEN; + goto fix; + } + + /* attribute len eats this space */ + remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len); + + /* check value size */ + if (entry->e_value_size == 0 || entry->e_value_size > remain) { + pctx->num = entry->e_value_size; + problem = PR_1_ATTR_VALUE_SIZE; + goto fix; + } + + /* check value placement */ + if (entry->e_value_offs + + EXT2_XATTR_SIZE(entry->e_value_size) != offs) { + printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs); + pctx->num = entry->e_value_offs; + problem = PR_1_ATTR_VALUE_OFFSET; + goto fix; + } + + /* e_value_block must be 0 in inode's ea */ + if (entry->e_value_block != 0) { + pctx->num = entry->e_value_block; + problem = PR_1_ATTR_VALUE_BLOCK; + goto fix; + } + + /* e_hash must be 0 in inode's ea */ + if (entry->e_hash != 0) { + pctx->num = entry->e_hash; + problem = PR_1_ATTR_HASH; + goto fix; + } + + remain -= entry->e_value_size; + offs -= EXT2_XATTR_SIZE(entry->e_value_size); + + entry = EXT2_EXT_ATTR_NEXT(entry); + } +fix: + /* + * it seems like a corruption. it's very unlikely we could repair + * EA(s) in automatic fashion -bzzz + */ + if (problem == 0 || !fix_problem(ctx, problem, pctx)) + return; + + /* simple remove all possible EA(s) */ + *((__u32 *)start) = 0UL; + e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode, + EXT2_INODE_SIZE(sb), "pass1"); +} + +static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct ext2_inode_large *inode; + __u32 *eamagic; + int min, max; + + inode = (struct ext2_inode_large *) pctx->inode; + if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) { + /* this isn't large inode. so, nothing to check */ + return; + } + + /* i_extra_isize must cover i_extra_isize + i_pad1 at least */ + min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1); + max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE; + /* + * For now we will allow i_extra_isize to be 0, but really + * implementations should never allow i_extra_isize to be 0 + */ + if (inode->i_extra_isize && + (inode->i_extra_isize < min || inode->i_extra_isize > max)) { + if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx)) + return; + inode->i_extra_isize = min; + e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, + EXT2_INODE_SIZE(sb), "pass1"); + return; + } + + eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize); + if (*eamagic == EXT2_EXT_ATTR_MAGIC) { + /* it seems inode has an extended attribute(s) in body */ + check_ea_in_inode(ctx, pctx); + } +} + +static void e2fsck_pass1(e2fsck_t ctx) +{ + int i; + __u64 max_sizes; + ext2_filsys fs = ctx->fs; + ext2_ino_t ino; + struct ext2_inode *inode; + ext2_inode_scan scan; + char *block_buf; + unsigned char frag, fsize; + struct problem_context pctx; + struct scan_callback_struct scan_struct; + struct ext2_super_block *sb = ctx->fs->super; + int imagic_fs; + int busted_fs_time = 0; + int inode_size; + + clear_problem_context(&pctx); + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_1_PASS_HEADER, &pctx); + + if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) && + !(ctx->options & E2F_OPT_NO)) { + if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50)) + ctx->dirs_to_hash = 0; + } + + /* Pass 1 */ + +#define EXT2_BPP(bits) (1ULL << ((bits) - 2)) + + for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) { + max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i); + max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i); + max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i); + max_sizes = (max_sizes * (1UL << i)) - 1; + ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes; + } +#undef EXT2_BPP + + imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES); + + /* + * Allocate bitmaps structures + */ + pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"), + &ctx->inode_used_map); + if (pctx.errcode) { + pctx.num = 1; + fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + pctx.errcode = ext2fs_allocate_inode_bitmap(fs, + _("directory inode map"), &ctx->inode_dir_map); + if (pctx.errcode) { + pctx.num = 2; + fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + pctx.errcode = ext2fs_allocate_inode_bitmap(fs, + _("regular file inode map"), &ctx->inode_reg_map); + if (pctx.errcode) { + pctx.num = 6; + fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"), + &ctx->block_found_map); + if (pctx.errcode) { + pctx.num = 1; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0, + &ctx->inode_link_info); + if (pctx.errcode) { + fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + inode_size = EXT2_INODE_SIZE(fs->super); + inode = (struct ext2_inode *) + e2fsck_allocate_memory(ctx, inode_size, "scratch inode"); + + inodes_to_process = (struct process_inode_block *) + e2fsck_allocate_memory(ctx, + (ctx->process_inode_size * + sizeof(struct process_inode_block)), + "array of inodes to process"); + process_inode_count = 0; + + pctx.errcode = ext2fs_init_dblist(fs, 0); + if (pctx.errcode) { + fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + /* + * If the last orphan field is set, clear it, since the pass1 + * processing will automatically find and clear the orphans. + * In the future, we may want to try using the last_orphan + * linked list ourselves, but for now, we clear it so that the + * ext3 mount code won't get confused. + */ + if (!(ctx->options & E2F_OPT_READONLY)) { + if (fs->super->s_last_orphan) { + fs->super->s_last_orphan = 0; + ext2fs_mark_super_dirty(fs); + } + } + + mark_table_blocks(ctx); + block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3, + "block interate buffer"); + e2fsck_use_inode_shortcuts(ctx, 1); + ehandler_operation(_("doing inode scan")); + pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, + &scan); + if (pctx.errcode) { + fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0); + ctx->stashed_inode = inode; + scan_struct.ctx = ctx; + scan_struct.block_buf = block_buf; + ext2fs_set_inode_callback(scan, scan_callback, &scan_struct); + if (ctx->progress) + if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count)) + return; + if ((fs->super->s_wtime < fs->super->s_inodes_count) || + (fs->super->s_mtime < fs->super->s_inodes_count)) + busted_fs_time = 1; + + while (1) { + pctx.errcode = ext2fs_get_next_inode_full(scan, &ino, + inode, inode_size); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { + continue; + } + if (pctx.errcode) { + fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (!ino) + break; + pctx.ino = ino; + pctx.inode = inode; + ctx->stashed_ino = ino; + if (inode->i_links_count) { + pctx.errcode = ext2fs_icount_store(ctx->inode_link_info, + ino, inode->i_links_count); + if (pctx.errcode) { + pctx.num = inode->i_links_count; + fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + if (ino == EXT2_BAD_INO) { + struct process_block_struct_1 pb; + + pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map, + &pb.fs_meta_blocks); + if (pctx.errcode) { + pctx.num = 4; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + pb.ino = EXT2_BAD_INO; + pb.num_blocks = pb.last_block = 0; + pb.num_illegal_blocks = 0; + pb.suppress = 0; pb.clear = 0; pb.is_dir = 0; + pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0; + pb.inode = inode; + pb.pctx = &pctx; + pb.ctx = ctx; + pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, + block_buf, process_bad_block, &pb); + ext2fs_free_block_bitmap(pb.fs_meta_blocks); + if (pctx.errcode) { + fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (pb.bbcheck) + if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) { + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); + clear_problem_context(&pctx); + continue; + } else if (ino == EXT2_ROOT_INO) { + /* + * Make sure the root inode is a directory; if + * not, offer to clear it. It will be + * regnerated in pass #3. + */ + if (!LINUX_S_ISDIR(inode->i_mode)) { + if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) { + inode->i_dtime = time(NULL); + inode->i_links_count = 0; + ext2fs_icount_store(ctx->inode_link_info, + ino, 0); + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + } + } + /* + * If dtime is set, offer to clear it. mke2fs + * version 0.2b created filesystems with the + * dtime field set for the root and lost+found + * directories. We won't worry about + * /lost+found, since that can be regenerated + * easily. But we will fix the root directory + * as a special case. + */ + if (inode->i_dtime && inode->i_links_count) { + if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) { + inode->i_dtime = 0; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + } + } + } else if (ino == EXT2_JOURNAL_INO) { + ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); + if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) { + if (!LINUX_S_ISREG(inode->i_mode) && + fix_problem(ctx, PR_1_JOURNAL_BAD_MODE, + &pctx)) { + inode->i_mode = LINUX_S_IFREG; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + } + check_blocks(ctx, &pctx, block_buf); + continue; + } + if ((inode->i_links_count || inode->i_blocks || + inode->i_blocks || inode->i_block[0]) && + fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR, + &pctx)) { + memset(inode, 0, inode_size); + ext2fs_icount_store(ctx->inode_link_info, + ino, 0); + e2fsck_write_inode_full(ctx, ino, inode, + inode_size, "pass1"); + } + } else if (ino < EXT2_FIRST_INODE(fs->super)) { + int problem = 0; + + ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); + if (ino == EXT2_BOOT_LOADER_INO) { + if (LINUX_S_ISDIR(inode->i_mode)) + problem = PR_1_RESERVED_BAD_MODE; + } else if (ino == EXT2_RESIZE_INO) { + if (inode->i_mode && + !LINUX_S_ISREG(inode->i_mode)) + problem = PR_1_RESERVED_BAD_MODE; + } else { + if (inode->i_mode != 0) + problem = PR_1_RESERVED_BAD_MODE; + } + if (problem) { + if (fix_problem(ctx, problem, &pctx)) { + inode->i_mode = 0; + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + } + } + check_blocks(ctx, &pctx, block_buf); + continue; + } + /* + * Check for inodes who might have been part of the + * orphaned list linked list. They should have gotten + * dealt with by now, unless the list had somehow been + * corrupted. + * + * FIXME: In the future, inodes which are still in use + * (and which are therefore) pending truncation should + * be handled specially. Right now we just clear the + * dtime field, and the normal e2fsck handling of + * inodes where i_size and the inode blocks are + * inconsistent is to fix i_size, instead of releasing + * the extra blocks. This won't catch the inodes that + * was at the end of the orphan list, but it's better + * than nothing. The right answer is that there + * shouldn't be any bugs in the orphan list handling. :-) + */ + if (inode->i_dtime && !busted_fs_time && + inode->i_dtime < ctx->fs->super->s_inodes_count) { + if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) { + inode->i_dtime = inode->i_links_count ? + 0 : time(NULL); + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + } + } + + /* + * This code assumes that deleted inodes have + * i_links_count set to 0. + */ + if (!inode->i_links_count) { + if (!inode->i_dtime && inode->i_mode) { + if (fix_problem(ctx, + PR_1_ZERO_DTIME, &pctx)) { + inode->i_dtime = time(NULL); + e2fsck_write_inode(ctx, ino, inode, + "pass1"); + } + } + continue; + } + /* + * n.b. 0.3c ext2fs code didn't clear i_links_count for + * deleted files. Oops. + * + * Since all new ext2 implementations get this right, + * we now assume that the case of non-zero + * i_links_count and non-zero dtime means that we + * should keep the file, not delete it. + * + */ + if (inode->i_dtime) { + if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) { + inode->i_dtime = 0; + e2fsck_write_inode(ctx, ino, inode, "pass1"); + } + } + + ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); + switch (fs->super->s_creator_os) { + case EXT2_OS_LINUX: + frag = inode->osd2.linux2.l_i_frag; + fsize = inode->osd2.linux2.l_i_fsize; + break; + case EXT2_OS_HURD: + frag = inode->osd2.hurd2.h_i_frag; + fsize = inode->osd2.hurd2.h_i_fsize; + break; + case EXT2_OS_MASIX: + frag = inode->osd2.masix2.m_i_frag; + fsize = inode->osd2.masix2.m_i_fsize; + break; + default: + frag = fsize = 0; + } + + if (inode->i_faddr || frag || fsize || + (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl)) + mark_inode_bad(ctx, ino); + if (inode->i_flags & EXT2_IMAGIC_FL) { + if (imagic_fs) { + if (!ctx->inode_imagic_map) + alloc_imagic_map(ctx); + ext2fs_mark_inode_bitmap(ctx->inode_imagic_map, + ino); + } else { + if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) { + inode->i_flags &= ~EXT2_IMAGIC_FL; + e2fsck_write_inode(ctx, ino, + inode, "pass1"); + } + } + } + + check_inode_extra_space(ctx, &pctx); + + if (LINUX_S_ISDIR(inode->i_mode)) { + ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino); + e2fsck_add_dir_info(ctx, ino, 0); + ctx->fs_directory_count++; + } else if (LINUX_S_ISREG (inode->i_mode)) { + ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino); + ctx->fs_regular_count++; + } else if (LINUX_S_ISCHR (inode->i_mode) && + e2fsck_pass1_check_device_inode(fs, inode)) { + check_immutable(ctx, &pctx); + check_size(ctx, &pctx); + ctx->fs_chardev_count++; + } else if (LINUX_S_ISBLK (inode->i_mode) && + e2fsck_pass1_check_device_inode(fs, inode)) { + check_immutable(ctx, &pctx); + check_size(ctx, &pctx); + ctx->fs_blockdev_count++; + } else if (LINUX_S_ISLNK (inode->i_mode) && + e2fsck_pass1_check_symlink(fs, inode, block_buf)) { + check_immutable(ctx, &pctx); + ctx->fs_symlinks_count++; + if (ext2fs_inode_data_blocks(fs, inode) == 0) { + ctx->fs_fast_symlinks_count++; + check_blocks(ctx, &pctx, block_buf); + continue; + } + } + else if (LINUX_S_ISFIFO (inode->i_mode) && + e2fsck_pass1_check_device_inode(fs, inode)) { + check_immutable(ctx, &pctx); + check_size(ctx, &pctx); + ctx->fs_fifo_count++; + } else if ((LINUX_S_ISSOCK (inode->i_mode)) && + e2fsck_pass1_check_device_inode(fs, inode)) { + check_immutable(ctx, &pctx); + check_size(ctx, &pctx); + ctx->fs_sockets_count++; + } else + mark_inode_bad(ctx, ino); + if (inode->i_block[EXT2_IND_BLOCK]) + ctx->fs_ind_count++; + if (inode->i_block[EXT2_DIND_BLOCK]) + ctx->fs_dind_count++; + if (inode->i_block[EXT2_TIND_BLOCK]) + ctx->fs_tind_count++; + if (inode->i_block[EXT2_IND_BLOCK] || + inode->i_block[EXT2_DIND_BLOCK] || + inode->i_block[EXT2_TIND_BLOCK] || + inode->i_file_acl) { + inodes_to_process[process_inode_count].ino = ino; + inodes_to_process[process_inode_count].inode = *inode; + process_inode_count++; + } else + check_blocks(ctx, &pctx, block_buf); + + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + + if (process_inode_count >= ctx->process_inode_size) { + process_inodes(ctx, block_buf); + + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + } + } + process_inodes(ctx, block_buf); + ext2fs_close_inode_scan(scan); + ehandler_operation(0); + + /* + * If any extended attribute blocks' reference counts need to + * be adjusted, either up (ctx->refcount_extra), or down + * (ctx->refcount), then fix them. + */ + if (ctx->refcount) { + adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1); + ea_refcount_free(ctx->refcount); + ctx->refcount = 0; + } + if (ctx->refcount_extra) { + adjust_extattr_refcount(ctx, ctx->refcount_extra, + block_buf, +1); + ea_refcount_free(ctx->refcount_extra); + ctx->refcount_extra = 0; + } + + if (ctx->invalid_bitmaps) + handle_fs_bad_blocks(ctx); + + /* We don't need the block_ea_map any more */ + ext2fs_free_block_bitmap(ctx->block_ea_map); + ctx->block_ea_map = 0; + + if (ctx->flags & E2F_FLAG_RESIZE_INODE) { + ext2fs_block_bitmap save_bmap; + + save_bmap = fs->block_map; + fs->block_map = ctx->block_found_map; + clear_problem_context(&pctx); + pctx.errcode = ext2fs_create_resize_inode(fs); + if (pctx.errcode) { + fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } + e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode, + "recreate inode"); + inode->i_mtime = time(NULL); + e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode, + "recreate inode"); + fs->block_map = save_bmap; + ctx->flags &= ~E2F_FLAG_RESIZE_INODE; + } + + if (ctx->flags & E2F_FLAG_RESTART) { + /* + * Only the master copy of the superblock and block + * group descriptors are going to be written during a + * restart, so set the superblock to be used to be the + * master superblock. + */ + ctx->use_superblock = 0; + unwind_pass1(); + goto endit; + } + + if (ctx->block_dup_map) { + if (ctx->options & E2F_OPT_PREEN) { + clear_problem_context(&pctx); + fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx); + } + e2fsck_pass1_dupblocks(ctx, block_buf); + } + ext2fs_free_mem(&inodes_to_process); +endit: + e2fsck_use_inode_shortcuts(ctx, 0); + + ext2fs_free_mem(&block_buf); + ext2fs_free_mem(&inode); +} + +/* + * When the inode_scan routines call this callback at the end of the + * glock group, call process_inodes. + */ +static errcode_t scan_callback(ext2_filsys fs, + dgrp_t group, void * priv_data) +{ + struct scan_callback_struct *scan_struct; + e2fsck_t ctx; + + scan_struct = (struct scan_callback_struct *) priv_data; + ctx = scan_struct->ctx; + + process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf); + + if (ctx->progress) + if ((ctx->progress)(ctx, 1, group+1, + ctx->fs->group_desc_count)) + return EXT2_ET_CANCEL_REQUESTED; + + return 0; +} + +/* + * Process the inodes in the "inodes to process" list. + */ +static void process_inodes(e2fsck_t ctx, char *block_buf) +{ + int i; + struct ext2_inode *old_stashed_inode; + ext2_ino_t old_stashed_ino; + const char *old_operation; + char buf[80]; + struct problem_context pctx; + + /* begin process_inodes */ + if (process_inode_count == 0) + return; + old_operation = ehandler_operation(0); + old_stashed_inode = ctx->stashed_inode; + old_stashed_ino = ctx->stashed_ino; + qsort(inodes_to_process, process_inode_count, + sizeof(struct process_inode_block), process_inode_cmp); + clear_problem_context(&pctx); + for (i=0; i < process_inode_count; i++) { + pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode; + pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino; + sprintf(buf, _("reading indirect blocks of inode %u"), + pctx.ino); + ehandler_operation(buf); + check_blocks(ctx, &pctx, block_buf); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + break; + } + ctx->stashed_inode = old_stashed_inode; + ctx->stashed_ino = old_stashed_ino; + process_inode_count = 0; + /* end process inodes */ + + ehandler_operation(old_operation); +} + +static int process_inode_cmp(const void *a, const void *b) +{ + const struct process_inode_block *ib_a = + (const struct process_inode_block *) a; + const struct process_inode_block *ib_b = + (const struct process_inode_block *) b; + int ret; + + ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] - + ib_b->inode.i_block[EXT2_IND_BLOCK]); + if (ret == 0) + ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl; + return ret; +} + +/* + * Mark an inode as being bad in some what + */ +static void mark_inode_bad(e2fsck_t ctx, ino_t ino) +{ + struct problem_context pctx; + + if (!ctx->inode_bad_map) { + clear_problem_context(&pctx); + + pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs, + _("bad inode map"), &ctx->inode_bad_map); + if (pctx.errcode) { + pctx.num = 3; + fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino); +} + + +/* + * This procedure will allocate the inode imagic table + */ +static void alloc_imagic_map(e2fsck_t ctx) +{ + struct problem_context pctx; + + clear_problem_context(&pctx); + pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs, + _("imagic inode map"), + &ctx->inode_imagic_map); + if (pctx.errcode) { + pctx.num = 5; + fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } +} + +/* + * Marks a block as in use, setting the dup_map if it's been set + * already. Called by process_block and process_bad_block. + * + * WARNING: Assumes checks have already been done to make sure block + * is valid. This is true in both process_block and process_bad_block. + */ +static void mark_block_used(e2fsck_t ctx, blk_t block) +{ + struct problem_context pctx; + + clear_problem_context(&pctx); + + if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) { + if (!ctx->block_dup_map) { + pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs, + _("multiply claimed block map"), + &ctx->block_dup_map); + if (pctx.errcode) { + pctx.num = 3; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, + &pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block); + } else { + ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block); + } +} + +/* + * Adjust the extended attribute block's reference counts at the end + * of pass 1, either by subtracting out references for EA blocks that + * are still referenced in ctx->refcount, or by adding references for + * EA blocks that had extra references as accounted for in + * ctx->refcount_extra. + */ +static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, + char *block_buf, int adjust_sign) +{ + struct ext2_ext_attr_header *header; + struct problem_context pctx; + ext2_filsys fs = ctx->fs; + blk_t blk; + __u32 should_be; + int count; + + clear_problem_context(&pctx); + + ea_refcount_intr_begin(refcount); + while (1) { + if ((blk = ea_refcount_intr_next(refcount, &count)) == 0) + break; + pctx.blk = blk; + pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf); + if (pctx.errcode) { + fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx); + return; + } + header = (struct ext2_ext_attr_header *) block_buf; + pctx.blkcount = header->h_refcount; + should_be = header->h_refcount + adjust_sign * count; + pctx.num = should_be; + if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) { + header->h_refcount = should_be; + pctx.errcode = ext2fs_write_ext_attr(fs, blk, + block_buf); + if (pctx.errcode) { + fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx); + continue; + } + } + } +} + +/* + * Handle processing the extended attribute blocks + */ +static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, + char *block_buf) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t ino = pctx->ino; + struct ext2_inode *inode = pctx->inode; + blk_t blk; + char * end; + struct ext2_ext_attr_header *header; + struct ext2_ext_attr_entry *entry; + int count; + region_t region; + + blk = inode->i_file_acl; + if (blk == 0) + return 0; + + /* + * If the Extended attribute flag isn't set, then a non-zero + * file acl means that the inode is corrupted. + * + * Or if the extended attribute block is an invalid block, + * then the inode is also corrupted. + */ + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) || + (blk < fs->super->s_first_data_block) || + (blk >= fs->super->s_blocks_count)) { + mark_inode_bad(ctx, ino); + return 0; + } + + /* If ea bitmap hasn't been allocated, create it */ + if (!ctx->block_ea_map) { + pctx->errcode = ext2fs_allocate_block_bitmap(fs, + _("ext attr block map"), + &ctx->block_ea_map); + if (pctx->errcode) { + pctx->num = 2; + fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + } + + /* Create the EA refcount structure if necessary */ + if (!ctx->refcount) { + pctx->errcode = ea_refcount_create(0, &ctx->refcount); + if (pctx->errcode) { + pctx->num = 1; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + } + + /* Have we seen this EA block before? */ + if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) { + if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0) + return 1; + /* Ooops, this EA was referenced more than it stated */ + if (!ctx->refcount_extra) { + pctx->errcode = ea_refcount_create(0, + &ctx->refcount_extra); + if (pctx->errcode) { + pctx->num = 2; + fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + } + ea_refcount_increment(ctx->refcount_extra, blk, 0); + return 1; + } + + /* + * OK, we haven't seen this EA block yet. So we need to + * validate it + */ + pctx->blk = blk; + pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf); + if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx)) + goto clear_extattr; + header = (struct ext2_ext_attr_header *) block_buf; + pctx->blk = inode->i_file_acl; + if (((ctx->ext_attr_ver == 1) && + (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) || + ((ctx->ext_attr_ver == 2) && + (header->h_magic != EXT2_EXT_ATTR_MAGIC))) { + if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx)) + goto clear_extattr; + } + + if (header->h_blocks != 1) { + if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx)) + goto clear_extattr; + } + + region = region_create(0, fs->blocksize); + if (!region) { + fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return 0; + } + if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) { + if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) + goto clear_extattr; + } + + entry = (struct ext2_ext_attr_entry *)(header+1); + end = block_buf + fs->blocksize; + while ((char *)entry < end && *(__u32 *)entry) { + if (region_allocate(region, (char *)entry - (char *)header, + EXT2_EXT_ATTR_LEN(entry->e_name_len))) { + if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) + goto clear_extattr; + } + if ((ctx->ext_attr_ver == 1 && + (entry->e_name_len == 0 || entry->e_name_index != 0)) || + (ctx->ext_attr_ver == 2 && + entry->e_name_index == 0)) { + if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx)) + goto clear_extattr; + } + if (entry->e_value_block != 0) { + if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx)) + goto clear_extattr; + } + if (entry->e_value_size && + region_allocate(region, entry->e_value_offs, + EXT2_EXT_ATTR_SIZE(entry->e_value_size))) { + if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) + goto clear_extattr; + } + entry = EXT2_EXT_ATTR_NEXT(entry); + } + if (region_allocate(region, (char *)entry - (char *)header, 4)) { + if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx)) + goto clear_extattr; + } + region_free(region); + + count = header->h_refcount - 1; + if (count) + ea_refcount_store(ctx->refcount, blk, count); + mark_block_used(ctx, blk); + ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk); + + return 1; + +clear_extattr: + inode->i_file_acl = 0; + e2fsck_write_inode(ctx, ino, inode, "check_ext_attr"); + return 0; +} + +/* Returns 1 if bad htree, 0 if OK */ +static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, + ext2_ino_t ino FSCK_ATTR((unused)), + struct ext2_inode *inode, + char *block_buf) +{ + struct ext2_dx_root_info *root; + ext2_filsys fs = ctx->fs; + errcode_t retval; + blk_t blk; + + if ((!LINUX_S_ISDIR(inode->i_mode) && + fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) || + (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) && + fix_problem(ctx, PR_1_HTREE_SET, pctx))) + return 1; + + blk = inode->i_block[0]; + if (((blk == 0) || + (blk < fs->super->s_first_data_block) || + (blk >= fs->super->s_blocks_count)) && + fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) + return 1; + + retval = io_channel_read_blk(fs->io, blk, 1, block_buf); + if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) + return 1; + + /* XXX should check that beginning matches a directory */ + root = (struct ext2_dx_root_info *) (block_buf + 24); + + if ((root->reserved_zero || root->info_length < 8) && + fix_problem(ctx, PR_1_HTREE_BADROOT, pctx)) + return 1; + + pctx->num = root->hash_version; + if ((root->hash_version != EXT2_HASH_LEGACY) && + (root->hash_version != EXT2_HASH_HALF_MD4) && + (root->hash_version != EXT2_HASH_TEA) && + fix_problem(ctx, PR_1_HTREE_HASHV, pctx)) + return 1; + + if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) && + fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx)) + return 1; + + pctx->num = root->indirect_levels; + if ((root->indirect_levels > 1) && + fix_problem(ctx, PR_1_HTREE_DEPTH, pctx)) + return 1; + + return 0; +} + +/* + * This subroutine is called on each inode to account for all of the + * blocks used by that inode. + */ +static void check_blocks(e2fsck_t ctx, struct problem_context *pctx, + char *block_buf) +{ + ext2_filsys fs = ctx->fs; + struct process_block_struct_1 pb; + ext2_ino_t ino = pctx->ino; + struct ext2_inode *inode = pctx->inode; + int bad_size = 0; + int dirty_inode = 0; + __u64 size; + + pb.ino = ino; + pb.num_blocks = 0; + pb.last_block = -1; + pb.num_illegal_blocks = 0; + pb.suppress = 0; pb.clear = 0; + pb.fragmented = 0; + pb.compressed = 0; + pb.previous_block = 0; + pb.is_dir = LINUX_S_ISDIR(inode->i_mode); + pb.is_reg = LINUX_S_ISREG(inode->i_mode); + pb.max_blocks = 1 << (31 - fs->super->s_log_block_size); + pb.inode = inode; + pb.pctx = pctx; + pb.ctx = ctx; + pctx->ino = ino; + pctx->errcode = 0; + + if (inode->i_flags & EXT2_COMPRBLK_FL) { + if (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_COMPRESSION) + pb.compressed = 1; + else { + if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) { + inode->i_flags &= ~EXT2_COMPRBLK_FL; + dirty_inode++; + } + } + } + + if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf)) + pb.num_blocks++; + + if (ext2fs_inode_has_valid_blocks(inode)) + pctx->errcode = ext2fs_block_iterate2(fs, ino, + pb.is_dir ? BLOCK_FLAG_HOLE : 0, + block_buf, process_block, &pb); + end_problem_latch(ctx, PR_LATCH_BLOCK); + end_problem_latch(ctx, PR_LATCH_TOOBIG); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + goto out; + if (pctx->errcode) + fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx); + + if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group) + ctx->fs_fragmented++; + + if (pb.clear) { + inode->i_links_count = 0; + ext2fs_icount_store(ctx->inode_link_info, ino, 0); + inode->i_dtime = time(NULL); + dirty_inode++; + ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); + /* + * The inode was probably partially accounted for + * before processing was aborted, so we need to + * restart the pass 1 scan. + */ + ctx->flags |= E2F_FLAG_RESTART; + goto out; + } + + if (inode->i_flags & EXT2_INDEX_FL) { + if (handle_htree(ctx, pctx, ino, inode, block_buf)) { + inode->i_flags &= ~EXT2_INDEX_FL; + dirty_inode++; + } else { +#ifdef ENABLE_HTREE + e2fsck_add_dx_dir(ctx, ino, pb.last_block+1); +#endif + } + } + if (ctx->dirs_to_hash && pb.is_dir && + !(inode->i_flags & EXT2_INDEX_FL) && + ((inode->i_size / fs->blocksize) >= 3)) + ext2fs_u32_list_add(ctx->dirs_to_hash, ino); + + if (!pb.num_blocks && pb.is_dir) { + if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) { + inode->i_links_count = 0; + ext2fs_icount_store(ctx->inode_link_info, ino, 0); + inode->i_dtime = time(NULL); + dirty_inode++; + ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); + ctx->fs_directory_count--; + goto out; + } + } + + pb.num_blocks *= (fs->blocksize / 512); + + if (pb.is_dir) { + int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (nblock > (pb.last_block + 1)) + bad_size = 1; + else if (nblock < (pb.last_block + 1)) { + if (((pb.last_block + 1) - nblock) > + fs->super->s_prealloc_dir_blocks) + bad_size = 2; + } + } else { + size = EXT2_I_SIZE(inode); + if ((pb.last_block >= 0) && + (size < (__u64) pb.last_block * fs->blocksize)) + bad_size = 3; + else if (size > ext2_max_sizes[fs->super->s_log_block_size]) + bad_size = 4; + } + /* i_size for symlinks is checked elsewhere */ + if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) { + pctx->num = (pb.last_block+1) * fs->blocksize; + if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) { + inode->i_size = pctx->num; + if (!LINUX_S_ISDIR(inode->i_mode)) + inode->i_size_high = pctx->num >> 32; + dirty_inode++; + } + pctx->num = 0; + } + if (LINUX_S_ISREG(inode->i_mode) && + (inode->i_size_high || inode->i_size & 0x80000000UL)) + ctx->large_files++; + if (pb.num_blocks != inode->i_blocks) { + pctx->num = pb.num_blocks; + if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) { + inode->i_blocks = pb.num_blocks; + dirty_inode++; + } + pctx->num = 0; + } +out: + if (dirty_inode) + e2fsck_write_inode(ctx, ino, inode, "check_blocks"); +} + + +/* + * This is a helper function for check_blocks(). + */ +static int process_block(ext2_filsys fs, + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct process_block_struct_1 *p; + struct problem_context *pctx; + blk_t blk = *block_nr; + int ret_code = 0; + int problem = 0; + e2fsck_t ctx; + + p = (struct process_block_struct_1 *) priv_data; + pctx = p->pctx; + ctx = p->ctx; + + if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) { + /* todo: Check that the comprblk_fl is high, that the + blkaddr pattern looks right (all non-holes up to + first EXT2FS_COMPRESSED_BLKADDR, then all + EXT2FS_COMPRESSED_BLKADDR up to end of cluster), + that the feature_incompat bit is high, and that the + inode is a regular file. If we're doing a "full + check" (a concept introduced to e2fsck by e2compr, + meaning that we look at data blocks as well as + metadata) then call some library routine that + checks the compressed data. I'll have to think + about this, because one particularly important + problem to be able to fix is to recalculate the + cluster size if necessary. I think that perhaps + we'd better do most/all e2compr-specific checks + separately, after the non-e2compr checks. If not + doing a full check, it may be useful to test that + the personality is linux; e.g. if it isn't then + perhaps this really is just an illegal block. */ + return 0; + } + + if (blk == 0) { + if (p->is_dir == 0) { + /* + * Should never happen, since only directories + * get called with BLOCK_FLAG_HOLE + */ +#ifdef DEBUG_E2FSCK + printf("process_block() called with blk == 0, " + "blockcnt=%d, inode %lu???\n", + blockcnt, p->ino); +#endif + return 0; + } + if (blockcnt < 0) + return 0; + if (blockcnt * fs->blocksize < p->inode->i_size) { + goto mark_dir; + } + return 0; + } + + /* + * Simplistic fragmentation check. We merely require that the + * file be contiguous. (Which can never be true for really + * big files that are greater than a block group.) + */ + if (!HOLE_BLKADDR(p->previous_block)) { + if (p->previous_block+1 != blk) + p->fragmented = 1; + } + p->previous_block = blk; + + if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size))) + problem = PR_1_TOOBIG_DIR; + if (p->is_reg && p->num_blocks+1 >= p->max_blocks) + problem = PR_1_TOOBIG_REG; + if (!p->is_dir && !p->is_reg && blockcnt > 0) + problem = PR_1_TOOBIG_SYMLINK; + + if (blk < fs->super->s_first_data_block || + blk >= fs->super->s_blocks_count) + problem = PR_1_ILLEGAL_BLOCK_NUM; + + if (problem) { + p->num_illegal_blocks++; + if (!p->suppress && (p->num_illegal_blocks % 12) == 0) { + if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) { + p->clear = 1; + return BLOCK_ABORT; + } + if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) { + p->suppress = 1; + set_latch_flags(PR_LATCH_BLOCK, + PRL_SUPPRESS, 0); + } + } + pctx->blk = blk; + pctx->blkcount = blockcnt; + if (fix_problem(ctx, problem, pctx)) { + blk = *block_nr = 0; + ret_code = BLOCK_CHANGED; + goto mark_dir; + } else + return 0; + } + + if (p->ino == EXT2_RESIZE_INO) { + /* + * The resize inode has already be sanity checked + * during pass #0 (the superblock checks). All we + * have to do is mark the double indirect block as + * being in use; all of the other blocks are handled + * by mark_table_blocks()). + */ + if (blockcnt == BLOCK_COUNT_DIND) + mark_block_used(ctx, blk); + } else + mark_block_used(ctx, blk); + p->num_blocks++; + if (blockcnt >= 0) + p->last_block = blockcnt; +mark_dir: + if (p->is_dir && (blockcnt >= 0)) { + pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino, + blk, blockcnt); + if (pctx->errcode) { + pctx->blk = blk; + pctx->num = blockcnt; + fix_problem(ctx, PR_1_ADD_DBLOCK, pctx); + /* Should never get here */ + ctx->flags |= E2F_FLAG_ABORT; + return BLOCK_ABORT; + } + } + return ret_code; +} + +static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)), + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data EXT2FS_ATTR((unused))) +{ + /* + * Note: This function processes blocks for the bad blocks + * inode, which is never compressed. So we don't use HOLE_BLKADDR(). + */ + + printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr); + return BLOCK_ERROR; +} + +/* + * This routine gets called at the end of pass 1 if bad blocks are + * detected in the superblock, group descriptors, inode_bitmaps, or + * block bitmaps. At this point, all of the blocks have been mapped + * out, so we can try to allocate new block(s) to replace the bad + * blocks. + */ +static void handle_fs_bad_blocks(e2fsck_t ctx) +{ + printf("Bad blocks detected on your filesystem\n" + "You should get your data off as the device will soon die\n"); +} + +/* + * This routine marks all blocks which are used by the superblock, + * group descriptors, inode bitmaps, and block bitmaps. + */ +static void mark_table_blocks(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk_t block, b; + dgrp_t i; + int j; + struct problem_context pctx; + + clear_problem_context(&pctx); + + block = fs->super->s_first_data_block; + for (i = 0; i < fs->group_desc_count; i++) { + pctx.group = i; + + ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map); + + /* + * Mark the blocks used for the inode table + */ + if (fs->group_desc[i].bg_inode_table) { + for (j = 0, b = fs->group_desc[i].bg_inode_table; + j < fs->inode_blocks_per_group; + j++, b++) { + if (ext2fs_test_block_bitmap(ctx->block_found_map, + b)) { + pctx.blk = b; + if (fix_problem(ctx, + PR_1_ITABLE_CONFLICT, &pctx)) { + ctx->invalid_inode_table_flag[i]++; + ctx->invalid_bitmaps++; + } + } else { + ext2fs_mark_block_bitmap(ctx->block_found_map, b); + } + } + } + + /* + * Mark block used for the block bitmap + */ + if (fs->group_desc[i].bg_block_bitmap) { + if (ext2fs_test_block_bitmap(ctx->block_found_map, + fs->group_desc[i].bg_block_bitmap)) { + pctx.blk = fs->group_desc[i].bg_block_bitmap; + if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) { + ctx->invalid_block_bitmap_flag[i]++; + ctx->invalid_bitmaps++; + } + } else { + ext2fs_mark_block_bitmap(ctx->block_found_map, + fs->group_desc[i].bg_block_bitmap); + } + } + /* + * Mark block used for the inode bitmap + */ + if (fs->group_desc[i].bg_inode_bitmap) { + if (ext2fs_test_block_bitmap(ctx->block_found_map, + fs->group_desc[i].bg_inode_bitmap)) { + pctx.blk = fs->group_desc[i].bg_inode_bitmap; + if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) { + ctx->invalid_inode_bitmap_flag[i]++; + ctx->invalid_bitmaps++; + } + } else { + ext2fs_mark_block_bitmap(ctx->block_found_map, + fs->group_desc[i].bg_inode_bitmap); + } + } + block += fs->super->s_blocks_per_group; + } +} + +/* + * Thes subroutines short circuits ext2fs_get_blocks and + * ext2fs_check_directory; we use them since we already have the inode + * structure, so there's no point in letting the ext2fs library read + * the inode again. + */ +static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino, + blk_t *blocks) +{ + e2fsck_t ctx = (e2fsck_t) fs->priv_data; + int i; + + if ((ino != ctx->stashed_ino) || !ctx->stashed_inode) + return EXT2_ET_CALLBACK_NOTHANDLED; + + for (i=0; i < EXT2_N_BLOCKS; i++) + blocks[i] = ctx->stashed_inode->i_block[i]; + return 0; +} + +static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + e2fsck_t ctx = (e2fsck_t) fs->priv_data; + + if ((ino != ctx->stashed_ino) || !ctx->stashed_inode) + return EXT2_ET_CALLBACK_NOTHANDLED; + *inode = *ctx->stashed_inode; + return 0; +} + +static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + e2fsck_t ctx = (e2fsck_t) fs->priv_data; + + if ((ino == ctx->stashed_ino) && ctx->stashed_inode) + *ctx->stashed_inode = *inode; + return EXT2_ET_CALLBACK_NOTHANDLED; +} + +static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino) +{ + e2fsck_t ctx = (e2fsck_t) fs->priv_data; + + if ((ino != ctx->stashed_ino) || !ctx->stashed_inode) + return EXT2_ET_CALLBACK_NOTHANDLED; + + if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode)) + return EXT2_ET_NO_DIRECTORY; + return 0; +} + +void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool) +{ + ext2_filsys fs = ctx->fs; + + if (bool) { + fs->get_blocks = pass1_get_blocks; + fs->check_directory = pass1_check_directory; + fs->read_inode = pass1_read_inode; + fs->write_inode = pass1_write_inode; + ctx->stashed_ino = 0; + } else { + fs->get_blocks = 0; + fs->check_directory = 0; + fs->read_inode = 0; + fs->write_inode = 0; + } +} + +/* + * pass1b.c --- Pass #1b of e2fsck + * + * This file contains pass1B, pass1C, and pass1D of e2fsck. They are + * only invoked if pass 1 discovered blocks which are in use by more + * than one inode. + * + * Pass1B scans the data blocks of all the inodes again, generating a + * complete list of duplicate blocks and which inodes have claimed + * them. + * + * Pass1C does a tree-traversal of the filesystem, to determine the + * parent directories of these inodes. This step is necessary so that + * e2fsck can print out the pathnames of affected inodes. + * + * Pass1D is a reconciliation pass. For each inode with duplicate + * blocks, the user is prompted if s/he would like to clone the file + * (so that the file gets a fresh copy of the duplicated blocks) or + * simply to delete the file. + * + */ + + +/* Needed for architectures where sizeof(int) != sizeof(void *) */ +#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val)) +#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr)) + +/* Define an extension to the ext2 library's block count information */ +#define BLOCK_COUNT_EXTATTR (-5) + +struct block_el { + blk_t block; + struct block_el *next; +}; + +struct inode_el { + ext2_ino_t inode; + struct inode_el *next; +}; + +struct dup_block { + int num_bad; + struct inode_el *inode_list; +}; + +/* + * This structure stores information about a particular inode which + * is sharing blocks with other inodes. This information is collected + * to display to the user, so that the user knows what files he or she + * is dealing with, when trying to decide how to resolve the conflict + * of multiply-claimed blocks. + */ +struct dup_inode { + ext2_ino_t dir; + int num_dupblocks; + struct ext2_inode inode; + struct block_el *block_list; +}; + +static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr, + e2_blkcnt_t blockcnt, blk_t ref_blk, + int ref_offset, void *priv_data); +static void delete_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char *block_buf); +static int clone_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char* block_buf); +static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk); + +static void pass1b(e2fsck_t ctx, char *block_buf); +static void pass1c(e2fsck_t ctx, char *block_buf); +static void pass1d(e2fsck_t ctx, char *block_buf); + +static int dup_inode_count = 0; + +static dict_t blk_dict, ino_dict; + +static ext2fs_inode_bitmap inode_dup_map; + +static int dict_int_cmp(const void *a, const void *b) +{ + intptr_t ia, ib; + + ia = (intptr_t)a; + ib = (intptr_t)b; + + return (ia-ib); +} + +/* + * Add a duplicate block record + */ +static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk, + struct ext2_inode *inode) +{ + dnode_t *n; + struct dup_block *db; + struct dup_inode *di; + struct block_el *blk_el; + struct inode_el *ino_el; + + n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk)); + if (n) + db = (struct dup_block *) dnode_get(n); + else { + db = (struct dup_block *) e2fsck_allocate_memory(ctx, + sizeof(struct dup_block), "duplicate block header"); + db->num_bad = 0; + db->inode_list = 0; + dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db); + } + ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx, + sizeof(struct inode_el), "inode element"); + ino_el->inode = ino; + ino_el->next = db->inode_list; + db->inode_list = ino_el; + db->num_bad++; + + n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino)); + if (n) + di = (struct dup_inode *) dnode_get(n); + else { + di = (struct dup_inode *) e2fsck_allocate_memory(ctx, + sizeof(struct dup_inode), "duplicate inode header"); + di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0; + di->num_dupblocks = 0; + di->block_list = 0; + di->inode = *inode; + dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di); + } + blk_el = (struct block_el *) e2fsck_allocate_memory(ctx, + sizeof(struct block_el), "block element"); + blk_el->block = blk; + blk_el->next = di->block_list; + di->block_list = blk_el; + di->num_dupblocks++; +} + +/* + * Free a duplicate inode record + */ +static void inode_dnode_free(dnode_t *node) +{ + struct dup_inode *di; + struct block_el *p, *next; + + di = (struct dup_inode *) dnode_get(node); + for (p = di->block_list; p; p = next) { + next = p->next; + free(p); + } + free(node); +} + +/* + * Free a duplicate block record + */ +static void block_dnode_free(dnode_t *node) +{ + struct dup_block *db; + struct inode_el *p, *next; + + db = (struct dup_block *) dnode_get(node); + for (p = db->inode_list; p; p = next) { + next = p->next; + free(p); + } + free(node); +} + + +/* + * Main procedure for handling duplicate blocks + */ +void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + struct problem_context pctx; + + clear_problem_context(&pctx); + + pctx.errcode = ext2fs_allocate_inode_bitmap(fs, + _("multiply claimed inode map"), &inode_dup_map); + if (pctx.errcode) { + fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp); + dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp); + dict_set_allocator(&ino_dict, inode_dnode_free); + dict_set_allocator(&blk_dict, block_dnode_free); + + pass1b(ctx, block_buf); + pass1c(ctx, block_buf); + pass1d(ctx, block_buf); + + /* + * Time to free all of the accumulated data structures that we + * don't need anymore. + */ + dict_free_nodes(&ino_dict); + dict_free_nodes(&blk_dict); +} + +/* + * Scan the inodes looking for inodes that contain duplicate blocks. + */ +struct process_block_struct_1b { + e2fsck_t ctx; + ext2_ino_t ino; + int dup_blocks; + struct ext2_inode *inode; + struct problem_context *pctx; +}; + +static void pass1b(e2fsck_t ctx, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t ino; + struct ext2_inode inode; + ext2_inode_scan scan; + struct process_block_struct_1b pb; + struct problem_context pctx; + + clear_problem_context(&pctx); + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_1B_PASS_HEADER, &pctx); + pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, + &scan); + if (pctx.errcode) { + fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ctx->stashed_inode = &inode; + pb.ctx = ctx; + pb.pctx = &pctx; + pctx.str = "pass1b"; + while (1) { + pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode); + if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) + continue; + if (pctx.errcode) { + fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (!ino) + break; + pctx.ino = ctx->stashed_ino = ino; + if ((ino != EXT2_BAD_INO) && + !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)) + continue; + + pb.ino = ino; + pb.dup_blocks = 0; + pb.inode = &inode; + + if (ext2fs_inode_has_valid_blocks(&inode) || + (ino == EXT2_BAD_INO)) + pctx.errcode = ext2fs_block_iterate2(fs, ino, + 0, block_buf, process_pass1b_block, &pb); + if (inode.i_file_acl) + process_pass1b_block(fs, &inode.i_file_acl, + BLOCK_COUNT_EXTATTR, 0, 0, &pb); + if (pb.dup_blocks) { + end_problem_latch(ctx, PR_LATCH_DBLOCK); + if (ino >= EXT2_FIRST_INODE(fs->super) || + ino == EXT2_ROOT_INO) + dup_inode_count++; + } + if (pctx.errcode) + fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); + } + ext2fs_close_inode_scan(scan); + e2fsck_use_inode_shortcuts(ctx, 0); +} + +static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)), + blk_t *block_nr, + e2_blkcnt_t blockcnt FSCK_ATTR((unused)), + blk_t ref_blk FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct process_block_struct_1b *p; + e2fsck_t ctx; + + if (HOLE_BLKADDR(*block_nr)) + return 0; + p = (struct process_block_struct_1b *) priv_data; + ctx = p->ctx; + + if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) + return 0; + + /* OK, this is a duplicate block */ + if (p->ino != EXT2_BAD_INO) { + p->pctx->blk = *block_nr; + fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx); + } + p->dup_blocks++; + ext2fs_mark_inode_bitmap(inode_dup_map, p->ino); + + add_dupe(ctx, p->ino, *block_nr, p->inode); + + return 0; +} + +/* + * Pass 1c: Scan directories for inodes with duplicate blocks. This + * is used so that we can print pathnames when prompting the user for + * what to do. + */ +struct search_dir_struct { + int count; + ext2_ino_t first_inode; + ext2_ino_t max_inode; +}; + +static int search_dirent_proc(ext2_ino_t dir, int entry, + struct ext2_dir_entry *dirent, + int offset FSCK_ATTR((unused)), + int blocksize FSCK_ATTR((unused)), + char *buf FSCK_ATTR((unused)), + void *priv_data) +{ + struct search_dir_struct *sd; + struct dup_inode *p; + dnode_t *n; + + sd = (struct search_dir_struct *) priv_data; + + if (dirent->inode > sd->max_inode) + /* Should abort this inode, but not everything */ + return 0; + + if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) || + !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode)) + return 0; + + n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode)); + if (!n) + return 0; + p = (struct dup_inode *) dnode_get(n); + p->dir = dir; + sd->count--; + + return sd->count ? 0 : DIRENT_ABORT; +} + + +static void pass1c(e2fsck_t ctx, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + struct search_dir_struct sd; + struct problem_context pctx; + + clear_problem_context(&pctx); + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_1C_PASS_HEADER, &pctx); + + /* + * Search through all directories to translate inodes to names + * (by searching for the containing directory for that inode.) + */ + sd.count = dup_inode_count; + sd.first_inode = EXT2_FIRST_INODE(fs->super); + sd.max_inode = fs->super->s_inodes_count; + ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf, + search_dirent_proc, &sd); +} + +static void pass1d(e2fsck_t ctx, char *block_buf) +{ + ext2_filsys fs = ctx->fs; + struct dup_inode *p, *t; + struct dup_block *q; + ext2_ino_t *shared, ino; + int shared_len; + int i; + int file_ok; + int meta_data = 0; + struct problem_context pctx; + dnode_t *n, *m; + struct block_el *s; + struct inode_el *r; + + clear_problem_context(&pctx); + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_1D_PASS_HEADER, &pctx); + e2fsck_read_bitmaps(ctx); + + pctx.num = dup_inode_count; /* dict_count(&ino_dict); */ + fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx); + shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx, + sizeof(ext2_ino_t) * dict_count(&ino_dict), + "Shared inode list"); + for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) { + p = (struct dup_inode *) dnode_get(n); + shared_len = 0; + file_ok = 1; + ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n)); + if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO) + continue; + + /* + * Find all of the inodes which share blocks with this + * one. First we find all of the duplicate blocks + * belonging to this inode, and then search each block + * get the list of inodes, and merge them together. + */ + for (s = p->block_list; s; s = s->next) { + m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block)); + if (!m) + continue; /* Should never happen... */ + q = (struct dup_block *) dnode_get(m); + if (q->num_bad > 1) + file_ok = 0; + if (check_if_fs_block(ctx, s->block)) { + file_ok = 0; + meta_data = 1; + } + + /* + * Add all inodes used by this block to the + * shared[] --- which is a unique list, so + * if an inode is already in shared[], don't + * add it again. + */ + for (r = q->inode_list; r; r = r->next) { + if (r->inode == ino) + continue; + for (i = 0; i < shared_len; i++) + if (shared[i] == r->inode) + break; + if (i == shared_len) { + shared[shared_len++] = r->inode; + } + } + } + + /* + * Report the inode that we are working on + */ + pctx.inode = &p->inode; + pctx.ino = ino; + pctx.dir = p->dir; + pctx.blkcount = p->num_dupblocks; + pctx.num = meta_data ? shared_len+1 : shared_len; + fix_problem(ctx, PR_1D_DUP_FILE, &pctx); + pctx.blkcount = 0; + pctx.num = 0; + + if (meta_data) + fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx); + + for (i = 0; i < shared_len; i++) { + m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i])); + if (!m) + continue; /* should never happen */ + t = (struct dup_inode *) dnode_get(m); + /* + * Report the inode that we are sharing with + */ + pctx.inode = &t->inode; + pctx.ino = shared[i]; + pctx.dir = t->dir; + fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx); + } + if (file_ok) { + fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx); + continue; + } + if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) { + pctx.errcode = clone_file(ctx, ino, p, block_buf); + if (pctx.errcode) + fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx); + else + continue; + } + if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx)) + delete_file(ctx, ino, p, block_buf); + else + ext2fs_unmark_valid(fs); + } + ext2fs_free_mem(&shared); +} + +/* + * Drop the refcount on the dup_block structure, and clear the entry + * in the block_dup_map if appropriate. + */ +static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p) +{ + p->num_bad--; + if (p->num_bad <= 0 || + (p->num_bad == 1 && !check_if_fs_block(ctx, block))) + ext2fs_unmark_block_bitmap(ctx->block_dup_map, block); +} + +static int delete_file_block(ext2_filsys fs, + blk_t *block_nr, + e2_blkcnt_t blockcnt FSCK_ATTR((unused)), + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct process_block_struct_1b *pb; + struct dup_block *p; + dnode_t *n; + e2fsck_t ctx; + + pb = (struct process_block_struct_1b *) priv_data; + ctx = pb->ctx; + + if (HOLE_BLKADDR(*block_nr)) + return 0; + + if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { + n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr)); + if (n) { + p = (struct dup_block *) dnode_get(n); + decrement_badcount(ctx, *block_nr, p); + } else + bb_error_msg(_("internal error; can't find dup_blk for %d"), + *block_nr); + } else { + ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); + ext2fs_block_alloc_stats(fs, *block_nr, -1); + } + + return 0; +} + +static void delete_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char* block_buf) +{ + ext2_filsys fs = ctx->fs; + struct process_block_struct_1b pb; + struct ext2_inode inode; + struct problem_context pctx; + unsigned int count; + + clear_problem_context(&pctx); + pctx.ino = pb.ino = ino; + pb.dup_blocks = dp->num_dupblocks; + pb.ctx = ctx; + pctx.str = "delete_file"; + + e2fsck_read_inode(ctx, ino, &inode, "delete_file"); + if (ext2fs_inode_has_valid_blocks(&inode)) + pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, + delete_file_block, &pb); + if (pctx.errcode) + fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); + ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); + if (ctx->inode_bad_map) + ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); + ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); + + /* Inode may have changed by block_iterate, so reread it */ + e2fsck_read_inode(ctx, ino, &inode, "delete_file"); + inode.i_links_count = 0; + inode.i_dtime = time(NULL); + if (inode.i_file_acl && + (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { + count = 1; + pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, + block_buf, -1, &count); + if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { + pctx.errcode = 0; + count = 1; + } + if (pctx.errcode) { + pctx.blk = inode.i_file_acl; + fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx); + } + /* + * If the count is zero, then arrange to have the + * block deleted. If the block is in the block_dup_map, + * also call delete_file_block since it will take care + * of keeping the accounting straight. + */ + if ((count == 0) || + ext2fs_test_block_bitmap(ctx->block_dup_map, + inode.i_file_acl)) + delete_file_block(fs, &inode.i_file_acl, + BLOCK_COUNT_EXTATTR, 0, 0, &pb); + } + e2fsck_write_inode(ctx, ino, &inode, "delete_file"); +} + +struct clone_struct { + errcode_t errcode; + ext2_ino_t dir; + char *buf; + e2fsck_t ctx; +}; + +static int clone_file_block(ext2_filsys fs, + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct dup_block *p; + blk_t new_block; + errcode_t retval; + struct clone_struct *cs = (struct clone_struct *) priv_data; + dnode_t *n; + e2fsck_t ctx; + + ctx = cs->ctx; + + if (HOLE_BLKADDR(*block_nr)) + return 0; + + if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { + n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr)); + if (n) { + p = (struct dup_block *) dnode_get(n); + retval = ext2fs_new_block(fs, 0, ctx->block_found_map, + &new_block); + if (retval) { + cs->errcode = retval; + return BLOCK_ABORT; + } + if (cs->dir && (blockcnt >= 0)) { + retval = ext2fs_set_dir_block(fs->dblist, + cs->dir, new_block, blockcnt); + if (retval) { + cs->errcode = retval; + return BLOCK_ABORT; + } + } + + retval = io_channel_read_blk(fs->io, *block_nr, 1, + cs->buf); + if (retval) { + cs->errcode = retval; + return BLOCK_ABORT; + } + retval = io_channel_write_blk(fs->io, new_block, 1, + cs->buf); + if (retval) { + cs->errcode = retval; + return BLOCK_ABORT; + } + decrement_badcount(ctx, *block_nr, p); + *block_nr = new_block; + ext2fs_mark_block_bitmap(ctx->block_found_map, + new_block); + ext2fs_mark_block_bitmap(fs->block_map, new_block); + return BLOCK_CHANGED; + } else + bb_error_msg(_("internal error; can't find dup_blk for %d"), + *block_nr); + } + return 0; +} + +static int clone_file(e2fsck_t ctx, ext2_ino_t ino, + struct dup_inode *dp, char* block_buf) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + struct clone_struct cs; + struct problem_context pctx; + blk_t blk; + dnode_t *n; + struct inode_el *ino_el; + struct dup_block *db; + struct dup_inode *di; + + clear_problem_context(&pctx); + cs.errcode = 0; + cs.dir = 0; + cs.ctx = ctx; + retval = ext2fs_get_mem(fs->blocksize, &cs.buf); + if (retval) + return retval; + + if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) + cs.dir = ino; + + pctx.ino = ino; + pctx.str = "clone_file"; + if (ext2fs_inode_has_valid_blocks(&dp->inode)) + pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, + clone_file_block, &cs); + ext2fs_mark_bb_dirty(fs); + if (pctx.errcode) { + fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); + retval = pctx.errcode; + goto errout; + } + if (cs.errcode) { + bb_error_msg(_("returned from clone_file_block")); + retval = cs.errcode; + goto errout; + } + /* The inode may have changed on disk, so we have to re-read it */ + e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA"); + blk = dp->inode.i_file_acl; + if (blk && (clone_file_block(fs, &dp->inode.i_file_acl, + BLOCK_COUNT_EXTATTR, 0, 0, &cs) == + BLOCK_CHANGED)) { + e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA"); + /* + * If we cloned the EA block, find all other inodes + * which refered to that EA block, and modify + * them to point to the new EA block. + */ + n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk)); + db = (struct dup_block *) dnode_get(n); + for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) { + if (ino_el->inode == ino) + continue; + n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode)); + di = (struct dup_inode *) dnode_get(n); + if (di->inode.i_file_acl == blk) { + di->inode.i_file_acl = dp->inode.i_file_acl; + e2fsck_write_inode(ctx, ino_el->inode, + &di->inode, "clone file EA"); + decrement_badcount(ctx, blk, db); + } + } + } + retval = 0; +errout: + ext2fs_free_mem(&cs.buf); + return retval; +} + +/* + * This routine returns 1 if a block overlaps with one of the superblocks, + * group descriptors, inode bitmaps, or block bitmaps. + */ +static int check_if_fs_block(e2fsck_t ctx, blk_t test_block) +{ + ext2_filsys fs = ctx->fs; + blk_t block; + dgrp_t i; + + block = fs->super->s_first_data_block; + for (i = 0; i < fs->group_desc_count; i++) { + + /* Check superblocks/block group descriptros */ + if (ext2fs_bg_has_super(fs, i)) { + if (test_block >= block && + (test_block <= block + fs->desc_blocks)) + return 1; + } + + /* Check the inode table */ + if ((fs->group_desc[i].bg_inode_table) && + (test_block >= fs->group_desc[i].bg_inode_table) && + (test_block < (fs->group_desc[i].bg_inode_table + + fs->inode_blocks_per_group))) + return 1; + + /* Check the bitmap blocks */ + if ((test_block == fs->group_desc[i].bg_block_bitmap) || + (test_block == fs->group_desc[i].bg_inode_bitmap)) + return 1; + + block += fs->super->s_blocks_per_group; + } + return 0; +} +/* + * pass2.c --- check directory structure + * + * Pass 2 of e2fsck iterates through all active directory inodes, and + * applies to following tests to each directory entry in the directory + * blocks in the inodes: + * + * - The length of the directory entry (rec_len) should be at + * least 8 bytes, and no more than the remaining space + * left in the directory block. + * - The length of the name in the directory entry (name_len) + * should be less than (rec_len - 8). + * - The inode number in the directory entry should be within + * legal bounds. + * - The inode number should refer to a in-use inode. + * - The first entry should be '.', and its inode should be + * the inode of the directory. + * - The second entry should be '..'. + * + * To minimize disk seek time, the directory blocks are processed in + * sorted order of block numbers. + * + * Pass 2 also collects the following information: + * - The inode numbers of the subdirectories for each directory. + * + * Pass 2 relies on the following information from previous passes: + * - The directory information collected in pass 1. + * - The inode_used_map bitmap + * - The inode_bad_map bitmap + * - The inode_dir_map bitmap + * + * Pass 2 frees the following data structures + * - The inode_bad_map bitmap + * - The inode_reg_map bitmap + */ + +/* + * Keeps track of how many times an inode is referenced. + */ +static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf); +static int check_dir_block(ext2_filsys fs, + struct ext2_db_entry *dir_blocks_info, + void *priv_data); +static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info, + struct problem_context *pctx); +static int update_dir_block(ext2_filsys fs, + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, + int ref_offset, + void *priv_data); +static void clear_htree(e2fsck_t ctx, ext2_ino_t ino); +static int htree_depth(struct dx_dir_info *dx_dir, + struct dx_dirblock_info *dx_db); +static int special_dir_block_cmp(const void *a, const void *b); + +struct check_dir_struct { + char *buf; + struct problem_context pctx; + int count, max; + e2fsck_t ctx; +}; + +static void e2fsck_pass2(e2fsck_t ctx) +{ + struct ext2_super_block *sb = ctx->fs->super; + struct problem_context pctx; + ext2_filsys fs = ctx->fs; + char *buf; + struct dir_info *dir; + struct check_dir_struct cd; + struct dx_dir_info *dx_dir; + struct dx_dirblock_info *dx_db, *dx_parent; + int b; + int i, depth; + problem_t code; + int bad_dir; + + clear_problem_context(&cd.pctx); + + /* Pass 2 */ + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); + + cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT, + 0, ctx->inode_link_info, + &ctx->inode_count); + if (cd.pctx.errcode) { + fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize, + "directory scan buffer"); + + /* + * Set up the parent pointer for the root directory, if + * present. (If the root directory is not present, we will + * create it in pass 3.) + */ + dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO); + if (dir) + dir->parent = EXT2_ROOT_INO; + + cd.buf = buf; + cd.ctx = ctx; + cd.count = 1; + cd.max = ext2fs_dblist_count(fs->dblist); + + if (ctx->progress) + (void) (ctx->progress)(ctx, 2, 0, cd.max); + + if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) + ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp); + + cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block, + &cd); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + if (cd.pctx.errcode) { + fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + +#ifdef ENABLE_HTREE + for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) { + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + if (dx_dir->numblocks == 0) + continue; + clear_problem_context(&pctx); + bad_dir = 0; + pctx.dir = dx_dir->ino; + dx_db = dx_dir->dx_block; + if (dx_db->flags & DX_FLAG_REFERENCED) + dx_db->flags |= DX_FLAG_DUP_REF; + else + dx_db->flags |= DX_FLAG_REFERENCED; + /* + * Find all of the first and last leaf blocks, and + * update their parent's min and max hash values + */ + for (b=0, dx_db = dx_dir->dx_block; + b < dx_dir->numblocks; + b++, dx_db++) { + if ((dx_db->type != DX_DIRBLOCK_LEAF) || + !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST))) + continue; + dx_parent = &dx_dir->dx_block[dx_db->parent]; + /* + * XXX Make sure dx_parent->min_hash > dx_db->min_hash + */ + if (dx_db->flags & DX_FLAG_FIRST) + dx_parent->min_hash = dx_db->min_hash; + /* + * XXX Make sure dx_parent->max_hash < dx_db->max_hash + */ + if (dx_db->flags & DX_FLAG_LAST) + dx_parent->max_hash = dx_db->max_hash; + } + + for (b=0, dx_db = dx_dir->dx_block; + b < dx_dir->numblocks; + b++, dx_db++) { + pctx.blkcount = b; + pctx.group = dx_db->parent; + code = 0; + if (!(dx_db->flags & DX_FLAG_FIRST) && + (dx_db->min_hash < dx_db->node_min_hash)) { + pctx.blk = dx_db->min_hash; + pctx.blk2 = dx_db->node_min_hash; + code = PR_2_HTREE_MIN_HASH; + fix_problem(ctx, code, &pctx); + bad_dir++; + } + if (dx_db->type == DX_DIRBLOCK_LEAF) { + depth = htree_depth(dx_dir, dx_db); + if (depth != dx_dir->depth) { + code = PR_2_HTREE_BAD_DEPTH; + fix_problem(ctx, code, &pctx); + bad_dir++; + } + } + /* + * This test doesn't apply for the root block + * at block #0 + */ + if (b && + (dx_db->max_hash > dx_db->node_max_hash)) { + pctx.blk = dx_db->max_hash; + pctx.blk2 = dx_db->node_max_hash; + code = PR_2_HTREE_MAX_HASH; + fix_problem(ctx, code, &pctx); + bad_dir++; + } + if (!(dx_db->flags & DX_FLAG_REFERENCED)) { + code = PR_2_HTREE_NOTREF; + fix_problem(ctx, code, &pctx); + bad_dir++; + } else if (dx_db->flags & DX_FLAG_DUP_REF) { + code = PR_2_HTREE_DUPREF; + fix_problem(ctx, code, &pctx); + bad_dir++; + } + if (code == 0) + continue; + } + if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) { + clear_htree(ctx, dx_dir->ino); + dx_dir->numblocks = 0; + } + } +#endif + ext2fs_free_mem(&buf); + ext2fs_free_dblist(fs->dblist); + + ext2fs_free_inode_bitmap(ctx->inode_bad_map); + ctx->inode_bad_map = 0; + ext2fs_free_inode_bitmap(ctx->inode_reg_map); + ctx->inode_reg_map = 0; + + clear_problem_context(&pctx); + if (ctx->large_files) { + if (!(sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_LARGE_FILE) && + fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { + sb->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + ext2fs_mark_super_dirty(fs); + } + if (sb->s_rev_level == EXT2_GOOD_OLD_REV && + fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { + ext2fs_update_dynamic_rev(fs); + ext2fs_mark_super_dirty(fs); + } + } else if (!ctx->large_files && + (sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) { + if (fs->flags & EXT2_FLAG_RW) { + sb->s_feature_ro_compat &= + ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + ext2fs_mark_super_dirty(fs); + } + } +} + +#define MAX_DEPTH 32000 +static int htree_depth(struct dx_dir_info *dx_dir, + struct dx_dirblock_info *dx_db) +{ + int depth = 0; + + while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) { + dx_db = &dx_dir->dx_block[dx_db->parent]; + depth++; + } + return depth; +} + +static int dict_de_cmp(const void *a, const void *b) +{ + const struct ext2_dir_entry *de_a, *de_b; + int a_len, b_len; + + de_a = (const struct ext2_dir_entry *) a; + a_len = de_a->name_len & 0xFF; + de_b = (const struct ext2_dir_entry *) b; + b_len = de_b->name_len & 0xFF; + + if (a_len != b_len) + return (a_len - b_len); + + return strncmp(de_a->name, de_b->name, a_len); +} + +/* + * This is special sort function that makes sure that directory blocks + * with a dirblock of zero are sorted to the beginning of the list. + * This guarantees that the root node of the htree directories are + * processed first, so we know what hash version to use. + */ +static int special_dir_block_cmp(const void *a, const void *b) +{ + const struct ext2_db_entry *db_a = + (const struct ext2_db_entry *) a; + const struct ext2_db_entry *db_b = + (const struct ext2_db_entry *) b; + + if (db_a->blockcnt && !db_b->blockcnt) + return 1; + + if (!db_a->blockcnt && db_b->blockcnt) + return -1; + + if (db_a->blk != db_b->blk) + return (int) (db_a->blk - db_b->blk); + + if (db_a->ino != db_b->ino) + return (int) (db_a->ino - db_b->ino); + + return (int) (db_a->blockcnt - db_b->blockcnt); +} + + +/* + * Make sure the first entry in the directory is '.', and that the + * directory entry is sane. + */ +static int check_dot(e2fsck_t ctx, + struct ext2_dir_entry *dirent, + ext2_ino_t ino, struct problem_context *pctx) +{ + struct ext2_dir_entry *nextdir; + int status = 0; + int created = 0; + int new_len; + int problem = 0; + + if (!dirent->inode) + problem = PR_2_MISSING_DOT; + else if (((dirent->name_len & 0xFF) != 1) || + (dirent->name[0] != '.')) + problem = PR_2_1ST_NOT_DOT; + else if (dirent->name[1] != '\0') + problem = PR_2_DOT_NULL_TERM; + + if (problem) { + if (fix_problem(ctx, problem, pctx)) { + if (dirent->rec_len < 12) + dirent->rec_len = 12; + dirent->inode = ino; + dirent->name_len = 1; + dirent->name[0] = '.'; + dirent->name[1] = '\0'; + status = 1; + created = 1; + } + } + if (dirent->inode != ino) { + if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { + dirent->inode = ino; + status = 1; + } + } + if (dirent->rec_len > 12) { + new_len = dirent->rec_len - 12; + if (new_len > 12) { + if (created || + fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { + nextdir = (struct ext2_dir_entry *) + ((char *) dirent + 12); + dirent->rec_len = 12; + nextdir->rec_len = new_len; + nextdir->inode = 0; + nextdir->name_len = 0; + status = 1; + } + } + } + return status; +} + +/* + * Make sure the second entry in the directory is '..', and that the + * directory entry is sane. We do not check the inode number of '..' + * here; this gets done in pass 3. + */ +static int check_dotdot(e2fsck_t ctx, + struct ext2_dir_entry *dirent, + struct dir_info *dir, struct problem_context *pctx) +{ + int problem = 0; + + if (!dirent->inode) + problem = PR_2_MISSING_DOT_DOT; + else if (((dirent->name_len & 0xFF) != 2) || + (dirent->name[0] != '.') || + (dirent->name[1] != '.')) + problem = PR_2_2ND_NOT_DOT_DOT; + else if (dirent->name[2] != '\0') + problem = PR_2_DOT_DOT_NULL_TERM; + + if (problem) { + if (fix_problem(ctx, problem, pctx)) { + if (dirent->rec_len < 12) + dirent->rec_len = 12; + /* + * Note: we don't have the parent inode just + * yet, so we will fill it in with the root + * inode. This will get fixed in pass 3. + */ + dirent->inode = EXT2_ROOT_INO; + dirent->name_len = 2; + dirent->name[0] = '.'; + dirent->name[1] = '.'; + dirent->name[2] = '\0'; + return 1; + } + return 0; + } + dir->dotdot = dirent->inode; + return 0; +} + +/* + * Check to make sure a directory entry doesn't contain any illegal + * characters. + */ +static int check_name(e2fsck_t ctx, + struct ext2_dir_entry *dirent, + struct problem_context *pctx) +{ + int i; + int fixup = -1; + int ret = 0; + + for ( i = 0; i < (dirent->name_len & 0xFF); i++) { + if (dirent->name[i] == '/' || dirent->name[i] == '\0') { + if (fixup < 0) { + fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); + } + if (fixup) { + dirent->name[i] = '.'; + ret = 1; + } + } + } + return ret; +} + +/* + * Check the directory filetype (if present) + */ + +/* + * Given a mode, return the ext2 file type + */ +static int ext2_file_type(unsigned int mode) +{ + if (LINUX_S_ISREG(mode)) + return EXT2_FT_REG_FILE; + + if (LINUX_S_ISDIR(mode)) + return EXT2_FT_DIR; + + if (LINUX_S_ISCHR(mode)) + return EXT2_FT_CHRDEV; + + if (LINUX_S_ISBLK(mode)) + return EXT2_FT_BLKDEV; + + if (LINUX_S_ISLNK(mode)) + return EXT2_FT_SYMLINK; + + if (LINUX_S_ISFIFO(mode)) + return EXT2_FT_FIFO; + + if (LINUX_S_ISSOCK(mode)) + return EXT2_FT_SOCK; + + return 0; +} + +static int check_filetype(e2fsck_t ctx, + struct ext2_dir_entry *dirent, + struct problem_context *pctx) +{ + int filetype = dirent->name_len >> 8; + int should_be = EXT2_FT_UNKNOWN; + struct ext2_inode inode; + + if (!(ctx->fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE)) { + if (filetype == 0 || + !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) + return 0; + dirent->name_len = dirent->name_len & 0xFF; + return 1; + } + + if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) { + should_be = EXT2_FT_DIR; + } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map, + dirent->inode)) { + should_be = EXT2_FT_REG_FILE; + } else if (ctx->inode_bad_map && + ext2fs_test_inode_bitmap(ctx->inode_bad_map, + dirent->inode)) + should_be = 0; + else { + e2fsck_read_inode(ctx, dirent->inode, &inode, + "check_filetype"); + should_be = ext2_file_type(inode.i_mode); + } + if (filetype == should_be) + return 0; + pctx->num = should_be; + + if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, + pctx) == 0) + return 0; + + dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8; + return 1; +} + +#ifdef ENABLE_HTREE +static void parse_int_node(ext2_filsys fs, + struct ext2_db_entry *db, + struct check_dir_struct *cd, + struct dx_dir_info *dx_dir, + char *block_buf) +{ + struct ext2_dx_root_info *root; + struct ext2_dx_entry *ent; + struct ext2_dx_countlimit *limit; + struct dx_dirblock_info *dx_db; + int i, expect_limit, count; + blk_t blk; + ext2_dirhash_t min_hash = 0xffffffff; + ext2_dirhash_t max_hash = 0; + ext2_dirhash_t hash = 0, prev_hash; + + if (db->blockcnt == 0) { + root = (struct ext2_dx_root_info *) (block_buf + 24); + ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length); + } else { + ent = (struct ext2_dx_entry *) (block_buf+8); + } + limit = (struct ext2_dx_countlimit *) ent; + + count = ext2fs_le16_to_cpu(limit->count); + expect_limit = (fs->blocksize - ((char *) ent - block_buf)) / + sizeof(struct ext2_dx_entry); + if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) { + cd->pctx.num = ext2fs_le16_to_cpu(limit->limit); + if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx)) + goto clear_and_exit; + } + if (count > expect_limit) { + cd->pctx.num = count; + if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx)) + goto clear_and_exit; + count = expect_limit; + } + + for (i=0; i < count; i++) { + prev_hash = hash; + hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0; + blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff; + /* Check to make sure the block is valid */ + if (blk > (blk_t) dx_dir->numblocks) { + cd->pctx.blk = blk; + if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK, + &cd->pctx)) + goto clear_and_exit; + } + if (hash < prev_hash && + fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx)) + goto clear_and_exit; + dx_db = &dx_dir->dx_block[blk]; + if (dx_db->flags & DX_FLAG_REFERENCED) { + dx_db->flags |= DX_FLAG_DUP_REF; + } else { + dx_db->flags |= DX_FLAG_REFERENCED; + dx_db->parent = db->blockcnt; + } + if (hash < min_hash) + min_hash = hash; + if (hash > max_hash) + max_hash = hash; + dx_db->node_min_hash = hash; + if ((i+1) < count) + dx_db->node_max_hash = + ext2fs_le32_to_cpu(ent[i+1].hash) & ~1; + else { + dx_db->node_max_hash = 0xfffffffe; + dx_db->flags |= DX_FLAG_LAST; + } + if (i == 0) + dx_db->flags |= DX_FLAG_FIRST; + } + dx_db = &dx_dir->dx_block[db->blockcnt]; + dx_db->min_hash = min_hash; + dx_db->max_hash = max_hash; + return; + +clear_and_exit: + clear_htree(cd->ctx, cd->pctx.ino); + dx_dir->numblocks = 0; +} +#endif /* ENABLE_HTREE */ + +/* + * Given a busted directory, try to salvage it somehow. + * + */ +static void salvage_directory(ext2_filsys fs, + struct ext2_dir_entry *dirent, + struct ext2_dir_entry *prev, + unsigned int *offset) +{ + char *cp = (char *) dirent; + int left = fs->blocksize - *offset - dirent->rec_len; + int name_len = dirent->name_len & 0xFF; + + /* + * Special case of directory entry of size 8: copy what's left + * of the directory block up to cover up the invalid hole. + */ + if ((left >= 12) && (dirent->rec_len == 8)) { + memmove(cp, cp+8, left); + memset(cp + left, 0, 8); + return; + } + /* + * If the directory entry overruns the end of the directory + * block, and the name is small enough to fit, then adjust the + * record length. + */ + if ((left < 0) && + (name_len + 8 <= dirent->rec_len + left) && + dirent->inode <= fs->super->s_inodes_count && + strnlen(dirent->name, name_len) == name_len) { + dirent->rec_len += left; + return; + } + /* + * If the directory entry is a multiple of four, so it is + * valid, let the previous directory entry absorb the invalid + * one. + */ + if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) { + prev->rec_len += dirent->rec_len; + *offset += dirent->rec_len; + return; + } + /* + * Default salvage method --- kill all of the directory + * entries for the rest of the block. We will either try to + * absorb it into the previous directory entry, or create a + * new empty directory entry the rest of the directory block. + */ + if (prev) { + prev->rec_len += fs->blocksize - *offset; + *offset = fs->blocksize; + } else { + dirent->rec_len = fs->blocksize - *offset; + dirent->name_len = 0; + dirent->inode = 0; + } +} + +static int check_dir_block(ext2_filsys fs, + struct ext2_db_entry *db, + void *priv_data) +{ + struct dir_info *subdir, *dir; + struct dx_dir_info *dx_dir; +#ifdef ENABLE_HTREE + struct dx_dirblock_info *dx_db = NULL; +#endif /* ENABLE_HTREE */ + struct ext2_dir_entry *dirent, *prev; + ext2_dirhash_t hash; + unsigned int offset = 0; + int dir_modified = 0; + int dot_state; + blk_t block_nr = db->blk; + ext2_ino_t ino = db->ino; + __u16 links; + struct check_dir_struct *cd; + char *buf; + e2fsck_t ctx; + int problem; + struct ext2_dx_root_info *root; + struct ext2_dx_countlimit *limit; + static dict_t de_dict; + struct problem_context pctx; + int dups_found = 0; + + cd = (struct check_dir_struct *) priv_data; + buf = cd->buf; + ctx = cd->ctx; + + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return DIRENT_ABORT; + + if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) + return DIRENT_ABORT; + + /* + * Make sure the inode is still in use (could have been + * deleted in the duplicate/bad blocks pass. + */ + if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) + return 0; + + cd->pctx.ino = ino; + cd->pctx.blk = block_nr; + cd->pctx.blkcount = db->blockcnt; + cd->pctx.ino2 = 0; + cd->pctx.dirent = 0; + cd->pctx.num = 0; + + if (db->blk == 0) { + if (allocate_dir_block(ctx, db, &cd->pctx)) + return 0; + block_nr = db->blk; + } + + if (db->blockcnt) + dot_state = 2; + else + dot_state = 0; + + if (ctx->dirs_to_hash && + ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) + dups_found++; + + cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf); + if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) + cd->pctx.errcode = 0; /* We'll handle this ourselves */ + if (cd->pctx.errcode) { + if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { + ctx->flags |= E2F_FLAG_ABORT; + return DIRENT_ABORT; + } + memset(buf, 0, fs->blocksize); + } +#ifdef ENABLE_HTREE + dx_dir = e2fsck_get_dx_dir_info(ctx, ino); + if (dx_dir && dx_dir->numblocks) { + if (db->blockcnt >= dx_dir->numblocks) { + printf("XXX should never happen!!!\n"); + abort(); + } + dx_db = &dx_dir->dx_block[db->blockcnt]; + dx_db->type = DX_DIRBLOCK_LEAF; + dx_db->phys = block_nr; + dx_db->min_hash = ~0; + dx_db->max_hash = 0; + + dirent = (struct ext2_dir_entry *) buf; + limit = (struct ext2_dx_countlimit *) (buf+8); + if (db->blockcnt == 0) { + root = (struct ext2_dx_root_info *) (buf + 24); + dx_db->type = DX_DIRBLOCK_ROOT; + dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; + if ((root->reserved_zero || + root->info_length < 8 || + root->indirect_levels > 1) && + fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { + clear_htree(ctx, ino); + dx_dir->numblocks = 0; + dx_db = 0; + } + dx_dir->hashversion = root->hash_version; + dx_dir->depth = root->indirect_levels + 1; + } else if ((dirent->inode == 0) && + (dirent->rec_len == fs->blocksize) && + (dirent->name_len == 0) && + (ext2fs_le16_to_cpu(limit->limit) == + ((fs->blocksize-8) / + sizeof(struct ext2_dx_entry)))) + dx_db->type = DX_DIRBLOCK_NODE; + } +#endif /* ENABLE_HTREE */ + + dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); + prev = 0; + do { + problem = 0; + dirent = (struct ext2_dir_entry *) (buf + offset); + cd->pctx.dirent = dirent; + cd->pctx.num = offset; + if (((offset + dirent->rec_len) > fs->blocksize) || + (dirent->rec_len < 12) || + ((dirent->rec_len % 4) != 0) || + (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { + if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { + salvage_directory(fs, dirent, prev, &offset); + dir_modified++; + continue; + } else + goto abort_free_dict; + } + if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) { + if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) { + dirent->name_len = EXT2_NAME_LEN; + dir_modified++; + } + } + + if (dot_state == 0) { + if (check_dot(ctx, dirent, ino, &cd->pctx)) + dir_modified++; + } else if (dot_state == 1) { + dir = e2fsck_get_dir_info(ctx, ino); + if (!dir) { + fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); + goto abort_free_dict; + } + if (check_dotdot(ctx, dirent, dir, &cd->pctx)) + dir_modified++; + } else if (dirent->inode == ino) { + problem = PR_2_LINK_DOT; + if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { + dirent->inode = 0; + dir_modified++; + goto next; + } + } + if (!dirent->inode) + goto next; + + /* + * Make sure the inode listed is a legal one. + */ + if (((dirent->inode != EXT2_ROOT_INO) && + (dirent->inode < EXT2_FIRST_INODE(fs->super))) || + (dirent->inode > fs->super->s_inodes_count)) { + problem = PR_2_BAD_INO; + } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, + dirent->inode))) { + /* + * If the inode is unused, offer to clear it. + */ + problem = PR_2_UNUSED_INODE; + } else if ((dot_state > 1) && + ((dirent->name_len & 0xFF) == 1) && + (dirent->name[0] == '.')) { + /* + * If there's a '.' entry in anything other + * than the first directory entry, it's a + * duplicate entry that should be removed. + */ + problem = PR_2_DUP_DOT; + } else if ((dot_state > 1) && + ((dirent->name_len & 0xFF) == 2) && + (dirent->name[0] == '.') && + (dirent->name[1] == '.')) { + /* + * If there's a '..' entry in anything other + * than the second directory entry, it's a + * duplicate entry that should be removed. + */ + problem = PR_2_DUP_DOT_DOT; + } else if ((dot_state > 1) && + (dirent->inode == EXT2_ROOT_INO)) { + /* + * Don't allow links to the root directory. + * We check this specially to make sure we + * catch this error case even if the root + * directory hasn't been created yet. + */ + problem = PR_2_LINK_ROOT; + } else if ((dot_state > 1) && + (dirent->name_len & 0xFF) == 0) { + /* + * Don't allow zero-length directory names. + */ + problem = PR_2_NULL_NAME; + } + + if (problem) { + if (fix_problem(ctx, problem, &cd->pctx)) { + dirent->inode = 0; + dir_modified++; + goto next; + } else { + ext2fs_unmark_valid(fs); + if (problem == PR_2_BAD_INO) + goto next; + } + } + + /* + * If the inode was marked as having bad fields in + * pass1, process it and offer to fix/clear it. + * (We wait until now so that we can display the + * pathname to the user.) + */ + if (ctx->inode_bad_map && + ext2fs_test_inode_bitmap(ctx->inode_bad_map, + dirent->inode)) { + if (e2fsck_process_bad_inode(ctx, ino, + dirent->inode, + buf + fs->blocksize)) { + dirent->inode = 0; + dir_modified++; + goto next; + } + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return DIRENT_ABORT; + } + + if (check_name(ctx, dirent, &cd->pctx)) + dir_modified++; + + if (check_filetype(ctx, dirent, &cd->pctx)) + dir_modified++; + +#ifdef ENABLE_HTREE + if (dx_db) { + ext2fs_dirhash(dx_dir->hashversion, dirent->name, + (dirent->name_len & 0xFF), + fs->super->s_hash_seed, &hash, 0); + if (hash < dx_db->min_hash) + dx_db->min_hash = hash; + if (hash > dx_db->max_hash) + dx_db->max_hash = hash; + } +#endif + + /* + * If this is a directory, then mark its parent in its + * dir_info structure. If the parent field is already + * filled in, then this directory has more than one + * hard link. We assume the first link is correct, + * and ask the user if he/she wants to clear this one. + */ + if ((dot_state > 1) && + (ext2fs_test_inode_bitmap(ctx->inode_dir_map, + dirent->inode))) { + subdir = e2fsck_get_dir_info(ctx, dirent->inode); + if (!subdir) { + cd->pctx.ino = dirent->inode; + fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); + goto abort_free_dict; + } + if (subdir->parent) { + cd->pctx.ino2 = subdir->parent; + if (fix_problem(ctx, PR_2_LINK_DIR, + &cd->pctx)) { + dirent->inode = 0; + dir_modified++; + goto next; + } + cd->pctx.ino2 = 0; + } else + subdir->parent = ino; + } + + if (dups_found) { + ; + } else if (dict_lookup(&de_dict, dirent)) { + clear_problem_context(&pctx); + pctx.ino = ino; + pctx.dirent = dirent; + fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); + if (!ctx->dirs_to_hash) + ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); + if (ctx->dirs_to_hash) + ext2fs_u32_list_add(ctx->dirs_to_hash, ino); + dups_found++; + } else + dict_alloc_insert(&de_dict, dirent, dirent); + + ext2fs_icount_increment(ctx->inode_count, dirent->inode, + &links); + if (links > 1) + ctx->fs_links_count++; + ctx->fs_total_count++; + next: + prev = dirent; + offset += dirent->rec_len; + dot_state++; + } while (offset < fs->blocksize); +#ifdef ENABLE_HTREE + if (dx_db) { + cd->pctx.dir = cd->pctx.ino; + if ((dx_db->type == DX_DIRBLOCK_ROOT) || + (dx_db->type == DX_DIRBLOCK_NODE)) + parse_int_node(fs, db, cd, dx_dir, buf); + } +#endif /* ENABLE_HTREE */ + if (offset != fs->blocksize) { + cd->pctx.num = dirent->rec_len - fs->blocksize + offset; + if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { + dirent->rec_len = cd->pctx.num; + dir_modified++; + } + } + if (dir_modified) { + cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); + if (cd->pctx.errcode) { + if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, + &cd->pctx)) + goto abort_free_dict; + } + ext2fs_mark_changed(fs); + } + dict_free_nodes(&de_dict); + return 0; +abort_free_dict: + dict_free_nodes(&de_dict); + ctx->flags |= E2F_FLAG_ABORT; + return DIRENT_ABORT; +} + +/* + * This function is called to deallocate a block, and is an interator + * functioned called by deallocate inode via ext2fs_iterate_block(). + */ +static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt FSCK_ATTR((unused)), + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + e2fsck_t ctx = (e2fsck_t) priv_data; + + if (HOLE_BLKADDR(*block_nr)) + return 0; + if ((*block_nr < fs->super->s_first_data_block) || + (*block_nr >= fs->super->s_blocks_count)) + return 0; + ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); + ext2fs_block_alloc_stats(fs, *block_nr, -1); + return 0; +} + +/* + * This fuction deallocates an inode + */ +static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + struct problem_context pctx; + __u32 count; + + ext2fs_icount_store(ctx->inode_link_info, ino, 0); + e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); + inode.i_links_count = 0; + inode.i_dtime = time(NULL); + e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode"); + clear_problem_context(&pctx); + pctx.ino = ino; + + /* + * Fix up the bitmaps... + */ + e2fsck_read_bitmaps(ctx); + ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); + ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); + if (ctx->inode_bad_map) + ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); + ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); + + if (inode.i_file_acl && + (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { + pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, + block_buf, -1, &count); + if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { + pctx.errcode = 0; + count = 1; + } + if (pctx.errcode) { + pctx.blk = inode.i_file_acl; + fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (count == 0) { + ext2fs_unmark_block_bitmap(ctx->block_found_map, + inode.i_file_acl); + ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1); + } + inode.i_file_acl = 0; + } + + if (!ext2fs_inode_has_valid_blocks(&inode)) + return; + + if (LINUX_S_ISREG(inode.i_mode) && + (inode.i_size_high || inode.i_size & 0x80000000UL)) + ctx->large_files--; + + pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, + deallocate_inode_block, ctx); + if (pctx.errcode) { + fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } +} + +/* + * This fuction clears the htree flag on an inode + */ +static void clear_htree(e2fsck_t ctx, ext2_ino_t ino) +{ + struct ext2_inode inode; + + e2fsck_read_inode(ctx, ino, &inode, "clear_htree"); + inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL; + e2fsck_write_inode(ctx, ino, &inode, "clear_htree"); + if (ctx->dirs_to_hash) + ext2fs_u32_list_add(ctx->dirs_to_hash, ino); +} + + +static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, + ext2_ino_t ino, char *buf) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + int inode_modified = 0; + int not_fixed = 0; + unsigned char *frag, *fsize; + struct problem_context pctx; + int problem = 0; + + e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); + + clear_problem_context(&pctx); + pctx.ino = ino; + pctx.dir = dir; + pctx.inode = &inode; + + if (inode.i_file_acl && + !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) && + fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { + inode.i_file_acl = 0; +#if BB_BIG_ENDIAN + /* + * This is a special kludge to deal with long symlinks + * on big endian systems. i_blocks had already been + * decremented earlier in pass 1, but since i_file_acl + * hadn't yet been cleared, ext2fs_read_inode() + * assumed that the file was short symlink and would + * not have byte swapped i_block[0]. Hence, we have + * to byte-swap it here. + */ + if (LINUX_S_ISLNK(inode.i_mode) && + (fs->flags & EXT2_FLAG_SWAP_BYTES) && + (inode.i_blocks == fs->blocksize >> 9)) + inode.i_block[0] = ext2fs_swab32(inode.i_block[0]); +#endif + inode_modified++; + } else + not_fixed++; + + if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && + !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && + !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && + !(LINUX_S_ISSOCK(inode.i_mode))) + problem = PR_2_BAD_MODE; + else if (LINUX_S_ISCHR(inode.i_mode) + && !e2fsck_pass1_check_device_inode(fs, &inode)) + problem = PR_2_BAD_CHAR_DEV; + else if (LINUX_S_ISBLK(inode.i_mode) + && !e2fsck_pass1_check_device_inode(fs, &inode)) + problem = PR_2_BAD_BLOCK_DEV; + else if (LINUX_S_ISFIFO(inode.i_mode) + && !e2fsck_pass1_check_device_inode(fs, &inode)) + problem = PR_2_BAD_FIFO; + else if (LINUX_S_ISSOCK(inode.i_mode) + && !e2fsck_pass1_check_device_inode(fs, &inode)) + problem = PR_2_BAD_SOCKET; + else if (LINUX_S_ISLNK(inode.i_mode) + && !e2fsck_pass1_check_symlink(fs, &inode, buf)) { + problem = PR_2_INVALID_SYMLINK; + } + + if (problem) { + if (fix_problem(ctx, problem, &pctx)) { + deallocate_inode(ctx, ino, 0); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return 0; + return 1; + } else + not_fixed++; + problem = 0; + } + + if (inode.i_faddr) { + if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { + inode.i_faddr = 0; + inode_modified++; + } else + not_fixed++; + } + + switch (fs->super->s_creator_os) { + case EXT2_OS_LINUX: + frag = &inode.osd2.linux2.l_i_frag; + fsize = &inode.osd2.linux2.l_i_fsize; + break; + case EXT2_OS_HURD: + frag = &inode.osd2.hurd2.h_i_frag; + fsize = &inode.osd2.hurd2.h_i_fsize; + break; + case EXT2_OS_MASIX: + frag = &inode.osd2.masix2.m_i_frag; + fsize = &inode.osd2.masix2.m_i_fsize; + break; + default: + frag = fsize = 0; + } + if (frag && *frag) { + pctx.num = *frag; + if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { + *frag = 0; + inode_modified++; + } else + not_fixed++; + pctx.num = 0; + } + if (fsize && *fsize) { + pctx.num = *fsize; + if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { + *fsize = 0; + inode_modified++; + } else + not_fixed++; + pctx.num = 0; + } + + if (inode.i_file_acl && + ((inode.i_file_acl < fs->super->s_first_data_block) || + (inode.i_file_acl >= fs->super->s_blocks_count))) { + if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { + inode.i_file_acl = 0; + inode_modified++; + } else + not_fixed++; + } + if (inode.i_dir_acl && + LINUX_S_ISDIR(inode.i_mode)) { + if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { + inode.i_dir_acl = 0; + inode_modified++; + } else + not_fixed++; + } + + if (inode_modified) + e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); + if (!not_fixed) + ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); + return 0; +} + + +/* + * allocate_dir_block --- this function allocates a new directory + * block for a particular inode; this is done if a directory has + * a "hole" in it, or if a directory has a illegal block number + * that was zeroed out and now needs to be replaced. + */ +static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db, + struct problem_context *pctx) +{ + ext2_filsys fs = ctx->fs; + blk_t blk; + char *block; + struct ext2_inode inode; + + if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) + return 1; + + /* + * Read the inode and block bitmaps in; we'll be messing with + * them. + */ + e2fsck_read_bitmaps(ctx); + + /* + * First, find a free block + */ + pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); + if (pctx->errcode) { + pctx->str = "ext2fs_new_block"; + fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); + return 1; + } + ext2fs_mark_block_bitmap(ctx->block_found_map, blk); + ext2fs_mark_block_bitmap(fs->block_map, blk); + ext2fs_mark_bb_dirty(fs); + + /* + * Now let's create the actual data block for the inode + */ + if (db->blockcnt) + pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); + else + pctx->errcode = ext2fs_new_dir_block(fs, db->ino, + EXT2_ROOT_INO, &block); + + if (pctx->errcode) { + pctx->str = "ext2fs_new_dir_block"; + fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); + return 1; + } + + pctx->errcode = ext2fs_write_dir_block(fs, blk, block); + ext2fs_free_mem(&block); + if (pctx->errcode) { + pctx->str = "ext2fs_write_dir_block"; + fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); + return 1; + } + + /* + * Update the inode block count + */ + e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); + inode.i_blocks += fs->blocksize / 512; + if (inode.i_size < (db->blockcnt+1) * fs->blocksize) + inode.i_size = (db->blockcnt+1) * fs->blocksize; + e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); + + /* + * Finally, update the block pointers for the inode + */ + db->blk = blk; + pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE, + 0, update_dir_block, db); + if (pctx->errcode) { + pctx->str = "ext2fs_block_iterate"; + fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); + return 1; + } + + return 0; +} + +/* + * This is a helper function for allocate_dir_block(). + */ +static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)), + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct ext2_db_entry *db; + + db = (struct ext2_db_entry *) priv_data; + if (db->blockcnt == (int) blockcnt) { + *block_nr = db->blk; + return BLOCK_CHANGED; + } + return 0; +} + +/* + * pass3.c -- pass #3 of e2fsck: Check for directory connectivity + * + * Pass #3 assures that all directories are connected to the + * filesystem tree, using the following algorithm: + * + * First, the root directory is checked to make sure it exists; if + * not, e2fsck will offer to create a new one. It is then marked as + * "done". + * + * Then, pass3 interates over all directory inodes; for each directory + * it attempts to trace up the filesystem tree, using dirinfo.parent + * until it reaches a directory which has been marked "done". If it + * cannot do so, then the directory must be disconnected, and e2fsck + * will offer to reconnect it to /lost+found. While it is chasing + * parent pointers up the filesystem tree, if pass3 sees a directory + * twice, then it has detected a filesystem loop, and it will again + * offer to reconnect the directory to /lost+found in to break the + * filesystem loop. + * + * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to + * reconnect inodes to /lost+found; this subroutine is also used by + * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which + * is responsible for creating /lost+found if it does not exist. + * + * Pass 3 frees the following data structures: + * - The dirinfo directory information cache. + */ + +static void check_root(e2fsck_t ctx); +static int check_directory(e2fsck_t ctx, struct dir_info *dir, + struct problem_context *pctx); +static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent); + +static ext2fs_inode_bitmap inode_loop_detect; +static ext2fs_inode_bitmap inode_done_map; + +static void e2fsck_pass3(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + int i; + struct problem_context pctx; + struct dir_info *dir; + unsigned long maxdirs, count; + + clear_problem_context(&pctx); + + /* Pass 3 */ + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_3_PASS_HEADER, &pctx); + + /* + * Allocate some bitmaps to do loop detection. + */ + pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"), + &inode_done_map); + if (pctx.errcode) { + pctx.num = 2; + fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + goto abort_exit; + } + check_root(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + goto abort_exit; + + ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO); + + maxdirs = e2fsck_get_num_dirinfo(ctx); + count = 1; + + if (ctx->progress) + if ((ctx->progress)(ctx, 3, 0, maxdirs)) + goto abort_exit; + + for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) { + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + goto abort_exit; + if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs)) + goto abort_exit; + if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino)) + if (check_directory(ctx, dir, &pctx)) + goto abort_exit; + } + + /* + * Force the creation of /lost+found if not present + */ + if ((ctx->flags & E2F_OPT_READONLY) == 0) + e2fsck_get_lost_and_found(ctx, 1); + + /* + * If there are any directories that need to be indexed or + * optimized, do it here. + */ + e2fsck_rehash_directories(ctx); + +abort_exit: + e2fsck_free_dir_info(ctx); + ext2fs_free_inode_bitmap(inode_loop_detect); + inode_loop_detect = 0; + ext2fs_free_inode_bitmap(inode_done_map); + inode_done_map = 0; +} + +/* + * This makes sure the root inode is present; if not, we ask if the + * user wants us to create it. Not creating it is a fatal error. + */ +static void check_root(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk_t blk; + struct ext2_inode inode; + char * block; + struct problem_context pctx; + + clear_problem_context(&pctx); + + if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) { + /* + * If the root inode is not a directory, die here. The + * user must have answered 'no' in pass1 when we + * offered to clear it. + */ + if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map, + EXT2_ROOT_INO))) { + fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + } + return; + } + + if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) { + fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + e2fsck_read_bitmaps(ctx); + + /* + * First, find a free block + */ + pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); + if (pctx.errcode) { + pctx.str = "ext2fs_new_block"; + fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_mark_block_bitmap(ctx->block_found_map, blk); + ext2fs_mark_block_bitmap(fs->block_map, blk); + ext2fs_mark_bb_dirty(fs); + + /* + * Now let's create the actual data block for the inode + */ + pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, + &block); + if (pctx.errcode) { + pctx.str = "ext2fs_new_dir_block"; + fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + pctx.errcode = ext2fs_write_dir_block(fs, blk, block); + if (pctx.errcode) { + pctx.str = "ext2fs_write_dir_block"; + fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_free_mem(&block); + + /* + * Set up the inode structure + */ + memset(&inode, 0, sizeof(inode)); + inode.i_mode = 040755; + inode.i_size = fs->blocksize; + inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL); + inode.i_links_count = 2; + inode.i_blocks = fs->blocksize / 512; + inode.i_block[0] = blk; + + /* + * Write out the inode. + */ + pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode); + if (pctx.errcode) { + pctx.str = "ext2fs_write_inode"; + fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + /* + * Miscellaneous bookkeeping... + */ + e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); + ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2); + ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2); + + ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO); + ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO); + ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO); + ext2fs_mark_ib_dirty(fs); +} + +/* + * This subroutine is responsible for making sure that a particular + * directory is connected to the root; if it isn't we trace it up as + * far as we can go, and then offer to connect the resulting parent to + * the lost+found. We have to do loop detection; if we ever discover + * a loop, we treat that as a disconnected directory and offer to + * reparent it to lost+found. + * + * However, loop detection is expensive, because for very large + * filesystems, the inode_loop_detect bitmap is huge, and clearing it + * is non-trivial. Loops in filesystems are also a rare error case, + * and we shouldn't optimize for error cases. So we try two passes of + * the algorithm. The first time, we ignore loop detection and merely + * increment a counter; if the counter exceeds some extreme threshold, + * then we try again with the loop detection bitmap enabled. + */ +static int check_directory(e2fsck_t ctx, struct dir_info *dir, + struct problem_context *pctx) +{ + ext2_filsys fs = ctx->fs; + struct dir_info *p = dir; + int loop_pass = 0, parent_count = 0; + + if (!p) + return 0; + + while (1) { + /* + * Mark this inode as being "done"; by the time we + * return from this function, the inode we either be + * verified as being connected to the directory tree, + * or we will have offered to reconnect this to + * lost+found. + * + * If it was marked done already, then we've reached a + * parent we've already checked. + */ + if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino)) + break; + + /* + * If this directory doesn't have a parent, or we've + * seen the parent once already, then offer to + * reparent it to lost+found + */ + if (!p->parent || + (loop_pass && + (ext2fs_test_inode_bitmap(inode_loop_detect, + p->parent)))) { + pctx->ino = p->ino; + if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) { + if (e2fsck_reconnect_file(ctx, pctx->ino)) + ext2fs_unmark_valid(fs); + else { + p = e2fsck_get_dir_info(ctx, pctx->ino); + p->parent = ctx->lost_and_found; + fix_dotdot(ctx, p, ctx->lost_and_found); + } + } + break; + } + p = e2fsck_get_dir_info(ctx, p->parent); + if (!p) { + fix_problem(ctx, PR_3_NO_DIRINFO, pctx); + return 0; + } + if (loop_pass) { + ext2fs_mark_inode_bitmap(inode_loop_detect, + p->ino); + } else if (parent_count++ > 2048) { + /* + * If we've run into a path depth that's + * greater than 2048, try again with the inode + * loop bitmap turned on and start from the + * top. + */ + loop_pass = 1; + if (inode_loop_detect) + ext2fs_clear_inode_bitmap(inode_loop_detect); + else { + pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect); + if (pctx->errcode) { + pctx->num = 1; + fix_problem(ctx, + PR_3_ALLOCATE_IBITMAP_ERROR, pctx); + ctx->flags |= E2F_FLAG_ABORT; + return -1; + } + } + p = dir; + } + } + + /* + * Make sure that .. and the parent directory are the same; + * offer to fix it if not. + */ + if (dir->parent != dir->dotdot) { + pctx->ino = dir->ino; + pctx->ino2 = dir->dotdot; + pctx->dir = dir->parent; + if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx)) + fix_dotdot(ctx, dir, dir->parent); + } + return 0; +} + +/* + * This routine gets the lost_and_found inode, making it a directory + * if necessary + */ +ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t ino; + blk_t blk; + errcode_t retval; + struct ext2_inode inode; + char * block; + static const char name[] = "lost+found"; + struct problem_context pctx; + struct dir_info *dirinfo; + + if (ctx->lost_and_found) + return ctx->lost_and_found; + + clear_problem_context(&pctx); + + retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, + sizeof(name)-1, 0, &ino); + if (retval && !fix) + return 0; + if (!retval) { + if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) { + ctx->lost_and_found = ino; + return ino; + } + + /* Lost+found isn't a directory! */ + if (!fix) + return 0; + pctx.ino = ino; + if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx)) + return 0; + + /* OK, unlink the old /lost+found file. */ + pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0); + if (pctx.errcode) { + pctx.str = "ext2fs_unlink"; + fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); + return 0; + } + dirinfo = e2fsck_get_dir_info(ctx, ino); + if (dirinfo) + dirinfo->parent = 0; + e2fsck_adjust_inode_count(ctx, ino, -1); + } else if (retval != EXT2_ET_FILE_NOT_FOUND) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); + } + if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) + return 0; + + /* + * Read the inode and block bitmaps in; we'll be messing with + * them. + */ + e2fsck_read_bitmaps(ctx); + + /* + * First, find a free block + */ + retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx); + return 0; + } + ext2fs_mark_block_bitmap(ctx->block_found_map, blk); + ext2fs_block_alloc_stats(fs, blk, +1); + + /* + * Next find a free inode. + */ + retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700, + ctx->inode_used_map, &ino); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx); + return 0; + } + ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); + ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino); + ext2fs_inode_alloc_stats2(fs, ino, +1, 1); + + /* + * Now let's create the actual data block for the inode + */ + retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx); + return 0; + } + + retval = ext2fs_write_dir_block(fs, blk, block); + ext2fs_free_mem(&block); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx); + return 0; + } + + /* + * Set up the inode structure + */ + memset(&inode, 0, sizeof(inode)); + inode.i_mode = 040700; + inode.i_size = fs->blocksize; + inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL); + inode.i_links_count = 2; + inode.i_blocks = fs->blocksize / 512; + inode.i_block[0] = blk; + + /* + * Next, write out the inode. + */ + pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode); + if (pctx.errcode) { + pctx.str = "ext2fs_write_inode"; + fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); + return 0; + } + /* + * Finally, create the directory link + */ + pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); + if (pctx.errcode) { + pctx.str = "ext2fs_link"; + fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); + return 0; + } + + /* + * Miscellaneous bookkeeping that needs to be kept straight. + */ + e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO); + e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1); + ext2fs_icount_store(ctx->inode_count, ino, 2); + ext2fs_icount_store(ctx->inode_link_info, ino, 2); + ctx->lost_and_found = ino; + return ino; +} + +/* + * This routine will connect a file to lost+found + */ +int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + char name[80]; + struct problem_context pctx; + struct ext2_inode inode; + int file_type = 0; + + clear_problem_context(&pctx); + pctx.ino = ino; + + if (!ctx->bad_lost_and_found && !ctx->lost_and_found) { + if (e2fsck_get_lost_and_found(ctx, 1) == 0) + ctx->bad_lost_and_found++; + } + if (ctx->bad_lost_and_found) { + fix_problem(ctx, PR_3_NO_LPF, &pctx); + return 1; + } + + sprintf(name, "#%u", ino); + if (ext2fs_read_inode(fs, ino, &inode) == 0) + file_type = ext2_file_type(inode.i_mode); + retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type); + if (retval == EXT2_ET_DIR_NO_SPACE) { + if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx)) + return 1; + retval = e2fsck_expand_directory(ctx, ctx->lost_and_found, + 1, 0); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx); + return 1; + } + retval = ext2fs_link(fs, ctx->lost_and_found, name, + ino, file_type); + } + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx); + return 1; + } + e2fsck_adjust_inode_count(ctx, ino, 1); + + return 0; +} + +/* + * Utility routine to adjust the inode counts on an inode. + */ +errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + struct ext2_inode inode; + + if (!ino) + return 0; + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + + if (adj == 1) { + ext2fs_icount_increment(ctx->inode_count, ino, 0); + if (inode.i_links_count == (__u16) ~0) + return 0; + ext2fs_icount_increment(ctx->inode_link_info, ino, 0); + inode.i_links_count++; + } else if (adj == -1) { + ext2fs_icount_decrement(ctx->inode_count, ino, 0); + if (inode.i_links_count == 0) + return 0; + ext2fs_icount_decrement(ctx->inode_link_info, ino, 0); + inode.i_links_count--; + } + + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) + return retval; + + return 0; +} + +/* + * Fix parent --- this routine fixes up the parent of a directory. + */ +struct fix_dotdot_struct { + ext2_filsys fs; + ext2_ino_t parent; + int done; + e2fsck_t ctx; +}; + +static int fix_dotdot_proc(struct ext2_dir_entry *dirent, + int offset FSCK_ATTR((unused)), + int blocksize FSCK_ATTR((unused)), + char *buf FSCK_ATTR((unused)), + void *priv_data) +{ + struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data; + errcode_t retval; + struct problem_context pctx; + + if ((dirent->name_len & 0xFF) != 2) + return 0; + if (strncmp(dirent->name, "..", 2)) + return 0; + + clear_problem_context(&pctx); + + retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1); + if (retval) { + pctx.errcode = retval; + fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); + } + retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1); + if (retval) { + pctx.errcode = retval; + fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); + } + dirent->inode = fp->parent; + + fp->done++; + return DIRENT_ABORT | DIRENT_CHANGED; +} + +static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + struct fix_dotdot_struct fp; + struct problem_context pctx; + + fp.fs = fs; + fp.parent = parent; + fp.done = 0; + fp.ctx = ctx; + + retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY, + 0, fix_dotdot_proc, &fp); + if (retval || !fp.done) { + clear_problem_context(&pctx); + pctx.ino = dir->ino; + pctx.errcode = retval; + fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR : + PR_3_FIX_PARENT_NOFIND, &pctx); + ext2fs_unmark_valid(fs); + } + dir->dotdot = parent; +} + +/* + * These routines are responsible for expanding a /lost+found if it is + * too small. + */ + +struct expand_dir_struct { + int num; + int guaranteed_size; + int newblocks; + int last_block; + errcode_t err; + e2fsck_t ctx; +}; + +static int expand_dir_proc(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; + blk_t new_blk; + static blk_t last_blk = 0; + char *block; + errcode_t retval; + e2fsck_t ctx; + + ctx = es->ctx; + + if (es->guaranteed_size && blockcnt >= es->guaranteed_size) + return BLOCK_ABORT; + + if (blockcnt > 0) + es->last_block = blockcnt; + if (*blocknr) { + last_blk = *blocknr; + return 0; + } + retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map, + &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt > 0) { + retval = ext2fs_new_dir_block(fs, 0, 0, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + es->num--; + retval = ext2fs_write_dir_block(fs, new_blk, block); + } else { + retval = ext2fs_get_mem(fs->blocksize, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + memset(block, 0, fs->blocksize); + retval = io_channel_write_blk(fs->io, new_blk, 1, block); + } + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + ext2fs_free_mem(&block); + *blocknr = new_blk; + ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk); + ext2fs_block_alloc_stats(fs, new_blk, +1); + es->newblocks++; + + if (es->num == 0) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; +} + +errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir, + int num, int guaranteed_size) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + struct expand_dir_struct es; + struct ext2_inode inode; + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + /* + * Read the inode and block bitmaps in; we'll be messing with + * them. + */ + e2fsck_read_bitmaps(ctx); + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + es.num = num; + es.guaranteed_size = guaranteed_size; + es.last_block = 0; + es.err = 0; + es.newblocks = 0; + es.ctx = ctx; + + retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, + 0, expand_dir_proc, &es); + + if (es.err) + return es.err; + + /* + * Update the size and block count fields in the inode. + */ + retval = ext2fs_read_inode(fs, dir, &inode); + if (retval) + return retval; + + inode.i_size = (es.last_block + 1) * fs->blocksize; + inode.i_blocks += (fs->blocksize / 512) * es.newblocks; + + e2fsck_write_inode(ctx, dir, &inode, "expand_directory"); + + return 0; +} + +/* + * pass4.c -- pass #4 of e2fsck: Check reference counts + * + * Pass 4 frees the following data structures: + * - A bitmap of which inodes are imagic inodes. (inode_imagic_map) + */ + +/* + * This routine is called when an inode is not connected to the + * directory tree. + * + * This subroutine returns 1 then the caller shouldn't bother with the + * rest of the pass 4 tests. + */ +static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + struct problem_context pctx; + + e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode"); + clear_problem_context(&pctx); + pctx.ino = i; + pctx.inode = &inode; + + /* + * Offer to delete any zero-length files that does not have + * blocks. If there is an EA block, it might have useful + * information, so we won't prompt to delete it, but let it be + * reconnected to lost+found. + */ + if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) || + LINUX_S_ISDIR(inode.i_mode))) { + if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) { + ext2fs_icount_store(ctx->inode_link_info, i, 0); + inode.i_links_count = 0; + inode.i_dtime = time(NULL); + e2fsck_write_inode(ctx, i, &inode, + "disconnect_inode"); + /* + * Fix up the bitmaps... + */ + e2fsck_read_bitmaps(ctx); + ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i); + ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i); + ext2fs_inode_alloc_stats2(fs, i, -1, + LINUX_S_ISDIR(inode.i_mode)); + return 0; + } + } + + /* + * Prompt to reconnect. + */ + if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) { + if (e2fsck_reconnect_file(ctx, i)) + ext2fs_unmark_valid(fs); + } else { + /* + * If we don't attach the inode, then skip the + * i_links_test since there's no point in trying to + * force i_links_count to zero. + */ + ext2fs_unmark_valid(fs); + return 1; + } + return 0; +} + + +static void e2fsck_pass4(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t i; + struct ext2_inode inode; + struct problem_context pctx; + __u16 link_count, link_counted; + char *buf = NULL; + int group, maxgroup; + + /* Pass 4 */ + + clear_problem_context(&pctx); + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_4_PASS_HEADER, &pctx); + + group = 0; + maxgroup = fs->group_desc_count; + if (ctx->progress) + if ((ctx->progress)(ctx, 4, 0, maxgroup)) + return; + + for (i=1; i <= fs->super->s_inodes_count; i++) { + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + if ((i % fs->super->s_inodes_per_group) == 0) { + group++; + if (ctx->progress) + if ((ctx->progress)(ctx, 4, group, maxgroup)) + return; + } + if (i == EXT2_BAD_INO || + (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) + continue; + if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) || + (ctx->inode_imagic_map && + ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i))) + continue; + ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count); + ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); + if (link_counted == 0) { + if (!buf) + buf = e2fsck_allocate_memory(ctx, + fs->blocksize, "bad_inode buffer"); + if (e2fsck_process_bad_inode(ctx, 0, i, buf)) + continue; + if (disconnect_inode(ctx, i)) + continue; + ext2fs_icount_fetch(ctx->inode_link_info, i, + &link_count); + ext2fs_icount_fetch(ctx->inode_count, i, + &link_counted); + } + if (link_counted != link_count) { + e2fsck_read_inode(ctx, i, &inode, "pass4"); + pctx.ino = i; + pctx.inode = &inode; + if (link_count != inode.i_links_count) { + pctx.num = link_count; + fix_problem(ctx, + PR_4_INCONSISTENT_COUNT, &pctx); + } + pctx.num = link_counted; + if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { + inode.i_links_count = link_counted; + e2fsck_write_inode(ctx, i, &inode, "pass4"); + } + } + } + ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; + ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; + ext2fs_free_inode_bitmap(ctx->inode_imagic_map); + ctx->inode_imagic_map = 0; + ext2fs_free_mem(&buf); +} + +/* + * pass5.c --- check block and inode bitmaps against on-disk bitmaps + */ + +#define NO_BLK ((blk_t) -1) + +static void print_bitmap_problem(e2fsck_t ctx, int problem, + struct problem_context *pctx) +{ + switch (problem) { + case PR_5_BLOCK_UNUSED: + if (pctx->blk == pctx->blk2) + pctx->blk2 = 0; + else + problem = PR_5_BLOCK_RANGE_UNUSED; + break; + case PR_5_BLOCK_USED: + if (pctx->blk == pctx->blk2) + pctx->blk2 = 0; + else + problem = PR_5_BLOCK_RANGE_USED; + break; + case PR_5_INODE_UNUSED: + if (pctx->ino == pctx->ino2) + pctx->ino2 = 0; + else + problem = PR_5_INODE_RANGE_UNUSED; + break; + case PR_5_INODE_USED: + if (pctx->ino == pctx->ino2) + pctx->ino2 = 0; + else + problem = PR_5_INODE_RANGE_USED; + break; + } + fix_problem(ctx, problem, pctx); + pctx->blk = pctx->blk2 = NO_BLK; + pctx->ino = pctx->ino2 = 0; +} + +static void check_block_bitmaps(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk_t i; + int *free_array; + int group = 0; + unsigned int blocks = 0; + unsigned int free_blocks = 0; + int group_free = 0; + int actual, bitmap; + struct problem_context pctx; + int problem, save_problem, fixit, had_problem; + errcode_t retval; + + clear_problem_context(&pctx); + free_array = (int *) e2fsck_allocate_memory(ctx, + fs->group_desc_count * sizeof(int), "free block count array"); + + if ((fs->super->s_first_data_block < + ext2fs_get_block_bitmap_start(ctx->block_found_map)) || + (fs->super->s_blocks_count-1 > + ext2fs_get_block_bitmap_end(ctx->block_found_map))) { + pctx.num = 1; + pctx.blk = fs->super->s_first_data_block; + pctx.blk2 = fs->super->s_blocks_count -1; + pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map); + pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map); + fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); + + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } + + if ((fs->super->s_first_data_block < + ext2fs_get_block_bitmap_start(fs->block_map)) || + (fs->super->s_blocks_count-1 > + ext2fs_get_block_bitmap_end(fs->block_map))) { + pctx.num = 2; + pctx.blk = fs->super->s_first_data_block; + pctx.blk2 = fs->super->s_blocks_count -1; + pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map); + pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map); + fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); + + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } + +redo_counts: + had_problem = 0; + save_problem = 0; + pctx.blk = pctx.blk2 = NO_BLK; + for (i = fs->super->s_first_data_block; + i < fs->super->s_blocks_count; + i++) { + actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i); + bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i); + + if (actual == bitmap) + goto do_counts; + + if (!actual && bitmap) { + /* + * Block not used, but marked in use in the bitmap. + */ + problem = PR_5_BLOCK_UNUSED; + } else { + /* + * Block used, but not marked in use in the bitmap. + */ + problem = PR_5_BLOCK_USED; + } + if (pctx.blk == NO_BLK) { + pctx.blk = pctx.blk2 = i; + save_problem = problem; + } else { + if ((problem == save_problem) && + (pctx.blk2 == i-1)) + pctx.blk2++; + else { + print_bitmap_problem(ctx, save_problem, &pctx); + pctx.blk = pctx.blk2 = i; + save_problem = problem; + } + } + ctx->flags |= E2F_FLAG_PROG_SUPPRESS; + had_problem++; + + do_counts: + if (!bitmap) { + group_free++; + free_blocks++; + } + blocks ++; + if ((blocks == fs->super->s_blocks_per_group) || + (i == fs->super->s_blocks_count-1)) { + free_array[group] = group_free; + group ++; + blocks = 0; + group_free = 0; + if (ctx->progress) + if ((ctx->progress)(ctx, 5, group, + fs->group_desc_count*2)) + return; + } + } + if (pctx.blk != NO_BLK) + print_bitmap_problem(ctx, save_problem, &pctx); + if (had_problem) + fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP); + else + fixit = -1; + ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; + + if (fixit == 1) { + ext2fs_free_block_bitmap(fs->block_map); + retval = ext2fs_copy_bitmap(ctx->block_found_map, + &fs->block_map); + if (retval) { + clear_problem_context(&pctx); + fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_set_bitmap_padding(fs->block_map); + ext2fs_mark_bb_dirty(fs); + + /* Redo the counts */ + blocks = 0; free_blocks = 0; group_free = 0; group = 0; + memset(free_array, 0, fs->group_desc_count * sizeof(int)); + goto redo_counts; + } else if (fixit == 0) + ext2fs_unmark_valid(fs); + + for (i = 0; i < fs->group_desc_count; i++) { + if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) { + pctx.group = i; + pctx.blk = fs->group_desc[i].bg_free_blocks_count; + pctx.blk2 = free_array[i]; + + if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, + &pctx)) { + fs->group_desc[i].bg_free_blocks_count = + free_array[i]; + ext2fs_mark_super_dirty(fs); + } else + ext2fs_unmark_valid(fs); + } + } + if (free_blocks != fs->super->s_free_blocks_count) { + pctx.group = 0; + pctx.blk = fs->super->s_free_blocks_count; + pctx.blk2 = free_blocks; + + if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) { + fs->super->s_free_blocks_count = free_blocks; + ext2fs_mark_super_dirty(fs); + } else + ext2fs_unmark_valid(fs); + } + ext2fs_free_mem(&free_array); +} + +static void check_inode_bitmaps(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t i; + unsigned int free_inodes = 0; + int group_free = 0; + int dirs_count = 0; + int group = 0; + unsigned int inodes = 0; + int *free_array; + int *dir_array; + int actual, bitmap; + errcode_t retval; + struct problem_context pctx; + int problem, save_problem, fixit, had_problem; + + clear_problem_context(&pctx); + free_array = (int *) e2fsck_allocate_memory(ctx, + fs->group_desc_count * sizeof(int), "free inode count array"); + + dir_array = (int *) e2fsck_allocate_memory(ctx, + fs->group_desc_count * sizeof(int), "directory count array"); + + if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) || + (fs->super->s_inodes_count > + ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) { + pctx.num = 3; + pctx.blk = 1; + pctx.blk2 = fs->super->s_inodes_count; + pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map); + pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map); + fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); + + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } + if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) || + (fs->super->s_inodes_count > + ext2fs_get_inode_bitmap_end(fs->inode_map))) { + pctx.num = 4; + pctx.blk = 1; + pctx.blk2 = fs->super->s_inodes_count; + pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map); + pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map); + fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); + + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } + +redo_counts: + had_problem = 0; + save_problem = 0; + pctx.ino = pctx.ino2 = 0; + for (i = 1; i <= fs->super->s_inodes_count; i++) { + actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i); + bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i); + + if (actual == bitmap) + goto do_counts; + + if (!actual && bitmap) { + /* + * Inode wasn't used, but marked in bitmap + */ + problem = PR_5_INODE_UNUSED; + } else /* if (actual && !bitmap) */ { + /* + * Inode used, but not in bitmap + */ + problem = PR_5_INODE_USED; + } + if (pctx.ino == 0) { + pctx.ino = pctx.ino2 = i; + save_problem = problem; + } else { + if ((problem == save_problem) && + (pctx.ino2 == i-1)) + pctx.ino2++; + else { + print_bitmap_problem(ctx, save_problem, &pctx); + pctx.ino = pctx.ino2 = i; + save_problem = problem; + } + } + ctx->flags |= E2F_FLAG_PROG_SUPPRESS; + had_problem++; + +do_counts: + if (!bitmap) { + group_free++; + free_inodes++; + } else { + if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i)) + dirs_count++; + } + inodes++; + if ((inodes == fs->super->s_inodes_per_group) || + (i == fs->super->s_inodes_count)) { + free_array[group] = group_free; + dir_array[group] = dirs_count; + group ++; + inodes = 0; + group_free = 0; + dirs_count = 0; + if (ctx->progress) + if ((ctx->progress)(ctx, 5, + group + fs->group_desc_count, + fs->group_desc_count*2)) + return; + } + } + if (pctx.ino) + print_bitmap_problem(ctx, save_problem, &pctx); + + if (had_problem) + fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP); + else + fixit = -1; + ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; + + if (fixit == 1) { + ext2fs_free_inode_bitmap(fs->inode_map); + retval = ext2fs_copy_bitmap(ctx->inode_used_map, + &fs->inode_map); + if (retval) { + clear_problem_context(&pctx); + fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + ext2fs_set_bitmap_padding(fs->inode_map); + ext2fs_mark_ib_dirty(fs); + + /* redo counts */ + inodes = 0; free_inodes = 0; group_free = 0; + dirs_count = 0; group = 0; + memset(free_array, 0, fs->group_desc_count * sizeof(int)); + memset(dir_array, 0, fs->group_desc_count * sizeof(int)); + goto redo_counts; + } else if (fixit == 0) + ext2fs_unmark_valid(fs); + + for (i = 0; i < fs->group_desc_count; i++) { + if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) { + pctx.group = i; + pctx.ino = fs->group_desc[i].bg_free_inodes_count; + pctx.ino2 = free_array[i]; + if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP, + &pctx)) { + fs->group_desc[i].bg_free_inodes_count = + free_array[i]; + ext2fs_mark_super_dirty(fs); + } else + ext2fs_unmark_valid(fs); + } + if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) { + pctx.group = i; + pctx.ino = fs->group_desc[i].bg_used_dirs_count; + pctx.ino2 = dir_array[i]; + + if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP, + &pctx)) { + fs->group_desc[i].bg_used_dirs_count = + dir_array[i]; + ext2fs_mark_super_dirty(fs); + } else + ext2fs_unmark_valid(fs); + } + } + if (free_inodes != fs->super->s_free_inodes_count) { + pctx.group = -1; + pctx.ino = fs->super->s_free_inodes_count; + pctx.ino2 = free_inodes; + + if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) { + fs->super->s_free_inodes_count = free_inodes; + ext2fs_mark_super_dirty(fs); + } else + ext2fs_unmark_valid(fs); + } + ext2fs_free_mem(&free_array); + ext2fs_free_mem(&dir_array); +} + +static void check_inode_end(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t end, save_inodes_count, i; + struct problem_context pctx; + + clear_problem_context(&pctx); + + end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; + pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end, + &save_inodes_count); + if (pctx.errcode) { + pctx.num = 1; + fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } + if (save_inodes_count == end) + return; + + for (i = save_inodes_count + 1; i <= end; i++) { + if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) { + if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) { + for (i = save_inodes_count + 1; i <= end; i++) + ext2fs_mark_inode_bitmap(fs->inode_map, + i); + ext2fs_mark_ib_dirty(fs); + } else + ext2fs_unmark_valid(fs); + break; + } + } + + pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, + save_inodes_count, 0); + if (pctx.errcode) { + pctx.num = 2; + fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } +} + +static void check_block_end(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk_t end, save_blocks_count, i; + struct problem_context pctx; + + clear_problem_context(&pctx); + + end = fs->block_map->start + + (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1; + pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end, + &save_blocks_count); + if (pctx.errcode) { + pctx.num = 3; + fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } + if (save_blocks_count == end) + return; + + for (i = save_blocks_count + 1; i <= end; i++) { + if (!ext2fs_test_block_bitmap(fs->block_map, i)) { + if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) { + for (i = save_blocks_count + 1; i <= end; i++) + ext2fs_mark_block_bitmap(fs->block_map, + i); + ext2fs_mark_bb_dirty(fs); + } else + ext2fs_unmark_valid(fs); + break; + } + } + + pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, + save_blocks_count, 0); + if (pctx.errcode) { + pctx.num = 4; + fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; /* fatal */ + return; + } +} + +static void e2fsck_pass5(e2fsck_t ctx) +{ + struct problem_context pctx; + + /* Pass 5 */ + + clear_problem_context(&pctx); + + if (!(ctx->options & E2F_OPT_PREEN)) + fix_problem(ctx, PR_5_PASS_HEADER, &pctx); + + if (ctx->progress) + if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2)) + return; + + e2fsck_read_bitmaps(ctx); + + check_block_bitmaps(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + check_inode_bitmaps(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + check_inode_end(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + check_block_end(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + + ext2fs_free_inode_bitmap(ctx->inode_used_map); + ctx->inode_used_map = 0; + ext2fs_free_inode_bitmap(ctx->inode_dir_map); + ctx->inode_dir_map = 0; + ext2fs_free_block_bitmap(ctx->block_found_map); + ctx->block_found_map = 0; +} + +/* + * problem.c --- report filesystem problems to the user + */ + +#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */ +#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */ +#define PR_NO_DEFAULT 0x000004 /* Default to no */ +#define PR_MSG_ONLY 0x000008 /* Print message only */ + +/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */ + +#define PR_FATAL 0x001000 /* Fatal error */ +#define PR_AFTER_CODE 0x002000 /* After asking the first question, */ + /* ask another */ +#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */ +#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */ +#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */ +#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */ +#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */ + + +#define PROMPT_NONE 0 +#define PROMPT_FIX 1 +#define PROMPT_CLEAR 2 +#define PROMPT_RELOCATE 3 +#define PROMPT_ALLOCATE 4 +#define PROMPT_EXPAND 5 +#define PROMPT_CONNECT 6 +#define PROMPT_CREATE 7 +#define PROMPT_SALVAGE 8 +#define PROMPT_TRUNCATE 9 +#define PROMPT_CLEAR_INODE 10 +#define PROMPT_ABORT 11 +#define PROMPT_SPLIT 12 +#define PROMPT_CONTINUE 13 +#define PROMPT_CLONE 14 +#define PROMPT_DELETE 15 +#define PROMPT_SUPPRESS 16 +#define PROMPT_UNLINK 17 +#define PROMPT_CLEAR_HTREE 18 +#define PROMPT_RECREATE 19 +#define PROMPT_NULL 20 + +struct e2fsck_problem { + problem_t e2p_code; + const char * e2p_description; + char prompt; + int flags; + problem_t second_code; +}; + +struct latch_descr { + int latch_code; + problem_t question; + problem_t end_message; + int flags; +}; + +/* + * These are the prompts which are used to ask the user if they want + * to fix a problem. + */ +static const char *const prompt[] = { + N_("(no prompt)"), /* 0 */ + N_("Fix"), /* 1 */ + N_("Clear"), /* 2 */ + N_("Relocate"), /* 3 */ + N_("Allocate"), /* 4 */ + N_("Expand"), /* 5 */ + N_("Connect to /lost+found"), /* 6 */ + N_("Create"), /* 7 */ + N_("Salvage"), /* 8 */ + N_("Truncate"), /* 9 */ + N_("Clear inode"), /* 10 */ + N_("Abort"), /* 11 */ + N_("Split"), /* 12 */ + N_("Continue"), /* 13 */ + N_("Clone multiply-claimed blocks"), /* 14 */ + N_("Delete file"), /* 15 */ + N_("Suppress messages"),/* 16 */ + N_("Unlink"), /* 17 */ + N_("Clear HTree index"),/* 18 */ + N_("Recreate"), /* 19 */ + "", /* 20 */ +}; + +/* + * These messages are printed when we are preen mode and we will be + * automatically fixing the problem. + */ +static const char *const preen_msg[] = { + N_("(NONE)"), /* 0 */ + N_("FIXED"), /* 1 */ + N_("CLEARED"), /* 2 */ + N_("RELOCATED"), /* 3 */ + N_("ALLOCATED"), /* 4 */ + N_("EXPANDED"), /* 5 */ + N_("RECONNECTED"), /* 6 */ + N_("CREATED"), /* 7 */ + N_("SALVAGED"), /* 8 */ + N_("TRUNCATED"), /* 9 */ + N_("INODE CLEARED"), /* 10 */ + N_("ABORTED"), /* 11 */ + N_("SPLIT"), /* 12 */ + N_("CONTINUING"), /* 13 */ + N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */ + N_("FILE DELETED"), /* 15 */ + N_("SUPPRESSED"), /* 16 */ + N_("UNLINKED"), /* 17 */ + N_("HTREE INDEX CLEARED"),/* 18 */ + N_("WILL RECREATE"), /* 19 */ + "", /* 20 */ +}; + +static const struct e2fsck_problem problem_table[] = { + + /* Pre-Pass 1 errors */ + + /* Block bitmap not in group */ + { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"), + PROMPT_RELOCATE, PR_LATCH_RELOC }, + + /* Inode bitmap not in group */ + { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"), + PROMPT_RELOCATE, PR_LATCH_RELOC }, + + /* Inode table not in group */ + { PR_0_ITABLE_NOT_GROUP, + N_("@i table for @g %g is not in @g. (@b %b)\n" + "WARNING: SEVERE DATA LOSS POSSIBLE.\n"), + PROMPT_RELOCATE, PR_LATCH_RELOC }, + + /* Superblock corrupt */ + { PR_0_SB_CORRUPT, + N_("\nThe @S could not be read or does not describe a correct ext2\n" + "@f. If the @v is valid and it really contains an ext2\n" + "@f (and not swap or ufs or something else), then the @S\n" + "is corrupt, and you might try running e2fsck with an alternate @S:\n" + " e2fsck -b %S <@v>\n\n"), + PROMPT_NONE, PR_FATAL }, + + /* Filesystem size is wrong */ + { PR_0_FS_SIZE_WRONG, + N_("The @f size (according to the @S) is %b @bs\n" + "The physical size of the @v is %c @bs\n" + "Either the @S or the partition table is likely to be corrupt!\n"), + PROMPT_ABORT, 0 }, + + /* Fragments not supported */ + { PR_0_NO_FRAGMENTS, + N_("@S @b_size = %b, fragsize = %c.\n" + "This version of e2fsck does not support fragment sizes different\n" + "from the @b size.\n"), + PROMPT_NONE, PR_FATAL }, + + /* Bad blocks_per_group */ + { PR_0_BLOCKS_PER_GROUP, + N_("@S @bs_per_group = %b, should have been %c\n"), + PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT }, + + /* Bad first_data_block */ + { PR_0_FIRST_DATA_BLOCK, + N_("@S first_data_@b = %b, should have been %c\n"), + PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT }, + + /* Adding UUID to filesystem */ + { PR_0_ADD_UUID, + N_("@f did not have a UUID; generating one.\n\n"), + PROMPT_NONE, 0 }, + + /* Relocate hint */ + { PR_0_RELOCATE_HINT, + N_("Note: if several inode or block bitmap blocks or part\n" + "of the inode table require relocation, you may wish to try\n" + "running e2fsck with the '-b %S' option first. The problem\n" + "may lie only with the primary block group descriptors, and\n" + "the backup block group descriptors may be OK.\n\n"), + PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE }, + + /* Miscellaneous superblock corruption */ + { PR_0_MISC_CORRUPT_SUPER, + N_("Corruption found in @S. (%s = %N).\n"), + PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT }, + + /* Error determing physical device size of filesystem */ + { PR_0_GETSIZE_ERROR, + N_("Error determining size of the physical @v: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Inode count in superblock is incorrect */ + { PR_0_INODE_COUNT_WRONG, + N_("@i count in @S is %i, @s %j.\n"), + PROMPT_FIX, 0 }, + + { PR_0_HURD_CLEAR_FILETYPE, + N_("The Hurd does not support the filetype feature.\n"), + PROMPT_CLEAR, 0 }, + + /* Journal inode is invalid */ + { PR_0_JOURNAL_BAD_INODE, + N_("@S has an @n ext3 @j (@i %i).\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* The external journal has (unsupported) multiple filesystems */ + { PR_0_JOURNAL_UNSUPP_MULTIFS, + N_("External @j has multiple @f users (unsupported).\n"), + PROMPT_NONE, PR_FATAL }, + + /* Can't find external journal */ + { PR_0_CANT_FIND_JOURNAL, + N_("Can't find external @j\n"), + PROMPT_NONE, PR_FATAL }, + + /* External journal has bad superblock */ + { PR_0_EXT_JOURNAL_BAD_SUPER, + N_("External @j has bad @S\n"), + PROMPT_NONE, PR_FATAL }, + + /* Superblock has a bad journal UUID */ + { PR_0_JOURNAL_BAD_UUID, + N_("External @j does not support this @f\n"), + PROMPT_NONE, PR_FATAL }, + + /* Journal has an unknown superblock type */ + { PR_0_JOURNAL_UNSUPP_SUPER, + N_("Ext3 @j @S is unknown type %N (unsupported).\n" + "It is likely that your copy of e2fsck is old and/or doesn't " + "support this @j format.\n" + "It is also possible the @j @S is corrupt.\n"), + PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER }, + + /* Journal superblock is corrupt */ + { PR_0_JOURNAL_BAD_SUPER, + N_("Ext3 @j @S is corrupt.\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* Superblock flag should be cleared */ + { PR_0_JOURNAL_HAS_JOURNAL, + N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Superblock flag is incorrect */ + { PR_0_JOURNAL_RECOVER_SET, + N_("@S has ext3 needs_recovery flag set, but no @j.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Journal has data, but recovery flag is clear */ + { PR_0_JOURNAL_RECOVERY_CLEAR, + N_("ext3 recovery flag is clear, but @j has data.\n"), + PROMPT_NONE, 0 }, + + /* Ask if we should clear the journal */ + { PR_0_JOURNAL_RESET_JOURNAL, + N_("Clear @j"), + PROMPT_NULL, PR_PREEN_NOMSG }, + + /* Ask if we should run the journal anyway */ + { PR_0_JOURNAL_RUN, + N_("Run @j anyway"), + PROMPT_NULL, 0 }, + + /* Run the journal by default */ + { PR_0_JOURNAL_RUN_DEFAULT, + N_("Recovery flag not set in backup @S, so running @j anyway.\n"), + PROMPT_NONE, 0 }, + + /* Clearing orphan inode */ + { PR_0_ORPHAN_CLEAR_INODE, + N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"), + PROMPT_NONE, 0 }, + + /* Illegal block found in orphaned inode */ + { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, + N_("@I @b #%B (%b) found in @o @i %i.\n"), + PROMPT_NONE, 0 }, + + /* Already cleared block found in orphaned inode */ + { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, + N_("Already cleared @b #%B (%b) found in @o @i %i.\n"), + PROMPT_NONE, 0 }, + + /* Illegal orphan inode in superblock */ + { PR_0_ORPHAN_ILLEGAL_HEAD_INODE, + N_("@I @o @i %i in @S.\n"), + PROMPT_NONE, 0 }, + + /* Illegal inode in orphaned inode list */ + { PR_0_ORPHAN_ILLEGAL_INODE, + N_("@I @i %i in @o @i list.\n"), + PROMPT_NONE, 0 }, + + /* Filesystem revision is 0, but feature flags are set */ + { PR_0_FS_REV_LEVEL, + N_("@f has feature flag(s) set, but is a revision 0 @f. "), + PROMPT_FIX, PR_PREEN_OK | PR_NO_OK }, + + /* Journal superblock has an unknown read-only feature flag set */ + { PR_0_JOURNAL_UNSUPP_ROCOMPAT, + N_("Ext3 @j @S has an unknown read-only feature flag set.\n"), + PROMPT_ABORT, 0 }, + + /* Journal superblock has an unknown incompatible feature flag set */ + { PR_0_JOURNAL_UNSUPP_INCOMPAT, + N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"), + PROMPT_ABORT, 0 }, + + /* Journal has unsupported version number */ + { PR_0_JOURNAL_UNSUPP_VERSION, + N_("@j version not supported by this e2fsck.\n"), + PROMPT_ABORT, 0 }, + + /* Moving journal to hidden file */ + { PR_0_MOVE_JOURNAL, + N_("Moving @j from /%s to hidden @i.\n\n"), + PROMPT_NONE, 0 }, + + /* Error moving journal to hidden file */ + { PR_0_ERR_MOVE_JOURNAL, + N_("Error moving @j: %m\n\n"), + PROMPT_NONE, 0 }, + + /* Clearing V2 journal superblock */ + { PR_0_CLEAR_V2_JOURNAL, + N_("Found @n V2 @j @S fields (from V1 @j).\n" + "Clearing fields beyond the V1 @j @S...\n\n"), + PROMPT_NONE, 0 }, + + /* Backup journal inode blocks */ + { PR_0_BACKUP_JNL, + N_("Backing up @j @i @b information.\n\n"), + PROMPT_NONE, 0 }, + + /* Reserved blocks w/o resize_inode */ + { PR_0_NONZERO_RESERVED_GDT_BLOCKS, + N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n" + "is %N; @s zero. "), + PROMPT_FIX, 0 }, + + /* Resize_inode not enabled, but resize inode is non-zero */ + { PR_0_CLEAR_RESIZE_INODE, + N_("Resize_@i not enabled, but the resize @i is non-zero. "), + PROMPT_CLEAR, 0 }, + + /* Resize inode invalid */ + { PR_0_RESIZE_INODE_INVALID, + N_("Resize @i not valid. "), + PROMPT_RECREATE, 0 }, + + /* Pass 1 errors */ + + /* Pass 1: Checking inodes, blocks, and sizes */ + { PR_1_PASS_HEADER, + N_("Pass 1: Checking @is, @bs, and sizes\n"), + PROMPT_NONE, 0 }, + + /* Root directory is not an inode */ + { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "), + PROMPT_CLEAR, 0 }, + + /* Root directory has dtime set */ + { PR_1_ROOT_DTIME, + N_("@r has dtime set (probably due to old mke2fs). "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Reserved inode has bad mode */ + { PR_1_RESERVED_BAD_MODE, + N_("Reserved @i %i (%Q) has @n mode. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Deleted inode has zero dtime */ + { PR_1_ZERO_DTIME, + N_("@D @i %i has zero dtime. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Inode in use, but dtime set */ + { PR_1_SET_DTIME, + N_("@i %i is in use, but has dtime set. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Zero-length directory */ + { PR_1_ZERO_LENGTH_DIR, + N_("@i %i is a @z @d. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Block bitmap conflicts with some other fs block */ + { PR_1_BB_CONFLICT, + N_("@g %g's @b @B at %b @C.\n"), + PROMPT_RELOCATE, 0 }, + + /* Inode bitmap conflicts with some other fs block */ + { PR_1_IB_CONFLICT, + N_("@g %g's @i @B at %b @C.\n"), + PROMPT_RELOCATE, 0 }, + + /* Inode table conflicts with some other fs block */ + { PR_1_ITABLE_CONFLICT, + N_("@g %g's @i table at %b @C.\n"), + PROMPT_RELOCATE, 0 }, + + /* Block bitmap is on a bad block */ + { PR_1_BB_BAD_BLOCK, + N_("@g %g's @b @B (%b) is bad. "), + PROMPT_RELOCATE, 0 }, + + /* Inode bitmap is on a bad block */ + { PR_1_IB_BAD_BLOCK, + N_("@g %g's @i @B (%b) is bad. "), + PROMPT_RELOCATE, 0 }, + + /* Inode has incorrect i_size */ + { PR_1_BAD_I_SIZE, + N_("@i %i, i_size is %Is, @s %N. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Inode has incorrect i_blocks */ + { PR_1_BAD_I_BLOCKS, + N_("@i %i, i_@bs is %Ib, @s %N. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Illegal blocknumber in inode */ + { PR_1_ILLEGAL_BLOCK_NUM, + N_("@I @b #%B (%b) in @i %i. "), + PROMPT_CLEAR, PR_LATCH_BLOCK }, + + /* Block number overlaps fs metadata */ + { PR_1_BLOCK_OVERLAPS_METADATA, + N_("@b #%B (%b) overlaps @f metadata in @i %i. "), + PROMPT_CLEAR, PR_LATCH_BLOCK }, + + /* Inode has illegal blocks (latch question) */ + { PR_1_INODE_BLOCK_LATCH, + N_("@i %i has illegal @b(s). "), + PROMPT_CLEAR, 0 }, + + /* Too many bad blocks in inode */ + { PR_1_TOO_MANY_BAD_BLOCKS, + N_("Too many illegal @bs in @i %i.\n"), + PROMPT_CLEAR_INODE, PR_NO_OK }, + + /* Illegal block number in bad block inode */ + { PR_1_BB_ILLEGAL_BLOCK_NUM, + N_("@I @b #%B (%b) in bad @b @i. "), + PROMPT_CLEAR, PR_LATCH_BBLOCK }, + + /* Bad block inode has illegal blocks (latch question) */ + { PR_1_INODE_BBLOCK_LATCH, + N_("Bad @b @i has illegal @b(s). "), + PROMPT_CLEAR, 0 }, + + /* Duplicate or bad blocks in use! */ + { PR_1_DUP_BLOCKS_PREENSTOP, + N_("Duplicate or bad @b in use!\n"), + PROMPT_NONE, 0 }, + + /* Bad block used as bad block indirect block */ + { PR_1_BBINODE_BAD_METABLOCK, + N_("Bad @b %b used as bad @b @i indirect @b. "), + PROMPT_CLEAR, PR_LATCH_BBLOCK }, + + /* Inconsistency can't be fixed prompt */ + { PR_1_BBINODE_BAD_METABLOCK_PROMPT, + N_("\nThe bad @b @i has probably been corrupted. You probably\n" + "should stop now and run ""e2fsck -c"" to scan for bad blocks\n" + "in the @f.\n"), + PROMPT_CONTINUE, PR_PREEN_NOMSG }, + + /* Bad primary block */ + { PR_1_BAD_PRIMARY_BLOCK, + N_("\nIf the @b is really bad, the @f cannot be fixed.\n"), + PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT }, + + /* Bad primary block prompt */ + { PR_1_BAD_PRIMARY_BLOCK_PROMPT, + N_("You can remove this @b from the bad @b list and hope\n" + "that the @b is really OK. But there are no guarantees.\n\n"), + PROMPT_CLEAR, PR_PREEN_NOMSG }, + + /* Bad primary superblock */ + { PR_1_BAD_PRIMARY_SUPERBLOCK, + N_("The primary @S (%b) is on the bad @b list.\n"), + PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK }, + + /* Bad primary block group descriptors */ + { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, + N_("Block %b in the primary @g descriptors " + "is on the bad @b list\n"), + PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK }, + + /* Bad superblock in group */ + { PR_1_BAD_SUPERBLOCK, + N_("Warning: Group %g's @S (%b) is bad.\n"), + PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Bad block group descriptors in group */ + { PR_1_BAD_GROUP_DESCRIPTORS, + N_("Warning: Group %g's copy of the @g descriptors has a bad " + "@b (%b).\n"), + PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block claimed for no reason */ + { PR_1_PROGERR_CLAIMED_BLOCK, + N_("Programming error? @b #%b claimed for no reason in " + "process_bad_@b.\n"), + PROMPT_NONE, PR_PREEN_OK }, + + /* Error allocating blocks for relocating metadata */ + { PR_1_RELOC_BLOCK_ALLOCATE, + N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"), + PROMPT_NONE, PR_PREEN_OK }, + + /* Error allocating block buffer during relocation process */ + { PR_1_RELOC_MEMORY_ALLOCATE, + N_("@A @b buffer for relocating %s\n"), + PROMPT_NONE, PR_PREEN_OK }, + + /* Relocating metadata group information from X to Y */ + { PR_1_RELOC_FROM_TO, + N_("Relocating @g %g's %s from %b to %c...\n"), + PROMPT_NONE, PR_PREEN_OK }, + + /* Relocating metatdata group information to X */ + { PR_1_RELOC_TO, + N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */ + PROMPT_NONE, PR_PREEN_OK }, + + /* Block read error during relocation process */ + { PR_1_RELOC_READ_ERR, + N_("Warning: could not read @b %b of %s: %m\n"), + PROMPT_NONE, PR_PREEN_OK }, + + /* Block write error during relocation process */ + { PR_1_RELOC_WRITE_ERR, + N_("Warning: could not write @b %b for %s: %m\n"), + PROMPT_NONE, PR_PREEN_OK }, + + /* Error allocating inode bitmap */ + { PR_1_ALLOCATE_IBITMAP_ERROR, + N_("@A @i @B (%N): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error allocating block bitmap */ + { PR_1_ALLOCATE_BBITMAP_ERROR, + N_("@A @b @B (%N): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error allocating icount structure */ + { PR_1_ALLOCATE_ICOUNT, + N_("@A icount link information: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error allocating dbcount */ + { PR_1_ALLOCATE_DBCOUNT, + N_("@A @d @b array: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error while scanning inodes */ + { PR_1_ISCAN_ERROR, + N_("Error while scanning @is (%i): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error while iterating over blocks */ + { PR_1_BLOCK_ITERATE, + N_("Error while iterating over @bs in @i %i: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error while storing inode count information */ + { PR_1_ICOUNT_STORE, + N_("Error storing @i count information (@i=%i, count=%N): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error while storing directory block information */ + { PR_1_ADD_DBLOCK, + N_("Error storing @d @b information " + "(@i=%i, @b=%b, num=%N): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error while reading inode (for clearing) */ + { PR_1_READ_INODE, + N_("Error reading @i %i: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Suppress messages prompt */ + { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK }, + + /* Imagic flag set on an inode when filesystem doesn't support it */ + { PR_1_SET_IMAGIC, + N_("@i %i has imagic flag set. "), + PROMPT_CLEAR, 0 }, + + /* Immutable flag set on a device or socket inode */ + { PR_1_SET_IMMUTABLE, + N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n" + "or append-only flag set. "), + PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK }, + + /* Compression flag set on an inode when filesystem doesn't support it */ + { PR_1_COMPR_SET, + N_("@i %i has @cion flag set on @f without @cion support. "), + PROMPT_CLEAR, 0 }, + + /* Non-zero size for device, fifo or socket inode */ + { PR_1_SET_NONZSIZE, + N_("Special (@v/socket/fifo) @i %i has non-zero size. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Filesystem revision is 0, but feature flags are set */ + { PR_1_FS_REV_LEVEL, + N_("@f has feature flag(s) set, but is a revision 0 @f. "), + PROMPT_FIX, PR_PREEN_OK | PR_NO_OK }, + + /* Journal inode is not in use, but contains data */ + { PR_1_JOURNAL_INODE_NOT_CLEAR, + N_("@j @i is not in use, but contains data. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Journal has bad mode */ + { PR_1_JOURNAL_BAD_MODE, + N_("@j is not regular file. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Deal with inodes that were part of orphan linked list */ + { PR_1_LOW_DTIME, + N_("@i %i was part of the @o @i list. "), + PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 }, + + /* Deal with inodes that were part of corrupted orphan linked + list (latch question) */ + { PR_1_ORPHAN_LIST_REFUGEES, + N_("@is that were part of a corrupted orphan linked list found. "), + PROMPT_FIX, 0 }, + + /* Error allocating refcount structure */ + { PR_1_ALLOCATE_REFCOUNT, + N_("@A refcount structure (%N): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error reading extended attribute block */ + { PR_1_READ_EA_BLOCK, + N_("Error reading @a @b %b for @i %i. "), + PROMPT_CLEAR, 0 }, + + /* Invalid extended attribute block */ + { PR_1_BAD_EA_BLOCK, + N_("@i %i has a bad @a @b %b. "), + PROMPT_CLEAR, 0 }, + + /* Error reading Extended Attribute block while fixing refcount */ + { PR_1_EXTATTR_READ_ABORT, + N_("Error reading @a @b %b (%m). "), + PROMPT_ABORT, 0 }, + + /* Extended attribute reference count incorrect */ + { PR_1_EXTATTR_REFCOUNT, + N_("@a @b %b has reference count %B, @s %N. "), + PROMPT_FIX, 0 }, + + /* Error writing Extended Attribute block while fixing refcount */ + { PR_1_EXTATTR_WRITE, + N_("Error writing @a @b %b (%m). "), + PROMPT_ABORT, 0 }, + + /* Multiple EA blocks not supported */ + { PR_1_EA_MULTI_BLOCK, + N_("@a @b %b has h_@bs > 1. "), + PROMPT_CLEAR, 0}, + + /* Error allocating EA region allocation structure */ + { PR_1_EA_ALLOC_REGION, + N_("@A @a @b %b. "), + PROMPT_ABORT, 0}, + + /* Error EA allocation collision */ + { PR_1_EA_ALLOC_COLLISION, + N_("@a @b %b is corrupt (allocation collision). "), + PROMPT_CLEAR, 0}, + + /* Bad extended attribute name */ + { PR_1_EA_BAD_NAME, + N_("@a @b %b is corrupt (@n name). "), + PROMPT_CLEAR, 0}, + + /* Bad extended attribute value */ + { PR_1_EA_BAD_VALUE, + N_("@a @b %b is corrupt (@n value). "), + PROMPT_CLEAR, 0}, + + /* Inode too big (latch question) */ + { PR_1_INODE_TOOBIG, + N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 }, + + /* Directory too big */ + { PR_1_TOOBIG_DIR, + N_("@b #%B (%b) causes @d to be too big. "), + PROMPT_CLEAR, PR_LATCH_TOOBIG }, + + /* Regular file too big */ + { PR_1_TOOBIG_REG, + N_("@b #%B (%b) causes file to be too big. "), + PROMPT_CLEAR, PR_LATCH_TOOBIG }, + + /* Symlink too big */ + { PR_1_TOOBIG_SYMLINK, + N_("@b #%B (%b) causes symlink to be too big. "), + PROMPT_CLEAR, PR_LATCH_TOOBIG }, + + /* INDEX_FL flag set on a non-HTREE filesystem */ + { PR_1_HTREE_SET, + N_("@i %i has INDEX_FL flag set on @f without htree support.\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* INDEX_FL flag set on a non-directory */ + { PR_1_HTREE_NODIR, + N_("@i %i has INDEX_FL flag set but is not a @d.\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Invalid root node in HTREE directory */ + { PR_1_HTREE_BADROOT, + N_("@h %i has an @n root node.\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Unsupported hash version in HTREE directory */ + { PR_1_HTREE_HASHV, + N_("@h %i has an unsupported hash version (%N)\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Incompatible flag in HTREE root node */ + { PR_1_HTREE_INCOMPAT, + N_("@h %i uses an incompatible htree root node flag.\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* HTREE too deep */ + { PR_1_HTREE_DEPTH, + N_("@h %i has a tree depth (%N) which is too big\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Bad block has indirect block that conflicts with filesystem block */ + { PR_1_BB_FS_BLOCK, + N_("Bad @b @i has an indirect @b (%b) that conflicts with\n" + "@f metadata. "), + PROMPT_CLEAR, PR_LATCH_BBLOCK }, + + /* Resize inode failed */ + { PR_1_RESIZE_INODE_CREATE, + N_("Resize @i (re)creation failed: %m."), + PROMPT_ABORT, 0 }, + + /* invalid inode->i_extra_isize */ + { PR_1_EXTRA_ISIZE, + N_("@i %i has a extra size (%IS) which is @n\n"), + PROMPT_FIX, PR_PREEN_OK }, + + /* invalid ea entry->e_name_len */ + { PR_1_ATTR_NAME_LEN, + N_("@a in @i %i has a namelen (%N) which is @n\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* invalid ea entry->e_value_size */ + { PR_1_ATTR_VALUE_SIZE, + N_("@a in @i %i has a value size (%N) which is @n\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* invalid ea entry->e_value_offs */ + { PR_1_ATTR_VALUE_OFFSET, + N_("@a in @i %i has a value offset (%N) which is @n\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* invalid ea entry->e_value_block */ + { PR_1_ATTR_VALUE_BLOCK, + N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* invalid ea entry->e_hash */ + { PR_1_ATTR_HASH, + N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Pass 1b errors */ + + /* Pass 1B: Rescan for duplicate/bad blocks */ + { PR_1B_PASS_HEADER, + N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n" + "Pass 1B: Rescanning for @m @bs\n"), + PROMPT_NONE, 0 }, + + /* Duplicate/bad block(s) header */ + { PR_1B_DUP_BLOCK_HEADER, + N_("@m @b(s) in @i %i:"), + PROMPT_NONE, 0 }, + + /* Duplicate/bad block(s) in inode */ + { PR_1B_DUP_BLOCK, + " %b", + PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR }, + + /* Duplicate/bad block(s) end */ + { PR_1B_DUP_BLOCK_END, + "\n", + PROMPT_NONE, PR_PREEN_NOHDR }, + + /* Error while scanning inodes */ + { PR_1B_ISCAN_ERROR, + N_("Error while scanning inodes (%i): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error allocating inode bitmap */ + { PR_1B_ALLOCATE_IBITMAP_ERROR, + N_("@A @i @B (@i_dup_map): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error while iterating over blocks */ + { PR_1B_BLOCK_ITERATE, + N_("Error while iterating over @bs in @i %i (%s): %m\n"), + PROMPT_NONE, 0 }, + + /* Error adjusting EA refcount */ + { PR_1B_ADJ_EA_REFCOUNT, + N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"), + PROMPT_NONE, 0 }, + + + /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */ + { PR_1C_PASS_HEADER, + N_("Pass 1C: Scanning directories for @is with @m @bs.\n"), + PROMPT_NONE, 0 }, + + + /* Pass 1D: Reconciling multiply-claimed blocks */ + { PR_1D_PASS_HEADER, + N_("Pass 1D: Reconciling @m @bs\n"), + PROMPT_NONE, 0 }, + + /* File has duplicate blocks */ + { PR_1D_DUP_FILE, + N_("File %Q (@i #%i, mod time %IM)\n" + " has %B @m @b(s), shared with %N file(s):\n"), + PROMPT_NONE, 0 }, + + /* List of files sharing duplicate blocks */ + { PR_1D_DUP_FILE_LIST, + N_("\t%Q (@i #%i, mod time %IM)\n"), + PROMPT_NONE, 0 }, + + /* File sharing blocks with filesystem metadata */ + { PR_1D_SHARE_METADATA, + N_("\t<@f metadata>\n"), + PROMPT_NONE, 0 }, + + /* Report of how many duplicate/bad inodes */ + { PR_1D_NUM_DUP_INODES, + N_("(There are %N @is containing @m @bs.)\n\n"), + PROMPT_NONE, 0 }, + + /* Duplicated blocks already reassigned or cloned. */ + { PR_1D_DUP_BLOCKS_DEALT, + N_("@m @bs already reassigned or cloned.\n\n"), + PROMPT_NONE, 0 }, + + /* Clone duplicate/bad blocks? */ + { PR_1D_CLONE_QUESTION, + "", PROMPT_CLONE, PR_NO_OK }, + + /* Delete file? */ + { PR_1D_DELETE_QUESTION, + "", PROMPT_DELETE, 0 }, + + /* Couldn't clone file (error) */ + { PR_1D_CLONE_ERROR, + N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 }, + + /* Pass 2 errors */ + + /* Pass 2: Checking directory structure */ + { PR_2_PASS_HEADER, + N_("Pass 2: Checking @d structure\n"), + PROMPT_NONE, 0 }, + + /* Bad inode number for '.' */ + { PR_2_BAD_INODE_DOT, + N_("@n @i number for '.' in @d @i %i.\n"), + PROMPT_FIX, 0 }, + + /* Directory entry has bad inode number */ + { PR_2_BAD_INO, + N_("@E has @n @i #: %Di.\n"), + PROMPT_CLEAR, 0 }, + + /* Directory entry has deleted or unused inode */ + { PR_2_UNUSED_INODE, + N_("@E has @D/unused @i %Di. "), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Directry entry is link to '.' */ + { PR_2_LINK_DOT, + N_("@E @L to '.' "), + PROMPT_CLEAR, 0 }, + + /* Directory entry points to inode now located in a bad block */ + { PR_2_BB_INODE, + N_("@E points to @i (%Di) located in a bad @b.\n"), + PROMPT_CLEAR, 0 }, + + /* Directory entry contains a link to a directory */ + { PR_2_LINK_DIR, + N_("@E @L to @d %P (%Di).\n"), + PROMPT_CLEAR, 0 }, + + /* Directory entry contains a link to the root directry */ + { PR_2_LINK_ROOT, + N_("@E @L to the @r.\n"), + PROMPT_CLEAR, 0 }, + + /* Directory entry has illegal characters in its name */ + { PR_2_BAD_NAME, + N_("@E has illegal characters in its name.\n"), + PROMPT_FIX, 0 }, + + /* Missing '.' in directory inode */ + { PR_2_MISSING_DOT, + N_("Missing '.' in @d @i %i.\n"), + PROMPT_FIX, 0 }, + + /* Missing '..' in directory inode */ + { PR_2_MISSING_DOT_DOT, + N_("Missing '..' in @d @i %i.\n"), + PROMPT_FIX, 0 }, + + /* First entry in directory inode doesn't contain '.' */ + { PR_2_1ST_NOT_DOT, + N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"), + PROMPT_FIX, 0 }, + + /* Second entry in directory inode doesn't contain '..' */ + { PR_2_2ND_NOT_DOT_DOT, + N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"), + PROMPT_FIX, 0 }, + + /* i_faddr should be zero */ + { PR_2_FADDR_ZERO, + N_("i_faddr @F %IF, @s zero.\n"), + PROMPT_CLEAR, 0 }, + + /* i_file_acl should be zero */ + { PR_2_FILE_ACL_ZERO, + N_("i_file_acl @F %If, @s zero.\n"), + PROMPT_CLEAR, 0 }, + + /* i_dir_acl should be zero */ + { PR_2_DIR_ACL_ZERO, + N_("i_dir_acl @F %Id, @s zero.\n"), + PROMPT_CLEAR, 0 }, + + /* i_frag should be zero */ + { PR_2_FRAG_ZERO, + N_("i_frag @F %N, @s zero.\n"), + PROMPT_CLEAR, 0 }, + + /* i_fsize should be zero */ + { PR_2_FSIZE_ZERO, + N_("i_fsize @F %N, @s zero.\n"), + PROMPT_CLEAR, 0 }, + + /* inode has bad mode */ + { PR_2_BAD_MODE, + N_("@i %i (%Q) has @n mode (%Im).\n"), + PROMPT_CLEAR, 0 }, + + /* directory corrupted */ + { PR_2_DIR_CORRUPTED, + N_("@d @i %i, @b %B, offset %N: @d corrupted\n"), + PROMPT_SALVAGE, 0 }, + + /* filename too long */ + { PR_2_FILENAME_LONG, + N_("@d @i %i, @b %B, offset %N: filename too long\n"), + PROMPT_TRUNCATE, 0 }, + + /* Directory inode has a missing block (hole) */ + { PR_2_DIRECTORY_HOLE, + N_("@d @i %i has an unallocated @b #%B. "), + PROMPT_ALLOCATE, 0 }, + + /* '.' is not NULL terminated */ + { PR_2_DOT_NULL_TERM, + N_("'.' @d @e in @d @i %i is not NULL terminated\n"), + PROMPT_FIX, 0 }, + + /* '..' is not NULL terminated */ + { PR_2_DOT_DOT_NULL_TERM, + N_("'..' @d @e in @d @i %i is not NULL terminated\n"), + PROMPT_FIX, 0 }, + + /* Illegal character device inode */ + { PR_2_BAD_CHAR_DEV, + N_("@i %i (%Q) is an @I character @v.\n"), + PROMPT_CLEAR, 0 }, + + /* Illegal block device inode */ + { PR_2_BAD_BLOCK_DEV, + N_("@i %i (%Q) is an @I @b @v.\n"), + PROMPT_CLEAR, 0 }, + + /* Duplicate '.' entry */ + { PR_2_DUP_DOT, + N_("@E is duplicate '.' @e.\n"), + PROMPT_FIX, 0 }, + + /* Duplicate '..' entry */ + { PR_2_DUP_DOT_DOT, + N_("@E is duplicate '..' @e.\n"), + PROMPT_FIX, 0 }, + + /* Internal error: couldn't find dir_info */ + { PR_2_NO_DIRINFO, + N_("Internal error: cannot find dir_info for %i.\n"), + PROMPT_NONE, PR_FATAL }, + + /* Final rec_len is wrong */ + { PR_2_FINAL_RECLEN, + N_("@E has rec_len of %Dr, @s %N.\n"), + PROMPT_FIX, 0 }, + + /* Error allocating icount structure */ + { PR_2_ALLOCATE_ICOUNT, + N_("@A icount structure: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error iterating over directory blocks */ + { PR_2_DBLIST_ITERATE, + N_("Error iterating over @d @bs: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error reading directory block */ + { PR_2_READ_DIRBLOCK, + N_("Error reading @d @b %b (@i %i): %m\n"), + PROMPT_CONTINUE, 0 }, + + /* Error writing directory block */ + { PR_2_WRITE_DIRBLOCK, + N_("Error writing @d @b %b (@i %i): %m\n"), + PROMPT_CONTINUE, 0 }, + + /* Error allocating new directory block */ + { PR_2_ALLOC_DIRBOCK, + N_("@A new @d @b for @i %i (%s): %m\n"), + PROMPT_NONE, 0 }, + + /* Error deallocating inode */ + { PR_2_DEALLOC_INODE, + N_("Error deallocating @i %i: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Directory entry for '.' is big. Split? */ + { PR_2_SPLIT_DOT, + N_("@d @e for '.' is big. "), + PROMPT_SPLIT, PR_NO_OK }, + + /* Illegal FIFO inode */ + { PR_2_BAD_FIFO, + N_("@i %i (%Q) is an @I FIFO.\n"), + PROMPT_CLEAR, 0 }, + + /* Illegal socket inode */ + { PR_2_BAD_SOCKET, + N_("@i %i (%Q) is an @I socket.\n"), + PROMPT_CLEAR, 0 }, + + /* Directory filetype not set */ + { PR_2_SET_FILETYPE, + N_("Setting filetype for @E to %N.\n"), + PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG }, + + /* Directory filetype incorrect */ + { PR_2_BAD_FILETYPE, + N_("@E has an incorrect filetype (was %Dt, @s %N).\n"), + PROMPT_FIX, 0 }, + + /* Directory filetype set on filesystem */ + { PR_2_CLEAR_FILETYPE, + N_("@E has filetype set.\n"), + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Directory filename is null */ + { PR_2_NULL_NAME, + N_("@E has a @z name.\n"), + PROMPT_CLEAR, 0 }, + + /* Invalid symlink */ + { PR_2_INVALID_SYMLINK, + N_("Symlink %Q (@i #%i) is @n.\n"), + PROMPT_CLEAR, 0 }, + + /* i_file_acl (extended attribute block) is bad */ + { PR_2_FILE_ACL_BAD, + N_("@a @b @F @n (%If).\n"), + PROMPT_CLEAR, 0 }, + + /* Filesystem contains large files, but has no such flag in sb */ + { PR_2_FEATURE_LARGE_FILES, + N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"), + PROMPT_FIX, 0 }, + + /* Node in HTREE directory not referenced */ + { PR_2_HTREE_NOTREF, + N_("@p @h %d: node (%B) not referenced\n"), + PROMPT_NONE, 0 }, + + /* Node in HTREE directory referenced twice */ + { PR_2_HTREE_DUPREF, + N_("@p @h %d: node (%B) referenced twice\n"), + PROMPT_NONE, 0 }, + + /* Node in HTREE directory has bad min hash */ + { PR_2_HTREE_MIN_HASH, + N_("@p @h %d: node (%B) has bad min hash\n"), + PROMPT_NONE, 0 }, + + /* Node in HTREE directory has bad max hash */ + { PR_2_HTREE_MAX_HASH, + N_("@p @h %d: node (%B) has bad max hash\n"), + PROMPT_NONE, 0 }, + + /* Clear invalid HTREE directory */ + { PR_2_HTREE_CLEAR, + N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 }, + + /* Bad block in htree interior node */ + { PR_2_HTREE_BADBLK, + N_("@p @h %d (%q): bad @b number %b.\n"), + PROMPT_CLEAR_HTREE, 0 }, + + /* Error adjusting EA refcount */ + { PR_2_ADJ_EA_REFCOUNT, + N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Invalid HTREE root node */ + { PR_2_HTREE_BAD_ROOT, + N_("@p @h %d: root node is @n\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Invalid HTREE limit */ + { PR_2_HTREE_BAD_LIMIT, + N_("@p @h %d: node (%B) has @n limit (%N)\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Invalid HTREE count */ + { PR_2_HTREE_BAD_COUNT, + N_("@p @h %d: node (%B) has @n count (%N)\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* HTREE interior node has out-of-order hashes in table */ + { PR_2_HTREE_HASH_ORDER, + N_("@p @h %d: node (%B) has an unordered hash table\n"), + PROMPT_CLEAR_HTREE, PR_PREEN_OK }, + + /* Node in HTREE directory has invalid depth */ + { PR_2_HTREE_BAD_DEPTH, + N_("@p @h %d: node (%B) has @n depth\n"), + PROMPT_NONE, 0 }, + + /* Duplicate directory entry found */ + { PR_2_DUPLICATE_DIRENT, + N_("Duplicate @E found. "), + PROMPT_CLEAR, 0 }, + + /* Non-unique filename found */ + { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */ + N_("@E has a non-unique filename.\nRename to %s"), + PROMPT_NULL, 0 }, + + /* Duplicate directory entry found */ + { PR_2_REPORT_DUP_DIRENT, + N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"), + PROMPT_NONE, 0 }, + + /* Pass 3 errors */ + + /* Pass 3: Checking directory connectivity */ + { PR_3_PASS_HEADER, + N_("Pass 3: Checking @d connectivity\n"), + PROMPT_NONE, 0 }, + + /* Root inode not allocated */ + { PR_3_NO_ROOT_INODE, + N_("@r not allocated. "), + PROMPT_ALLOCATE, 0 }, + + /* No room in lost+found */ + { PR_3_EXPAND_LF_DIR, + N_("No room in @l @d. "), + PROMPT_EXPAND, 0 }, + + /* Unconnected directory inode */ + { PR_3_UNCONNECTED_DIR, + N_("Unconnected @d @i %i (%p)\n"), + PROMPT_CONNECT, 0 }, + + /* /lost+found not found */ + { PR_3_NO_LF_DIR, + N_("/@l not found. "), + PROMPT_CREATE, PR_PREEN_OK }, + + /* .. entry is incorrect */ + { PR_3_BAD_DOT_DOT, + N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"), + PROMPT_FIX, 0 }, + + /* Bad or non-existent /lost+found. Cannot reconnect */ + { PR_3_NO_LPF, + N_("Bad or non-existent /@l. Cannot reconnect.\n"), + PROMPT_NONE, 0 }, + + /* Could not expand /lost+found */ + { PR_3_CANT_EXPAND_LPF, + N_("Could not expand /@l: %m\n"), + PROMPT_NONE, 0 }, + + /* Could not reconnect inode */ + { PR_3_CANT_RECONNECT, + N_("Could not reconnect %i: %m\n"), + PROMPT_NONE, 0 }, + + /* Error while trying to find /lost+found */ + { PR_3_ERR_FIND_LPF, + N_("Error while trying to find /@l: %m\n"), + PROMPT_NONE, 0 }, + + /* Error in ext2fs_new_block while creating /lost+found */ + { PR_3_ERR_LPF_NEW_BLOCK, + N_("ext2fs_new_@b: %m while trying to create /@l @d\n"), + PROMPT_NONE, 0 }, + + /* Error in ext2fs_new_inode while creating /lost+found */ + { PR_3_ERR_LPF_NEW_INODE, + N_("ext2fs_new_@i: %m while trying to create /@l @d\n"), + PROMPT_NONE, 0 }, + + /* Error in ext2fs_new_dir_block while creating /lost+found */ + { PR_3_ERR_LPF_NEW_DIR_BLOCK, + N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"), + PROMPT_NONE, 0 }, + + /* Error while writing directory block for /lost+found */ + { PR_3_ERR_LPF_WRITE_BLOCK, + N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"), + PROMPT_NONE, 0 }, + + /* Error while adjusting inode count */ + { PR_3_ADJUST_INODE, + N_("Error while adjusting @i count on @i %i\n"), + PROMPT_NONE, 0 }, + + /* Couldn't fix parent directory -- error */ + { PR_3_FIX_PARENT_ERR, + N_("Couldn't fix parent of @i %i: %m\n\n"), + PROMPT_NONE, 0 }, + + /* Couldn't fix parent directory -- couldn't find it */ + { PR_3_FIX_PARENT_NOFIND, + N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"), + PROMPT_NONE, 0 }, + + /* Error allocating inode bitmap */ + { PR_3_ALLOCATE_IBITMAP_ERROR, + N_("@A @i @B (%N): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error creating root directory */ + { PR_3_CREATE_ROOT_ERROR, + N_("Error creating root @d (%s): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error creating lost and found directory */ + { PR_3_CREATE_LPF_ERROR, + N_("Error creating /@l @d (%s): %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Root inode is not directory; aborting */ + { PR_3_ROOT_NOT_DIR_ABORT, + N_("@r is not a @d; aborting.\n"), + PROMPT_NONE, PR_FATAL }, + + /* Cannot proceed without a root inode. */ + { PR_3_NO_ROOT_INODE_ABORT, + N_("can't proceed without a @r.\n"), + PROMPT_NONE, PR_FATAL }, + + /* Internal error: couldn't find dir_info */ + { PR_3_NO_DIRINFO, + N_("Internal error: cannot find dir_info for %i.\n"), + PROMPT_NONE, PR_FATAL }, + + /* Lost+found not a directory */ + { PR_3_LPF_NOTDIR, + N_("/@l is not a @d (ino=%i)\n"), + PROMPT_UNLINK, 0 }, + + /* Pass 3A Directory Optimization */ + + /* Pass 3A: Optimizing directories */ + { PR_3A_PASS_HEADER, + N_("Pass 3A: Optimizing directories\n"), + PROMPT_NONE, PR_PREEN_NOMSG }, + + /* Error iterating over directories */ + { PR_3A_OPTIMIZE_ITER, + N_("Failed to create dirs_to_hash iterator: %m"), + PROMPT_NONE, 0 }, + + /* Error rehash directory */ + { PR_3A_OPTIMIZE_DIR_ERR, + N_("Failed to optimize directory %q (%d): %m"), + PROMPT_NONE, 0 }, + + /* Rehashing dir header */ + { PR_3A_OPTIMIZE_DIR_HEADER, + N_("Optimizing directories: "), + PROMPT_NONE, PR_MSG_ONLY }, + + /* Rehashing directory %d */ + { PR_3A_OPTIMIZE_DIR, + " %d", + PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR}, + + /* Rehashing dir end */ + { PR_3A_OPTIMIZE_DIR_END, + "\n", + PROMPT_NONE, PR_PREEN_NOHDR }, + + /* Pass 4 errors */ + + /* Pass 4: Checking reference counts */ + { PR_4_PASS_HEADER, + N_("Pass 4: Checking reference counts\n"), + PROMPT_NONE, 0 }, + + /* Unattached zero-length inode */ + { PR_4_ZERO_LEN_INODE, + N_("@u @z @i %i. "), + PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK }, + + /* Unattached inode */ + { PR_4_UNATTACHED_INODE, + N_("@u @i %i\n"), + PROMPT_CONNECT, 0 }, + + /* Inode ref count wrong */ + { PR_4_BAD_REF_COUNT, + N_("@i %i ref count is %Il, @s %N. "), + PROMPT_FIX, PR_PREEN_OK }, + + { PR_4_INCONSISTENT_COUNT, + N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n" + "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n" + "@i_link_info[%i] is %N, @i.i_links_count is %Il. " + "They @s the same!\n"), + PROMPT_NONE, 0 }, + + /* Pass 5 errors */ + + /* Pass 5: Checking group summary information */ + { PR_5_PASS_HEADER, + N_("Pass 5: Checking @g summary information\n"), + PROMPT_NONE, 0 }, + + /* Padding at end of inode bitmap is not set. */ + { PR_5_INODE_BMAP_PADDING, + N_("Padding at end of @i @B is not set. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Padding at end of block bitmap is not set. */ + { PR_5_BLOCK_BMAP_PADDING, + N_("Padding at end of @b @B is not set. "), + PROMPT_FIX, PR_PREEN_OK }, + + /* Block bitmap differences header */ + { PR_5_BLOCK_BITMAP_HEADER, + N_("@b @B differences: "), + PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG}, + + /* Block not used, but marked in bitmap */ + { PR_5_BLOCK_UNUSED, + " -%b", + PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block used, but not marked used in bitmap */ + { PR_5_BLOCK_USED, + " +%b", + PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block bitmap differences end */ + { PR_5_BLOCK_BITMAP_END, + "\n", + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Inode bitmap differences header */ + { PR_5_INODE_BITMAP_HEADER, + N_("@i @B differences: "), + PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Inode not used, but marked in bitmap */ + { PR_5_INODE_UNUSED, + " -%i", + PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Inode used, but not marked used in bitmap */ + { PR_5_INODE_USED, + " +%i", + PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Inode bitmap differences end */ + { PR_5_INODE_BITMAP_END, + "\n", + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Free inodes count for group wrong */ + { PR_5_FREE_INODE_COUNT_GROUP, + N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"), + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Directories count for group wrong */ + { PR_5_FREE_DIR_COUNT_GROUP, + N_("Directories count wrong for @g #%g (%i, counted=%j).\n"), + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Free inodes count wrong */ + { PR_5_FREE_INODE_COUNT, + N_("Free @is count wrong (%i, counted=%j).\n"), + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Free blocks count for group wrong */ + { PR_5_FREE_BLOCK_COUNT_GROUP, + N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"), + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Free blocks count wrong */ + { PR_5_FREE_BLOCK_COUNT, + N_("Free @bs count wrong (%b, counted=%c).\n"), + PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Programming error: bitmap endpoints don't match */ + { PR_5_BMAP_ENDPOINTS, + N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't " + "match calculated @B endpoints (%i, %j)\n"), + PROMPT_NONE, PR_FATAL }, + + /* Internal error: fudging end of bitmap */ + { PR_5_FUDGE_BITMAP_ERROR, + N_("Internal error: fudging end of bitmap (%N)\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error copying in replacement inode bitmap */ + { PR_5_COPY_IBITMAP_ERROR, + N_("Error copying in replacement @i @B: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Error copying in replacement block bitmap */ + { PR_5_COPY_BBITMAP_ERROR, + N_("Error copying in replacement @b @B: %m\n"), + PROMPT_NONE, PR_FATAL }, + + /* Block range not used, but marked in bitmap */ + { PR_5_BLOCK_RANGE_UNUSED, + " -(%b--%c)", + PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Block range used, but not marked used in bitmap */ + { PR_5_BLOCK_RANGE_USED, + " +(%b--%c)", + PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Inode range not used, but marked in bitmap */ + { PR_5_INODE_RANGE_UNUSED, + " -(%i--%j)", + PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + /* Inode range used, but not marked used in bitmap */ + { PR_5_INODE_RANGE_USED, + " +(%i--%j)", + PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG }, + + { 0 } +}; + +/* + * This is the latch flags register. It allows several problems to be + * "latched" together. This means that the user has to answer but one + * question for the set of problems, and all of the associated + * problems will be either fixed or not fixed. + */ +static struct latch_descr pr_latch_info[] = { + { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 }, + { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 }, + { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END }, + { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END }, + { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 }, + { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END }, + { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 }, + { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 }, + { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END }, + { -1, 0, 0 }, +}; + +static const struct e2fsck_problem *find_problem(problem_t code) +{ + int i; + + for (i=0; problem_table[i].e2p_code; i++) { + if (problem_table[i].e2p_code == code) + return &problem_table[i]; + } + return 0; +} + +static struct latch_descr *find_latch(int code) +{ + int i; + + for (i=0; pr_latch_info[i].latch_code >= 0; i++) { + if (pr_latch_info[i].latch_code == code) + return &pr_latch_info[i]; + } + return 0; +} + +int end_problem_latch(e2fsck_t ctx, int mask) +{ + struct latch_descr *ldesc; + struct problem_context pctx; + int answer = -1; + + ldesc = find_latch(mask); + if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) { + clear_problem_context(&pctx); + answer = fix_problem(ctx, ldesc->end_message, &pctx); + } + ldesc->flags &= ~(PRL_VARIABLE); + return answer; +} + +int set_latch_flags(int mask, int setflags, int clearflags) +{ + struct latch_descr *ldesc; + + ldesc = find_latch(mask); + if (!ldesc) + return -1; + ldesc->flags |= setflags; + ldesc->flags &= ~clearflags; + return 0; +} + +void clear_problem_context(struct problem_context *ctx) +{ + memset(ctx, 0, sizeof(struct problem_context)); + ctx->blkcount = -1; + ctx->group = -1; +} + +int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx) +{ + ext2_filsys fs = ctx->fs; + const struct e2fsck_problem *ptr; + struct latch_descr *ldesc = NULL; + const char *message; + int def_yn, answer, ans; + int print_answer = 0; + int suppress = 0; + + ptr = find_problem(code); + if (!ptr) { + printf(_("Unhandled error code (0x%x)!\n"), code); + return 0; + } + def_yn = 1; + if ((ptr->flags & PR_NO_DEFAULT) || + ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) || + (ctx->options & E2F_OPT_NO)) + def_yn= 0; + + /* + * Do special latch processing. This is where we ask the + * latch question, if it exists + */ + if (ptr->flags & PR_LATCH_MASK) { + ldesc = find_latch(ptr->flags & PR_LATCH_MASK); + if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) { + ans = fix_problem(ctx, ldesc->question, pctx); + if (ans == 1) + ldesc->flags |= PRL_YES; + if (ans == 0) + ldesc->flags |= PRL_NO; + ldesc->flags |= PRL_LATCHED; + } + if (ldesc->flags & PRL_SUPPRESS) + suppress++; + } + if ((ptr->flags & PR_PREEN_NOMSG) && + (ctx->options & E2F_OPT_PREEN)) + suppress++; + if ((ptr->flags & PR_NO_NOMSG) && + (ctx->options & E2F_OPT_NO)) + suppress++; + if (!suppress) { + message = ptr->e2p_description; + if ((ctx->options & E2F_OPT_PREEN) && + !(ptr->flags & PR_PREEN_NOHDR)) { + printf("%s: ", ctx->device_name ? + ctx->device_name : ctx->filesystem_name); + } + if (*message) + print_e2fsck_message(ctx, _(message), pctx, 1); + } + if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE)) + preenhalt(ctx); + + if (ptr->flags & PR_FATAL) + bb_error_msg_and_die(0); + + if (ptr->prompt == PROMPT_NONE) { + if (ptr->flags & PR_NOCOLLATE) + answer = -1; + else + answer = def_yn; + } else { + if (ctx->options & E2F_OPT_PREEN) { + answer = def_yn; + if (!(ptr->flags & PR_PREEN_NOMSG)) + print_answer = 1; + } else if ((ptr->flags & PR_LATCH_MASK) && + (ldesc->flags & (PRL_YES | PRL_NO))) { + if (!suppress) + print_answer = 1; + if (ldesc->flags & PRL_YES) + answer = 1; + else + answer = 0; + } else + answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn); + if (!answer && !(ptr->flags & PR_NO_OK)) + ext2fs_unmark_valid(fs); + + if (print_answer) + printf("%s.\n", answer ? + _(preen_msg[(int) ptr->prompt]) : _("IGNORED")); + } + + if ((ptr->prompt == PROMPT_ABORT) && answer) + bb_error_msg_and_die(0); + + if (ptr->flags & PR_AFTER_CODE) + answer = fix_problem(ctx, ptr->second_code, pctx); + + return answer; +} + +/* + * linux/fs/recovery.c + * + * Written by Stephen C. Tweedie , 1999 + */ + +/* + * Maintain information about the progress of the recovery job, so that + * the different passes can carry information between them. + */ +struct recovery_info +{ + tid_t start_transaction; + tid_t end_transaction; + + int nr_replays; + int nr_revokes; + int nr_revoke_hits; +}; + +enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY}; +static int do_one_pass(journal_t *journal, + struct recovery_info *info, enum passtype pass); +static int scan_revoke_records(journal_t *, struct buffer_head *, + tid_t, struct recovery_info *); + +/* + * Read a block from the journal + */ + +static int jread(struct buffer_head **bhp, journal_t *journal, + unsigned int offset) +{ + int err; + unsigned long blocknr; + struct buffer_head *bh; + + *bhp = NULL; + + err = journal_bmap(journal, offset, &blocknr); + + if (err) { + printf("JBD: bad block at offset %u\n", offset); + return err; + } + + bh = getblk(journal->j_dev, blocknr, journal->j_blocksize); + if (!bh) + return -ENOMEM; + + if (!buffer_uptodate(bh)) { + /* If this is a brand new buffer, start readahead. + Otherwise, we assume we are already reading it. */ + if (!buffer_req(bh)) + do_readahead(journal, offset); + wait_on_buffer(bh); + } + + if (!buffer_uptodate(bh)) { + printf("JBD: Failed to read block at offset %u\n", offset); + brelse(bh); + return -EIO; + } + + *bhp = bh; + return 0; +} + + +/* + * Count the number of in-use tags in a journal descriptor block. + */ + +static int count_tags(struct buffer_head *bh, int size) +{ + char * tagp; + journal_block_tag_t * tag; + int nr = 0; + + tagp = &bh->b_data[sizeof(journal_header_t)]; + + while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) { + tag = (journal_block_tag_t *) tagp; + + nr++; + tagp += sizeof(journal_block_tag_t); + if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID))) + tagp += 16; + + if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG)) + break; + } + + return nr; +} + + +/* Make sure we wrap around the log correctly! */ +#define wrap(journal, var) \ +do { \ + if (var >= (journal)->j_last) \ + var -= ((journal)->j_last - (journal)->j_first); \ +} while (0) + +/** + * int journal_recover(journal_t *journal) - recovers a on-disk journal + * @journal: the journal to recover + * + * The primary function for recovering the log contents when mounting a + * journaled device. + * + * Recovery is done in three passes. In the first pass, we look for the + * end of the log. In the second, we assemble the list of revoke + * blocks. In the third and final pass, we replay any un-revoked blocks + * in the log. + */ +int journal_recover(journal_t *journal) +{ + int err; + journal_superblock_t * sb; + + struct recovery_info info; + + memset(&info, 0, sizeof(info)); + sb = journal->j_superblock; + + /* + * The journal superblock's s_start field (the current log head) + * is always zero if, and only if, the journal was cleanly + * unmounted. + */ + + if (!sb->s_start) { + journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1; + return 0; + } + + err = do_one_pass(journal, &info, PASS_SCAN); + if (!err) + err = do_one_pass(journal, &info, PASS_REVOKE); + if (!err) + err = do_one_pass(journal, &info, PASS_REPLAY); + + /* Restart the log at the next transaction ID, thus invalidating + * any existing commit records in the log. */ + journal->j_transaction_sequence = ++info.end_transaction; + + journal_clear_revoke(journal); + sync_blockdev(journal->j_fs_dev); + return err; +} + +static int do_one_pass(journal_t *journal, + struct recovery_info *info, enum passtype pass) +{ + unsigned int first_commit_ID, next_commit_ID; + unsigned long next_log_block; + int err, success = 0; + journal_superblock_t * sb; + journal_header_t * tmp; + struct buffer_head * bh; + unsigned int sequence; + int blocktype; + + /* Precompute the maximum metadata descriptors in a descriptor block */ + int MAX_BLOCKS_PER_DESC; + MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) + / sizeof(journal_block_tag_t)); + + /* + * First thing is to establish what we expect to find in the log + * (in terms of transaction IDs), and where (in terms of log + * block offsets): query the superblock. + */ + + sb = journal->j_superblock; + next_commit_ID = ntohl(sb->s_sequence); + next_log_block = ntohl(sb->s_start); + + first_commit_ID = next_commit_ID; + if (pass == PASS_SCAN) + info->start_transaction = first_commit_ID; + + /* + * Now we walk through the log, transaction by transaction, + * making sure that each transaction has a commit block in the + * expected place. Each complete transaction gets replayed back + * into the main filesystem. + */ + + while (1) { + int flags; + char * tagp; + journal_block_tag_t * tag; + struct buffer_head * obh; + struct buffer_head * nbh; + + /* If we already know where to stop the log traversal, + * check right now that we haven't gone past the end of + * the log. */ + + if (pass != PASS_SCAN) + if (tid_geq(next_commit_ID, info->end_transaction)) + break; + + /* Skip over each chunk of the transaction looking + * either the next descriptor block or the final commit + * record. */ + + err = jread(&bh, journal, next_log_block); + if (err) + goto failed; + + next_log_block++; + wrap(journal, next_log_block); + + /* What kind of buffer is it? + * + * If it is a descriptor block, check that it has the + * expected sequence number. Otherwise, we're all done + * here. */ + + tmp = (journal_header_t *)bh->b_data; + + if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) { + brelse(bh); + break; + } + + blocktype = ntohl(tmp->h_blocktype); + sequence = ntohl(tmp->h_sequence); + + if (sequence != next_commit_ID) { + brelse(bh); + break; + } + + /* OK, we have a valid descriptor block which matches + * all of the sequence number checks. What are we going + * to do with it? That depends on the pass... */ + + switch (blocktype) { + case JFS_DESCRIPTOR_BLOCK: + /* If it is a valid descriptor block, replay it + * in pass REPLAY; otherwise, just skip over the + * blocks it describes. */ + if (pass != PASS_REPLAY) { + next_log_block += + count_tags(bh, journal->j_blocksize); + wrap(journal, next_log_block); + brelse(bh); + continue; + } + + /* A descriptor block: we can now write all of + * the data blocks. Yay, useful work is finally + * getting done here! */ + + tagp = &bh->b_data[sizeof(journal_header_t)]; + while ((tagp - bh->b_data +sizeof(journal_block_tag_t)) + <= journal->j_blocksize) { + unsigned long io_block; + + tag = (journal_block_tag_t *) tagp; + flags = ntohl(tag->t_flags); + + io_block = next_log_block++; + wrap(journal, next_log_block); + err = jread(&obh, journal, io_block); + if (err) { + /* Recover what we can, but + * report failure at the end. */ + success = err; + printf("JBD: IO error %d recovering " + "block %ld in log\n", + err, io_block); + } else { + unsigned long blocknr; + + blocknr = ntohl(tag->t_blocknr); + + /* If the block has been + * revoked, then we're all done + * here. */ + if (journal_test_revoke + (journal, blocknr, + next_commit_ID)) { + brelse(obh); + ++info->nr_revoke_hits; + goto skip_write; + } + + /* Find a buffer for the new + * data being restored */ + nbh = getblk(journal->j_fs_dev, + blocknr, + journal->j_blocksize); + if (nbh == NULL) { + printf("JBD: Out of memory " + "during recovery.\n"); + err = -ENOMEM; + brelse(bh); + brelse(obh); + goto failed; + } + + lock_buffer(nbh); + memcpy(nbh->b_data, obh->b_data, + journal->j_blocksize); + if (flags & JFS_FLAG_ESCAPE) { + *((unsigned int *)bh->b_data) = + htonl(JFS_MAGIC_NUMBER); + } + + mark_buffer_uptodate(nbh, 1); + mark_buffer_dirty(nbh); + ++info->nr_replays; + /* ll_rw_block(WRITE, 1, &nbh); */ + unlock_buffer(nbh); + brelse(obh); + brelse(nbh); + } + + skip_write: + tagp += sizeof(journal_block_tag_t); + if (!(flags & JFS_FLAG_SAME_UUID)) + tagp += 16; + + if (flags & JFS_FLAG_LAST_TAG) + break; + } + + brelse(bh); + continue; + + case JFS_COMMIT_BLOCK: + /* Found an expected commit block: not much to + * do other than move on to the next sequence + * number. */ + brelse(bh); + next_commit_ID++; + continue; + + case JFS_REVOKE_BLOCK: + /* If we aren't in the REVOKE pass, then we can + * just skip over this block. */ + if (pass != PASS_REVOKE) { + brelse(bh); + continue; + } + + err = scan_revoke_records(journal, bh, + next_commit_ID, info); + brelse(bh); + if (err) + goto failed; + continue; + + default: + goto done; + } + } + + done: + /* + * We broke out of the log scan loop: either we came to the + * known end of the log or we found an unexpected block in the + * log. If the latter happened, then we know that the "current" + * transaction marks the end of the valid log. + */ + + if (pass == PASS_SCAN) + info->end_transaction = next_commit_ID; + else { + /* It's really bad news if different passes end up at + * different places (but possible due to IO errors). */ + if (info->end_transaction != next_commit_ID) { + printf("JBD: recovery pass %d ended at " + "transaction %u, expected %u\n", + pass, next_commit_ID, info->end_transaction); + if (!success) + success = -EIO; + } + } + + return success; + + failed: + return err; +} + + +/* Scan a revoke record, marking all blocks mentioned as revoked. */ + +static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, + tid_t sequence, struct recovery_info *info) +{ + journal_revoke_header_t *header; + int offset, max; + + header = (journal_revoke_header_t *) bh->b_data; + offset = sizeof(journal_revoke_header_t); + max = ntohl(header->r_count); + + while (offset < max) { + unsigned long blocknr; + int err; + + blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset))); + offset += 4; + err = journal_set_revoke(journal, blocknr, sequence); + if (err) + return err; + ++info->nr_revokes; + } + return 0; +} + + +/* + * rehash.c --- rebuild hash tree directories + * + * This algorithm is designed for simplicity of implementation and to + * pack the directory as much as possible. It however requires twice + * as much memory as the size of the directory. The maximum size + * directory supported using a 4k blocksize is roughly a gigabyte, and + * so there may very well be problems with machines that don't have + * virtual memory, and obscenely large directories. + * + * An alternate algorithm which is much more disk intensive could be + * written, and probably will need to be written in the future. The + * design goals of such an algorithm are: (a) use (roughly) constant + * amounts of memory, no matter how large the directory, (b) the + * directory must be safe at all times, even if e2fsck is interrupted + * in the middle, (c) we must use minimal amounts of extra disk + * blocks. This pretty much requires an incremental approach, where + * we are reading from one part of the directory, and inserting into + * the front half. So the algorithm will have to keep track of a + * moving block boundary between the new tree and the old tree, and + * files will need to be moved from the old directory and inserted + * into the new tree. If the new directory requires space which isn't + * yet available, blocks from the beginning part of the old directory + * may need to be moved to the end of the directory to make room for + * the new tree: + * + * -------------------------------------------------------- + * | new tree | | old tree | + * -------------------------------------------------------- + * ^ ptr ^ptr + * tail new head old + * + * This is going to be a pain in the tuckus to implement, and will + * require a lot more disk accesses. So I'm going to skip it for now; + * it's only really going to be an issue for really, really big + * filesystems (when we reach the level of tens of millions of files + * in a single directory). It will probably be easier to simply + * require that e2fsck use VM first. + */ + +struct fill_dir_struct { + char *buf; + struct ext2_inode *inode; + int err; + e2fsck_t ctx; + struct hash_entry *harray; + int max_array, num_array; + int dir_size; + int compress; + ino_t parent; +}; + +struct hash_entry { + ext2_dirhash_t hash; + ext2_dirhash_t minor_hash; + struct ext2_dir_entry *dir; +}; + +struct out_dir { + int num; + int max; + char *buf; + ext2_dirhash_t *hashes; +}; + +static int fill_dir_block(ext2_filsys fs, + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data; + struct hash_entry *new_array, *ent; + struct ext2_dir_entry *dirent; + char *dir; + unsigned int offset, dir_offset; + + if (blockcnt < 0) + return 0; + + offset = blockcnt * fs->blocksize; + if (offset + fs->blocksize > fd->inode->i_size) { + fd->err = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + dir = (fd->buf+offset); + if (HOLE_BLKADDR(*block_nr)) { + memset(dir, 0, fs->blocksize); + dirent = (struct ext2_dir_entry *) dir; + dirent->rec_len = fs->blocksize; + } else { + fd->err = ext2fs_read_dir_block(fs, *block_nr, dir); + if (fd->err) + return BLOCK_ABORT; + } + /* While the directory block is "hot", index it. */ + dir_offset = 0; + while (dir_offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (dir + dir_offset); + if (((dir_offset + dirent->rec_len) > fs->blocksize) || + (dirent->rec_len < 8) || + ((dirent->rec_len % 4) != 0) || + (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { + fd->err = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + dir_offset += dirent->rec_len; + if (dirent->inode == 0) + continue; + if (!fd->compress && ((dirent->name_len&0xFF) == 1) && + (dirent->name[0] == '.')) + continue; + if (!fd->compress && ((dirent->name_len&0xFF) == 2) && + (dirent->name[0] == '.') && (dirent->name[1] == '.')) { + fd->parent = dirent->inode; + continue; + } + if (fd->num_array >= fd->max_array) { + new_array = xrealloc(fd->harray, + sizeof(struct hash_entry) * (fd->max_array+500)); + fd->harray = new_array; + fd->max_array += 500; + } + ent = fd->harray + fd->num_array++; + ent->dir = dirent; + fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); + if (fd->compress) + ent->hash = ent->minor_hash = 0; + else { + fd->err = ext2fs_dirhash(fs->super->s_def_hash_version, + dirent->name, + dirent->name_len & 0xFF, + fs->super->s_hash_seed, + &ent->hash, &ent->minor_hash); + if (fd->err) + return BLOCK_ABORT; + } + } + + return 0; +} + +/* Used for sorting the hash entry */ +static int name_cmp(const void *a, const void *b) +{ + const struct hash_entry *he_a = (const struct hash_entry *) a; + const struct hash_entry *he_b = (const struct hash_entry *) b; + int ret; + int min_len; + + min_len = he_a->dir->name_len; + if (min_len > he_b->dir->name_len) + min_len = he_b->dir->name_len; + + ret = strncmp(he_a->dir->name, he_b->dir->name, min_len); + if (ret == 0) { + if (he_a->dir->name_len > he_b->dir->name_len) + ret = 1; + else if (he_a->dir->name_len < he_b->dir->name_len) + ret = -1; + else + ret = he_b->dir->inode - he_a->dir->inode; + } + return ret; +} + +/* Used for sorting the hash entry */ +static int hash_cmp(const void *a, const void *b) +{ + const struct hash_entry *he_a = (const struct hash_entry *) a; + const struct hash_entry *he_b = (const struct hash_entry *) b; + int ret; + + if (he_a->hash > he_b->hash) + ret = 1; + else if (he_a->hash < he_b->hash) + ret = -1; + else { + if (he_a->minor_hash > he_b->minor_hash) + ret = 1; + else if (he_a->minor_hash < he_b->minor_hash) + ret = -1; + else + ret = name_cmp(a, b); + } + return ret; +} + +static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir, + int blocks) +{ + void *new_mem; + + if (outdir->max) { + new_mem = xrealloc(outdir->buf, blocks * fs->blocksize); + outdir->buf = new_mem; + new_mem = xrealloc(outdir->hashes, + blocks * sizeof(ext2_dirhash_t)); + outdir->hashes = new_mem; + } else { + outdir->buf = xmalloc(blocks * fs->blocksize); + outdir->hashes = xmalloc(blocks * sizeof(ext2_dirhash_t)); + outdir->num = 0; + } + outdir->max = blocks; + return 0; +} + +static void free_out_dir(struct out_dir *outdir) +{ + free(outdir->buf); + free(outdir->hashes); + outdir->max = 0; + outdir->num =0; +} + +static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir, + char ** ret) +{ + errcode_t retval; + + if (outdir->num >= outdir->max) { + retval = alloc_size_dir(fs, outdir, outdir->max + 50); + if (retval) + return retval; + } + *ret = outdir->buf + (outdir->num++ * fs->blocksize); + memset(*ret, 0, fs->blocksize); + return 0; +} + +/* + * This function is used to make a unique filename. We do this by + * appending ~0, and then incrementing the number. However, we cannot + * expand the length of the filename beyond the padding available in + * the directory entry. + */ +static void mutate_name(char *str, __u16 *len) +{ + int i; + __u16 l = *len & 0xFF, h = *len & 0xff00; + + /* + * First check to see if it looks the name has been mutated + * already + */ + for (i = l-1; i > 0; i--) { + if (!isdigit(str[i])) + break; + } + if ((i == l-1) || (str[i] != '~')) { + if (((l-1) & 3) < 2) + l += 2; + else + l = (l+3) & ~3; + str[l-2] = '~'; + str[l-1] = '0'; + *len = l | h; + return; + } + for (i = l-1; i >= 0; i--) { + if (isdigit(str[i])) { + if (str[i] == '9') + str[i] = '0'; + else { + str[i]++; + return; + } + continue; + } + if (i == 1) { + if (str[0] == 'z') + str[0] = 'A'; + else if (str[0] == 'Z') { + str[0] = '~'; + str[1] = '0'; + } else + str[0]++; + } else if (i > 0) { + str[i] = '1'; + str[i-1] = '~'; + } else { + if (str[0] == '~') + str[0] = 'a'; + else + str[0]++; + } + break; + } +} + +static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs, + ext2_ino_t ino, + struct fill_dir_struct *fd) +{ + struct problem_context pctx; + struct hash_entry *ent, *prev; + int i, j; + int fixed = 0; + char new_name[256]; + __u16 new_len; + + clear_problem_context(&pctx); + pctx.ino = ino; + + for (i=1; i < fd->num_array; i++) { + ent = fd->harray + i; + prev = ent - 1; + if (!ent->dir->inode || + ((ent->dir->name_len & 0xFF) != + (prev->dir->name_len & 0xFF)) || + (strncmp(ent->dir->name, prev->dir->name, + ent->dir->name_len & 0xFF))) + continue; + pctx.dirent = ent->dir; + if ((ent->dir->inode == prev->dir->inode) && + fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) { + e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1); + ent->dir->inode = 0; + fixed++; + continue; + } + memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF); + new_len = ent->dir->name_len; + mutate_name(new_name, &new_len); + for (j=0; j < fd->num_array; j++) { + if ((i==j) || + ((ent->dir->name_len & 0xFF) != + (fd->harray[j].dir->name_len & 0xFF)) || + (strncmp(new_name, fd->harray[j].dir->name, + new_len & 0xFF))) + continue; + mutate_name(new_name, &new_len); + + j = -1; + } + new_name[new_len & 0xFF] = 0; + pctx.str = new_name; + if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) { + memcpy(ent->dir->name, new_name, new_len & 0xFF); + ent->dir->name_len = new_len; + ext2fs_dirhash(fs->super->s_def_hash_version, + ent->dir->name, + ent->dir->name_len & 0xFF, + fs->super->s_hash_seed, + &ent->hash, &ent->minor_hash); + fixed++; + } + } + return fixed; +} + + +static errcode_t copy_dir_entries(ext2_filsys fs, + struct fill_dir_struct *fd, + struct out_dir *outdir) +{ + errcode_t retval; + char *block_start; + struct hash_entry *ent; + struct ext2_dir_entry *dirent; + int i, rec_len, left; + ext2_dirhash_t prev_hash; + int offset; + + outdir->max = 0; + retval = alloc_size_dir(fs, outdir, + (fd->dir_size / fs->blocksize) + 2); + if (retval) + return retval; + outdir->num = fd->compress ? 0 : 1; + offset = 0; + outdir->hashes[0] = 0; + prev_hash = 1; + if ((retval = get_next_block(fs, outdir, &block_start))) + return retval; + dirent = (struct ext2_dir_entry *) block_start; + left = fs->blocksize; + for (i=0; i < fd->num_array; i++) { + ent = fd->harray + i; + if (ent->dir->inode == 0) + continue; + rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF); + if (rec_len > left) { + if (left) + dirent->rec_len += left; + if ((retval = get_next_block(fs, outdir, + &block_start))) + return retval; + offset = 0; + } + left = fs->blocksize - offset; + dirent = (struct ext2_dir_entry *) (block_start + offset); + if (offset == 0) { + if (ent->hash == prev_hash) + outdir->hashes[outdir->num-1] = ent->hash | 1; + else + outdir->hashes[outdir->num-1] = ent->hash; + } + dirent->inode = ent->dir->inode; + dirent->name_len = ent->dir->name_len; + dirent->rec_len = rec_len; + memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF); + offset += rec_len; + left -= rec_len; + if (left < 12) { + dirent->rec_len += left; + offset += left; + left = 0; + } + prev_hash = ent->hash; + } + if (left) + dirent->rec_len += left; + + return 0; +} + + +static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, + ext2_ino_t ino, ext2_ino_t parent) +{ + struct ext2_dir_entry *dir; + struct ext2_dx_root_info *root; + struct ext2_dx_countlimit *limits; + int filetype = 0; + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) + filetype = EXT2_FT_DIR << 8; + + memset(buf, 0, fs->blocksize); + dir = (struct ext2_dir_entry *) buf; + dir->inode = ino; + dir->name[0] = '.'; + dir->name_len = 1 | filetype; + dir->rec_len = 12; + dir = (struct ext2_dir_entry *) (buf + 12); + dir->inode = parent; + dir->name[0] = '.'; + dir->name[1] = '.'; + dir->name_len = 2 | filetype; + dir->rec_len = fs->blocksize - 12; + + root = (struct ext2_dx_root_info *) (buf+24); + root->reserved_zero = 0; + root->hash_version = fs->super->s_def_hash_version; + root->info_length = 8; + root->indirect_levels = 0; + root->unused_flags = 0; + + limits = (struct ext2_dx_countlimit *) (buf+32); + limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry); + limits->count = 0; + + return root; +} + + +static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) +{ + struct ext2_dir_entry *dir; + struct ext2_dx_countlimit *limits; + + memset(buf, 0, fs->blocksize); + dir = (struct ext2_dir_entry *) buf; + dir->inode = 0; + dir->rec_len = fs->blocksize; + + limits = (struct ext2_dx_countlimit *) (buf+8); + limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry); + limits->count = 0; + + return (struct ext2_dx_entry *) limits; +} + +/* + * This function takes the leaf nodes which have been written in + * outdir, and populates the root node and any necessary interior nodes. + */ +static errcode_t calculate_tree(ext2_filsys fs, + struct out_dir *outdir, + ext2_ino_t ino, + ext2_ino_t parent) +{ + struct ext2_dx_root_info *root_info; + struct ext2_dx_entry *root, *dx_ent = NULL; + struct ext2_dx_countlimit *root_limit, *limit; + errcode_t retval; + char * block_start; + int i, c1, c2, nblks; + int limit_offset, root_offset; + + root_info = set_root_node(fs, outdir->buf, ino, parent); + root_offset = limit_offset = ((char *) root_info - outdir->buf) + + root_info->info_length; + root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); + c1 = root_limit->limit; + nblks = outdir->num; + + /* Write out the pointer blocks */ + if (nblks-1 <= c1) { + /* Just write out the root block, and we're done */ + root = (struct ext2_dx_entry *) (outdir->buf + root_offset); + for (i=1; i < nblks; i++) { + root->block = ext2fs_cpu_to_le32(i); + if (i != 1) + root->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + root++; + c1--; + } + } else { + c2 = 0; + limit = 0; + root_info->indirect_levels = 1; + for (i=1; i < nblks; i++) { + if (c1 == 0) + return ENOSPC; + if (c2 == 0) { + if (limit) + limit->limit = limit->count = + ext2fs_cpu_to_le16(limit->limit); + root = (struct ext2_dx_entry *) + (outdir->buf + root_offset); + root->block = ext2fs_cpu_to_le32(outdir->num); + if (i != 1) + root->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + if ((retval = get_next_block(fs, outdir, + &block_start))) + return retval; + dx_ent = set_int_node(fs, block_start); + limit = (struct ext2_dx_countlimit *) dx_ent; + c2 = limit->limit; + root_offset += sizeof(struct ext2_dx_entry); + c1--; + } + dx_ent->block = ext2fs_cpu_to_le32(i); + if (c2 != limit->limit) + dx_ent->hash = + ext2fs_cpu_to_le32(outdir->hashes[i]); + dx_ent++; + c2--; + } + limit->count = ext2fs_cpu_to_le16(limit->limit - c2); + limit->limit = ext2fs_cpu_to_le16(limit->limit); + } + root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); + root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1); + root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit); + + return 0; +} + +struct write_dir_struct { + struct out_dir *outdir; + errcode_t err; + e2fsck_t ctx; + int cleared; +}; + +/* + * Helper function which writes out a directory block. + */ +static int write_dir_block(ext2_filsys fs, + blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; + blk_t blk; + char *dir; + + if (*block_nr == 0) + return 0; + if (blockcnt >= wd->outdir->num) { + e2fsck_read_bitmaps(wd->ctx); + blk = *block_nr; + ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk); + ext2fs_block_alloc_stats(fs, blk, -1); + *block_nr = 0; + wd->cleared++; + return BLOCK_CHANGED; + } + if (blockcnt < 0) + return 0; + + dir = wd->outdir->buf + (blockcnt * fs->blocksize); + wd->err = ext2fs_write_dir_block(fs, *block_nr, dir); + if (wd->err) + return BLOCK_ABORT; + return 0; +} + +static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs, + struct out_dir *outdir, + ext2_ino_t ino, int compress) +{ + struct write_dir_struct wd; + errcode_t retval; + struct ext2_inode inode; + + retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num); + if (retval) + return retval; + + wd.outdir = outdir; + wd.err = 0; + wd.ctx = ctx; + wd.cleared = 0; + + retval = ext2fs_block_iterate2(fs, ino, 0, 0, + write_dir_block, &wd); + if (retval) + return retval; + if (wd.err) + return wd.err; + + e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); + if (compress) + inode.i_flags &= ~EXT2_INDEX_FL; + else + inode.i_flags |= EXT2_INDEX_FL; + inode.i_size = outdir->num * fs->blocksize; + inode.i_blocks -= (fs->blocksize / 512) * wd.cleared; + e2fsck_write_inode(ctx, ino, &inode, "rehash_dir"); + + return 0; +} + +static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + struct ext2_inode inode; + char *dir_buf = NULL; + struct fill_dir_struct fd; + struct out_dir outdir; + + outdir.max = outdir.num = 0; + outdir.buf = 0; + outdir.hashes = 0; + e2fsck_read_inode(ctx, ino, &inode, "rehash_dir"); + + retval = ENOMEM; + fd.harray = 0; + dir_buf = xmalloc(inode.i_size); + + fd.max_array = inode.i_size / 32; + fd.num_array = 0; + fd.harray = xmalloc(fd.max_array * sizeof(struct hash_entry)); + + fd.ctx = ctx; + fd.buf = dir_buf; + fd.inode = &inode; + fd.err = 0; + fd.dir_size = 0; + fd.compress = 0; + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || + (inode.i_size / fs->blocksize) < 2) + fd.compress = 1; + fd.parent = 0; + + /* Read in the entire directory into memory */ + retval = ext2fs_block_iterate2(fs, ino, 0, 0, + fill_dir_block, &fd); + if (fd.err) { + retval = fd.err; + goto errout; + } + + /* Sort the list */ +resort: + if (fd.compress) + qsort(fd.harray+2, fd.num_array-2, + sizeof(struct hash_entry), name_cmp); + else + qsort(fd.harray, fd.num_array, + sizeof(struct hash_entry), hash_cmp); + + /* + * Look for duplicates + */ + if (duplicate_search_and_fix(ctx, fs, ino, &fd)) + goto resort; + + if (ctx->options & E2F_OPT_NO) { + retval = 0; + goto errout; + } + + /* + * Copy the directory entries. In a htree directory these + * will become the leaf nodes. + */ + retval = copy_dir_entries(fs, &fd, &outdir); + if (retval) + goto errout; + + free(dir_buf); dir_buf = 0; + + if (!fd.compress) { + /* Calculate the interior nodes */ + retval = calculate_tree(fs, &outdir, ino, fd.parent); + if (retval) + goto errout; + } + + retval = write_directory(ctx, fs, &outdir, ino, fd.compress); + +errout: + free(dir_buf); + free(fd.harray); + + free_out_dir(&outdir); + return retval; +} + +void e2fsck_rehash_directories(e2fsck_t ctx) +{ + struct problem_context pctx; + struct dir_info *dir; + ext2_u32_iterate iter; + ext2_ino_t ino; + errcode_t retval; + int i, cur, max, all_dirs, dir_index, first = 1; + + all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS; + + if (!ctx->dirs_to_hash && !all_dirs) + return; + + e2fsck_get_lost_and_found(ctx, 0); + + clear_problem_context(&pctx); + + dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX; + cur = 0; + if (all_dirs) { + i = 0; + max = e2fsck_get_num_dirinfo(ctx); + } else { + retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash, + &iter); + if (retval) { + pctx.errcode = retval; + fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx); + return; + } + max = ext2fs_u32_list_count(ctx->dirs_to_hash); + } + while (1) { + if (all_dirs) { + if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0) + break; + ino = dir->ino; + } else { + if (!ext2fs_u32_list_iterate(iter, &ino)) + break; + } + if (ino == ctx->lost_and_found) + continue; + pctx.dir = ino; + if (first) { + fix_problem(ctx, PR_3A_PASS_HEADER, &pctx); + first = 0; + } + pctx.errcode = e2fsck_rehash_dir(ctx, ino); + if (pctx.errcode) { + end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR); + fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx); + } + if (ctx->progress && !ctx->progress_fd) + e2fsck_simple_progress(ctx, "Rebuilding directory", + 100.0 * (float) (++cur) / (float) max, ino); + } + end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR); + if (!all_dirs) + ext2fs_u32_list_iterate_end(iter); + + ext2fs_u32_list_free(ctx->dirs_to_hash); + ctx->dirs_to_hash = 0; +} + +/* + * linux/fs/revoke.c + * + * Journal revoke routines for the generic filesystem journaling code; + * part of the ext2fs journaling system. + * + * Revoke is the mechanism used to prevent old log records for deleted + * metadata from being replayed on top of newer data using the same + * blocks. The revoke mechanism is used in two separate places: + * + * + Commit: during commit we write the entire list of the current + * transaction's revoked blocks to the journal + * + * + Recovery: during recovery we record the transaction ID of all + * revoked blocks. If there are multiple revoke records in the log + * for a single block, only the last one counts, and if there is a log + * entry for a block beyond the last revoke, then that log entry still + * gets replayed. + * + * We can get interactions between revokes and new log data within a + * single transaction: + * + * Block is revoked and then journaled: + * The desired end result is the journaling of the new block, so we + * cancel the revoke before the transaction commits. + * + * Block is journaled and then revoked: + * The revoke must take precedence over the write of the block, so we + * need either to cancel the journal entry or to write the revoke + * later in the log than the log block. In this case, we choose the + * latter: journaling a block cancels any revoke record for that block + * in the current transaction, so any revoke for that block in the + * transaction must have happened after the block was journaled and so + * the revoke must take precedence. + * + * Block is revoked and then written as data: + * The data write is allowed to succeed, but the revoke is _not_ + * cancelled. We still need to prevent old log records from + * overwriting the new data. We don't even need to clear the revoke + * bit here. + * + * Revoke information on buffers is a tri-state value: + * + * RevokeValid clear: no cached revoke status, need to look it up + * RevokeValid set, Revoked clear: + * buffer has not been revoked, and cancel_revoke + * need do nothing. + * RevokeValid set, Revoked set: + * buffer has been revoked. + */ + +static kmem_cache_t *revoke_record_cache; +static kmem_cache_t *revoke_table_cache; + +/* Each revoke record represents one single revoked block. During + journal replay, this involves recording the transaction ID of the + last transaction to revoke this block. */ + +struct jbd_revoke_record_s +{ + struct list_head hash; + tid_t sequence; /* Used for recovery only */ + unsigned long blocknr; +}; + + +/* The revoke table is just a simple hash table of revoke records. */ +struct jbd_revoke_table_s +{ + /* It is conceivable that we might want a larger hash table + * for recovery. Must be a power of two. */ + int hash_size; + int hash_shift; + struct list_head *hash_table; +}; + + +/* Utility functions to maintain the revoke table */ + +/* Borrowed from buffer.c: this is a tried and tested block hash function */ +static int hash(journal_t *journal, unsigned long block) +{ + struct jbd_revoke_table_s *table = journal->j_revoke; + int hash_shift = table->hash_shift; + + return ((block << (hash_shift - 6)) ^ + (block >> 13) ^ + (block << (hash_shift - 12))) & (table->hash_size - 1); +} + +static int insert_revoke_hash(journal_t *journal, unsigned long blocknr, + tid_t seq) +{ + struct list_head *hash_list; + struct jbd_revoke_record_s *record; + + record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS); + if (!record) + goto oom; + + record->sequence = seq; + record->blocknr = blocknr; + hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)]; + list_add(&record->hash, hash_list); + return 0; + +oom: + return -ENOMEM; +} + +/* Find a revoke record in the journal's hash table. */ + +static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal, + unsigned long blocknr) +{ + struct list_head *hash_list; + struct jbd_revoke_record_s *record; + + hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)]; + + record = (struct jbd_revoke_record_s *) hash_list->next; + while (&(record->hash) != hash_list) { + if (record->blocknr == blocknr) + return record; + record = (struct jbd_revoke_record_s *) record->hash.next; + } + return NULL; +} + +int journal_init_revoke_caches(void) +{ + revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s)); + if (revoke_record_cache == 0) + return -ENOMEM; + + revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s)); + if (revoke_table_cache == 0) { + do_cache_destroy(revoke_record_cache); + revoke_record_cache = NULL; + return -ENOMEM; + } + return 0; +} + +void journal_destroy_revoke_caches(void) +{ + do_cache_destroy(revoke_record_cache); + revoke_record_cache = 0; + do_cache_destroy(revoke_table_cache); + revoke_table_cache = 0; +} + +/* Initialise the revoke table for a given journal to a given size. */ + +int journal_init_revoke(journal_t *journal, int hash_size) +{ + int shift, tmp; + + journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL); + if (!journal->j_revoke) + return -ENOMEM; + + /* Check that the hash_size is a power of two */ + journal->j_revoke->hash_size = hash_size; + + shift = 0; + tmp = hash_size; + while ((tmp >>= 1UL) != 0UL) + shift++; + journal->j_revoke->hash_shift = shift; + + journal->j_revoke->hash_table = xmalloc(hash_size * sizeof(struct list_head)); + + for (tmp = 0; tmp < hash_size; tmp++) + INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]); + + return 0; +} + +/* Destoy a journal's revoke table. The table must already be empty! */ + +void journal_destroy_revoke(journal_t *journal) +{ + struct jbd_revoke_table_s *table; + struct list_head *hash_list; + int i; + + table = journal->j_revoke; + if (!table) + return; + + for (i=0; ihash_size; i++) { + hash_list = &table->hash_table[i]; + } + + free(table->hash_table); + free(table); + journal->j_revoke = NULL; +} + +/* + * Revoke support for recovery. + * + * Recovery needs to be able to: + * + * record all revoke records, including the tid of the latest instance + * of each revoke in the journal + * + * check whether a given block in a given transaction should be replayed + * (ie. has not been revoked by a revoke record in that or a subsequent + * transaction) + * + * empty the revoke table after recovery. + */ + +/* + * First, setting revoke records. We create a new revoke record for + * every block ever revoked in the log as we scan it for recovery, and + * we update the existing records if we find multiple revokes for a + * single block. + */ + +int journal_set_revoke(journal_t *journal, unsigned long blocknr, + tid_t sequence) +{ + struct jbd_revoke_record_s *record; + + record = find_revoke_record(journal, blocknr); + if (record) { + /* If we have multiple occurences, only record the + * latest sequence number in the hashed record */ + if (tid_gt(sequence, record->sequence)) + record->sequence = sequence; + return 0; + } + return insert_revoke_hash(journal, blocknr, sequence); +} + +/* + * Test revoke records. For a given block referenced in the log, has + * that block been revoked? A revoke record with a given transaction + * sequence number revokes all blocks in that transaction and earlier + * ones, but later transactions still need replayed. + */ + +int journal_test_revoke(journal_t *journal, unsigned long blocknr, + tid_t sequence) +{ + struct jbd_revoke_record_s *record; + + record = find_revoke_record(journal, blocknr); + if (!record) + return 0; + if (tid_gt(sequence, record->sequence)) + return 0; + return 1; +} + +/* + * Finally, once recovery is over, we need to clear the revoke table so + * that it can be reused by the running filesystem. + */ + +void journal_clear_revoke(journal_t *journal) +{ + int i; + struct list_head *hash_list; + struct jbd_revoke_record_s *record; + struct jbd_revoke_table_s *revoke_var; + + revoke_var = journal->j_revoke; + + for (i = 0; i < revoke_var->hash_size; i++) { + hash_list = &revoke_var->hash_table[i]; + while (!list_empty(hash_list)) { + record = (struct jbd_revoke_record_s*) hash_list->next; + list_del(&record->hash); + free(record); + } + } +} + +/* + * e2fsck.c - superblock checks + */ + +#define MIN_CHECK 1 +#define MAX_CHECK 2 + +static void check_super_value(e2fsck_t ctx, const char *descr, + unsigned long value, int flags, + unsigned long min_val, unsigned long max_val) +{ + struct problem_context pctx; + + if (((flags & MIN_CHECK) && (value < min_val)) || + ((flags & MAX_CHECK) && (value > max_val))) { + clear_problem_context(&pctx); + pctx.num = value; + pctx.str = descr; + fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx); + ctx->flags |= E2F_FLAG_ABORT; /* never get here! */ + } +} + +/* + * This routine may get stubbed out in special compilations of the + * e2fsck code.. + */ +#ifndef EXT2_SPECIAL_DEVICE_SIZE +static errcode_t e2fsck_get_device_size(e2fsck_t ctx) +{ + return (ext2fs_get_device_size(ctx->filesystem_name, + EXT2_BLOCK_SIZE(ctx->fs->super), + &ctx->num_blocks)); +} +#endif + +/* + * helper function to release an inode + */ +struct process_block_struct { + e2fsck_t ctx; + char *buf; + struct problem_context *pctx; + int truncating; + int truncate_offset; + e2_blkcnt_t truncate_block; + int truncated_blocks; + int abort; + errcode_t errcode; +}; + +static int release_inode_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_blk FSCK_ATTR((unused)), + int ref_offset FSCK_ATTR((unused)), + void *priv_data) +{ + struct process_block_struct *pb; + e2fsck_t ctx; + struct problem_context *pctx; + blk_t blk = *block_nr; + int retval = 0; + + pb = (struct process_block_struct *) priv_data; + ctx = pb->ctx; + pctx = pb->pctx; + + pctx->blk = blk; + pctx->blkcount = blockcnt; + + if (HOLE_BLKADDR(blk)) + return 0; + + if ((blk < fs->super->s_first_data_block) || + (blk >= fs->super->s_blocks_count)) { + fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx); + return_abort: + pb->abort = 1; + return BLOCK_ABORT; + } + + if (!ext2fs_test_block_bitmap(fs->block_map, blk)) { + fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx); + goto return_abort; + } + + /* + * If we are deleting an orphan, then we leave the fields alone. + * If we are truncating an orphan, then update the inode fields + * and clean up any partial block data. + */ + if (pb->truncating) { + /* + * We only remove indirect blocks if they are + * completely empty. + */ + if (blockcnt < 0) { + int i, limit; + blk_t *bp; + + pb->errcode = io_channel_read_blk(fs->io, blk, 1, + pb->buf); + if (pb->errcode) + goto return_abort; + + limit = fs->blocksize >> 2; + for (i = 0, bp = (blk_t *) pb->buf; + i < limit; i++, bp++) + if (*bp) + return 0; + } + /* + * We don't remove direct blocks until we've reached + * the truncation block. + */ + if (blockcnt >= 0 && blockcnt < pb->truncate_block) + return 0; + /* + * If part of the last block needs truncating, we do + * it here. + */ + if ((blockcnt == pb->truncate_block) && pb->truncate_offset) { + pb->errcode = io_channel_read_blk(fs->io, blk, 1, + pb->buf); + if (pb->errcode) + goto return_abort; + memset(pb->buf + pb->truncate_offset, 0, + fs->blocksize - pb->truncate_offset); + pb->errcode = io_channel_write_blk(fs->io, blk, 1, + pb->buf); + if (pb->errcode) + goto return_abort; + } + pb->truncated_blocks++; + *block_nr = 0; + retval |= BLOCK_CHANGED; + } + + ext2fs_block_alloc_stats(fs, blk, -1); + return retval; +} + +/* + * This function releases an inode. Returns 1 if an inconsistency was + * found. If the inode has a link count, then it is being truncated and + * not deleted. + */ +static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, + struct ext2_inode *inode, char *block_buf, + struct problem_context *pctx) +{ + struct process_block_struct pb; + ext2_filsys fs = ctx->fs; + errcode_t retval; + __u32 count; + + if (!ext2fs_inode_has_valid_blocks(inode)) + return 0; + + pb.buf = block_buf + 3 * ctx->fs->blocksize; + pb.ctx = ctx; + pb.abort = 0; + pb.errcode = 0; + pb.pctx = pctx; + if (inode->i_links_count) { + pb.truncating = 1; + pb.truncate_block = (e2_blkcnt_t) + ((((long long)inode->i_size_high << 32) + + inode->i_size + fs->blocksize - 1) / + fs->blocksize); + pb.truncate_offset = inode->i_size % fs->blocksize; + } else { + pb.truncating = 0; + pb.truncate_block = 0; + pb.truncate_offset = 0; + } + pb.truncated_blocks = 0; + retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE, + block_buf, release_inode_block, &pb); + if (retval) { + bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"), + ino); + return 1; + } + if (pb.abort) + return 1; + + /* Refresh the inode since ext2fs_block_iterate may have changed it */ + e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks"); + + if (pb.truncated_blocks) + inode->i_blocks -= pb.truncated_blocks * + (fs->blocksize / 512); + + if (inode->i_file_acl) { + retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl, + block_buf, -1, &count); + if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) { + retval = 0; + count = 1; + } + if (retval) { + bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"), + ino); + return 1; + } + if (count == 0) + ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1); + inode->i_file_acl = 0; + } + return 0; +} + +/* + * This function releases all of the orphan inodes. It returns 1 if + * it hit some error, and 0 on success. + */ +static int release_orphan_inodes(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + ext2_ino_t ino, next_ino; + struct ext2_inode inode; + struct problem_context pctx; + char *block_buf; + + if ((ino = fs->super->s_last_orphan) == 0) + return 0; + + /* + * Win or lose, we won't be using the head of the orphan inode + * list again. + */ + fs->super->s_last_orphan = 0; + ext2fs_mark_super_dirty(fs); + + /* + * If the filesystem contains errors, don't run the orphan + * list, since the orphan list can't be trusted; and we're + * going to be running a full e2fsck run anyway... + */ + if (fs->super->s_state & EXT2_ERROR_FS) + return 0; + + if ((ino < EXT2_FIRST_INODE(fs->super)) || + (ino > fs->super->s_inodes_count)) { + clear_problem_context(&pctx); + pctx.ino = ino; + fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx); + return 1; + } + + block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, + "block iterate buffer"); + e2fsck_read_bitmaps(ctx); + + while (ino) { + e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes"); + clear_problem_context(&pctx); + pctx.ino = ino; + pctx.inode = &inode; + pctx.str = inode.i_links_count ? _("Truncating") : + _("Clearing"); + + fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx); + + next_ino = inode.i_dtime; + if (next_ino && + ((next_ino < EXT2_FIRST_INODE(fs->super)) || + (next_ino > fs->super->s_inodes_count))) { + pctx.ino = next_ino; + fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx); + goto return_abort; + } + + if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx)) + goto return_abort; + + if (!inode.i_links_count) { + ext2fs_inode_alloc_stats2(fs, ino, -1, + LINUX_S_ISDIR(inode.i_mode)); + inode.i_dtime = time(NULL); + } else { + inode.i_dtime = 0; + } + e2fsck_write_inode(ctx, ino, &inode, "delete_file"); + ino = next_ino; + } + ext2fs_free_mem(&block_buf); + return 0; + return_abort: + ext2fs_free_mem(&block_buf); + return 1; +} + +/* + * Check the resize inode to make sure it is sane. We check both for + * the case where on-line resizing is not enabled (in which case the + * resize inode should be cleared) as well as the case where on-line + * resizing is enabled. + */ +static void check_resize_inode(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + struct ext2_inode inode; + struct problem_context pctx; + int i, j, gdt_off, ind_off; + blk_t blk, pblk, expect; + __u32 *dind_buf = NULL, *ind_buf; + errcode_t retval; + + clear_problem_context(&pctx); + + /* + * If the resize inode feature isn't set, then + * s_reserved_gdt_blocks must be zero. + */ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_RESIZE_INODE)) { + if (fs->super->s_reserved_gdt_blocks) { + pctx.num = fs->super->s_reserved_gdt_blocks; + if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS, + &pctx)) { + fs->super->s_reserved_gdt_blocks = 0; + ext2fs_mark_super_dirty(fs); + } + } + } + + /* Read the resize inode */ + pctx.ino = EXT2_RESIZE_INO; + retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); + if (retval) { + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_RESIZE_INODE) + ctx->flags |= E2F_FLAG_RESIZE_INODE; + return; + } + + /* + * If the resize inode feature isn't set, check to make sure + * the resize inode is cleared; then we're done. + */ + if (!(fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_RESIZE_INODE)) { + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) && + fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, + "clear_resize"); + } + return; + } + + /* + * The resize inode feature is enabled; check to make sure the + * only block in use is the double indirect block + */ + blk = inode.i_block[EXT2_DIND_BLOCK]; + for (i=0; i < EXT2_N_BLOCKS; i++) { + if (i != EXT2_DIND_BLOCK && inode.i_block[i]) + break; + } + if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count || + !(inode.i_mode & LINUX_S_IFREG) || + (blk < fs->super->s_first_data_block || + blk >= fs->super->s_blocks_count)) { + resize_inode_invalid: + if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) { + memset(&inode, 0, sizeof(inode)); + e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode, + "clear_resize"); + ctx->flags |= E2F_FLAG_RESIZE_INODE; + } + if (!(ctx->options & E2F_OPT_READONLY)) { + fs->super->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); + } + goto cleanup; + } + dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2, + "resize dind buffer"); + ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize); + + retval = ext2fs_read_ind_block(fs, blk, dind_buf); + if (retval) + goto resize_inode_invalid; + + gdt_off = fs->desc_blocks; + pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks; + for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4; + i++, gdt_off++, pblk++) { + gdt_off %= fs->blocksize/4; + if (dind_buf[gdt_off] != pblk) + goto resize_inode_invalid; + retval = ext2fs_read_ind_block(fs, pblk, ind_buf); + if (retval) + goto resize_inode_invalid; + ind_off = 0; + for (j = 1; j < fs->group_desc_count; j++) { + if (!ext2fs_bg_has_super(fs, j)) + continue; + expect = pblk + (j * fs->super->s_blocks_per_group); + if (ind_buf[ind_off] != expect) + goto resize_inode_invalid; + ind_off++; + } + } + + cleanup: + ext2fs_free_mem(&dind_buf); +} + +static void check_super_block(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + blk_t first_block, last_block; + struct ext2_super_block *sb = fs->super; + struct ext2_group_desc *gd; + blk_t blocks_per_group = fs->super->s_blocks_per_group; + blk_t bpg_max; + int inodes_per_block; + int ipg_max; + int inode_size; + dgrp_t i; + blk_t should_be; + struct problem_context pctx; + __u32 free_blocks = 0, free_inodes = 0; + + inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super); + ipg_max = inodes_per_block * (blocks_per_group - 4); + if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb)) + ipg_max = EXT2_MAX_INODES_PER_GROUP(sb); + bpg_max = 8 * EXT2_BLOCK_SIZE(sb); + if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb)) + bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb); + + ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx, + sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap"); + ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx, + sizeof(int) * fs->group_desc_count, "invalid_block_bitmap"); + ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx, + sizeof(int) * fs->group_desc_count, "invalid_inode_table"); + + clear_problem_context(&pctx); + + /* + * Verify the super block constants... + */ + check_super_value(ctx, "inodes_count", sb->s_inodes_count, + MIN_CHECK, 1, 0); + check_super_value(ctx, "blocks_count", sb->s_blocks_count, + MIN_CHECK, 1, 0); + check_super_value(ctx, "first_data_block", sb->s_first_data_block, + MAX_CHECK, 0, sb->s_blocks_count); + check_super_value(ctx, "log_block_size", sb->s_log_block_size, + MIN_CHECK | MAX_CHECK, 0, + EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE); + check_super_value(ctx, "log_frag_size", sb->s_log_frag_size, + MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size); + check_super_value(ctx, "frags_per_group", sb->s_frags_per_group, + MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group, + bpg_max); + check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group, + MIN_CHECK | MAX_CHECK, 8, bpg_max); + check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group, + MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max); + check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count, + MAX_CHECK, 0, sb->s_blocks_count / 2); + check_super_value(ctx, "reserved_gdt_blocks", + sb->s_reserved_gdt_blocks, MAX_CHECK, 0, + fs->blocksize/4); + inode_size = EXT2_INODE_SIZE(sb); + check_super_value(ctx, "inode_size", + inode_size, MIN_CHECK | MAX_CHECK, + EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize); + if (inode_size & (inode_size - 1)) { + pctx.num = inode_size; + pctx.str = "inode_size"; + fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx); + ctx->flags |= E2F_FLAG_ABORT; /* never get here! */ + return; + } + + if (!ctx->num_blocks) { + pctx.errcode = e2fsck_get_device_size(ctx); + if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) { + fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) && + (ctx->num_blocks < sb->s_blocks_count)) { + pctx.blk = sb->s_blocks_count; + pctx.blk2 = ctx->num_blocks; + if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) { + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + } + + if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) { + pctx.blk = EXT2_BLOCK_SIZE(sb); + pctx.blk2 = EXT2_FRAG_SIZE(sb); + fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + should_be = sb->s_frags_per_group >> + (sb->s_log_block_size - sb->s_log_frag_size); + if (sb->s_blocks_per_group != should_be) { + pctx.blk = sb->s_blocks_per_group; + pctx.blk2 = should_be; + fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + should_be = (sb->s_log_block_size == 0) ? 1 : 0; + if (sb->s_first_data_block != should_be) { + pctx.blk = sb->s_first_data_block; + pctx.blk2 = should_be; + fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + + should_be = sb->s_inodes_per_group * fs->group_desc_count; + if (sb->s_inodes_count != should_be) { + pctx.ino = sb->s_inodes_count; + pctx.ino2 = should_be; + if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) { + sb->s_inodes_count = should_be; + ext2fs_mark_super_dirty(fs); + } + } + + /* + * Verify the group descriptors.... + */ + first_block = sb->s_first_data_block; + last_block = first_block + blocks_per_group; + + for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) { + pctx.group = i; + + if (i == fs->group_desc_count - 1) + last_block = sb->s_blocks_count; + if ((gd->bg_block_bitmap < first_block) || + (gd->bg_block_bitmap >= last_block)) { + pctx.blk = gd->bg_block_bitmap; + if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx)) + gd->bg_block_bitmap = 0; + } + if (gd->bg_block_bitmap == 0) { + ctx->invalid_block_bitmap_flag[i]++; + ctx->invalid_bitmaps++; + } + if ((gd->bg_inode_bitmap < first_block) || + (gd->bg_inode_bitmap >= last_block)) { + pctx.blk = gd->bg_inode_bitmap; + if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx)) + gd->bg_inode_bitmap = 0; + } + if (gd->bg_inode_bitmap == 0) { + ctx->invalid_inode_bitmap_flag[i]++; + ctx->invalid_bitmaps++; + } + if ((gd->bg_inode_table < first_block) || + ((gd->bg_inode_table + + fs->inode_blocks_per_group - 1) >= last_block)) { + pctx.blk = gd->bg_inode_table; + if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx)) + gd->bg_inode_table = 0; + } + if (gd->bg_inode_table == 0) { + ctx->invalid_inode_table_flag[i]++; + ctx->invalid_bitmaps++; + } + free_blocks += gd->bg_free_blocks_count; + free_inodes += gd->bg_free_inodes_count; + first_block += sb->s_blocks_per_group; + last_block += sb->s_blocks_per_group; + + if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) || + (gd->bg_free_inodes_count > sb->s_inodes_per_group) || + (gd->bg_used_dirs_count > sb->s_inodes_per_group)) + ext2fs_unmark_valid(fs); + } + + /* + * Update the global counts from the block group counts. This + * is needed for an experimental patch which eliminates + * locking the entire filesystem when allocating blocks or + * inodes; if the filesystem is not unmounted cleanly, the + * global counts may not be accurate. + */ + if ((free_blocks != sb->s_free_blocks_count) || + (free_inodes != sb->s_free_inodes_count)) { + if (ctx->options & E2F_OPT_READONLY) + ext2fs_unmark_valid(fs); + else { + sb->s_free_blocks_count = free_blocks; + sb->s_free_inodes_count = free_inodes; + ext2fs_mark_super_dirty(fs); + } + } + + if ((sb->s_free_blocks_count > sb->s_blocks_count) || + (sb->s_free_inodes_count > sb->s_inodes_count)) + ext2fs_unmark_valid(fs); + + + /* + * If we have invalid bitmaps, set the error state of the + * filesystem. + */ + if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) { + sb->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); + } + + clear_problem_context(&pctx); + + /* + * If the UUID field isn't assigned, assign it. + */ + if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) { + if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) { + uuid_generate(sb->s_uuid); + ext2fs_mark_super_dirty(fs); + fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; + } + } + + /* FIXME - HURD support? + * For the Hurd, check to see if the filetype option is set, + * since it doesn't support it. + */ + if (!(ctx->options & E2F_OPT_READONLY) && + fs->super->s_creator_os == EXT2_OS_HURD && + (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE)) { + if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) { + fs->super->s_feature_incompat &= + ~EXT2_FEATURE_INCOMPAT_FILETYPE; + ext2fs_mark_super_dirty(fs); + } + } + + /* + * If we have any of the compatibility flags set, we need to have a + * revision 1 filesystem. Most kernels will not check the flags on + * a rev 0 filesystem and we may have corruption issues because of + * the incompatible changes to the filesystem. + */ + if (!(ctx->options & E2F_OPT_READONLY) && + fs->super->s_rev_level == EXT2_GOOD_OLD_REV && + (fs->super->s_feature_compat || + fs->super->s_feature_ro_compat || + fs->super->s_feature_incompat) && + fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) { + ext2fs_update_dynamic_rev(fs); + ext2fs_mark_super_dirty(fs); + } + + check_resize_inode(ctx); + + /* + * Clean up any orphan inodes, if present. + */ + if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) { + fs->super->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); + } + + /* + * Move the ext3 journal file, if necessary. + */ + e2fsck_move_ext3_journal(ctx); +} + +/* + * swapfs.c --- byte-swap an ext2 filesystem + */ + +#ifdef ENABLE_SWAPFS + +struct swap_block_struct { + ext2_ino_t ino; + int isdir; + errcode_t errcode; + char *dir_buf; + struct ext2_inode *inode; +}; + +/* + * This is a helper function for block_iterate. We mark all of the + * indirect and direct blocks as changed, so that block_iterate will + * write them out. + */ +static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, + void *priv_data) +{ + errcode_t retval; + + struct swap_block_struct *sb = (struct swap_block_struct *) priv_data; + + if (sb->isdir && (blockcnt >= 0) && *block_nr) { + retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf); + if (retval) { + sb->errcode = retval; + return BLOCK_ABORT; + } + retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf); + if (retval) { + sb->errcode = retval; + return BLOCK_ABORT; + } + } + if (blockcnt >= 0) { + if (blockcnt < EXT2_NDIR_BLOCKS) + return 0; + return BLOCK_CHANGED; + } + if (blockcnt == BLOCK_COUNT_IND) { + if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK]) + return 0; + return BLOCK_CHANGED; + } + if (blockcnt == BLOCK_COUNT_DIND) { + if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK]) + return 0; + return BLOCK_CHANGED; + } + if (blockcnt == BLOCK_COUNT_TIND) { + if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK]) + return 0; + return BLOCK_CHANGED; + } + return BLOCK_CHANGED; +} + +/* + * This function is responsible for byte-swapping all of the indirect, + * block pointers. It is also responsible for byte-swapping directories. + */ +static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf, + struct ext2_inode *inode) +{ + errcode_t retval; + struct swap_block_struct sb; + + sb.ino = ino; + sb.inode = inode; + sb.dir_buf = block_buf + ctx->fs->blocksize*3; + sb.errcode = 0; + sb.isdir = 0; + if (LINUX_S_ISDIR(inode->i_mode)) + sb.isdir = 1; + + retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf, + swap_block, &sb); + if (retval) { + bb_error_msg(_("while calling ext2fs_block_iterate")); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (sb.errcode) { + bb_error_msg(_("while calling iterator function")); + ctx->flags |= E2F_FLAG_ABORT; + return; + } +} + +static void swap_inodes(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + dgrp_t group; + unsigned int i; + ext2_ino_t ino = 1; + char *buf, *block_buf; + errcode_t retval; + struct ext2_inode * inode; + + e2fsck_use_inode_shortcuts(ctx, 1); + + retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group, + &buf); + if (retval) { + bb_error_msg(_("while allocating inode buffer")); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, + "block interate buffer"); + for (group = 0; group < fs->group_desc_count; group++) { + retval = io_channel_read_blk(fs->io, + fs->group_desc[group].bg_inode_table, + fs->inode_blocks_per_group, buf); + if (retval) { + bb_error_msg(_("while reading inode table (group %d)"), + group); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + inode = (struct ext2_inode *) buf; + for (i=0; i < fs->super->s_inodes_per_group; + i++, ino++, inode++) { + ctx->stashed_ino = ino; + ctx->stashed_inode = inode; + + if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) + ext2fs_swap_inode(fs, inode, inode, 0); + + /* + * Skip deleted files. + */ + if (inode->i_links_count == 0) + continue; + + if (LINUX_S_ISDIR(inode->i_mode) || + ((inode->i_block[EXT2_IND_BLOCK] || + inode->i_block[EXT2_DIND_BLOCK] || + inode->i_block[EXT2_TIND_BLOCK]) && + ext2fs_inode_has_valid_blocks(inode))) + swap_inode_blocks(ctx, ino, block_buf, inode); + + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + + if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) + ext2fs_swap_inode(fs, inode, inode, 1); + } + retval = io_channel_write_blk(fs->io, + fs->group_desc[group].bg_inode_table, + fs->inode_blocks_per_group, buf); + if (retval) { + bb_error_msg(_("while writing inode table (group %d)"), + group); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + } + ext2fs_free_mem(&buf); + ext2fs_free_mem(&block_buf); + e2fsck_use_inode_shortcuts(ctx, 0); + ext2fs_flush_icache(fs); +} + +#if defined(__powerpc__) && BB_BIG_ENDIAN +/* + * On the PowerPC, the big-endian variant of the ext2 filesystem + * has its bitmaps stored as 32-bit words with bit 0 as the LSB + * of each word. Thus a bitmap with only bit 0 set would be, as + * a string of bytes, 00 00 00 01 00 ... + * To cope with this, we byte-reverse each word of a bitmap if + * we have a big-endian filesystem, that is, if we are *not* + * byte-swapping other word-sized numbers. + */ +#define EXT2_BIG_ENDIAN_BITMAPS +#endif + +#ifdef EXT2_BIG_ENDIAN_BITMAPS +static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap) +{ + __u32 *p = (__u32 *) bmap->bitmap; + int n, nbytes = (bmap->end - bmap->start + 7) / 8; + + for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) + *p = ext2fs_swab32(*p); +} +#endif + + +#ifdef ENABLE_SWAPFS +static void swap_filesys(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + if (!(ctx->options & E2F_OPT_PREEN)) + printf(_("Pass 0: Doing byte-swap of filesystem\n")); + + /* Byte swap */ + + if (fs->super->s_mnt_count) { + fprintf(stderr, _("%s: the filesystem must be freshly " + "checked using fsck\n" + "and not mounted before trying to " + "byte-swap it.\n"), ctx->device_name); + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + fs->flags &= ~(EXT2_FLAG_SWAP_BYTES| + EXT2_FLAG_SWAP_BYTES_WRITE); + fs->flags |= EXT2_FLAG_SWAP_BYTES_READ; + } else { + fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ; + fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE; + } + swap_inodes(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + return; + if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) + fs->flags |= EXT2_FLAG_SWAP_BYTES; + fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ| + EXT2_FLAG_SWAP_BYTES_WRITE); + +#ifdef EXT2_BIG_ENDIAN_BITMAPS + e2fsck_read_bitmaps(ctx); + ext2fs_swap_bitmap(fs->inode_map); + ext2fs_swap_bitmap(fs->block_map); + fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; +#endif + fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; + ext2fs_flush(fs); + fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; +} +#endif /* ENABLE_SWAPFS */ + +#endif + +/* + * util.c --- miscellaneous utilities + */ + + +void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, + const char *description) +{ + void *ret; + char buf[256]; + + ret = xzalloc(size); + return ret; +} + +static char *string_copy(const char *str, int len) +{ + char *ret; + + if (!str) + return NULL; + if (!len) + len = strlen(str); + ret = xmalloc(len+1); + strncpy(ret, str, len); + ret[len] = 0; + return ret; +} + +#ifndef HAVE_CONIO_H +static int read_a_char(void) +{ + char c; + int r; + int fail = 0; + + while (1) { + if (e2fsck_global_ctx && + (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) { + return 3; + } + r = read(0, &c, 1); + if (r == 1) + return c; + if (fail++ > 100) + break; + } + return EOF; +} +#endif + +static int ask_yn(const char * string, int def) +{ + int c; + const char *defstr; + static const char short_yes[] = "yY"; + static const char short_no[] = "nN"; + +#ifdef HAVE_TERMIOS_H + struct termios termios, tmp; + + tcgetattr (0, &termios); + tmp = termios; + tmp.c_lflag &= ~(ICANON | ECHO); + tmp.c_cc[VMIN] = 1; + tmp.c_cc[VTIME] = 0; + tcsetattr_stdin_TCSANOW(&tmp); +#endif + + if (def == 1) + defstr = ""; + else if (def == 0) + defstr = ""; + else + defstr = " (y/n)"; + printf("%s%s? ", string, defstr); + while (1) { + fflush (stdout); + if ((c = read_a_char()) == EOF) + break; + if (c == 3) { +#ifdef HAVE_TERMIOS_H + tcsetattr_stdin_TCSANOW(&termios); +#endif + if (e2fsck_global_ctx && + e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) { + puts("\n"); + longjmp(e2fsck_global_ctx->abort_loc, 1); + } + puts(_("cancelled!\n")); + return 0; + } + if (strchr(short_yes, (char) c)) { + def = 1; + break; + } + else if (strchr(short_no, (char) c)) { + def = 0; + break; + } + else if ((c == ' ' || c == '\n') && (def != -1)) + break; + } + if (def) + puts("yes\n"); + else + puts ("no\n"); +#ifdef HAVE_TERMIOS_H + tcsetattr_stdin_TCSANOW(&termios); +#endif + return def; +} + +int ask (e2fsck_t ctx, const char * string, int def) +{ + if (ctx->options & E2F_OPT_NO) { + printf(_("%s? no\n\n"), string); + return 0; + } + if (ctx->options & E2F_OPT_YES) { + printf(_("%s? yes\n\n"), string); + return 1; + } + if (ctx->options & E2F_OPT_PREEN) { + printf("%s? %s\n\n", string, def ? _("yes") : _("no")); + return def; + } + return ask_yn(string, def); +} + +void e2fsck_read_bitmaps(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + + if (ctx->invalid_bitmaps) { + bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } + + ehandler_operation(_("reading inode and block bitmaps")); + retval = ext2fs_read_bitmaps(fs); + ehandler_operation(0); + if (retval) { + bb_error_msg(_("while retrying to read bitmaps for %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } +} + +static void e2fsck_write_bitmaps(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + errcode_t retval; + + if (ext2fs_test_bb_dirty(fs)) { + ehandler_operation(_("writing block bitmaps")); + retval = ext2fs_write_block_bitmap(fs); + ehandler_operation(0); + if (retval) { + bb_error_msg(_("while retrying to write block bitmaps for %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } + } + + if (ext2fs_test_ib_dirty(fs)) { + ehandler_operation(_("writing inode bitmaps")); + retval = ext2fs_write_inode_bitmap(fs); + ehandler_operation(0); + if (retval) { + bb_error_msg(_("while retrying to write inode bitmaps for %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } + } +} + +void preenhalt(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + + if (!(ctx->options & E2F_OPT_PREEN)) + return; + fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; " + "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"), + ctx->device_name); + if (fs != NULL) { + fs->super->s_state |= EXT2_ERROR_FS; + ext2fs_mark_super_dirty(fs); + ext2fs_close(fs); + } + exit(EXIT_UNCORRECTED); +} + +void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino, + struct ext2_inode * inode, const char *proc) +{ + int retval; + + retval = ext2fs_read_inode(ctx->fs, ino, inode); + if (retval) { + bb_error_msg(_("while reading inode %ld in %s"), ino, proc); + bb_error_msg_and_die(0); + } +} + +extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino, + struct ext2_inode * inode, int bufsize, + const char *proc) +{ + int retval; + + retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize); + if (retval) { + bb_error_msg(_("while writing inode %ld in %s"), ino, proc); + bb_error_msg_and_die(0); + } +} + +extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino, + struct ext2_inode * inode, const char *proc) +{ + int retval; + + retval = ext2fs_write_inode(ctx->fs, ino, inode); + if (retval) { + bb_error_msg(_("while writing inode %ld in %s"), ino, proc); + bb_error_msg_and_die(0); + } +} + +blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, + io_manager manager) +{ + struct ext2_super_block *sb; + io_channel io = NULL; + void *buf = NULL; + int blocksize; + blk_t superblock, ret_sb = 8193; + + if (fs && fs->super) { + ret_sb = (fs->super->s_blocks_per_group + + fs->super->s_first_data_block); + if (ctx) { + ctx->superblock = ret_sb; + ctx->blocksize = fs->blocksize; + } + return ret_sb; + } + + if (ctx) { + if (ctx->blocksize) { + ret_sb = ctx->blocksize * 8; + if (ctx->blocksize == 1024) + ret_sb++; + ctx->superblock = ret_sb; + return ret_sb; + } + ctx->superblock = ret_sb; + ctx->blocksize = 1024; + } + + if (!name || !manager) + goto cleanup; + + if (manager->open(name, 0, &io) != 0) + goto cleanup; + + if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf)) + goto cleanup; + sb = (struct ext2_super_block *) buf; + + for (blocksize = EXT2_MIN_BLOCK_SIZE; + blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) { + superblock = blocksize*8; + if (blocksize == 1024) + superblock++; + io_channel_set_blksize(io, blocksize); + if (io_channel_read_blk(io, superblock, + -SUPERBLOCK_SIZE, buf)) + continue; +#if BB_BIG_ENDIAN + if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) + ext2fs_swap_super(sb); +#endif + if (sb->s_magic == EXT2_SUPER_MAGIC) { + ret_sb = superblock; + if (ctx) { + ctx->superblock = superblock; + ctx->blocksize = blocksize; + } + break; + } + } + +cleanup: + if (io) + io_channel_close(io); + ext2fs_free_mem(&buf); + return ret_sb; +} + + +/* + * This function runs through the e2fsck passes and calls them all, + * returning restart, abort, or cancel as necessary... + */ +typedef void (*pass_t)(e2fsck_t ctx); + +static const pass_t e2fsck_passes[] = { + e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4, + e2fsck_pass5, 0 }; + +#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART) + +static int e2fsck_run(e2fsck_t ctx) +{ + int i; + pass_t e2fsck_pass; + + if (setjmp(ctx->abort_loc)) { + ctx->flags &= ~E2F_FLAG_SETJMP_OK; + return (ctx->flags & E2F_FLAG_RUN_RETURN); + } + ctx->flags |= E2F_FLAG_SETJMP_OK; + + for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) { + if (ctx->flags & E2F_FLAG_RUN_RETURN) + break; + e2fsck_pass(ctx); + if (ctx->progress) + (void) (ctx->progress)(ctx, 0, 0, 0); + } + ctx->flags &= ~E2F_FLAG_SETJMP_OK; + + if (ctx->flags & E2F_FLAG_RUN_RETURN) + return (ctx->flags & E2F_FLAG_RUN_RETURN); + return 0; +} + + +/* + * unix.c - The unix-specific code for e2fsck + */ + + +/* Command line options */ +static int swapfs; +#ifdef ENABLE_SWAPFS +static int normalize_swapfs; +#endif +static int cflag; /* check disk */ +static int show_version_only; +static int verbose; + +#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural) + +static void show_stats(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + int inodes, inodes_used, blocks, blocks_used; + int dir_links; + int num_files, num_links; + int frag_percent; + + dir_links = 2 * ctx->fs_directory_count - 1; + num_files = ctx->fs_total_count - dir_links; + num_links = ctx->fs_links_count - dir_links; + inodes = fs->super->s_inodes_count; + inodes_used = (fs->super->s_inodes_count - + fs->super->s_free_inodes_count); + blocks = fs->super->s_blocks_count; + blocks_used = (fs->super->s_blocks_count - + fs->super->s_free_blocks_count); + + frag_percent = (10000 * ctx->fs_fragmented) / inodes_used; + frag_percent = (frag_percent + 5) / 10; + + if (!verbose) { + printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n", + ctx->device_name, inodes_used, inodes, + frag_percent / 10, frag_percent % 10, + blocks_used, blocks); + return; + } + printf("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used), + 100 * inodes_used / inodes); + printf("%8d non-contiguous inode%s (%0d.%d%%)\n", + P_E2("", "s", ctx->fs_fragmented), + frag_percent / 10, frag_percent % 10); + printf(_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"), + ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count); + printf("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used), + (int) ((long long) 100 * blocks_used / blocks)); + printf("%8d large file%s\n", P_E2("", "s", ctx->large_files)); + printf("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count)); + printf("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count)); + printf("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count)); + printf("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count)); + printf("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count)); + printf("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links)); + printf("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count)); + printf(" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count)); + printf("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count)); + printf("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links)); +} + +static void check_mount(e2fsck_t ctx) +{ + errcode_t retval; + int cont; + + retval = ext2fs_check_if_mounted(ctx->filesystem_name, + &ctx->mount_flags); + if (retval) { + bb_error_msg(_("while determining whether %s is mounted"), + ctx->filesystem_name); + return; + } + + /* + * If the filesystem isn't mounted, or it's the root filesystem + * and it's mounted read-only, then everything's fine. + */ + if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) || + ((ctx->mount_flags & EXT2_MF_ISROOT) && + (ctx->mount_flags & EXT2_MF_READONLY))) + return; + + if (ctx->options & E2F_OPT_READONLY) { + printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name); + return; + } + + printf(_("%s is mounted. "), ctx->filesystem_name); + if (!ctx->interactive) + bb_error_msg_and_die(_("can't continue, aborting")); + printf(_("\n\n\007\007\007\007WARNING!!! " + "Running e2fsck on a mounted filesystem may cause\n" + "SEVERE filesystem damage.\007\007\007\n\n")); + cont = ask_yn(_("Do you really want to continue"), -1); + if (!cont) { + printf(_("check aborted.\n")); + exit(0); + } +} + +static int is_on_batt(void) +{ + FILE *f; + DIR *d; + char tmp[80], tmp2[80], fname[80]; + unsigned int acflag; + struct dirent* de; + + f = fopen_for_read("/proc/apm"); + if (f) { + if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4) + acflag = 1; + fclose(f); + return (acflag != 1); + } + d = opendir("/proc/acpi/ac_adapter"); + if (d) { + while ((de=readdir(d)) != NULL) { + if (!strncmp(".", de->d_name, 1)) + continue; + snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state", + de->d_name); + f = fopen_for_read(fname); + if (!f) + continue; + if (fscanf(f, "%s %s", tmp2, tmp) != 2) + tmp[0] = 0; + fclose(f); + if (strncmp(tmp, "off-line", 8) == 0) { + closedir(d); + return 1; + } + } + closedir(d); + } + return 0; +} + +/* + * This routine checks to see if a filesystem can be skipped; if so, + * it will exit with EXIT_OK. Under some conditions it will print a + * message explaining why a check is being forced. + */ +static void check_if_skip(e2fsck_t ctx) +{ + ext2_filsys fs = ctx->fs; + const char *reason = NULL; + unsigned int reason_arg = 0; + long next_check; + int batt = is_on_batt(); + time_t now = time(NULL); + + if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs) + return; + + if ((fs->super->s_state & EXT2_ERROR_FS) || + !ext2fs_test_valid(fs)) + reason = _(" contains a file system with errors"); + else if ((fs->super->s_state & EXT2_VALID_FS) == 0) + reason = _(" was not cleanly unmounted"); + else if ((fs->super->s_max_mnt_count > 0) && + (fs->super->s_mnt_count >= + (unsigned) fs->super->s_max_mnt_count)) { + reason = _(" has been mounted %u times without being checked"); + reason_arg = fs->super->s_mnt_count; + if (batt && (fs->super->s_mnt_count < + (unsigned) fs->super->s_max_mnt_count*2)) + reason = 0; + } else if (fs->super->s_checkinterval && + ((now - fs->super->s_lastcheck) >= + fs->super->s_checkinterval)) { + reason = _(" has gone %u days without being checked"); + reason_arg = (now - fs->super->s_lastcheck)/(3600*24); + if (batt && ((now - fs->super->s_lastcheck) < + fs->super->s_checkinterval*2)) + reason = 0; + } + if (reason) { + fputs(ctx->device_name, stdout); + printf(reason, reason_arg); + fputs(_(", check forced.\n"), stdout); + return; + } + printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name, + fs->super->s_inodes_count - fs->super->s_free_inodes_count, + fs->super->s_inodes_count, + fs->super->s_blocks_count - fs->super->s_free_blocks_count, + fs->super->s_blocks_count); + next_check = 100000; + if (fs->super->s_max_mnt_count > 0) { + next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count; + if (next_check <= 0) + next_check = 1; + } + if (fs->super->s_checkinterval && + ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval)) + next_check = 1; + if (next_check <= 5) { + if (next_check == 1) + fputs(_(" (check after next mount)"), stdout); + else + printf(_(" (check in %ld mounts)"), next_check); + } + bb_putchar('\n'); + ext2fs_close(fs); + ctx->fs = NULL; + e2fsck_free_context(ctx); + exit(EXIT_OK); +} + +/* + * For completion notice + */ +struct percent_tbl { + int max_pass; + int table[32]; +}; +static const struct percent_tbl e2fsck_tbl = { + 5, { 0, 70, 90, 92, 95, 100 } +}; + +static char bar[128], spaces[128]; + +static float calc_percent(const struct percent_tbl *tbl, int pass, int curr, + int max) +{ + float percent; + + if (pass <= 0) + return 0.0; + if (pass > tbl->max_pass || max == 0) + return 100.0; + percent = ((float) curr) / ((float) max); + return ((percent * (tbl->table[pass] - tbl->table[pass-1])) + + tbl->table[pass-1]); +} + +void e2fsck_clear_progbar(e2fsck_t ctx) +{ + if (!(ctx->flags & E2F_FLAG_PROG_BAR)) + return; + + printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80), + ctx->stop_meta); + fflush(stdout); + ctx->flags &= ~E2F_FLAG_PROG_BAR; +} + +int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent, + unsigned int dpynum) +{ + static const char spinner[] = "\\|/-"; + int i; + unsigned int tick; + struct timeval tv; + int dpywidth; + int fixed_percent; + + if (ctx->flags & E2F_FLAG_PROG_SUPPRESS) + return 0; + + /* + * Calculate the new progress position. If the + * percentage hasn't changed, then we skip out right + * away. + */ + fixed_percent = (int) ((10 * percent) + 0.5); + if (ctx->progress_last_percent == fixed_percent) + return 0; + ctx->progress_last_percent = fixed_percent; + + /* + * If we've already updated the spinner once within + * the last 1/8th of a second, no point doing it + * again. + */ + gettimeofday(&tv, NULL); + tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8)); + if ((tick == ctx->progress_last_time) && + (fixed_percent != 0) && (fixed_percent != 1000)) + return 0; + ctx->progress_last_time = tick; + + /* + * Advance the spinner, and note that the progress bar + * will be on the screen + */ + ctx->progress_pos = (ctx->progress_pos+1) & 3; + ctx->flags |= E2F_FLAG_PROG_BAR; + + dpywidth = 66 - strlen(label); + dpywidth = 8 * (dpywidth / 8); + if (dpynum) + dpywidth -= 8; + + i = ((percent * dpywidth) + 50) / 100; + printf("%s%s: |%s%s", ctx->start_meta, label, + bar + (sizeof(bar) - (i+1)), + spaces + (sizeof(spaces) - (dpywidth - i + 1))); + if (fixed_percent == 1000) + bb_putchar('|'); + else + bb_putchar(spinner[ctx->progress_pos & 3]); + printf(" %4.1f%% ", percent); + if (dpynum) + printf("%u\r", dpynum); + else + fputs(" \r", stdout); + fputs(ctx->stop_meta, stdout); + + if (fixed_percent == 1000) + e2fsck_clear_progbar(ctx); + fflush(stdout); + + return 0; +} + +static int e2fsck_update_progress(e2fsck_t ctx, int pass, + unsigned long cur, unsigned long max) +{ + char buf[80]; + float percent; + + if (pass == 0) + return 0; + + if (ctx->progress_fd) { + sprintf(buf, "%d %lu %lu\n", pass, cur, max); + xwrite_str(ctx->progress_fd, buf); + } else { + percent = calc_percent(&e2fsck_tbl, pass, cur, max); + e2fsck_simple_progress(ctx, ctx->device_name, + percent, 0); + } + return 0; +} + +static void reserve_stdio_fds(void) +{ + int fd; + + while (1) { + fd = open(bb_dev_null, O_RDWR); + if (fd > 2) + break; + if (fd < 0) { + fprintf(stderr, _("ERROR: Cannot open " + "/dev/null (%s)\n"), + strerror(errno)); + break; + } + } + close(fd); +} + +static void signal_progress_on(int sig FSCK_ATTR((unused))) +{ + e2fsck_t ctx = e2fsck_global_ctx; + + if (!ctx) + return; + + ctx->progress = e2fsck_update_progress; + ctx->progress_fd = 0; +} + +static void signal_progress_off(int sig FSCK_ATTR((unused))) +{ + e2fsck_t ctx = e2fsck_global_ctx; + + if (!ctx) + return; + + e2fsck_clear_progbar(ctx); + ctx->progress = 0; +} + +static void signal_cancel(int sig FSCK_ATTR((unused))) +{ + e2fsck_t ctx = e2fsck_global_ctx; + + if (!ctx) + exit(FSCK_CANCELED); + + ctx->flags |= E2F_FLAG_CANCEL; +} + +static void parse_extended_opts(e2fsck_t ctx, const char *opts) +{ + char *buf, *token, *next, *p, *arg; + int ea_ver; + int extended_usage = 0; + + buf = string_copy(opts, 0); + for (token = buf; token && *token; token = next) { + p = strchr(token, ','); + next = 0; + if (p) { + *p = 0; + next = p+1; + } + arg = strchr(token, '='); + if (arg) { + *arg = 0; + arg++; + } + if (strcmp(token, "ea_ver") == 0) { + if (!arg) { + extended_usage++; + continue; + } + ea_ver = strtoul(arg, &p, 0); + if (*p || + ((ea_ver != 1) && (ea_ver != 2))) { + fprintf(stderr, + _("Invalid EA version.\n")); + extended_usage++; + continue; + } + ctx->ext_attr_ver = ea_ver; + } else { + fprintf(stderr, _("Unknown extended option: %s\n"), + token); + extended_usage++; + } + } + if (extended_usage) { + bb_error_msg_and_die( + "Extended options are separated by commas, " + "and may take an argument which\n" + "is set off by an equals ('=') sign. " + "Valid extended options are:\n" + "\tea_ver=\n\n"); + } +} + + +static errcode_t PRS(int argc, char **argv, e2fsck_t *ret_ctx) +{ + int flush = 0; + int c, fd; + e2fsck_t ctx; + errcode_t retval; + struct sigaction sa; + char *extended_opts = NULL; + + retval = e2fsck_allocate_context(&ctx); + if (retval) + return retval; + + *ret_ctx = ctx; + + setvbuf(stdout, NULL, _IONBF, BUFSIZ); + setvbuf(stderr, NULL, _IONBF, BUFSIZ); + if (isatty(0) && isatty(1)) { + ctx->interactive = 1; + } else { + ctx->start_meta[0] = '\001'; + ctx->stop_meta[0] = '\002'; + } + memset(bar, '=', sizeof(bar)-1); + memset(spaces, ' ', sizeof(spaces)-1); + blkid_get_cache(&ctx->blkid, NULL); + + if (argc && *argv) + ctx->program_name = *argv; + else + ctx->program_name = "e2fsck"; + while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF) + switch (c) { + case 'C': + ctx->progress = e2fsck_update_progress; + ctx->progress_fd = atoi(optarg); + if (!ctx->progress_fd) + break; + /* Validate the file descriptor to avoid disasters */ + fd = dup(ctx->progress_fd); + if (fd < 0) { + fprintf(stderr, + _("Error validating file descriptor %d: %s\n"), + ctx->progress_fd, + error_message(errno)); + bb_error_msg_and_die(_("Invalid completion information file descriptor")); + } else + close(fd); + break; + case 'D': + ctx->options |= E2F_OPT_COMPRESS_DIRS; + break; + case 'E': + extended_opts = optarg; + break; + case 'p': + case 'a': + if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) { + conflict_opt: + bb_error_msg_and_die(_("only one the options -p/-a, -n or -y may be specified")); + } + ctx->options |= E2F_OPT_PREEN; + break; + case 'n': + if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN)) + goto conflict_opt; + ctx->options |= E2F_OPT_NO; + break; + case 'y': + if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO)) + goto conflict_opt; + ctx->options |= E2F_OPT_YES; + break; + case 't': + /* FIXME - This needs to go away in a future path - will change binary */ + fprintf(stderr, _("The -t option is not " + "supported on this version of e2fsck.\n")); + break; + case 'c': + if (cflag++) + ctx->options |= E2F_OPT_WRITECHECK; + ctx->options |= E2F_OPT_CHECKBLOCKS; + break; + case 'r': + /* What we do by default, anyway! */ + break; + case 'b': + ctx->use_superblock = atoi(optarg); + ctx->flags |= E2F_FLAG_SB_SPECIFIED; + break; + case 'B': + ctx->blocksize = atoi(optarg); + break; + case 'I': + ctx->inode_buffer_blocks = atoi(optarg); + break; + case 'j': + ctx->journal_name = string_copy(optarg, 0); + break; + case 'P': + ctx->process_inode_size = atoi(optarg); + break; + case 'd': + ctx->options |= E2F_OPT_DEBUG; + break; + case 'f': + ctx->options |= E2F_OPT_FORCE; + break; + case 'F': + flush = 1; + break; + case 'v': + verbose = 1; + break; + case 'V': + show_version_only = 1; + break; + case 'N': + ctx->device_name = optarg; + break; +#ifdef ENABLE_SWAPFS + case 's': + normalize_swapfs = 1; + case 'S': + swapfs = 1; + break; +#else + case 's': + case 'S': + fprintf(stderr, _("Byte-swapping filesystems " + "not compiled in this version " + "of e2fsck\n")); + exit(1); +#endif + default: + bb_show_usage(); + } + if (show_version_only) + return 0; + if (optind != argc - 1) + bb_show_usage(); + if ((ctx->options & E2F_OPT_NO) && + !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS)) + ctx->options |= E2F_OPT_READONLY; + ctx->io_options = strchr(argv[optind], '?'); + if (ctx->io_options) + *ctx->io_options++ = 0; + ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0); + if (!ctx->filesystem_name) { + bb_error_msg(_("Unable to resolve '%s'"), argv[optind]); + bb_error_msg_and_die(0); + } + if (extended_opts) + parse_extended_opts(ctx, extended_opts); + + if (flush) { + fd = open(ctx->filesystem_name, O_RDONLY, 0); + if (fd < 0) { + bb_error_msg(_("while opening %s for flushing"), + ctx->filesystem_name); + bb_error_msg_and_die(0); + } + if ((retval = ext2fs_sync_device(fd, 1))) { + bb_error_msg(_("while trying to flush %s"), + ctx->filesystem_name); + bb_error_msg_and_die(0); + } + close(fd); + } +#ifdef ENABLE_SWAPFS + if (swapfs && cflag) { + fprintf(stderr, _("Incompatible options not " + "allowed when byte-swapping.\n")); + exit(EXIT_USAGE); + } +#endif + /* + * Set up signal action + */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = signal_cancel; + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); +#ifdef SA_RESTART + sa.sa_flags = SA_RESTART; +#endif + e2fsck_global_ctx = ctx; + sa.sa_handler = signal_progress_on; + sigaction(SIGUSR1, &sa, 0); + sa.sa_handler = signal_progress_off; + sigaction(SIGUSR2, &sa, 0); + + /* Update our PATH to include /sbin if we need to run badblocks */ + if (cflag) + e2fs_set_sbin_path(); + return 0; +} + +static const char my_ver_string[] = E2FSPROGS_VERSION; +static const char my_ver_date[] = E2FSPROGS_DATE; + +int e2fsck_main (int argc, char **argv); +int e2fsck_main (int argc, char **argv) +{ + errcode_t retval; + int exit_value = EXIT_OK; + ext2_filsys fs = 0; + io_manager io_ptr; + struct ext2_super_block *sb; + const char *lib_ver_date; + int my_ver, lib_ver; + e2fsck_t ctx; + struct problem_context pctx; + int flags, run_result; + + clear_problem_context(&pctx); + + my_ver = ext2fs_parse_version_string(my_ver_string); + lib_ver = ext2fs_get_library_version(0, &lib_ver_date); + if (my_ver > lib_ver) { + fprintf( stderr, _("Error: ext2fs library version " + "out of date!\n")); + show_version_only++; + } + + retval = PRS(argc, argv, &ctx); + if (retval) { + bb_error_msg(_("while trying to initialize program")); + exit(EXIT_ERROR); + } + reserve_stdio_fds(); + + if (!(ctx->options & E2F_OPT_PREEN) || show_version_only) + fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string, + my_ver_date); + + if (show_version_only) { + fprintf(stderr, _("\tUsing %s, %s\n"), + error_message(EXT2_ET_BASE), lib_ver_date); + exit(EXIT_OK); + } + + check_mount(ctx); + + if (!(ctx->options & E2F_OPT_PREEN) && + !(ctx->options & E2F_OPT_NO) && + !(ctx->options & E2F_OPT_YES)) { + if (!ctx->interactive) + bb_error_msg_and_die(_("need terminal for interactive repairs")); + } + ctx->superblock = ctx->use_superblock; +restart: +#ifdef CONFIG_TESTIO_DEBUG + io_ptr = test_io_manager; + test_io_backing_manager = unix_io_manager; +#else + io_ptr = unix_io_manager; +#endif + flags = 0; + if ((ctx->options & E2F_OPT_READONLY) == 0) + flags |= EXT2_FLAG_RW; + + if (ctx->superblock && ctx->blocksize) { + retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, + flags, ctx->superblock, ctx->blocksize, + io_ptr, &fs); + } else if (ctx->superblock) { + int blocksize; + for (blocksize = EXT2_MIN_BLOCK_SIZE; + blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) { + retval = ext2fs_open2(ctx->filesystem_name, + ctx->io_options, flags, + ctx->superblock, blocksize, + io_ptr, &fs); + if (!retval) + break; + } + } else + retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, + flags, 0, 0, io_ptr, &fs); + if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && + !(ctx->flags & E2F_FLAG_SB_SPECIFIED) && + ((retval == EXT2_ET_BAD_MAGIC) || + ((retval == 0) && ext2fs_check_desc(fs)))) { + if (!fs || (fs->group_desc_count > 1)) { + printf(_("%s trying backup blocks...\n"), + retval ? _("Couldn't find ext2 superblock,") : + _("Group descriptors look bad...")); + get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr); + if (fs) + ext2fs_close(fs); + goto restart; + } + } + if (retval) { + bb_error_msg(_("while trying to open %s"), + ctx->filesystem_name); + if (retval == EXT2_ET_REV_TOO_HIGH) { + printf(_("The filesystem revision is apparently " + "too high for this version of e2fsck.\n" + "(Or the filesystem superblock " + "is corrupt)\n\n")); + fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); + } else if (retval == EXT2_ET_SHORT_READ) + printf(_("Could this be a zero-length partition?\n")); + else if ((retval == EPERM) || (retval == EACCES)) + printf(_("You must have %s access to the " + "filesystem or be root\n"), + (ctx->options & E2F_OPT_READONLY) ? + "r/o" : "r/w"); + else if (retval == ENXIO) + printf(_("Possibly non-existent or swap device?\n")); +#ifdef EROFS + else if (retval == EROFS) + printf(_("Disk write-protected; use the -n option " + "to do a read-only\n" + "check of the device.\n")); +#endif + else + fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); + bb_error_msg_and_die(0); + } + ctx->fs = fs; + fs->priv_data = ctx; + sb = fs->super; + if (sb->s_rev_level > E2FSCK_CURRENT_REV) { + bb_error_msg(_("while trying to open %s"), + ctx->filesystem_name); + get_newer: + bb_error_msg_and_die(_("Get a newer version of e2fsck!")); + } + + /* + * Set the device name, which is used whenever we print error + * or informational messages to the user. + */ + if (ctx->device_name == 0 && + (sb->s_volume_name[0] != 0)) { + ctx->device_name = string_copy(sb->s_volume_name, + sizeof(sb->s_volume_name)); + } + if (ctx->device_name == 0) + ctx->device_name = ctx->filesystem_name; + + /* + * Make sure the ext3 superblock fields are consistent. + */ + retval = e2fsck_check_ext3_journal(ctx); + if (retval) { + bb_error_msg(_("while checking ext3 journal for %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } + + /* + * Check to see if we need to do ext3-style recovery. If so, + * do it, and then restart the fsck. + */ + if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { + if (ctx->options & E2F_OPT_READONLY) { + printf(_("Warning: skipping journal recovery " + "because doing a read-only filesystem " + "check.\n")); + io_channel_flush(ctx->fs->io); + } else { + if (ctx->flags & E2F_FLAG_RESTARTED) { + /* + * Whoops, we attempted to run the + * journal twice. This should never + * happen, unless the hardware or + * device driver is being bogus. + */ + bb_error_msg(_("can't set superblock flags on %s"), ctx->device_name); + bb_error_msg_and_die(0); + } + retval = e2fsck_run_ext3_journal(ctx); + if (retval) { + bb_error_msg(_("while recovering ext3 journal of %s"), + ctx->device_name); + bb_error_msg_and_die(0); + } + ext2fs_close(ctx->fs); + ctx->fs = 0; + ctx->flags |= E2F_FLAG_RESTARTED; + goto restart; + } + } + + /* + * Check for compatibility with the feature sets. We need to + * be more stringent than ext2fs_open(). + */ + if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) || + (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) { + bb_error_msg("(%s)", ctx->device_name); + goto get_newer; + } + if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + bb_error_msg("(%s)", ctx->device_name); + goto get_newer; + } +#ifdef ENABLE_COMPRESSION + /* FIXME - do we support this at all? */ + if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) + bb_error_msg(_("warning: compression support is experimental")); +#endif +#ifndef ENABLE_HTREE + if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) { + bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t" + "but filesystem %s has HTREE directories."), + ctx->device_name); + goto get_newer; + } +#endif + + /* + * If the user specified a specific superblock, presumably the + * master superblock has been trashed. So we mark the + * superblock as dirty, so it can be written out. + */ + if (ctx->superblock && + !(ctx->options & E2F_OPT_READONLY)) + ext2fs_mark_super_dirty(fs); + + /* + * We only update the master superblock because (a) paranoia; + * we don't want to corrupt the backup superblocks, and (b) we + * don't need to update the mount count and last checked + * fields in the backup superblock (the kernel doesn't + * update the backup superblocks anyway). + */ + fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; + + ehandler_init(fs->io); + + if (ctx->superblock) + set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0); + ext2fs_mark_valid(fs); + check_super_block(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + bb_error_msg_and_die(0); + check_if_skip(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + bb_error_msg_and_die(0); +#ifdef ENABLE_SWAPFS + +#ifdef WORDS_BIGENDIAN +#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES +#else +#define NATIVE_FLAG 0 +#endif + + + if (normalize_swapfs) { + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) { + fprintf(stderr, _("%s: Filesystem byte order " + "already normalized.\n"), ctx->device_name); + bb_error_msg_and_die(0); + } + } + if (swapfs) { + swap_filesys(ctx); + if (ctx->flags & E2F_FLAG_SIGNAL_MASK) + bb_error_msg_and_die(0); + } +#endif + + /* + * Mark the system as valid, 'til proven otherwise + */ + ext2fs_mark_valid(fs); + + retval = ext2fs_read_bb_inode(fs, &fs->badblocks); + if (retval) { + bb_error_msg(_("while reading bad blocks inode")); + preenhalt(ctx); + printf(_("This doesn't bode well," + " but we'll try to go on...\n")); + } + + run_result = e2fsck_run(ctx); + e2fsck_clear_progbar(ctx); + if (run_result == E2F_FLAG_RESTART) { + printf(_("Restarting e2fsck from the beginning...\n")); + retval = e2fsck_reset_context(ctx); + if (retval) { + bb_error_msg(_("while resetting context")); + bb_error_msg_and_die(0); + } + ext2fs_close(fs); + goto restart; + } + if (run_result & E2F_FLAG_CANCEL) { + printf(_("%s: e2fsck canceled.\n"), ctx->device_name ? + ctx->device_name : ctx->filesystem_name); + exit_value |= FSCK_CANCELED; + } + if (run_result & E2F_FLAG_ABORT) + bb_error_msg_and_die(_("aborted")); + + /* Cleanup */ + if (ext2fs_test_changed(fs)) { + exit_value |= EXIT_NONDESTRUCT; + if (!(ctx->options & E2F_OPT_PREEN)) + printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"), + ctx->device_name); + if (ctx->mount_flags & EXT2_MF_ISROOT) { + printf(_("%s: ***** REBOOT LINUX *****\n"), + ctx->device_name); + exit_value |= EXIT_DESTRUCT; + } + } + if (!ext2fs_test_valid(fs)) { + printf(_("\n%s: ********** WARNING: Filesystem still has " + "errors **********\n\n"), ctx->device_name); + exit_value |= EXIT_UNCORRECTED; + exit_value &= ~EXIT_NONDESTRUCT; + } + if (exit_value & FSCK_CANCELED) + exit_value &= ~EXIT_NONDESTRUCT; + else { + show_stats(ctx); + if (!(ctx->options & E2F_OPT_READONLY)) { + if (ext2fs_test_valid(fs)) { + if (!(sb->s_state & EXT2_VALID_FS)) + exit_value |= EXIT_NONDESTRUCT; + sb->s_state = EXT2_VALID_FS; + } else + sb->s_state &= ~EXT2_VALID_FS; + sb->s_mnt_count = 0; + sb->s_lastcheck = time(NULL); + ext2fs_mark_super_dirty(fs); + } + } + + e2fsck_write_bitmaps(ctx); + + ext2fs_close(fs); + ctx->fs = NULL; + free(ctx->filesystem_name); + free(ctx->journal_name); + e2fsck_free_context(ctx); + + return exit_value; +} diff --git a/e2fsprogs/old_e2fsprogs/e2fsck.h b/e2fsprogs/old_e2fsprogs/e2fsck.h new file mode 100644 index 000000000..c159fabab --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2fsck.h @@ -0,0 +1,638 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ext2fs/kernel-list.h" +#include +#include + +/* + * Now pull in the real linux/jfs.h definitions. + */ +#include "ext2fs/kernel-jbd.h" + + + +#include "fsck.h" + +#include "ext2fs/ext2_fs.h" +#include "blkid/blkid.h" +#include "ext2fs/ext2_ext_attr.h" +#include "uuid/uuid.h" +#include "libbb.h" + +#ifdef HAVE_CONIO_H +#undef HAVE_TERMIOS_H +#include +#define read_a_char() getch() +#else +#ifdef HAVE_TERMIOS_H +#include +#endif +#endif + + +/* + * The last ext2fs revision level that this version of e2fsck is able to + * support + */ +#define E2FSCK_CURRENT_REV 1 + +/* Used by the region allocation code */ +typedef __u32 region_addr_t; +typedef struct region_struct *region_t; + +struct dx_dirblock_info { + int type; + blk_t phys; + int flags; + blk_t parent; + ext2_dirhash_t min_hash; + ext2_dirhash_t max_hash; + ext2_dirhash_t node_min_hash; + ext2_dirhash_t node_max_hash; +}; + +/* +These defines are used in the type field of dx_dirblock_info +*/ + +#define DX_DIRBLOCK_ROOT 1 +#define DX_DIRBLOCK_LEAF 2 +#define DX_DIRBLOCK_NODE 3 + + +/* +The following defines are used in the 'flags' field of a dx_dirblock_info +*/ +#define DX_FLAG_REFERENCED 1 +#define DX_FLAG_DUP_REF 2 +#define DX_FLAG_FIRST 4 +#define DX_FLAG_LAST 8 + +/* + * E2fsck options + */ +#define E2F_OPT_READONLY 0x0001 +#define E2F_OPT_PREEN 0x0002 +#define E2F_OPT_YES 0x0004 +#define E2F_OPT_NO 0x0008 +#define E2F_OPT_TIME 0x0010 +#define E2F_OPT_CHECKBLOCKS 0x0040 +#define E2F_OPT_DEBUG 0x0080 +#define E2F_OPT_FORCE 0x0100 +#define E2F_OPT_WRITECHECK 0x0200 +#define E2F_OPT_COMPRESS_DIRS 0x0400 + +/* + * E2fsck flags + */ +#define E2F_FLAG_ABORT 0x0001 /* Abort signaled */ +#define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */ +#define E2F_FLAG_SIGNAL_MASK 0x0003 +#define E2F_FLAG_RESTART 0x0004 /* Restart signaled */ + +#define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */ + +#define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */ +#define E2F_FLAG_PROG_SUPPRESS 0x0040 /* Progress suspended */ +#define E2F_FLAG_JOURNAL_INODE 0x0080 /* Create a new ext3 journal inode */ +#define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly + * specified by the user */ +#define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */ +#define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */ + + +/*Don't know where these come from*/ +#define READ 0 +#define WRITE 1 +#define cpu_to_be32(n) htonl(n) +#define be32_to_cpu(n) ntohl(n) + +/* + * We define a set of "latch groups"; these are problems which are + * handled as a set. The user answers once for a particular latch + * group. + */ +#define PR_LATCH_MASK 0x0ff0 /* Latch mask */ +#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */ +#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */ +#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */ +#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */ +#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */ +#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */ +#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */ +#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */ +#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */ + +#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1) + +/* + * Latch group descriptor flags + */ +#define PRL_YES 0x0001 /* Answer yes */ +#define PRL_NO 0x0002 /* Answer no */ +#define PRL_LATCHED 0x0004 /* The latch group is latched */ +#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */ + +#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */ + +/* + * Pre-Pass 1 errors + */ + +#define PR_0_BB_NOT_GROUP 0x000001 /* Block bitmap not in group */ +#define PR_0_IB_NOT_GROUP 0x000002 /* Inode bitmap not in group */ +#define PR_0_ITABLE_NOT_GROUP 0x000003 /* Inode table not in group */ +#define PR_0_SB_CORRUPT 0x000004 /* Superblock corrupt */ +#define PR_0_FS_SIZE_WRONG 0x000005 /* Filesystem size is wrong */ +#define PR_0_NO_FRAGMENTS 0x000006 /* Fragments not supported */ +#define PR_0_BLOCKS_PER_GROUP 0x000007 /* Bad blocks_per_group */ +#define PR_0_FIRST_DATA_BLOCK 0x000008 /* Bad first_data_block */ +#define PR_0_ADD_UUID 0x000009 /* Adding UUID to filesystem */ +#define PR_0_RELOCATE_HINT 0x00000A /* Relocate hint */ +#define PR_0_MISC_CORRUPT_SUPER 0x00000B /* Miscellaneous superblock corruption */ +#define PR_0_GETSIZE_ERROR 0x00000C /* Error determing physical device size of filesystem */ +#define PR_0_INODE_COUNT_WRONG 0x00000D /* Inode count in the superblock incorrect */ +#define PR_0_HURD_CLEAR_FILETYPE 0x00000E /* The Hurd does not support the filetype feature */ +#define PR_0_JOURNAL_BAD_INODE 0x00000F /* The Hurd does not support the filetype feature */ +#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010 /* The external journal has multiple filesystems (which we can't handle yet) */ +#define PR_0_CANT_FIND_JOURNAL 0x000011 /* Can't find external journal */ +#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012/* External journal has bad superblock */ +#define PR_0_JOURNAL_BAD_UUID 0x000013 /* Superblock has a bad journal UUID */ +#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014 /* Journal has an unknown superblock type */ +#define PR_0_JOURNAL_BAD_SUPER 0x000015 /* Journal superblock is corrupt */ +#define PR_0_JOURNAL_HAS_JOURNAL 0x000016 /* Journal superblock is corrupt */ +#define PR_0_JOURNAL_RECOVER_SET 0x000017 /* Superblock has recovery flag set but no journal */ +#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018 /* Journal has data, but recovery flag is clear */ +#define PR_0_JOURNAL_RESET_JOURNAL 0x000019 /* Ask if we should clear the journal */ +#define PR_0_FS_REV_LEVEL 0x00001A /* Filesystem revision is 0, but feature flags are set */ +#define PR_0_ORPHAN_CLEAR_INODE 0x000020 /* Clearing orphan inode */ +#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021 /* Illegal block found in orphaned inode */ +#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022 /* Already cleared block found in orphaned inode */ +#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023 /* Illegal orphan inode in superblock */ +#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024 /* Illegal inode in orphaned inode list */ +#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025 /* Journal has unsupported read-only feature - abort */ +#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026 /* Journal has unsupported incompatible feature - abort */ +#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027 /* Journal has unsupported version number */ +#define PR_0_MOVE_JOURNAL 0x000028 /* Moving journal to hidden file */ +#define PR_0_ERR_MOVE_JOURNAL 0x000029 /* Error moving journal */ +#define PR_0_CLEAR_V2_JOURNAL 0x00002A /* Clearing V2 journal superblock */ +#define PR_0_JOURNAL_RUN 0x00002B /* Run journal anyway */ +#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C /* Run journal anyway by default */ +#define PR_0_BACKUP_JNL 0x00002D /* Backup journal inode blocks */ +#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E /* Reserved blocks w/o resize_inode */ +#define PR_0_CLEAR_RESIZE_INODE 0x00002F /* Resize_inode not enabled, but resize inode is non-zero */ +#define PR_0_RESIZE_INODE_INVALID 0x000030 /* Resize inode invalid */ + +/* + * Pass 1 errors + */ + +#define PR_1_PASS_HEADER 0x010000 /* Pass 1: Checking inodes, blocks, and sizes */ +#define PR_1_ROOT_NO_DIR 0x010001 /* Root directory is not an inode */ +#define PR_1_ROOT_DTIME 0x010002 /* Root directory has dtime set */ +#define PR_1_RESERVED_BAD_MODE 0x010003 /* Reserved inode has bad mode */ +#define PR_1_ZERO_DTIME 0x010004 /* Deleted inode has zero dtime */ +#define PR_1_SET_DTIME 0x010005 /* Inode in use, but dtime set */ +#define PR_1_ZERO_LENGTH_DIR 0x010006 /* Zero-length directory */ +#define PR_1_BB_CONFLICT 0x010007 /* Block bitmap conflicts with some other fs block */ +#define PR_1_IB_CONFLICT 0x010008 /* Inode bitmap conflicts with some other fs block */ +#define PR_1_ITABLE_CONFLICT 0x010009 /* Inode table conflicts with some other fs block */ +#define PR_1_BB_BAD_BLOCK 0x01000A /* Block bitmap is on a bad block */ +#define PR_1_IB_BAD_BLOCK 0x01000B /* Inode bitmap is on a bad block */ +#define PR_1_BAD_I_SIZE 0x01000C /* Inode has incorrect i_size */ +#define PR_1_BAD_I_BLOCKS 0x01000D /* Inode has incorrect i_blocks */ +#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E /* Illegal block number in inode */ +#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F /* Block number overlaps fs metadata */ +#define PR_1_INODE_BLOCK_LATCH 0x010010 /* Inode has illegal blocks (latch question) */ +#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011 /* Too many bad blocks in inode */ +#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012 /* Illegal block number in bad block inode */ +#define PR_1_INODE_BBLOCK_LATCH 0x010013 /* Bad block inode has illegal blocks (latch question) */ +#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014 /* Duplicate or bad blocks in use! */ +#define PR_1_BBINODE_BAD_METABLOCK 0x010015 /* Bad block used as bad block indirect block */ +#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016 /* Inconsistency can't be fixed prompt */ +#define PR_1_BAD_PRIMARY_BLOCK 0x010017 /* Bad primary block */ +#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018 /* Bad primary block prompt */ +#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019 /* Bad primary superblock */ +#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A /* Bad primary block group descriptors */ +#define PR_1_BAD_SUPERBLOCK 0x01001B /* Bad superblock in group */ +#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C /* Bad block group descriptors in group */ +#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D /* Block claimed for no reason */ +#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E /* Error allocating blocks for relocating metadata */ +#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F /* Error allocating block buffer during relocation process */ +#define PR_1_RELOC_FROM_TO 0x010020 /* Relocating metadata group information from X to Y */ +#define PR_1_RELOC_TO 0x010021 /* Relocating metatdata group information to X */ +#define PR_1_RELOC_READ_ERR 0x010022 /* Block read error during relocation process */ +#define PR_1_RELOC_WRITE_ERR 0x010023 /* Block write error during relocation process */ +#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024 /* Error allocating inode bitmap */ +#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025 /* Error allocating block bitmap */ +#define PR_1_ALLOCATE_ICOUNT 0x010026 /* Error allocating icount structure */ +#define PR_1_ALLOCATE_DBCOUNT 0x010027 /* Error allocating dbcount */ +#define PR_1_ISCAN_ERROR 0x010028 /* Error while scanning inodes */ +#define PR_1_BLOCK_ITERATE 0x010029 /* Error while iterating over blocks */ +#define PR_1_ICOUNT_STORE 0x01002A /* Error while storing inode count information */ +#define PR_1_ADD_DBLOCK 0x01002B /* Error while storing directory block information */ +#define PR_1_READ_INODE 0x01002C /* Error while reading inode (for clearing) */ +#define PR_1_SUPPRESS_MESSAGES 0x01002D /* Suppress messages prompt */ +#define PR_1_SET_IMAGIC 0x01002F /* Imagic flag set on an inode when filesystem doesn't support it */ +#define PR_1_SET_IMMUTABLE 0x010030 /* Immutable flag set on a device or socket inode */ +#define PR_1_COMPR_SET 0x010031 /* Compression flag set on a non-compressed filesystem */ +#define PR_1_SET_NONZSIZE 0x010032 /* Non-zero size on device, fifo or socket inode */ +#define PR_1_FS_REV_LEVEL 0x010033 /* Filesystem revision is 0, but feature flags are set */ +#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034 /* Journal inode not in use, needs clearing */ +#define PR_1_JOURNAL_BAD_MODE 0x010035 /* Journal inode has wrong mode */ +#define PR_1_LOW_DTIME 0x010036 /* Inode that was part of orphan linked list */ +#define PR_1_ORPHAN_LIST_REFUGEES 0x010037 /* Latch question which asks how to deal with low dtime inodes */ +#define PR_1_ALLOCATE_REFCOUNT 0x010038 /* Error allocating refcount structure */ +#define PR_1_READ_EA_BLOCK 0x010039 /* Error reading Extended Attribute block */ +#define PR_1_BAD_EA_BLOCK 0x01003A /* Invalid Extended Attribute block */ +#define PR_1_EXTATTR_READ_ABORT 0x01003B /* Error reading Extended Attribute block while fixing refcount -- abort */ +#define PR_1_EXTATTR_REFCOUNT 0x01003C /* Extended attribute reference count incorrect */ +#define PR_1_EXTATTR_WRITE 0x01003D /* Error writing Extended Attribute block while fixing refcount */ +#define PR_1_EA_MULTI_BLOCK 0x01003E /* Multiple EA blocks not supported */ +#define PR_1_EA_ALLOC_REGION 0x01003F /* Error allocating EA region allocation structure */ +#define PR_1_EA_ALLOC_COLLISION 0x010040 /* Error EA allocation collision */ +#define PR_1_EA_BAD_NAME 0x010041 /* Bad extended attribute name */ +#define PR_1_EA_BAD_VALUE 0x010042 /* Bad extended attribute value */ +#define PR_1_INODE_TOOBIG 0x010043 /* Inode too big (latch question) */ +#define PR_1_TOOBIG_DIR 0x010044 /* Directory too big */ +#define PR_1_TOOBIG_REG 0x010045 /* Regular file too big */ +#define PR_1_TOOBIG_SYMLINK 0x010046 /* Symlink too big */ +#define PR_1_HTREE_SET 0x010047 /* INDEX_FL flag set on a non-HTREE filesystem */ +#define PR_1_HTREE_NODIR 0x010048 /* INDEX_FL flag set on a non-directory */ +#define PR_1_HTREE_BADROOT 0x010049 /* Invalid root node in HTREE directory */ +#define PR_1_HTREE_HASHV 0x01004A /* Unsupported hash version in HTREE directory */ +#define PR_1_HTREE_INCOMPAT 0x01004B /* Incompatible flag in HTREE root node */ +#define PR_1_HTREE_DEPTH 0x01004C /* HTREE too deep */ +#define PR_1_BB_FS_BLOCK 0x01004D /* Bad block has indirect block that conflicts with filesystem block */ +#define PR_1_RESIZE_INODE_CREATE 0x01004E /* Resize inode failed */ +#define PR_1_EXTRA_ISIZE 0x01004F /* inode->i_size is too long */ +#define PR_1_ATTR_NAME_LEN 0x010050 /* attribute name is too long */ +#define PR_1_ATTR_VALUE_OFFSET 0x010051 /* wrong EA value offset */ +#define PR_1_ATTR_VALUE_BLOCK 0x010052 /* wrong EA blocknumber */ +#define PR_1_ATTR_VALUE_SIZE 0x010053 /* wrong EA value size */ +#define PR_1_ATTR_HASH 0x010054 /* wrong EA hash value */ + +/* + * Pass 1b errors + */ + +#define PR_1B_PASS_HEADER 0x011000 /* Pass 1B: Rescan for duplicate/bad blocks */ +#define PR_1B_DUP_BLOCK_HEADER 0x011001 /* Duplicate/bad block(s) header */ +#define PR_1B_DUP_BLOCK 0x011002 /* Duplicate/bad block(s) in inode */ +#define PR_1B_DUP_BLOCK_END 0x011003 /* Duplicate/bad block(s) end */ +#define PR_1B_ISCAN_ERROR 0x011004 /* Error while scanning inodes */ +#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005 /* Error allocating inode bitmap */ +#define PR_1B_BLOCK_ITERATE 0x0110006 /* Error while iterating over blocks */ +#define PR_1B_ADJ_EA_REFCOUNT 0x0110007 /* Error adjusting EA refcount */ +#define PR_1C_PASS_HEADER 0x012000 /* Pass 1C: Scan directories for inodes with dup blocks. */ +#define PR_1D_PASS_HEADER 0x013000 /* Pass 1D: Reconciling duplicate blocks */ +#define PR_1D_DUP_FILE 0x013001 /* File has duplicate blocks */ +#define PR_1D_DUP_FILE_LIST 0x013002 /* List of files sharing duplicate blocks */ +#define PR_1D_SHARE_METADATA 0x013003 /* File sharing blocks with filesystem metadata */ +#define PR_1D_NUM_DUP_INODES 0x013004 /* Report of how many duplicate/bad inodes */ +#define PR_1D_DUP_BLOCKS_DEALT 0x013005 /* Duplicated blocks already reassigned or cloned. */ +#define PR_1D_CLONE_QUESTION 0x013006 /* Clone duplicate/bad blocks? */ +#define PR_1D_DELETE_QUESTION 0x013007 /* Delete file? */ +#define PR_1D_CLONE_ERROR 0x013008 /* Couldn't clone file (error) */ + +/* + * Pass 2 errors + */ + +#define PR_2_PASS_HEADER 0x020000 /* Pass 2: Checking directory structure */ +#define PR_2_BAD_INODE_DOT 0x020001 /* Bad inode number for '.' */ +#define PR_2_BAD_INO 0x020002 /* Directory entry has bad inode number */ +#define PR_2_UNUSED_INODE 0x020003 /* Directory entry has deleted or unused inode */ +#define PR_2_LINK_DOT 0x020004 /* Directry entry is link to '.' */ +#define PR_2_BB_INODE 0x020005 /* Directory entry points to inode now located in a bad block */ +#define PR_2_LINK_DIR 0x020006 /* Directory entry contains a link to a directory */ +#define PR_2_LINK_ROOT 0x020007 /* Directory entry contains a link to the root directry */ +#define PR_2_BAD_NAME 0x020008 /* Directory entry has illegal characters in its name */ +#define PR_2_MISSING_DOT 0x020009 /* Missing '.' in directory inode */ +#define PR_2_MISSING_DOT_DOT 0x02000A /* Missing '..' in directory inode */ +#define PR_2_1ST_NOT_DOT 0x02000B /* First entry in directory inode doesn't contain '.' */ +#define PR_2_2ND_NOT_DOT_DOT 0x02000C /* Second entry in directory inode doesn't contain '..' */ +#define PR_2_FADDR_ZERO 0x02000D /* i_faddr should be zero */ +#define PR_2_FILE_ACL_ZERO 0x02000E /* i_file_acl should be zero */ +#define PR_2_DIR_ACL_ZERO 0x02000F /* i_dir_acl should be zero */ +#define PR_2_FRAG_ZERO 0x020010 /* i_frag should be zero */ +#define PR_2_FSIZE_ZERO 0x020011 /* i_fsize should be zero */ +#define PR_2_BAD_MODE 0x020012 /* inode has bad mode */ +#define PR_2_DIR_CORRUPTED 0x020013 /* directory corrupted */ +#define PR_2_FILENAME_LONG 0x020014 /* filename too long */ +#define PR_2_DIRECTORY_HOLE 0x020015 /* Directory inode has a missing block (hole) */ +#define PR_2_DOT_NULL_TERM 0x020016 /* '.' is not NULL terminated */ +#define PR_2_DOT_DOT_NULL_TERM 0x020017 /* '..' is not NULL terminated */ +#define PR_2_BAD_CHAR_DEV 0x020018 /* Illegal character device in inode */ +#define PR_2_BAD_BLOCK_DEV 0x020019 /* Illegal block device in inode */ +#define PR_2_DUP_DOT 0x02001A /* Duplicate '.' entry */ +#define PR_2_DUP_DOT_DOT 0x02001B /* Duplicate '..' entry */ +#define PR_2_NO_DIRINFO 0x02001C /* Internal error: couldn't find dir_info */ +#define PR_2_FINAL_RECLEN 0x02001D /* Final rec_len is wrong */ +#define PR_2_ALLOCATE_ICOUNT 0x02001E /* Error allocating icount structure */ +#define PR_2_DBLIST_ITERATE 0x02001F /* Error iterating over directory blocks */ +#define PR_2_READ_DIRBLOCK 0x020020 /* Error reading directory block */ +#define PR_2_WRITE_DIRBLOCK 0x020021 /* Error writing directory block */ +#define PR_2_ALLOC_DIRBOCK 0x020022 /* Error allocating new directory block */ +#define PR_2_DEALLOC_INODE 0x020023 /* Error deallocating inode */ +#define PR_2_SPLIT_DOT 0x020024 /* Directory entry for '.' is big. Split? */ +#define PR_2_BAD_FIFO 0x020025 /* Illegal FIFO */ +#define PR_2_BAD_SOCKET 0x020026 /* Illegal socket */ +#define PR_2_SET_FILETYPE 0x020027 /* Directory filetype not set */ +#define PR_2_BAD_FILETYPE 0x020028 /* Directory filetype incorrect */ +#define PR_2_CLEAR_FILETYPE 0x020029 /* Directory filetype set when it shouldn't be */ +#define PR_2_NULL_NAME 0x020030 /* Directory filename can't be zero-length */ +#define PR_2_INVALID_SYMLINK 0x020031 /* Invalid symlink */ +#define PR_2_FILE_ACL_BAD 0x020032 /* i_file_acl (extended attribute) is bad */ +#define PR_2_FEATURE_LARGE_FILES 0x020033 /* Filesystem contains large files, but has no such flag in sb */ +#define PR_2_HTREE_NOTREF 0x020034 /* Node in HTREE directory not referenced */ +#define PR_2_HTREE_DUPREF 0x020035 /* Node in HTREE directory referenced twice */ +#define PR_2_HTREE_MIN_HASH 0x020036 /* Node in HTREE directory has bad min hash */ +#define PR_2_HTREE_MAX_HASH 0x020037 /* Node in HTREE directory has bad max hash */ +#define PR_2_HTREE_CLEAR 0x020038 /* Clear invalid HTREE directory */ +#define PR_2_HTREE_BADBLK 0x02003A /* Bad block in htree interior node */ +#define PR_2_ADJ_EA_REFCOUNT 0x02003B /* Error adjusting EA refcount */ +#define PR_2_HTREE_BAD_ROOT 0x02003C /* Invalid HTREE root node */ +#define PR_2_HTREE_BAD_LIMIT 0x02003D /* Invalid HTREE limit */ +#define PR_2_HTREE_BAD_COUNT 0x02003E /* Invalid HTREE count */ +#define PR_2_HTREE_HASH_ORDER 0x02003F /* HTREE interior node has out-of-order hashes in table */ +#define PR_2_HTREE_BAD_DEPTH 0x020040 /* Node in HTREE directory has bad depth */ +#define PR_2_DUPLICATE_DIRENT 0x020041 /* Duplicate directory entry found */ +#define PR_2_NON_UNIQUE_FILE 0x020042 /* Non-unique filename found */ +#define PR_2_REPORT_DUP_DIRENT 0x020043 /* Duplicate directory entry found */ + +/* + * Pass 3 errors + */ + +#define PR_3_PASS_HEADER 0x030000 /* Pass 3: Checking directory connectivity */ +#define PR_3_NO_ROOT_INODE 0x030001 /* Root inode not allocated */ +#define PR_3_EXPAND_LF_DIR 0x030002 /* No room in lost+found */ +#define PR_3_UNCONNECTED_DIR 0x030003 /* Unconnected directory inode */ +#define PR_3_NO_LF_DIR 0x030004 /* /lost+found not found */ +#define PR_3_BAD_DOT_DOT 0x030005 /* .. entry is incorrect */ +#define PR_3_NO_LPF 0x030006 /* Bad or non-existent /lost+found. Cannot reconnect */ +#define PR_3_CANT_EXPAND_LPF 0x030007 /* Could not expand /lost+found */ +#define PR_3_CANT_RECONNECT 0x030008 /* Could not reconnect inode */ +#define PR_3_ERR_FIND_LPF 0x030009 /* Error while trying to find /lost+found */ +#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A /* Error in ext2fs_new_block while creating /lost+found */ +#define PR_3_ERR_LPF_NEW_INODE 0x03000B /* Error in ext2fs_new_inode while creating /lost+found */ +#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C /* Error in ext2fs_new_dir_block while creating /lost+found */ +#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D /* Error while writing directory block for /lost+found */ +#define PR_3_ADJUST_INODE 0x03000E /* Error while adjusting inode count */ +#define PR_3_FIX_PARENT_ERR 0x03000F /* Couldn't fix parent directory -- error */ +#define PR_3_FIX_PARENT_NOFIND 0x030010 /* Couldn't fix parent directory -- couldn't find it */ +#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011 /* Error allocating inode bitmap */ +#define PR_3_CREATE_ROOT_ERROR 0x030012 /* Error creating root directory */ +#define PR_3_CREATE_LPF_ERROR 0x030013 /* Error creating lost and found directory */ +#define PR_3_ROOT_NOT_DIR_ABORT 0x030014 /* Root inode is not directory; aborting */ +#define PR_3_NO_ROOT_INODE_ABORT 0x030015 /* Cannot proceed without a root inode. */ +#define PR_3_NO_DIRINFO 0x030016 /* Internal error: couldn't find dir_info */ +#define PR_3_LPF_NOTDIR 0x030017 /* Lost+found is not a directory */ + +/* + * Pass 3a --- rehashing diretories + */ +#define PR_3A_PASS_HEADER 0x031000 /* Pass 3a: Reindexing directories */ +#define PR_3A_OPTIMIZE_ITER 0x031001 /* Error iterating over directories */ +#define PR_3A_OPTIMIZE_DIR_ERR 0x031002 /* Error rehash directory */ +#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003 /* Rehashing dir header */ +#define PR_3A_OPTIMIZE_DIR 0x031004 /* Rehashing directory %d */ +#define PR_3A_OPTIMIZE_DIR_END 0x031005 /* Rehashing dir end */ + +/* + * Pass 4 errors + */ + +#define PR_4_PASS_HEADER 0x040000 /* Pass 4: Checking reference counts */ +#define PR_4_ZERO_LEN_INODE 0x040001 /* Unattached zero-length inode */ +#define PR_4_UNATTACHED_INODE 0x040002 /* Unattached inode */ +#define PR_4_BAD_REF_COUNT 0x040003 /* Inode ref count wrong */ +#define PR_4_INCONSISTENT_COUNT 0x040004 /* Inconsistent inode count information cached */ + +/* + * Pass 5 errors + */ + +#define PR_5_PASS_HEADER 0x050000 /* Pass 5: Checking group summary information */ +#define PR_5_INODE_BMAP_PADDING 0x050001 /* Padding at end of inode bitmap is not set. */ +#define PR_5_BLOCK_BMAP_PADDING 0x050002 /* Padding at end of block bitmap is not set. */ +#define PR_5_BLOCK_BITMAP_HEADER 0x050003 /* Block bitmap differences header */ +#define PR_5_BLOCK_UNUSED 0x050004 /* Block not used, but marked in bitmap */ +#define PR_5_BLOCK_USED 0x050005 /* Block used, but not marked used in bitmap */ +#define PR_5_BLOCK_BITMAP_END 0x050006 /* Block bitmap differences end */ +#define PR_5_INODE_BITMAP_HEADER 0x050007 /* Inode bitmap differences header */ +#define PR_5_INODE_UNUSED 0x050008 /* Inode not used, but marked in bitmap */ +#define PR_5_INODE_USED 0x050009 /* Inode used, but not marked used in bitmap */ +#define PR_5_INODE_BITMAP_END 0x05000A /* Inode bitmap differences end */ +#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B /* Free inodes count for group wrong */ +#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C /* Directories count for group wrong */ +#define PR_5_FREE_INODE_COUNT 0x05000D /* Free inodes count wrong */ +#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E /* Free blocks count for group wrong */ +#define PR_5_FREE_BLOCK_COUNT 0x05000F /* Free blocks count wrong */ +#define PR_5_BMAP_ENDPOINTS 0x050010 /* Programming error: bitmap endpoints don't match */ +#define PR_5_FUDGE_BITMAP_ERROR 0x050011 /* Internal error: fudging end of bitmap */ +#define PR_5_COPY_IBITMAP_ERROR 0x050012 /* Error copying in replacement inode bitmap */ +#define PR_5_COPY_BBITMAP_ERROR 0x050013 /* Error copying in replacement block bitmap */ +#define PR_5_BLOCK_RANGE_UNUSED 0x050014 /* Block range not used, but marked in bitmap */ +#define PR_5_BLOCK_RANGE_USED 0x050015 /* Block range used, but not marked used in bitmap */ +#define PR_5_INODE_RANGE_UNUSED 0x050016 /* Inode range not used, but marked in bitmap */ +#define PR_5_INODE_RANGE_USED 0x050017 /* Inode rangeused, but not marked used in bitmap */ + + +/* + * The directory information structure; stores directory information + * collected in earlier passes, to avoid disk i/o in fetching the + * directory information. + */ +struct dir_info { + ext2_ino_t ino; /* Inode number */ + ext2_ino_t dotdot; /* Parent according to '..' */ + ext2_ino_t parent; /* Parent according to treewalk */ +}; + + + +/* + * The indexed directory information structure; stores information for + * directories which contain a hash tree index. + */ +struct dx_dir_info { + ext2_ino_t ino; /* Inode number */ + int numblocks; /* number of blocks */ + int hashversion; + short depth; /* depth of tree */ + struct dx_dirblock_info *dx_block; /* Array of size numblocks */ +}; + +/* + * Define the extended attribute refcount structure + */ +typedef struct ea_refcount *ext2_refcount_t; + +struct e2fsck_struct { + ext2_filsys fs; + const char *program_name; + char *filesystem_name; + char *device_name; + char *io_options; + int flags; /* E2fsck internal flags */ + int options; + blk_t use_superblock; /* sb requested by user */ + blk_t superblock; /* sb used to open fs */ + int blocksize; /* blocksize */ + blk_t num_blocks; /* Total number of blocks */ + int mount_flags; + blkid_cache blkid; /* blkid cache */ + + jmp_buf abort_loc; + + unsigned long abort_code; + + int (*progress)(e2fsck_t ctx, int pass, unsigned long cur, + unsigned long max); + + ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */ + ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */ + ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */ + ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */ + ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/ + + ext2fs_block_bitmap block_found_map; /* Blocks which are in use */ + ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */ + ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */ + + /* + * Inode count arrays + */ + ext2_icount_t inode_count; + ext2_icount_t inode_link_info; + + ext2_refcount_t refcount; + ext2_refcount_t refcount_extra; + + /* + * Array of flags indicating whether an inode bitmap, block + * bitmap, or inode table is invalid + */ + int *invalid_inode_bitmap_flag; + int *invalid_block_bitmap_flag; + int *invalid_inode_table_flag; + int invalid_bitmaps; /* There are invalid bitmaps/itable */ + + /* + * Block buffer + */ + char *block_buf; + + /* + * For pass1_check_directory and pass1_get_blocks + */ + ext2_ino_t stashed_ino; + struct ext2_inode *stashed_inode; + + /* + * Location of the lost and found directory + */ + ext2_ino_t lost_and_found; + int bad_lost_and_found; + + /* + * Directory information + */ + int dir_info_count; + int dir_info_size; + struct dir_info *dir_info; + + /* + * Indexed directory information + */ + int dx_dir_info_count; + int dx_dir_info_size; + struct dx_dir_info *dx_dir_info; + + /* + * Directories to hash + */ + ext2_u32_list dirs_to_hash; + + /* + * Tuning parameters + */ + int process_inode_size; + int inode_buffer_blocks; + + /* + * ext3 journal support + */ + io_channel journal_io; + char *journal_name; + + /* + * How we display the progress update (for unix) + */ + int progress_fd; + int progress_pos; + int progress_last_percent; + unsigned int progress_last_time; + int interactive; /* Are we connected directly to a tty? */ + char start_meta[2], stop_meta[2]; + + /* File counts */ + int fs_directory_count; + int fs_regular_count; + int fs_blockdev_count; + int fs_chardev_count; + int fs_links_count; + int fs_symlinks_count; + int fs_fast_symlinks_count; + int fs_fifo_count; + int fs_total_count; + int fs_sockets_count; + int fs_ind_count; + int fs_dind_count; + int fs_tind_count; + int fs_fragmented; + int large_files; + int fs_ext_attr_inodes; + int fs_ext_attr_blocks; + + int ext_attr_ver; + + /* + * For the use of callers of the e2fsck functions; not used by + * e2fsck functions themselves. + */ + void *priv_data; +}; + + +#define tid_gt(x, y) ((x - y) > 0) + +static inline int tid_geq(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference >= 0); +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src b/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src new file mode 100644 index 000000000..482630cc2 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src @@ -0,0 +1,18 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +NEEDED-$(CONFIG_CHATTR) = y +NEEDED-$(CONFIG_LSATTR) = y +NEEDED-$(CONFIG_MKE2FS) = y +NEEDED-$(CONFIG_TUNE2FS) = y + +lib-y:= + +INSERT + +lib-$(NEEDED-y) += fgetsetflags.o fgetsetversion.o pf.o iod.o mntopts.o \ + feature.o ls.o uuid.o pe.o ostype.o ps.o hashstr.o \ + parse_num.o diff --git a/e2fsprogs/old_e2fsprogs/e2p/e2p.h b/e2fsprogs/old_e2fsprogs/e2p/e2p.h new file mode 100644 index 000000000..bad2d6ac9 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/e2p.h @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +#include "libbb.h" +#include /* Needed by dirent.h on netbsd */ +#include +#include + +#include "../ext2fs/ext2_fs.h" + +#define E2P_FEATURE_COMPAT 0 +#define E2P_FEATURE_INCOMPAT 1 +#define E2P_FEATURE_RO_INCOMPAT 2 +#ifndef EXT3_FEATURE_INCOMPAT_EXTENTS +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 +#endif + +/* `options' for print_e2flags() */ + +#define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */ + +/*int fgetversion (const char * name, unsigned long * version);*/ +/*int fsetversion (const char * name, unsigned long version);*/ +int fgetsetversion(const char * name, unsigned long * get_version, unsigned long set_version); +#define fgetversion(name, version) fgetsetversion(name, version, 0) +#define fsetversion(name, version) fgetsetversion(name, NULL, version) + +/*int fgetflags (const char * name, unsigned long * flags);*/ +/*int fsetflags (const char * name, unsigned long flags);*/ +int fgetsetflags(const char * name, unsigned long * get_flags, unsigned long set_flags); +#define fgetflags(name, flags) fgetsetflags(name, flags, 0) +#define fsetflags(name, flags) fgetsetflags(name, NULL, flags) + +int getflags (int fd, unsigned long * flags); +int getversion (int fd, unsigned long * version); +int iterate_on_dir (const char * dir_name, + int (*func) (const char *, struct dirent *, void *), + void * private); +/*void list_super(struct ext2_super_block * s);*/ +void list_super2(struct ext2_super_block * s, FILE *f); +#define list_super(s) list_super2(s, stdout) +void print_fs_errors (FILE *f, unsigned short errors); +void print_flags (FILE *f, unsigned long flags, unsigned options); +void print_fs_state (FILE *f, unsigned short state); +int setflags (int fd, unsigned long flags); +int setversion (int fd, unsigned long version); + +const char *e2p_feature2string(int compat, unsigned int mask); +int e2p_string2feature(char *string, int *compat, unsigned int *mask); +int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array); + +int e2p_is_null_uuid(void *uu); +void e2p_uuid_to_str(void *uu, char *out); +const char *e2p_uuid2str(void *uu); + +const char *e2p_hash2string(int num); +int e2p_string2hash(char *string); + +const char *e2p_mntopt2string(unsigned int mask); +int e2p_string2mntopt(char *string, unsigned int *mask); +int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok); + +unsigned long parse_num_blocks(const char *arg, int log_block_size); + +char *e2p_os2string(int os_type); +int e2p_string2os(char *str); diff --git a/e2fsprogs/old_e2fsprogs/e2p/feature.c b/e2fsprogs/old_e2fsprogs/e2p/feature.c new file mode 100644 index 000000000..b45754f97 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/feature.c @@ -0,0 +1,187 @@ +/* vi: set sw=4 ts=4: */ +/* + * feature.c --- convert between features and strings + * + * Copyright (C) 1999 Theodore Ts'o + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + * + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct feature { + int compat; + unsigned int mask; + const char *string; +}; + +static const struct feature feature_list[] = { + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC, + "dir_prealloc" }, + { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL, + "has_journal" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES, + "imagic_inodes" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR, + "ext_attr" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX, + "dir_index" }, + { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE, + "resize_inode" }, + { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, + "sparse_super" }, + { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE, + "large_file" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, + "compression" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE, + "filetype" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER, + "needs_recovery" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV, + "journal_dev" }, + { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, + "extents" }, + { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG, + "meta_bg" }, + { 0, 0, 0 }, +}; + +const char *e2p_feature2string(int compat, unsigned int mask) +{ + const struct feature *f; + static char buf[20]; + char fchar; + int fnum; + + for (f = feature_list; f->string; f++) { + if ((compat == f->compat) && + (mask == f->mask)) + return f->string; + } + switch (compat) { + case E2P_FEATURE_COMPAT: + fchar = 'C'; + break; + case E2P_FEATURE_INCOMPAT: + fchar = 'I'; + break; + case E2P_FEATURE_RO_INCOMPAT: + fchar = 'R'; + break; + default: + fchar = '?'; + break; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "FEATURE_%c%d", fchar, fnum); + return buf; +} + +int e2p_string2feature(char *string, int *compat_type, unsigned int *mask) +{ + const struct feature *f; + char *eptr; + int num; + + for (f = feature_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *compat_type = f->compat; + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "FEATURE_", 8)) + return 1; + + switch (string[8]) { + case 'c': + case 'C': + *compat_type = E2P_FEATURE_COMPAT; + break; + case 'i': + case 'I': + *compat_type = E2P_FEATURE_INCOMPAT; + break; + case 'r': + case 'R': + *compat_type = E2P_FEATURE_RO_INCOMPAT; + break; + default: + return 1; + } + if (string[9] == 0) + return 1; + num = strtol(string+9, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} + +static inline char *skip_over_blanks(char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + return cp; +} + +static inline char *skip_over_word(char *cp) +{ + while (*cp && !isspace(*cp) && *cp != ',') + cp++; + return cp; +} + +/* + * Edit a feature set array as requested by the user. The ok_array, + * if set, allows the application to limit what features the user is + * allowed to set or clear using this function. + */ +int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array) +{ + char *cp, *buf, *next; + int neg; + unsigned int mask; + int compat_type; + + buf = xstrdup(str); + cp = buf; + while (cp && *cp) { + neg = 0; + cp = skip_over_blanks(cp); + next = skip_over_word(cp); + if (*next == 0) + next = 0; + else + *next = 0; + switch (*cp) { + case '-': + case '^': + neg++; + case '+': + cp++; + break; + } + if (e2p_string2feature(cp, &compat_type, &mask)) + return 1; + if (ok_array && !(ok_array[compat_type] & mask)) + return 1; + if (neg) + compat_array[compat_type] &= ~mask; + else + compat_array[compat_type] |= mask; + cp = next ? next+1 : 0; + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c b/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c new file mode 100644 index 000000000..008b79850 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * fgetflags.c - Get a file flags on an ext2 file system + * fsetflags.c - Set a file flags on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_EXT2_IOCTLS +#include +#include +#endif + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +int fgetsetflags (const char * name, unsigned long * get_flags, unsigned long set_flags) +{ +#ifdef HAVE_EXT2_IOCTLS + struct stat buf; + int fd, r, f, save_errno = 0; + + if (!stat(name, &buf) && + !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) { + goto notsupp; + } + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + if (!get_flags) { + f = (int) set_flags; + r = ioctl (fd, EXT2_IOC_SETFLAGS, &f); + } else { + r = ioctl (fd, EXT2_IOC_GETFLAGS, &f); + *get_flags = f; + } + if (r == -1) + save_errno = errno; + close (fd); + if (save_errno) + errno = save_errno; + return r; +notsupp: +#endif /* HAVE_EXT2_IOCTLS */ + errno = EOPNOTSUPP; + return -1; +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c b/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c new file mode 100644 index 000000000..8d79054d6 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * fgetversion.c - Get a file version on an ext2 file system + * fsetversion.c - Set a file version on an ext2 file system + * + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "e2p.h" + +#ifdef O_LARGEFILE +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE) +#else +#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK) +#endif + +/* + To do fsetversion: unsigned long *ptr_version must be set to NULL. + and unsigned long version must be set to a value + To do fgetversion: unsigned long *ptr_version must NOT be set to NULL + and unsigned long version is ignored. + TITO. +*/ + +int fgetsetversion (const char * name, unsigned long * get_version, unsigned long set_version) +{ +#ifdef HAVE_EXT2_IOCTLS + int fd, r, ver, save_errno = 0; + + fd = open (name, OPEN_FLAGS); + if (fd == -1) + return -1; + if (!get_version) { + ver = (int) set_version; + r = ioctl (fd, EXT2_IOC_SETVERSION, &ver); + } else { + r = ioctl (fd, EXT2_IOC_GETVERSION, &ver); + *get_version = ver; + } + if (r == -1) + save_errno = errno; + close (fd); + if (save_errno) + errno = save_errno; + return r; +#else /* ! HAVE_EXT2_IOCTLS */ + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/hashstr.c b/e2fsprogs/old_e2fsprogs/e2p/hashstr.c new file mode 100644 index 000000000..697ffadc3 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/hashstr.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * feature.c --- convert between features and strings + * + * Copyright (C) 1999 Theodore Ts'o + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + * + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct hash { + int num; + const char *string; +}; + +static const struct hash hash_list[] = { + { EXT2_HASH_LEGACY, "legacy" }, + { EXT2_HASH_HALF_MD4, "half_md4" }, + { EXT2_HASH_TEA, "tea" }, + { 0, 0 }, +}; + +const char *e2p_hash2string(int num) +{ + const struct hash *p; + static char buf[20]; + + for (p = hash_list; p->string; p++) { + if (num == p->num) + return p->string; + } + sprintf(buf, "HASHALG_%d", num); + return buf; +} + +/* + * Returns the hash algorithm, or -1 on error + */ +int e2p_string2hash(char *string) +{ + const struct hash *p; + char *eptr; + int num; + + for (p = hash_list; p->string; p++) { + if (!strcasecmp(string, p->string)) { + return p->num; + } + } + if (strncasecmp(string, "HASHALG_", 8)) + return -1; + + if (string[8] == 0) + return -1; + num = strtol(string+8, &eptr, 10); + if (num > 255 || num < 0) + return -1; + if (*eptr) + return -1; + return num; +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/iod.c b/e2fsprogs/old_e2fsprogs/e2p/iod.c new file mode 100644 index 000000000..23ab8d5b5 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/iod.c @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + * iod.c - Iterate a function on each entry of a directory + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#include "e2p.h" +#include +#include +#include + +int iterate_on_dir (const char * dir_name, + int (*func) (const char *, struct dirent *, void *), + void * private) +{ + DIR * dir; + struct dirent *de, *dep; + int max_len, len; + + max_len = PATH_MAX + sizeof(struct dirent); + de = xmalloc(max_len+1); + memset(de, 0, max_len+1); + + dir = opendir (dir_name); + if (dir == NULL) { + free(de); + return -1; + } + while ((dep = readdir (dir))) { + len = sizeof(struct dirent); + if (len < dep->d_reclen) + len = dep->d_reclen; + if (len > max_len) + len = max_len; + memcpy(de, dep, len); + (*func) (dir_name, de, private); + } + free(de); + closedir(dir); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/ls.c b/e2fsprogs/old_e2fsprogs/e2p/ls.c new file mode 100644 index 000000000..9d29db6af --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/ls.c @@ -0,0 +1,273 @@ +/* vi: set sw=4 ts=4: */ +/* + * ls.c - List the contents of an ext2fs superblock + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * Copyright (C) 1995, 1996, 1997 Theodore Ts'o + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "e2p.h" + +static void print_user(unsigned short uid, FILE *f) +{ + struct passwd *pw = getpwuid(uid); + fprintf(f, "%u (user %s)\n", uid, + (pw == NULL ? "unknown" : pw->pw_name)); +} + +static void print_group(unsigned short gid, FILE *f) +{ + struct group *gr = getgrgid(gid); + fprintf(f, "%u (group %s)\n", gid, + (gr == NULL ? "unknown" : gr->gr_name)); +} + +#define MONTH_INT (86400 * 30) +#define WEEK_INT (86400 * 7) +#define DAY_INT (86400) +#define HOUR_INT (60 * 60) +#define MINUTE_INT (60) + +static const char *interval_string(unsigned int secs) +{ + static char buf[256], tmp[80]; + int hr, min, num; + + buf[0] = 0; + + if (secs == 0) + return ""; + + if (secs >= MONTH_INT) { + num = secs / MONTH_INT; + secs -= num*MONTH_INT; + sprintf(buf, "%d month%s", num, (num>1) ? "s" : ""); + } + if (secs >= WEEK_INT) { + num = secs / WEEK_INT; + secs -= num*WEEK_INT; + sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "", + num, (num>1) ? "s" : ""); + strcat(buf, tmp); + } + if (secs >= DAY_INT) { + num = secs / DAY_INT; + secs -= num*DAY_INT; + sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "", + num, (num>1) ? "s" : ""); + strcat(buf, tmp); + } + if (secs > 0) { + hr = secs / HOUR_INT; + secs -= hr*HOUR_INT; + min = secs / MINUTE_INT; + secs -= min*MINUTE_INT; + sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "", + hr, min, secs); + strcat(buf, tmp); + } + return buf; +} + +static void print_features(struct ext2_super_block * s, FILE *f) +{ +#ifdef EXT2_DYNAMIC_REV + int i, j, printed=0; + __u32 *mask = &s->s_feature_compat, m; + + fprintf(f, "Filesystem features: "); + for (i=0; i <3; i++,mask++) { + for (j=0,m=1; j < 32; j++, m<<=1) { + if (*mask & m) { + fprintf(f, " %s", e2p_feature2string(i, m)); + printed++; + } + } + } + if (printed == 0) + fprintf(f, " (none)"); + fprintf(f, "\n"); +#endif +} + +static void print_mntopts(struct ext2_super_block * s, FILE *f) +{ +#ifdef EXT2_DYNAMIC_REV + int i, printed=0; + __u32 mask = s->s_default_mount_opts, m; + + fprintf(f, "Default mount options: "); + if (mask & EXT3_DEFM_JMODE) { + fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE)); + printed++; + } + for (i=0,m=1; i < 32; i++, m<<=1) { + if (m & EXT3_DEFM_JMODE) + continue; + if (mask & m) { + fprintf(f, " %s", e2p_mntopt2string(m)); + printed++; + } + } + if (printed == 0) + fprintf(f, " (none)"); + fprintf(f, "\n"); +#endif +} + + +#ifndef EXT2_INODE_SIZE +#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode) +#endif + +#ifndef EXT2_GOOD_OLD_REV +#define EXT2_GOOD_OLD_REV 0 +#endif + +void list_super2(struct ext2_super_block * sb, FILE *f) +{ + int inode_blocks_per_group; + char buf[80], *str; + time_t tm; + + inode_blocks_per_group = (((sb->s_inodes_per_group * + EXT2_INODE_SIZE(sb)) + + EXT2_BLOCK_SIZE(sb) - 1) / + EXT2_BLOCK_SIZE(sb)); + if (sb->s_volume_name[0]) { + memset(buf, 0, sizeof(buf)); + strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name)); + } else + strcpy(buf, ""); + fprintf(f, "Filesystem volume name: %s\n", buf); + if (sb->s_last_mounted[0]) { + memset(buf, 0, sizeof(buf)); + strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted)); + } else + strcpy(buf, ""); + fprintf(f, + "Last mounted on: %s\n" + "Filesystem UUID: %s\n" + "Filesystem magic number: 0x%04X\n" + "Filesystem revision #: %d", + buf, e2p_uuid2str(sb->s_uuid), sb->s_magic, sb->s_rev_level); + if (sb->s_rev_level == EXT2_GOOD_OLD_REV) { + fprintf(f, " (original)\n"); +#ifdef EXT2_DYNAMIC_REV + } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) { + fprintf(f, " (dynamic)\n"); +#endif + } else + fprintf(f, " (unknown)\n"); + print_features(sb, f); + print_mntopts(sb, f); + fprintf(f, "Filesystem state: "); + print_fs_state (f, sb->s_state); + fprintf(f, "\nErrors behavior: "); + print_fs_errors(f, sb->s_errors); + str = e2p_os2string(sb->s_creator_os); + fprintf(f, + "\n" + "Filesystem OS type: %s\n" + "Inode count: %u\n" + "Block count: %u\n" + "Reserved block count: %u\n" + "Free blocks: %u\n" + "Free inodes: %u\n" + "First block: %u\n" + "Block size: %u\n" + "Fragment size: %u\n", + str, sb->s_inodes_count, sb->s_blocks_count, sb->s_r_blocks_count, + sb->s_free_blocks_count, sb->s_free_inodes_count, + sb->s_first_data_block, EXT2_BLOCK_SIZE(sb), EXT2_FRAG_SIZE(sb)); + free(str); + if (sb->s_reserved_gdt_blocks) + fprintf(f, "Reserved GDT blocks: %u\n", + sb->s_reserved_gdt_blocks); + fprintf(f, + "Blocks per group: %u\n" + "Fragments per group: %u\n" + "Inodes per group: %u\n" + "Inode blocks per group: %u\n", + sb->s_blocks_per_group, sb->s_frags_per_group, + sb->s_inodes_per_group, inode_blocks_per_group); + if (sb->s_first_meta_bg) + fprintf(f, "First meta block group: %u\n", + sb->s_first_meta_bg); + if (sb->s_mkfs_time) { + tm = sb->s_mkfs_time; + fprintf(f, "Filesystem created: %s", ctime(&tm)); + } + tm = sb->s_mtime; + fprintf(f, "Last mount time: %s", + sb->s_mtime ? ctime(&tm) : "n/a\n"); + tm = sb->s_wtime; + fprintf(f, + "Last write time: %s" + "Mount count: %u\n" + "Maximum mount count: %d\n", + ctime(&tm), sb->s_mnt_count, sb->s_max_mnt_count); + tm = sb->s_lastcheck; + fprintf(f, + "Last checked: %s" + "Check interval: %u (%s)\n", + ctime(&tm), + sb->s_checkinterval, interval_string(sb->s_checkinterval)); + if (sb->s_checkinterval) + { + time_t next; + + next = sb->s_lastcheck + sb->s_checkinterval; + fprintf(f, "Next check after: %s", ctime(&next)); + } + fprintf(f, "Reserved blocks uid: "); + print_user(sb->s_def_resuid, f); + fprintf(f, "Reserved blocks gid: "); + print_group(sb->s_def_resgid, f); + if (sb->s_rev_level >= EXT2_DYNAMIC_REV) { + fprintf(f, + "First inode: %d\n" + "Inode size: %d\n", + sb->s_first_ino, sb->s_inode_size); + } + if (!e2p_is_null_uuid(sb->s_journal_uuid)) + fprintf(f, "Journal UUID: %s\n", + e2p_uuid2str(sb->s_journal_uuid)); + if (sb->s_journal_inum) + fprintf(f, "Journal inode: %u\n", + sb->s_journal_inum); + if (sb->s_journal_dev) + fprintf(f, "Journal device: 0x%04x\n", + sb->s_journal_dev); + if (sb->s_last_orphan) + fprintf(f, "First orphan inode: %u\n", + sb->s_last_orphan); + if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || + sb->s_def_hash_version) + fprintf(f, "Default directory hash: %s\n", + e2p_hash2string(sb->s_def_hash_version)); + if (!e2p_is_null_uuid(sb->s_hash_seed)) + fprintf(f, "Directory Hash Seed: %s\n", + e2p_uuid2str(sb->s_hash_seed)); + if (sb->s_jnl_backup_type) { + fprintf(f, "Journal backup: "); + if (sb->s_jnl_backup_type == 1) + fprintf(f, "inode blocks\n"); + else + fprintf(f, "type %u\n", sb->s_jnl_backup_type); + } +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/mntopts.c b/e2fsprogs/old_e2fsprogs/e2p/mntopts.c new file mode 100644 index 000000000..17c26c480 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/mntopts.c @@ -0,0 +1,134 @@ +/* vi: set sw=4 ts=4: */ +/* + * mountopts.c --- convert between default mount options and strings + * + * Copyright (C) 2002 Theodore Ts'o + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + * + */ + +#include +#include +#include +#include +#include + +#include "e2p.h" + +struct mntopt { + unsigned int mask; + const char *string; +}; + +static const struct mntopt mntopt_list[] = { + { EXT2_DEFM_DEBUG, "debug" }, + { EXT2_DEFM_BSDGROUPS, "bsdgroups" }, + { EXT2_DEFM_XATTR_USER, "user_xattr" }, + { EXT2_DEFM_ACL, "acl" }, + { EXT2_DEFM_UID16, "uid16" }, + { EXT3_DEFM_JMODE_DATA, "journal_data" }, + { EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" }, + { EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" }, + { 0, 0 }, +}; + +const char *e2p_mntopt2string(unsigned int mask) +{ + const struct mntopt *f; + static char buf[20]; + int fnum; + + for (f = mntopt_list; f->string; f++) { + if (mask == f->mask) + return f->string; + } + for (fnum = 0; mask >>= 1; fnum++); + sprintf(buf, "MNTOPT_%d", fnum); + return buf; +} + +int e2p_string2mntopt(char *string, unsigned int *mask) +{ + const struct mntopt *f; + char *eptr; + int num; + + for (f = mntopt_list; f->string; f++) { + if (!strcasecmp(string, f->string)) { + *mask = f->mask; + return 0; + } + } + if (strncasecmp(string, "MNTOPT_", 8)) + return 1; + + if (string[8] == 0) + return 1; + num = strtol(string+8, &eptr, 10); + if (num > 32 || num < 0) + return 1; + if (*eptr) + return 1; + *mask = 1 << num; + return 0; +} + +static char *skip_over_blanks(char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + while (*cp && !isspace(*cp) && *cp != ',') + cp++; + return cp; +} + +/* + * Edit a mntopt set array as requested by the user. The ok + * parameter, if non-zero, allows the application to limit what + * mntopts the user is allowed to set or clear using this function. + */ +int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok) +{ + char *cp, *buf, *next; + int neg; + unsigned int mask; + + buf = xstrdup(str); + cp = buf; + while (cp && *cp) { + neg = 0; + cp = skip_over_blanks(cp); + next = skip_over_word(cp); + if (*next == 0) + next = 0; + else + *next = 0; + switch (*cp) { + case '-': + case '^': + neg++; + case '+': + cp++; + break; + } + if (e2p_string2mntopt(cp, &mask)) + return 1; + if (ok && !(ok & mask)) + return 1; + if (mask & EXT3_DEFM_JMODE) + *mntopts &= ~EXT3_DEFM_JMODE; + if (neg) + *mntopts &= ~mask; + else + *mntopts |= mask; + cp = next ? next+1 : 0; + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/ostype.c b/e2fsprogs/old_e2fsprogs/e2p/ostype.c new file mode 100644 index 000000000..6a2f178f3 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/ostype.c @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * getostype.c - Get the Filesystem OS type + * + * Copyright (C) 2004,2005 Theodore Ts'o + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +#include "e2p.h" +#include +#include + +static const char *const os_tab[] = + { "Linux", + "Hurd", + "Masix", + "FreeBSD", + "Lites", + 0 }; + +/* + * Convert an os_type to a string + */ +char *e2p_os2string(int os_type) +{ + const char *os; + char *ret; + + if (os_type <= EXT2_OS_LITES) + os = os_tab[os_type]; + else + os = "(unknown os)"; + + ret = xstrdup(os); + return ret; +} + +/* + * Convert an os_type to a string + */ +int e2p_string2os(char *str) +{ + const char *const *cpp; + int i = 0; + + for (cpp = os_tab; *cpp; cpp++, i++) { + if (!strcasecmp(str, *cpp)) + return i; + } + return -1; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + char *s; + int i, os; + + for (i=0; i <= EXT2_OS_LITES; i++) { + s = e2p_os2string(i); + os = e2p_string2os(s); + printf("%d: %s (%d)\n", i, s, os); + if (i != os) { + fprintf(stderr, "Failure!\n"); + exit(1); + } + } + exit(0); +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/e2p/parse_num.c b/e2fsprogs/old_e2fsprogs/e2p/parse_num.c new file mode 100644 index 000000000..6db076f9c --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/parse_num.c @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * parse_num.c - Parse the number of blocks + * + * Copyright (C) 2004,2005 Theodore Ts'o + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +#include "e2p.h" + +#include + +unsigned long parse_num_blocks(const char *arg, int log_block_size) +{ + char *p; + unsigned long long num; + + num = strtoull(arg, &p, 0); + + if (p[0] && p[1]) + return 0; + + switch (*p) { /* Using fall-through logic */ + case 'T': case 't': + num <<= 10; + case 'G': case 'g': + num <<= 10; + case 'M': case 'm': + num <<= 10; + case 'K': case 'k': + num >>= log_block_size; + break; + case 's': + num >>= 1; + break; + case '\0': + break; + default: + return 0; + } + return num; +} + +#ifdef DEBUG +#include +#include + +main(int argc, char **argv) +{ + unsigned long num; + int log_block_size = 0; + + if (argc != 2) { + fprintf(stderr, "Usage: %s arg\n", argv[0]); + exit(1); + } + + num = parse_num_blocks(argv[1], log_block_size); + + printf("Parsed number: %lu\n", num); + exit(0); +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/e2p/pe.c b/e2fsprogs/old_e2fsprogs/e2p/pe.c new file mode 100644 index 000000000..835274b54 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/pe.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * pe.c - Print a second extended filesystem errors behavior + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* + * History: + * 94/01/09 - Creation + */ + +#include + +#include "e2p.h" + +void print_fs_errors(FILE *f, unsigned short errors) +{ + char *disp = NULL; + switch (errors) { + case EXT2_ERRORS_CONTINUE: disp = "Continue"; break; + case EXT2_ERRORS_RO: disp = "Remount read-only"; break; + case EXT2_ERRORS_PANIC: disp = "Panic"; break; + default: disp = "Unknown (continue)"; + } + fprintf(f, disp); +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/pf.c b/e2fsprogs/old_e2fsprogs/e2p/pf.c new file mode 100644 index 000000000..02cbec7e0 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/pf.c @@ -0,0 +1,74 @@ +/* vi: set sw=4 ts=4: */ +/* + * pf.c - Print file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + */ + +#include + +#include "e2p.h" + +struct flags_name { + unsigned long flag; + const char *short_name; + const char *long_name; +}; + +static const struct flags_name flags_array[] = { + { EXT2_SECRM_FL, "s", "Secure_Deletion" }, + { EXT2_UNRM_FL, "u" , "Undelete" }, + { EXT2_SYNC_FL, "S", "Synchronous_Updates" }, + { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" }, + { EXT2_IMMUTABLE_FL, "i", "Immutable" }, + { EXT2_APPEND_FL, "a", "Append_Only" }, + { EXT2_NODUMP_FL, "d", "No_Dump" }, + { EXT2_NOATIME_FL, "A", "No_Atime" }, + { EXT2_COMPR_FL, "c", "Compression_Requested" }, +#ifdef ENABLE_COMPRESSION + { EXT2_COMPRBLK_FL, "B", "Compressed_File" }, + { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" }, + { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" }, + { EXT2_ECOMPR_FL, "E", "Compression_Error" }, +#endif + { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" }, + { EXT2_INDEX_FL, "I", "Indexed_direcctory" }, + { EXT2_NOTAIL_FL, "t", "No_Tailmerging" }, + { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, + { 0, NULL, NULL } +}; + +void print_flags (FILE *f, unsigned long flags, unsigned options) +{ + int long_opt = (options & PFOPT_LONG); + const struct flags_name *fp; + int first = 1; + + for (fp = flags_array; fp->flag != 0; fp++) { + if (flags & fp->flag) { + if (long_opt) { + if (first) + first = 0; + else + fputs(", ", f); + fputs(fp->long_name, f); + } else + fputs(fp->short_name, f); + } else { + if (!long_opt) + fputs("-", f); + } + } + if (long_opt && first) + fputs("---", f); +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/ps.c b/e2fsprogs/old_e2fsprogs/e2p/ps.c new file mode 100644 index 000000000..a6b4099db --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/ps.c @@ -0,0 +1,27 @@ +/* vi: set sw=4 ts=4: */ +/* + * ps.c - Print filesystem state + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* + * History: + * 93/12/22 - Creation + */ + +#include + +#include "e2p.h" + +void print_fs_state(FILE *f, unsigned short state) +{ + fprintf(f, (state & EXT2_VALID_FS ? " clean" : " not clean")); + if (state & EXT2_ERROR_FS) + fprintf(f, " with errors"); +} diff --git a/e2fsprogs/old_e2fsprogs/e2p/uuid.c b/e2fsprogs/old_e2fsprogs/e2p/uuid.c new file mode 100644 index 000000000..474d64a5a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/e2p/uuid.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * uuid.c -- utility routines for manipulating UUID's. + */ + +#include +#include +#include "../ext2fs/ext2_types.h" + +#include "e2p.h" + +struct uuid { + __u32 time_low; + __u16 time_mid; + __u16 time_hi_and_version; + __u16 clock_seq; + __u8 node[6]; +}; + +/* Returns 1 if the uuid is the NULL uuid */ +int e2p_is_null_uuid(void *uu) +{ + __u8 *cp; + int i; + + for (i=0, cp = uu; i < 16; i++) + if (*cp) + return 0; + return 1; +} + +static void e2p_unpack_uuid(void *in, struct uuid *uu) +{ + __u8 *ptr = in; + __u32 tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} + +void e2p_uuid_to_str(void *uu, char *out) +{ + struct uuid uuid; + + e2p_unpack_uuid(uu, &uuid); + sprintf(out, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +const char *e2p_uuid2str(void *uu) +{ + static char buf[80]; + if (e2p_is_null_uuid(uu)) + return ""; + e2p_uuid_to_str(uu, buf); + return buf; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src b/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src new file mode 100644 index 000000000..12adc6e46 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src @@ -0,0 +1,26 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +NEEDED-$(CONFIG_E2FSCK) = y +NEEDED-$(CONFIG_FSCK) = y +NEEDED-$(CONFIG_MKE2FS) = y +NEEDED-$(CONFIG_TUNE2FS) = y + +lib-y:= + +INSERT + +lib-$(NEEDED-y) += gen_bitmap.o bitops.o ismounted.o mkjournal.o unix_io.o \ + rw_bitmaps.o initialize.o bitmaps.o block.o \ + ind_block.o inode.o freefs.o alloc_stats.o closefs.o \ + openfs.o io_manager.o finddev.o read_bb.o alloc.o badblocks.o \ + getsize.o getsectsize.o alloc_tables.o read_bb_file.o mkdir.o \ + bb_inode.o newdir.o alloc_sb.o lookup.o dirblock.o expanddir.o \ + dir_iterate.o link.o res_gdt.o icount.o get_pathname.o dblist.o \ + dirhash.o version.o flushb.o unlink.o check_desc.o valid_blk.o \ + ext_attr.o bmap.o dblist_dir.o ext2fs_inline.o swapfs.o + +CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c new file mode 100644 index 000000000..cbb63e15a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c @@ -0,0 +1,173 @@ +/* vi: set sw=4 ts=4: */ +/* + * alloc.c --- allocate new inodes, blocks for ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Right now, just search forward from the parent directory's block + * group to find the next free inode. + * + * Should have a special policy for directories. + */ +errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, + int mode EXT2FS_ATTR((unused)), + ext2fs_inode_bitmap map, ext2_ino_t *ret) +{ + ext2_ino_t dir_group = 0; + ext2_ino_t i; + ext2_ino_t start_inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->inode_map; + if (!map) + return EXT2_ET_NO_INODE_BITMAP; + + if (dir > 0) + dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); + + start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; + if (start_inode < EXT2_FIRST_INODE(fs->super)) + start_inode = EXT2_FIRST_INODE(fs->super); + i = start_inode; + + do { + if (!ext2fs_fast_test_inode_bitmap(map, i)) + break; + i++; + if (i > fs->super->s_inodes_count) + i = EXT2_FIRST_INODE(fs->super); + } while (i != start_inode); + + if (ext2fs_test_inode_bitmap(map, i)) + return EXT2_ET_INODE_ALLOC_FAIL; + *ret = i; + return 0; +} + +/* + * Stupid algorithm --- we now just search forward starting from the + * goal. Should put in a smarter one someday.... + */ +errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, + ext2fs_block_bitmap map, blk_t *ret) +{ + blk_t i; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!goal || (goal >= fs->super->s_blocks_count)) + goal = fs->super->s_first_data_block; + i = goal; + do { + if (!ext2fs_fast_test_block_bitmap(map, i)) { + *ret = i; + return 0; + } + i++; + if (i >= fs->super->s_blocks_count) + i = fs->super->s_first_data_block; + } while (i != goal); + return EXT2_ET_BLOCK_ALLOC_FAIL; +} + +/* + * This function zeros out the allocated block, and updates all of the + * appropriate filesystem records. + */ +errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, + char *block_buf, blk_t *ret) +{ + errcode_t retval; + blk_t block; + char *buf = NULL; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + memset(block_buf, 0, fs->blocksize); + + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + goto fail; + } + + retval = ext2fs_new_block(fs, goal, 0, &block); + if (retval) + goto fail; + + retval = io_channel_write_blk(fs->io, block, 1, block_buf); + if (retval) + goto fail; + + ext2fs_block_alloc_stats(fs, block, +1); + *ret = block; + return 0; + +fail: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, + int num, ext2fs_block_bitmap map, blk_t *ret) +{ + blk_t b = start; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!map) + map = fs->block_map; + if (!map) + return EXT2_ET_NO_BLOCK_BITMAP; + if (!b) + b = fs->super->s_first_data_block; + if (!finish) + finish = start; + if (!num) + num = 1; + do { + if (b+num-1 > fs->super->s_blocks_count) + b = fs->super->s_first_data_block; + if (ext2fs_fast_test_block_bitmap_range(map, b, num)) { + *ret = b; + return 0; + } + b++; + } while (b != finish); + return EXT2_ET_BLOCK_ALLOC_FAIL; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c new file mode 100644 index 000000000..a7437c96f --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c @@ -0,0 +1,58 @@ +/* vi: set sw=4 ts=4: */ +/* + * alloc_sb.c --- Allocate the superblock and block group descriptors for a + * newly initialized filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +int ext2fs_reserve_super_and_bgd(ext2_filsys fs, + dgrp_t group, + ext2fs_block_bitmap bmap) +{ + blk_t super_blk, old_desc_blk, new_desc_blk; + int j, old_desc_blocks, num_blocks; + + num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk, + &old_desc_blk, &new_desc_blk, 0); + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + if (super_blk || (group == 0)) + ext2fs_mark_block_bitmap(bmap, super_blk); + + if (old_desc_blk) { + for (j=0; j < old_desc_blocks; j++) + ext2fs_mark_block_bitmap(bmap, old_desc_blk + j); + } + if (new_desc_blk) + ext2fs_mark_block_bitmap(bmap, new_desc_blk); + + return num_blocks; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c new file mode 100644 index 000000000..f3ab06a23 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * alloc_stats.c --- Update allocation statistics for ext2fs + * + * Copyright (C) 2001 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, + int inuse, int isdir) +{ + int group = ext2fs_group_of_ino(fs, ino); + + if (inuse > 0) + ext2fs_mark_inode_bitmap(fs->inode_map, ino); + else + ext2fs_unmark_inode_bitmap(fs->inode_map, ino); + fs->group_desc[group].bg_free_inodes_count -= inuse; + if (isdir) + fs->group_desc[group].bg_used_dirs_count += inuse; + fs->super->s_free_inodes_count -= inuse; + ext2fs_mark_super_dirty(fs); + ext2fs_mark_ib_dirty(fs); +} + +void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse) +{ + ext2fs_inode_alloc_stats2(fs, ino, inuse, 0); +} + +void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse) +{ + int group = ext2fs_group_of_blk(fs, blk); + + if (inuse > 0) + ext2fs_mark_block_bitmap(fs->block_map, blk); + else + ext2fs_unmark_block_bitmap(fs->block_map, blk); + fs->group_desc[group].bg_free_blocks_count -= inuse; + fs->super->s_free_blocks_count -= inuse; + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c new file mode 100644 index 000000000..7c60e2bf5 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c @@ -0,0 +1,114 @@ +/* vi: set sw=4 ts=4: */ +/* + * alloc_tables.c --- Allocate tables for a newly initialized + * filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, + ext2fs_block_bitmap bmap) +{ + errcode_t retval; + blk_t group_blk, start_blk, last_blk, new_blk, blk; + int j; + + group_blk = fs->super->s_first_data_block + + (group * fs->super->s_blocks_per_group); + + last_blk = group_blk + fs->super->s_blocks_per_group; + if (last_blk >= fs->super->s_blocks_count) + last_blk = fs->super->s_blocks_count - 1; + + if (!bmap) + bmap = fs->block_map; + + /* + * Allocate the block and inode bitmaps, if necessary + */ + if (fs->stride) { + start_blk = group_blk + fs->inode_blocks_per_group; + start_blk += ((fs->stride * group) % + (last_blk - start_blk)); + if (start_blk > last_blk) + start_blk = group_blk; + } else + start_blk = group_blk; + + if (!fs->group_desc[group].bg_block_bitmap) { + retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, + 1, bmap, &new_blk); + if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) + retval = ext2fs_get_free_blocks(fs, group_blk, + last_blk, 1, bmap, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap(bmap, new_blk); + fs->group_desc[group].bg_block_bitmap = new_blk; + } + + if (!fs->group_desc[group].bg_inode_bitmap) { + retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, + 1, bmap, &new_blk); + if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) + retval = ext2fs_get_free_blocks(fs, group_blk, + last_blk, 1, bmap, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap(bmap, new_blk); + fs->group_desc[group].bg_inode_bitmap = new_blk; + } + + /* + * Allocate the inode table + */ + if (!fs->group_desc[group].bg_inode_table) { + retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, + fs->inode_blocks_per_group, + bmap, &new_blk); + if (retval) + return retval; + for (j=0, blk = new_blk; + j < fs->inode_blocks_per_group; + j++, blk++) + ext2fs_mark_block_bitmap(bmap, blk); + fs->group_desc[group].bg_inode_table = new_blk; + } + + return 0; +} + +errcode_t ext2fs_allocate_tables(ext2_filsys fs) +{ + errcode_t retval; + dgrp_t i; + + for (i = 0; i < fs->group_desc_count; i++) { + retval = ext2fs_allocate_group_table(fs, i, fs->block_map); + if (retval) + return retval; + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c b/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c new file mode 100644 index 000000000..6e5cc10b8 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c @@ -0,0 +1,328 @@ +/* vi: set sw=4 ts=4: */ +/* + * badblocks.c --- routines to manipulate the bad block structure + * + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * Helper function for making a badblocks list + */ +static errcode_t make_u32_list(int size, int num, __u32 *list, + ext2_u32_list *ret) +{ + ext2_u32_list bb; + errcode_t retval; + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb); + if (retval) + return retval; + memset(bb, 0, sizeof(struct ext2_struct_u32_list)); + bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; + bb->size = size ? size : 10; + bb->num = num; + retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list); + if (!bb->list) { + ext2fs_free_mem(&bb); + return retval; + } + if (list) + memcpy(bb->list, list, bb->size * sizeof(blk_t)); + else + memset(bb->list, 0, bb->size * sizeof(blk_t)); + *ret = bb; + return 0; +} + + +/* + * This procedure creates an empty u32 list. + */ +errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size) +{ + return make_u32_list(size, 0, 0, ret); +} + +/* + * This procedure creates an empty badblocks list. + */ +errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) +{ + return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret); +} + + +/* + * This procedure copies a badblocks list + */ +errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest) +{ + errcode_t retval; + + retval = make_u32_list(src->size, src->num, src->list, dest); + if (retval) + return retval; + (*dest)->badblocks_flags = src->badblocks_flags; + return 0; +} + +errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, + ext2_badblocks_list *dest) +{ + return ext2fs_u32_copy((ext2_u32_list) src, + (ext2_u32_list *) dest); +} + +/* + * This procedure frees a badblocks list. + * + * (note: moved to closefs.c) + */ + + +/* + * This procedure adds a block to a badblocks list. + */ +errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) +{ + errcode_t retval; + int i, j; + unsigned long old_size; + + EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + if (bb->num >= bb->size) { + old_size = bb->size * sizeof(__u32); + bb->size += 100; + retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32), + &bb->list); + if (retval) { + bb->size -= 100; + return retval; + } + } + + /* + * Add special case code for appending to the end of the list + */ + i = bb->num-1; + if ((bb->num != 0) && (bb->list[i] == blk)) + return 0; + if ((bb->num == 0) || (bb->list[i] < blk)) { + bb->list[bb->num++] = blk; + return 0; + } + + j = bb->num; + for (i=0; i < bb->num; i++) { + if (bb->list[i] == blk) + return 0; + if (bb->list[i] > blk) { + j = i; + break; + } + } + for (i=bb->num; i > j; i--) + bb->list[i] = bb->list[i-1]; + bb->list[j] = blk; + bb->num++; + return 0; +} + +errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) +{ + return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk); +} + +/* + * This procedure finds a particular block is on a badblocks + * list. + */ +int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk) +{ + int low, high, mid; + + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return -1; + + if (bb->num == 0) + return -1; + + low = 0; + high = bb->num-1; + if (blk == bb->list[low]) + return low; + if (blk == bb->list[high]) + return high; + + while (low < high) { + mid = (low+high)/2; + if (mid == low || mid == high) + break; + if (blk == bb->list[mid]) + return mid; + if (blk < bb->list[mid]) + high = mid; + else + low = mid; + } + return -1; +} + +/* + * This procedure tests to see if a particular block is on a badblocks + * list. + */ +int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk) +{ + if (ext2fs_u32_list_find(bb, blk) < 0) + return 0; + else + return 1; +} + +int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) +{ + return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk); +} + + +/* + * Remove a block from the badblock list + */ +int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk) +{ + int remloc, i; + + if (bb->num == 0) + return -1; + + remloc = ext2fs_u32_list_find(bb, blk); + if (remloc < 0) + return -1; + + for (i = remloc; i < bb->num - 1; i++) + bb->list[i] = bb->list[i+1]; + bb->num--; + return 0; +} + +void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk) +{ + ext2fs_u32_list_del(bb, blk); +} + +errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, + ext2_u32_iterate *ret) +{ + ext2_u32_iterate iter; + errcode_t retval; + + EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter); + if (retval) + return retval; + + iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE; + iter->bb = bb; + iter->ptr = 0; + *ret = iter; + return 0; +} + +errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret) +{ + return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb, + (ext2_u32_iterate *) ret); +} + + +int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk) +{ + ext2_u32_list bb; + + if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) + return 0; + + bb = iter->bb; + + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return 0; + + if (iter->ptr < bb->num) { + *blk = bb->list[iter->ptr++]; + return 1; + } + *blk = 0; + return 0; +} + +int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_u32_list_iterate((ext2_u32_iterate) iter, + (__u32 *) blk); +} + + +void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter) +{ + if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) + return; + + iter->bb = 0; + ext2fs_free_mem(&iter); +} + +void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) +{ + ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter); +} + + +int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2) +{ + EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST); + EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST); + + if (bb1->num != bb2->num) + return 0; + + if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0) + return 0; + return 1; +} + +int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2) +{ + return ext2fs_u32_list_equal((ext2_u32_list) bb1, + (ext2_u32_list) bb2); +} + +int ext2fs_u32_list_count(ext2_u32_list bb) +{ + return bb->num; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c b/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c new file mode 100644 index 000000000..419ac7785 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c @@ -0,0 +1,64 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_compat.c --- compatibility badblocks routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t badblocks_list_create(badblocks_list *ret, int size) +{ + return ext2fs_badblocks_list_create(ret, size); +} + +void badblocks_list_free(badblocks_list bb) +{ + ext2fs_badblocks_list_free(bb); +} + +errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_add(bb, blk); +} + +int badblocks_list_test(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_test(bb, blk); +} + +errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret) +{ + return ext2fs_badblocks_list_iterate_begin(bb, ret); +} + +int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_badblocks_list_iterate(iter, blk); +} + +void badblocks_list_iterate_end(badblocks_iterate iter) +{ + ext2fs_badblocks_list_iterate_end(iter); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c b/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c new file mode 100644 index 000000000..a96789618 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c @@ -0,0 +1,262 @@ +/* vi: set sw=4 ts=4: */ +/* + * bb_inode.c --- routines to update the bad block inode. + * + * WARNING: This routine modifies a lot of state in the filesystem; if + * this routine returns an error, the bad block inode may be in an + * inconsistent state. + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct set_badblock_record { + ext2_badblocks_iterate bb_iter; + int bad_block_count; + blk_t *ind_blocks; + int max_ind_blocks; + int ind_blocks_size; + int ind_blocks_ptr; + char *block_buf; + errcode_t err; +}; + +static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, int ref_offset, + void *priv_data); +static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block, int ref_offset, + void *priv_data); + +/* + * Given a bad blocks bitmap, update the bad blocks inode to reflect + * the map. + */ +errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) +{ + errcode_t retval; + struct set_badblock_record rec; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!fs->block_map) + return EXT2_ET_NO_BLOCK_BITMAP; + + rec.bad_block_count = 0; + rec.ind_blocks_size = rec.ind_blocks_ptr = 0; + rec.max_ind_blocks = 10; + retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t), + &rec.ind_blocks); + if (retval) + return retval; + memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); + retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); + if (retval) + goto cleanup; + memset(rec.block_buf, 0, fs->blocksize); + rec.err = 0; + + /* + * First clear the old bad blocks (while saving the indirect blocks) + */ + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_DEPTH_TRAVERSE, 0, + clear_bad_block_proc, &rec); + if (retval) + goto cleanup; + if (rec.err) { + retval = rec.err; + goto cleanup; + } + + /* + * Now set the bad blocks! + * + * First, mark the bad blocks as used. This prevents a bad + * block from being used as an indirecto block for the bad + * block inode (!). + */ + if (bb_list) { + retval = ext2fs_badblocks_list_iterate_begin(bb_list, + &rec.bb_iter); + if (retval) + goto cleanup; + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_APPEND, 0, + set_bad_block_proc, &rec); + ext2fs_badblocks_list_iterate_end(rec.bb_iter); + if (retval) + goto cleanup; + if (rec.err) { + retval = rec.err; + goto cleanup; + } + } + + /* + * Update the bad block inode's mod time and block count + * field. + */ + retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + goto cleanup; + + inode.i_atime = inode.i_mtime = time(NULL); + if (!inode.i_ctime) + inode.i_ctime = time(NULL); + inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512); + inode.i_size = rec.bad_block_count * fs->blocksize; + + retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + goto cleanup; + +cleanup: + ext2fs_free_mem(&rec.ind_blocks); + ext2fs_free_mem(&rec.block_buf); + return retval; +} + +/* + * Helper function for update_bb_inode() + * + * Clear the bad blocks in the bad block inode, while saving the + * indirect blocks. + */ +#ifdef __TURBOC__ +# pragma argsused +#endif +static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct set_badblock_record *rec = (struct set_badblock_record *) + priv_data; + errcode_t retval; + unsigned long old_size; + + if (!*block_nr) + return 0; + + /* + * If the block number is outrageous, clear it and ignore it. + */ + if (*block_nr >= fs->super->s_blocks_count || + *block_nr < fs->super->s_first_data_block) { + *block_nr = 0; + return BLOCK_CHANGED; + } + + if (blockcnt < 0) { + if (rec->ind_blocks_size >= rec->max_ind_blocks) { + old_size = rec->max_ind_blocks * sizeof(blk_t); + rec->max_ind_blocks += 10; + retval = ext2fs_resize_mem(old_size, + rec->max_ind_blocks * sizeof(blk_t), + &rec->ind_blocks); + if (retval) { + rec->max_ind_blocks -= 10; + rec->err = retval; + return BLOCK_ABORT; + } + } + rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; + } + + /* + * Mark the block as unused, and update accounting information + */ + ext2fs_block_alloc_stats(fs, *block_nr, -1); + + *block_nr = 0; + return BLOCK_CHANGED; +} + + +/* + * Helper function for update_bb_inode() + * + * Set the block list in the bad block inode, using the supplied bitmap. + */ +#ifdef __TURBOC__ + #pragma argsused +#endif +static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct set_badblock_record *rec = (struct set_badblock_record *) + priv_data; + errcode_t retval; + blk_t blk; + + if (blockcnt >= 0) { + /* + * Get the next bad block. + */ + if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) + return BLOCK_ABORT; + rec->bad_block_count++; + } else { + /* + * An indirect block; fetch a block from the + * previously used indirect block list. The block + * most be not marked as used; if so, get another one. + * If we run out of reserved indirect blocks, allocate + * a new one. + */ + retry: + if (rec->ind_blocks_ptr < rec->ind_blocks_size) { + blk = rec->ind_blocks[rec->ind_blocks_ptr++]; + if (ext2fs_test_block_bitmap(fs->block_map, blk)) + goto retry; + } else { + retval = ext2fs_new_block(fs, 0, 0, &blk); + if (retval) { + rec->err = retval; + return BLOCK_ABORT; + } + } + retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf); + if (retval) { + rec->err = retval; + return BLOCK_ABORT; + } + } + + /* + * Update block counts + */ + ext2fs_block_alloc_stats(fs, blk, +1); + + *block_nr = blk; + return BLOCK_CHANGED; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c b/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c new file mode 100644 index 000000000..637ed27af --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c @@ -0,0 +1,211 @@ +/* vi: set sw=4 ts=4: */ +/* + * bitmaps.c --- routines to read, write, and manipulate the inode and + * block bitmaps. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end, + const char *descr, char *init_map, + ext2fs_generic_bitmap *ret) +{ + ext2fs_generic_bitmap bitmap; + errcode_t retval; + size_t size; + + retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), + &bitmap); + if (retval) + return retval; + + bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; + bitmap->fs = NULL; + bitmap->start = start; + bitmap->end = end; + bitmap->real_end = real_end; + bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; + if (descr) { + retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); + if (retval) { + ext2fs_free_mem(&bitmap); + return retval; + } + strcpy(bitmap->description, descr); + } else + bitmap->description = 0; + + size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); + retval = ext2fs_get_mem(size, &bitmap->bitmap); + if (retval) { + ext2fs_free_mem(&bitmap->description); + ext2fs_free_mem(&bitmap); + return retval; + } + + if (init_map) + memcpy(bitmap->bitmap, init_map, size); + else + memset(bitmap->bitmap, 0, size); + *ret = bitmap; + return 0; +} + +errcode_t ext2fs_allocate_generic_bitmap(__u32 start, + __u32 end, + __u32 real_end, + const char *descr, + ext2fs_generic_bitmap *ret) +{ + return make_bitmap(start, end, real_end, descr, 0, ret); +} + +errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest) +{ + errcode_t retval; + ext2fs_generic_bitmap new_map; + + retval = make_bitmap(src->start, src->end, src->real_end, + src->description, src->bitmap, &new_map); + if (retval) + return retval; + new_map->magic = src->magic; + new_map->fs = src->fs; + new_map->base_error_code = src->base_error_code; + *dest = new_map; + return 0; +} + +void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map) +{ + __u32 i, j; + + for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++) + ext2fs_set_bit(j, map->bitmap); +} + +errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_inode_bitmap *ret) +{ + ext2fs_inode_bitmap bitmap; + errcode_t retval; + __u32 start, end, real_end; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + start = 1; + end = fs->super->s_inodes_count; + real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count); + + retval = ext2fs_allocate_generic_bitmap(start, end, real_end, + descr, &bitmap); + if (retval) + return retval; + + bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP; + bitmap->fs = fs; + bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; + + *ret = bitmap; + return 0; +} + +errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_block_bitmap *ret) +{ + ext2fs_block_bitmap bitmap; + errcode_t retval; + __u32 start, end, real_end; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + start = fs->super->s_first_data_block; + end = fs->super->s_blocks_count-1; + real_end = (EXT2_BLOCKS_PER_GROUP(fs->super) + * fs->group_desc_count)-1 + start; + + retval = ext2fs_allocate_generic_bitmap(start, end, real_end, + descr, &bitmap); + if (retval) + return retval; + + bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP; + bitmap->fs = fs; + bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; + + *ret = bitmap; + return 0; +} + +errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, + ext2_ino_t end, ext2_ino_t *oend) +{ + EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP); + + if (end > bitmap->real_end) + return EXT2_ET_FUDGE_INODE_BITMAP_END; + if (oend) + *oend = bitmap->end; + bitmap->end = end; + return 0; +} + +errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, + blk_t end, blk_t *oend) +{ + EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP); + + if (end > bitmap->real_end) + return EXT2_ET_FUDGE_BLOCK_BITMAP_END; + if (oend) + *oend = bitmap->end; + bitmap->end = end; + return 0; +} + +void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap) +{ + if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP)) + return; + + memset(bitmap->bitmap, 0, + (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); +} + +void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) +{ + if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP)) + return; + + memset(bitmap->bitmap, 0, + (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c b/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c new file mode 100644 index 000000000..3d08394d8 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c @@ -0,0 +1,90 @@ +/* vi: set sw=4 ts=4: */ +/* + * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined + * routines. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef _EXT2_HAVE_ASM_BITOPS_ + +/* + * For the benefit of those who are trying to port Linux to another + * architecture, here are some C-language equivalents. You should + * recode these in the native assmebly language, if at all possible. + * + * C language equivalents written by Theodore Ts'o, 9/26/92. + * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian + * systems, as well as non-32 bit systems. + */ + +int ext2fs_set_bit(unsigned int nr,void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR |= mask; + return retval; +} + +int ext2fs_clear_bit(unsigned int nr, void * addr) +{ + int mask, retval; + unsigned char *ADDR = (unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + retval = mask & *ADDR; + *ADDR &= ~mask; + return retval; +} + +int ext2fs_test_bit(unsigned int nr, const void * addr) +{ + int mask; + const unsigned char *ADDR = (const unsigned char *) addr; + + ADDR += nr >> 3; + mask = 1 << (nr & 0x07); + return (mask & *ADDR); +} + +#endif /* !_EXT2_HAVE_ASM_BITOPS_ */ + +void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, + const char *description) +{ +#ifndef OMIT_COM_ERR + if (description) + bb_error_msg("#%lu for %s", arg, description); + else + bb_error_msg("#%lu", arg); +#endif +} + +void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, + int code, unsigned long arg) +{ +#ifndef OMIT_COM_ERR + if (bitmap->description) + bb_error_msg("#%lu for %s", arg, bitmap->description); + else + bb_error_msg("#%lu", arg); +#endif +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h b/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h new file mode 100644 index 000000000..7271a497b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h @@ -0,0 +1,105 @@ +/* vi: set sw=4 ts=4: */ +/* + * bitops.h --- Bitmap frobbing code. The byte swapping routines are + * also included here. + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * i386 bitops operations taken from , Copyright 1992, + * Linus Torvalds. + */ +#include + +extern int ext2fs_set_bit(unsigned int nr,void * addr); +extern int ext2fs_clear_bit(unsigned int nr, void * addr); +extern int ext2fs_test_bit(unsigned int nr, const void * addr); +extern __u16 ext2fs_swab16(__u16 val); +extern __u32 ext2fs_swab32(__u32 val); + +#ifdef WORDS_BIGENDIAN +#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x)) +#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x)) +#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x)) +#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x)) +#define ext2fs_cpu_to_be32(x) ((__u32)(x)) +#define ext2fs_be32_to_cpu(x) ((__u32)(x)) +#define ext2fs_cpu_to_be16(x) ((__u16)(x)) +#define ext2fs_be16_to_cpu(x) ((__u16)(x)) +#else +#define ext2fs_cpu_to_le32(x) ((__u32)(x)) +#define ext2fs_le32_to_cpu(x) ((__u32)(x)) +#define ext2fs_cpu_to_le16(x) ((__u16)(x)) +#define ext2fs_le16_to_cpu(x) ((__u16)(x)) +#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x)) +#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x)) +#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x)) +#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x)) +#endif + +/* + * EXT2FS bitmap manipulation routines. + */ + +/* Support for sending warning messages from the inline subroutines */ +extern const char *ext2fs_block_string; +extern const char *ext2fs_inode_string; +extern const char *ext2fs_mark_string; +extern const char *ext2fs_unmark_string; +extern const char *ext2fs_test_string; +extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, + const char *description); +extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, + int code, unsigned long arg); + +extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); +extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); + +extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); +extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); + +extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); +extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block); + +extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode); +extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); +extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); +extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); + +extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map); + +/* These two routines moved to gen_bitmap.c */ +extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno); +extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/block.c b/e2fsprogs/old_e2fsprogs/ext2fs/block.c new file mode 100644 index 000000000..dbd04f846 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/block.c @@ -0,0 +1,437 @@ +/* vi: set sw=4 ts=4: */ +/* + * block.c --- iterate over all blocks in an inode + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct block_context { + ext2_filsys fs; + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t bcount, + blk_t ref_blk, + int ref_offset, + void *priv_data); + e2_blkcnt_t bcount; + int bsize; + int flags; + errcode_t errcode; + char *ind_buf; + char *dind_buf; + char *tind_buf; + void *priv_data; +}; + +static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) + ret = (*ctx->func)(ctx->fs, ind_block, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + if (!*ind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit; + return ret; + } + if (*ind_block >= ctx->fs->super->s_blocks_count || + *ind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_IND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->ind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + if (*block_nr == 0) + continue; + flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + offset += sizeof(blk_t); + } + } + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) + ret |= (*ctx->func)(ctx->fs, ind_block, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + return ret; +} + +static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) + ret = (*ctx->func)(ctx->fs, dind_block, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + if (!*dind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit; + return ret; + } + if (*dind_block >= ctx->fs->super->s_blocks_count || + *dind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->dind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit; + continue; + } + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) + ret |= (*ctx->func)(ctx->fs, dind_block, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + return ret; +} + +static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) + ret = (*ctx->func)(ctx->fs, tind_block, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + if (!*tind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit*limit; + return ret; + } + if (*tind_block >= ctx->fs->super->s_blocks_count || + *tind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->tind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit*limit; + continue; + } + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) + ret |= (*ctx->func)(ctx->fs, tind_block, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + + return ret; +} + +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + int i; + int got_inode = 0; + int ret = 0; + blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */ + struct ext2_inode inode; + errcode_t retval; + struct block_context ctx; + int limit; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * Check to see if we need to limit large files + */ + if (flags & BLOCK_FLAG_NO_LARGE) { + ctx.errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx.errcode) + return ctx.errcode; + got_inode = 1; + if (!LINUX_S_ISDIR(inode.i_mode) && + (inode.i_size_high != 0)) + return EXT2_ET_FILE_TOO_BIG; + } + + retval = ext2fs_get_blocks(fs, ino, blocks); + if (retval) + return retval; + + limit = fs->blocksize >> 2; + + ctx.fs = fs; + ctx.func = func; + ctx.priv_data = priv_data; + ctx.flags = flags; + ctx.bcount = 0; + if (block_buf) { + ctx.ind_buf = block_buf; + } else { + retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf); + if (retval) + return retval; + } + ctx.dind_buf = ctx.ind_buf + fs->blocksize; + ctx.tind_buf = ctx.dind_buf + fs->blocksize; + + /* + * Iterate over the HURD translator block (if present) + */ + if ((fs->super->s_creator_os == EXT2_OS_HURD) && + !(flags & BLOCK_FLAG_DATA_ONLY)) { + ctx.errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx.errcode) + goto abort_exit; + got_inode = 1; + if (inode.osd1.hurd1.h_i_translator) { + ret |= (*ctx.func)(fs, + &inode.osd1.hurd1.h_i_translator, + BLOCK_COUNT_TRANSLATOR, + 0, 0, priv_data); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } + + /* + * Iterate over normal data blocks + */ + for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) { + if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) { + ret |= (*ctx.func)(fs, &blocks[i], + ctx.bcount, 0, i, priv_data); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } + if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, + 0, EXT2_IND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit; + if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, + 0, EXT2_DIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit * limit; + if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, + 0, EXT2_TIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + +abort_exit: + if (ret & BLOCK_CHANGED) { + if (!got_inode) { + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + } + for (i=0; i < EXT2_N_BLOCKS; i++) + inode.i_block[i] = blocks[i]; + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) + return retval; + } + + if (!block_buf) + ext2fs_free_mem(&ctx.ind_buf); + + return (ret & BLOCK_ERROR) ? ctx.errcode : 0; +} + +/* + * Emulate the old ext2fs_block_iterate function! + */ + +struct xlate { + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int bcount, + void *priv_data); + void *real_private; +}; + +#ifdef __TURBOC__ +# pragma argsused +#endif +static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct xlate *xl = (struct xlate *) priv_data; + + return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); +} + +errcode_t ext2fs_block_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, + block_buf, xlate_func, &xl); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c b/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c new file mode 100644 index 000000000..796b0e4f5 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c @@ -0,0 +1,261 @@ +/* vi: set sw=4 ts=4: */ +/* + * bmap.c --- logical to physical block mapping + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, + blk_t block, blk_t *phys_blk); + +#define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) + +static errcode_t block_ind_bmap(ext2_filsys fs, int flags, + blk_t ind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + errcode_t retval; + blk_t b; + + if (!ind) { + if (flags & BMAP_SET) + return EXT2_ET_SET_BMAP_NO_IND; + *ret_blk = 0; + return 0; + } + retval = io_channel_read_blk(fs->io, ind, 1, block_buf); + if (retval) + return retval; + + if (flags & BMAP_SET) { + b = *ret_blk; +#if BB_BIG_ENDIAN + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) + b = ext2fs_swab32(b); +#endif + ((blk_t *) block_buf)[nr] = b; + return io_channel_write_blk(fs->io, ind, 1, block_buf); + } + + b = ((blk_t *) block_buf)[nr]; + +#if BB_BIG_ENDIAN + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) + b = ext2fs_swab32(b); +#endif + + if (!b && (flags & BMAP_ALLOC)) { + b = nr ? ((blk_t *) block_buf)[nr-1] : 0; + retval = ext2fs_alloc_block(fs, b, + block_buf + fs->blocksize, &b); + if (retval) + return retval; + +#if BB_BIG_ENDIAN + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) + ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); + else +#endif + ((blk_t *) block_buf)[nr] = b; + + retval = io_channel_write_blk(fs->io, ind, 1, block_buf); + if (retval) + return retval; + + (*blocks_alloc)++; + } + + *ret_blk = b; + return 0; +} + +static errcode_t block_dind_bmap(ext2_filsys fs, int flags, + blk_t dind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + blk_t b; + errcode_t retval; + blk_t addr_per_block; + + addr_per_block = (blk_t) fs->blocksize >> 2; + + retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, + blocks_alloc, nr / addr_per_block, &b); + if (retval) + return retval; + retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, + nr % addr_per_block, ret_blk); + return retval; +} + +static errcode_t block_tind_bmap(ext2_filsys fs, int flags, + blk_t tind, char *block_buf, + int *blocks_alloc, + blk_t nr, blk_t *ret_blk) +{ + blk_t b; + errcode_t retval; + blk_t addr_per_block; + + addr_per_block = (blk_t) fs->blocksize >> 2; + + retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, + blocks_alloc, nr / addr_per_block, &b); + if (retval) + return retval; + retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, + nr % addr_per_block, ret_blk); + return retval; +} + +errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, + char *block_buf, int bmap_flags, blk_t block, + blk_t *phys_blk) +{ + struct ext2_inode inode_buf; + blk_t addr_per_block; + blk_t b; + char *buf = NULL; + errcode_t retval = 0; + int blocks_alloc = 0, inode_dirty = 0; + + if (!(bmap_flags & BMAP_SET)) + *phys_blk = 0; + + /* Read inode structure if necessary */ + if (!inode) { + retval = ext2fs_read_inode(fs, ino, &inode_buf); + if (retval) + return retval; + inode = &inode_buf; + } + addr_per_block = (blk_t) fs->blocksize >> 2; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize * 2, &buf); + if (retval) + return retval; + block_buf = buf; + } + + if (block < EXT2_NDIR_BLOCKS) { + if (bmap_flags & BMAP_SET) { + b = *phys_blk; +#if BB_BIG_ENDIAN + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) + b = ext2fs_swab32(b); +#endif + inode_bmap(inode, block) = b; + inode_dirty++; + goto done; + } + + *phys_blk = inode_bmap(inode, block); + b = block ? inode_bmap(inode, block-1) : 0; + + if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, block) = b; + blocks_alloc++; + *phys_blk = b; + } + goto done; + } + + /* Indirect block */ + block -= EXT2_NDIR_BLOCKS; + if (block < addr_per_block) { + b = inode_bmap(inode, EXT2_IND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_IND_BLOCK-1); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_IND_BLOCK) = b; + blocks_alloc++; + } + retval = block_ind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, phys_blk); + goto done; + } + + /* Doubly indirect block */ + block -= addr_per_block; + if (block < addr_per_block * addr_per_block) { + b = inode_bmap(inode, EXT2_DIND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_IND_BLOCK); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_DIND_BLOCK) = b; + blocks_alloc++; + } + retval = block_dind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, phys_blk); + goto done; + } + + /* Triply indirect block */ + block -= addr_per_block * addr_per_block; + b = inode_bmap(inode, EXT2_TIND_BLOCK); + if (!b) { + if (!(bmap_flags & BMAP_ALLOC)) { + if (bmap_flags & BMAP_SET) + retval = EXT2_ET_SET_BMAP_NO_IND; + goto done; + } + + b = inode_bmap(inode, EXT2_DIND_BLOCK); + retval = ext2fs_alloc_block(fs, b, block_buf, &b); + if (retval) + goto done; + inode_bmap(inode, EXT2_TIND_BLOCK) = b; + blocks_alloc++; + } + retval = block_tind_bmap(fs, bmap_flags, b, block_buf, + &blocks_alloc, block, phys_blk); +done: + ext2fs_free_mem(&buf); + if ((retval == 0) && (blocks_alloc || inode_dirty)) { + inode->i_blocks += (blocks_alloc * fs->blocksize) / 512; + retval = ext2fs_write_inode(fs, ino, inode); + } + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c b/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c new file mode 100644 index 000000000..ec9244d0b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c @@ -0,0 +1,155 @@ +/* vi: set sw=4 ts=4: */ +/* + * bmove.c --- Move blocks around to make way for a particular + * filesystem structure. + * + * Copyright (C) 1997 Theodore Ts'o. This file may be redistributed + * under the terms of the GNU Public License. + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +struct process_block_struct { + ext2_ino_t ino; + struct ext2_inode * inode; + ext2fs_block_bitmap reserve; + ext2fs_block_bitmap alloc_map; + errcode_t error; + char *buf; + int add_dir; + int flags; +}; + +static int process_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt, blk_t ref_block, + int ref_offset, void *priv_data) +{ + struct process_block_struct *pb; + errcode_t retval; + int ret; + blk_t block, orig; + + pb = (struct process_block_struct *) priv_data; + block = orig = *block_nr; + ret = 0; + + /* + * Let's see if this is one which we need to relocate + */ + if (ext2fs_test_block_bitmap(pb->reserve, block)) { + do { + if (++block >= fs->super->s_blocks_count) + block = fs->super->s_first_data_block; + if (block == orig) { + pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; + return BLOCK_ABORT; + } + } while (ext2fs_test_block_bitmap(pb->reserve, block) || + ext2fs_test_block_bitmap(pb->alloc_map, block)); + + retval = io_channel_read_blk(fs->io, orig, 1, pb->buf); + if (retval) { + pb->error = retval; + return BLOCK_ABORT; + } + retval = io_channel_write_blk(fs->io, block, 1, pb->buf); + if (retval) { + pb->error = retval; + return BLOCK_ABORT; + } + *block_nr = block; + ext2fs_mark_block_bitmap(pb->alloc_map, block); + ret = BLOCK_CHANGED; + if (pb->flags & EXT2_BMOVE_DEBUG) + printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino, + blockcnt, orig, block); + } + if (pb->add_dir) { + retval = ext2fs_add_dir_block(fs->dblist, pb->ino, + block, (int) blockcnt); + if (retval) { + pb->error = retval; + ret |= BLOCK_ABORT; + } + } + return ret; +} + +errcode_t ext2fs_move_blocks(ext2_filsys fs, + ext2fs_block_bitmap reserve, + ext2fs_block_bitmap alloc_map, + int flags) +{ + ext2_ino_t ino; + struct ext2_inode inode; + errcode_t retval; + struct process_block_struct pb; + ext2_inode_scan scan; + char *block_buf; + + retval = ext2fs_open_inode_scan(fs, 0, &scan); + if (retval) + return retval; + + pb.reserve = reserve; + pb.error = 0; + pb.alloc_map = alloc_map ? alloc_map : fs->block_map; + pb.flags = flags; + + retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf); + if (retval) + return retval; + pb.buf = block_buf + fs->blocksize * 3; + + /* + * If GET_DBLIST is set in the flags field, then we should + * gather directory block information while we're doing the + * block move. + */ + if (flags & EXT2_BMOVE_GET_DBLIST) { + ext2fs_free_dblist(fs->dblist); + fs->dblist = NULL; + retval = ext2fs_init_dblist(fs, 0); + if (retval) + return retval; + } + + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval) + return retval; + + while (ino) { + if ((inode.i_links_count == 0) || + !ext2fs_inode_has_valid_blocks(&inode)) + goto next; + + pb.ino = ino; + pb.inode = &inode; + + pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && + flags & EXT2_BMOVE_GET_DBLIST); + + retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, + process_block, &pb); + if (retval) + return retval; + if (pb.error) + return pb.error; + + next: + retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) + goto next; + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/brel.h b/e2fsprogs/old_e2fsprogs/ext2fs/brel.h new file mode 100644 index 000000000..87bf72be4 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/brel.h @@ -0,0 +1,86 @@ +/* vi: set sw=4 ts=4: */ +/* + * brel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +struct ext2_block_relocate_entry { + blk_t new; + __s16 offset; + __u16 flags; + union { + blk_t block_ref; + ext2_ino_t inode_ref; + } owner; +}; + +#define RELOCATE_TYPE_REF 0x0007 +#define RELOCATE_BLOCK_REF 0x0001 +#define RELOCATE_INODE_REF 0x0002 + +typedef struct ext2_block_relocation_table *ext2_brel; + +struct ext2_block_relocation_table { + __u32 magic; + char *name; + blk_t current; + void *priv_data; + + /* + * Add a block relocation entry. + */ + errcode_t (*put)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Get a block relocation entry. + */ + errcode_t (*get)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Initialize for iterating over the block relocation entries. + */ + errcode_t (*start_iter)(ext2_brel brel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); + + /* + * Move the inode relocation table from one block number to + * another. + */ + errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); + + /* + * Remove a block relocation entry. + */ + errcode_t (*delete)(ext2_brel brel, blk_t old); + + + /* + * Free the block relocation table. + */ + errcode_t (*free)(ext2_brel brel); +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *brel); + +#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) +#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) +#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) +#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) +#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) +#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) +#define ext2fs_brel_free(brel) ((brel)->free((brel))) diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c b/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c new file mode 100644 index 000000000..652a3509c --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c @@ -0,0 +1,196 @@ +/* vi: set sw=4 ts=4: */ +/* + * brel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * TODO: rewrite to not use a direct array!!! (Fortunately this + * module isn't really used yet.) + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "brel.h" + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_start_iter(ext2_brel brel); +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); +static errcode_t bma_delete(ext2_brel brel, blk_t old); +static errcode_t bma_free(ext2_brel brel); + +struct brel_ma { + __u32 magic; + blk_t max_block; + struct ext2_block_relocate_entry *entries; +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *new_brel) +{ + ext2_brel brel = 0; + errcode_t retval; + struct brel_ma *ma = 0; + size_t size; + + *new_brel = 0; + + /* + * Allocate memory structures + */ + retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table), + &brel); + if (retval) + goto errout; + memset(brel, 0, sizeof(struct ext2_block_relocation_table)); + + retval = ext2fs_get_mem(strlen(name)+1, &brel->name); + if (retval) + goto errout; + strcpy(brel->name, name); + + retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma); + if (retval) + goto errout; + memset(ma, 0, sizeof(struct brel_ma)); + brel->priv_data = ma; + + size = (size_t) (sizeof(struct ext2_block_relocate_entry) * + (max_block+1)); + retval = ext2fs_get_mem(size, &ma->entries); + if (retval) + goto errout; + memset(ma->entries, 0, size); + ma->max_block = max_block; + + /* + * Fill in the brel data structure + */ + brel->put = bma_put; + brel->get = bma_get; + brel->start_iter = bma_start_iter; + brel->next = bma_next; + brel->move = bma_move; + brel->delete = bma_delete; + brel->free = bma_free; + + *new_brel = brel; + return 0; + +errout: + bma_free(brel); + return retval; +} + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + ma->entries[(unsigned)old] = *ent; + return 0; +} + +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + *ent = ma->entries[old]; + return 0; +} + +static errcode_t bma_start_iter(ext2_brel brel) +{ + brel->current = 0; + return 0; +} + +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + while (++brel->current < ma->max_block) { + if (ma->entries[(unsigned)brel->current].new == 0) + continue; + *old = brel->current; + *ent = ma->entries[(unsigned)brel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if ((old > ma->max_block) || (new > ma->max_block)) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + ma->entries[(unsigned)new] = ma->entries[old]; + ma->entries[(unsigned)old].new = 0; + return 0; +} + +static errcode_t bma_delete(ext2_brel brel, blk_t old) +{ + struct brel_ma *ma; + + ma = brel->priv_data; + if (old > ma->max_block) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned)old].new == 0) + return ENOENT; + ma->entries[(unsigned)old].new = 0; + return 0; +} + +static errcode_t bma_free(ext2_brel brel) +{ + struct brel_ma *ma; + + if (!brel) + return 0; + + ma = brel->priv_data; + + if (ma) { + ext2fs_free_mem(&ma->entries); + ext2fs_free_mem(&ma); + } + ext2fs_free_mem(&brel->name); + ext2fs_free_mem(&brel); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c b/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c new file mode 100644 index 000000000..dd4b0e9cf --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * check_desc.c --- Check the group descriptors of an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This routine sanity checks the group descriptors + */ +errcode_t ext2fs_check_desc(ext2_filsys fs) +{ + dgrp_t i; + blk_t block = fs->super->s_first_data_block; + blk_t next; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + for (i = 0; i < fs->group_desc_count; i++) { + next = block + fs->super->s_blocks_per_group; + /* + * Check to make sure block bitmap for group is + * located within the group. + */ + if (fs->group_desc[i].bg_block_bitmap < block || + fs->group_desc[i].bg_block_bitmap >= next) + return EXT2_ET_GDESC_BAD_BLOCK_MAP; + /* + * Check to make sure inode bitmap for group is + * located within the group + */ + if (fs->group_desc[i].bg_inode_bitmap < block || + fs->group_desc[i].bg_inode_bitmap >= next) + return EXT2_ET_GDESC_BAD_INODE_MAP; + /* + * Check to make sure inode table for group is located + * within the group + */ + if (fs->group_desc[i].bg_inode_table < block || + ((fs->group_desc[i].bg_inode_table + + fs->inode_blocks_per_group) >= next)) + return EXT2_ET_GDESC_BAD_INODE_TABLE; + + block = next; + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c b/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c new file mode 100644 index 000000000..bfa15e22a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c @@ -0,0 +1,380 @@ +/* vi: set sw=4 ts=4: */ +/* + * closefs.c --- close an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int test_root(int a, int b) +{ + if (a == 0) + return 1; + while (1) { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } +} + +int ext2fs_bg_has_super(ext2_filsys fs, int group_block) +{ + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) + return 1; + + if (test_root(group_block, 3) || (test_root(group_block, 5)) || + test_root(group_block, 7)) + return 1; + + return 0; +} + +int ext2fs_super_and_bgd_loc(ext2_filsys fs, + dgrp_t group, + blk_t *ret_super_blk, + blk_t *ret_old_desc_blk, + blk_t *ret_new_desc_blk, + int *ret_meta_bg) +{ + blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; + unsigned int meta_bg, meta_bg_size; + int numblocks, has_super; + int old_desc_blocks; + + group_block = fs->super->s_first_data_block + + (group * fs->super->s_blocks_per_group); + + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = + fs->desc_blocks + fs->super->s_reserved_gdt_blocks; + + if (group == fs->group_desc_count-1) { + numblocks = (fs->super->s_blocks_count - + fs->super->s_first_data_block) % + fs->super->s_blocks_per_group; + if (!numblocks) + numblocks = fs->super->s_blocks_per_group; + } else + numblocks = fs->super->s_blocks_per_group; + + has_super = ext2fs_bg_has_super(fs, group); + + if (has_super) { + super_blk = group_block; + numblocks--; + } + meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc)); + meta_bg = group / meta_bg_size; + + if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || + (meta_bg < fs->super->s_first_meta_bg)) { + if (has_super) { + old_desc_blk = group_block + 1; + numblocks -= old_desc_blocks; + } + } else { + if (((group % meta_bg_size) == 0) || + ((group % meta_bg_size) == 1) || + ((group % meta_bg_size) == (meta_bg_size-1))) { + if (has_super) + has_super = 1; + new_desc_blk = group_block + has_super; + numblocks--; + } + } + + numblocks -= 2 + fs->inode_blocks_per_group; + + if (ret_super_blk) + *ret_super_blk = super_blk; + if (ret_old_desc_blk) + *ret_old_desc_blk = old_desc_blk; + if (ret_new_desc_blk) + *ret_new_desc_blk = new_desc_blk; + if (ret_meta_bg) + *ret_meta_bg = meta_bg; + return numblocks; +} + + +/* + * This function forces out the primary superblock. We need to only + * write out those fields which we have changed, since if the + * filesystem is mounted, it may have changed some of the other + * fields. + * + * It takes as input a superblock which has already been byte swapped + * (if necessary). + * + */ +static errcode_t write_primary_superblock(ext2_filsys fs, + struct ext2_super_block *super) +{ + __u16 *old_super, *new_super; + int check_idx, write_idx, size; + errcode_t retval; + + if (!fs->io->manager->write_byte || !fs->orig_super) { + io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); + retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, + super); + io_channel_set_blksize(fs->io, fs->blocksize); + return retval; + } + + old_super = (__u16 *) fs->orig_super; + new_super = (__u16 *) super; + + for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { + if (old_super[check_idx] == new_super[check_idx]) + continue; + write_idx = check_idx; + for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) + if (old_super[check_idx] == new_super[check_idx]) + break; + size = 2 * (check_idx - write_idx); + retval = io_channel_write_byte(fs->io, + SUPERBLOCK_OFFSET + (2 * write_idx), size, + new_super + write_idx); + if (retval) + return retval; + } + memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); + return 0; +} + + +/* + * Updates the revision to EXT2_DYNAMIC_REV + */ +void ext2fs_update_dynamic_rev(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + + if (sb->s_rev_level > EXT2_GOOD_OLD_REV) + return; + + sb->s_rev_level = EXT2_DYNAMIC_REV; + sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; + sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; + /* s_uuid is handled by e2fsck already */ + /* other fields should be left alone */ +} + +static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, + blk_t group_block, + struct ext2_super_block *super_shadow) +{ + dgrp_t sgrp = group; + + if (sgrp > ((1 << 16) - 1)) + sgrp = (1 << 16) - 1; +#if BB_BIG_ENDIAN + if (fs->flags & EXT2_FLAG_SWAP_BYTES) + super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); + else +#endif + fs->super->s_block_group_nr = sgrp; + + return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, + super_shadow); +} + + +errcode_t ext2fs_flush(ext2_filsys fs) +{ + dgrp_t i; + blk_t group_block; + errcode_t retval; + unsigned long fs_state; + struct ext2_super_block *super_shadow = NULL; + struct ext2_group_desc *group_shadow = NULL; + char *group_ptr; + int old_desc_blocks; +#if BB_BIG_ENDIAN + dgrp_t j; + struct ext2_group_desc *s, *t; +#endif + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs_state = fs->super->s_state; + + fs->super->s_wtime = time(NULL); + fs->super->s_block_group_nr = 0; +#if BB_BIG_ENDIAN + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + retval = EXT2_ET_NO_MEMORY; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); + if (retval) + goto errout; + retval = ext2fs_get_mem((size_t)(fs->blocksize * + fs->desc_blocks), + &group_shadow); + if (retval) + goto errout; + memset(group_shadow, 0, (size_t) fs->blocksize * + fs->desc_blocks); + + /* swap the group descriptors */ + for (j=0, s=fs->group_desc, t=group_shadow; + j < fs->group_desc_count; j++, t++, s++) { + *t = *s; + ext2fs_swap_group_desc(t); + } + } else { + super_shadow = fs->super; + group_shadow = fs->group_desc; + } +#else + super_shadow = fs->super; + group_shadow = fs->group_desc; +#endif + + /* + * If this is an external journal device, don't write out the + * block group descriptors or any of the backup superblocks + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + goto write_primary_superblock_only; + + /* + * Set the state of the FS to be non-valid. (The state has + * already been backed up earlier, and will be restored after + * we write out the backup superblocks.) + */ + fs->super->s_state &= ~EXT2_VALID_FS; +#if BB_BIG_ENDIAN + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + *super_shadow = *fs->super; + ext2fs_swap_super(super_shadow); + } +#endif + + /* + * Write out the master group descriptors, and the backup + * superblocks and group descriptors. + */ + group_block = fs->super->s_first_data_block; + group_ptr = (char *) group_shadow; + if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) + old_desc_blocks = fs->super->s_first_meta_bg; + else + old_desc_blocks = fs->desc_blocks; + + for (i = 0; i < fs->group_desc_count; i++) { + blk_t super_blk, old_desc_blk, new_desc_blk; + int meta_bg; + + ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, + &new_desc_blk, &meta_bg); + + if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { + retval = write_backup_super(fs, i, super_blk, + super_shadow); + if (retval) + goto errout; + } + if (fs->flags & EXT2_FLAG_SUPER_ONLY) + continue; + if ((old_desc_blk) && + (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { + retval = io_channel_write_blk(fs->io, + old_desc_blk, old_desc_blocks, group_ptr); + if (retval) + goto errout; + } + if (new_desc_blk) { + retval = io_channel_write_blk(fs->io, new_desc_blk, + 1, group_ptr + (meta_bg*fs->blocksize)); + if (retval) + goto errout; + } + } + fs->super->s_block_group_nr = 0; + fs->super->s_state = fs_state; +#if BB_BIG_ENDIAN + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + *super_shadow = *fs->super; + ext2fs_swap_super(super_shadow); + } +#endif + + /* + * If the write_bitmaps() function is present, call it to + * flush the bitmaps. This is done this way so that a simple + * program that doesn't mess with the bitmaps doesn't need to + * drag in the bitmaps.c code. + */ + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + goto errout; + } + +write_primary_superblock_only: + /* + * Write out master superblock. This has to be done + * separately, since it is located at a fixed location + * (SUPERBLOCK_OFFSET). We flush all other pending changes + * out to disk first, just to avoid a race condition with an + * insy-tinsy window.... + */ + retval = io_channel_flush(fs->io); + retval = write_primary_superblock(fs, super_shadow); + if (retval) + goto errout; + + fs->flags &= ~EXT2_FLAG_DIRTY; + + retval = io_channel_flush(fs->io); +errout: + fs->super->s_state = fs_state; + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + if (super_shadow) + ext2fs_free_mem(&super_shadow); + if (group_shadow) + ext2fs_free_mem(&group_shadow); + } + return retval; +} + +errcode_t ext2fs_close(ext2_filsys fs) +{ + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (fs->flags & EXT2_FLAG_DIRTY) { + retval = ext2fs_flush(fs); + if (retval) + return retval; + } + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + return retval; + } + ext2fs_free(fs); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c b/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c new file mode 100644 index 000000000..7f78ff804 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * cmp_bitmaps.c --- routines to compare inode and block bitmaps. + * + * Copyright (C) 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, + ext2fs_block_bitmap bm2) +{ + blk_t i; + + EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP); + EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP); + + if ((bm1->start != bm2->start) || + (bm1->end != bm2->end) || + (memcmp(bm1->bitmap, bm2->bitmap, + (size_t) (bm1->end - bm1->start)/8))) + return EXT2_ET_NEQ_BLOCK_BITMAP; + + for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) + if (ext2fs_fast_test_block_bitmap(bm1, i) != + ext2fs_fast_test_block_bitmap(bm2, i)) + return EXT2_ET_NEQ_BLOCK_BITMAP; + + return 0; +} + +errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, + ext2fs_inode_bitmap bm2) +{ + ext2_ino_t i; + + EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP); + EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP); + + if ((bm1->start != bm2->start) || + (bm1->end != bm2->end) || + (memcmp(bm1->bitmap, bm2->bitmap, + (size_t) (bm1->end - bm1->start)/8))) + return EXT2_ET_NEQ_INODE_BITMAP; + + for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) + if (ext2fs_fast_test_inode_bitmap(bm1, i) != + ext2fs_fast_test_inode_bitmap(bm2, i)) + return EXT2_ET_NEQ_INODE_BITMAP; + + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c b/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c new file mode 100644 index 000000000..06ff6d807 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c @@ -0,0 +1,260 @@ +/* vi: set sw=4 ts=4: */ +/* + * dblist.c -- directory block list functions + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int dir_block_cmp(const void *a, const void *b); + +/* + * Returns the number of directories in the filesystem as reported by + * the group descriptors. Of course, the group descriptors could be + * wrong! + */ +errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) +{ + dgrp_t i; + ext2_ino_t num_dirs, max_dirs; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + num_dirs = 0; + max_dirs = fs->super->s_inodes_per_group; + for (i = 0; i < fs->group_desc_count; i++) { + if (fs->group_desc[i].bg_used_dirs_count > max_dirs) + num_dirs += max_dirs / 8; + else + num_dirs += fs->group_desc[i].bg_used_dirs_count; + } + if (num_dirs > fs->super->s_inodes_count) + num_dirs = fs->super->s_inodes_count; + + *ret_num_dirs = num_dirs; + + return 0; +} + +/* + * helper function for making a new directory block list (for + * initialize and copy). + */ +static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count, + struct ext2_db_entry *list, + ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + size_t len; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if ((ret_dblist == 0) && fs->dblist && + (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) + return 0; + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist); + if (retval) + return retval; + memset(dblist, 0, sizeof(struct ext2_struct_dblist)); + + dblist->magic = EXT2_ET_MAGIC_DBLIST; + dblist->fs = fs; + if (size) + dblist->size = size; + else { + retval = ext2fs_get_num_dirs(fs, &dblist->size); + if (retval) + goto cleanup; + dblist->size = (dblist->size * 2) + 12; + } + len = (size_t) sizeof(struct ext2_db_entry) * dblist->size; + dblist->count = count; + retval = ext2fs_get_mem(len, &dblist->list); + if (retval) + goto cleanup; + + if (list) + memcpy(dblist->list, list, len); + else + memset(dblist->list, 0, len); + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + return 0; +cleanup: + ext2fs_free_mem(&dblist); + return retval; +} + +/* + * Initialize a directory block list + */ +errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + + retval = make_dblist(fs, 0, 0, 0, &dblist); + if (retval) + return retval; + + dblist->sorted = 1; + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + + return 0; +} + +/* + * Copy a directory block list + */ +errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest) +{ + ext2_dblist dblist; + errcode_t retval; + + retval = make_dblist(src->fs, src->size, src->count, src->list, + &dblist); + if (retval) + return retval; + dblist->sorted = src->sorted; + *dest = dblist; + return 0; +} + +/* + * Close a directory block list + * + * (moved to closefs.c) + */ + + +/* + * Add a directory block to the directory block list + */ +errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, + int blockcnt) +{ + struct ext2_db_entry *new_entry; + errcode_t retval; + unsigned long old_size; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count >= dblist->size) { + old_size = dblist->size * sizeof(struct ext2_db_entry); + dblist->size += 100; + retval = ext2fs_resize_mem(old_size, (size_t) dblist->size * + sizeof(struct ext2_db_entry), + &dblist->list); + if (retval) { + dblist->size -= 100; + return retval; + } + } + new_entry = dblist->list + ( (int) dblist->count++); + new_entry->blk = blk; + new_entry->ino = ino; + new_entry->blockcnt = blockcnt; + + dblist->sorted = 0; + + return 0; +} + +/* + * Change the directory block to the directory block list + */ +errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, + int blockcnt) +{ + dgrp_t i; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + for (i=0; i < dblist->count; i++) { + if ((dblist->list[i].ino != ino) || + (dblist->list[i].blockcnt != blockcnt)) + continue; + dblist->list[i].blk = blk; + dblist->sorted = 0; + return 0; + } + return EXT2_ET_DB_NOT_FOUND; +} + +void ext2fs_dblist_sort(ext2_dblist dblist, + int (*sortfunc)(const void *, + const void *)) +{ + if (!sortfunc) + sortfunc = dir_block_cmp; + qsort(dblist->list, (size_t) dblist->count, + sizeof(struct ext2_db_entry), sortfunc); + dblist->sorted = 1; +} + +/* + * This function iterates over the directory block list + */ +errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, + struct ext2_db_entry *db_info, + void *priv_data), + void *priv_data) +{ + ext2_ino_t i; + int ret; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (!dblist->sorted) + ext2fs_dblist_sort(dblist, 0); + for (i=0; i < dblist->count; i++) { + ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data); + if (ret & DBLIST_ABORT) + return 0; + } + return 0; +} + +static int dir_block_cmp(const void *a, const void *b) +{ + const struct ext2_db_entry *db_a = + (const struct ext2_db_entry *) a; + const struct ext2_db_entry *db_b = + (const struct ext2_db_entry *) b; + + if (db_a->blk != db_b->blk) + return (int) (db_a->blk - db_b->blk); + + if (db_a->ino != db_b->ino) + return (int) (db_a->ino - db_b->ino); + + return (int) (db_a->blockcnt - db_b->blockcnt); +} + +int ext2fs_dblist_count(ext2_dblist dblist) +{ + return (int) dblist->count; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c b/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c new file mode 100644 index 000000000..b23920466 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c @@ -0,0 +1,76 @@ +/* vi: set sw=4 ts=4: */ +/* + * dblist_dir.c --- iterate by directory entry + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, + void *priv_data); + +errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + errcode_t retval; + struct dir_context ctx; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + ctx.dir = 0; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + + retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx); + + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, + void *priv_data) +{ + struct dir_context *ctx; + + ctx = (struct dir_context *) priv_data; + ctx->dir = db_info->ino; + + return ext2fs_process_dir_block(fs, &db_info->blk, + db_info->blockcnt, 0, 0, priv_data); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c b/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c new file mode 100644 index 000000000..eb5dae0a6 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c @@ -0,0 +1,219 @@ +/* vi: set sw=4 ts=4: */ +/* + * dir_iterate.c --- ext2fs directory iteration operations + * + * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +/* + * This function checks to see whether or not a potential deleted + * directory entry looks valid. What we do is check the deleted entry + * and each successive entry to make sure that they all look valid and + * that the last deleted entry ends at the beginning of the next + * undeleted entry. Returns 1 if the deleted entry looks valid, zero + * if not valid. + */ +static int ext2fs_validate_entry(char *buf, int offset, int final_offset) +{ + struct ext2_dir_entry *dirent; + + while (offset < final_offset) { + dirent = (struct ext2_dir_entry *)(buf + offset); + offset += dirent->rec_len; + if ((dirent->rec_len < 8) || + ((dirent->rec_len % 4) != 0) || + (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) + return 0; + } + return (offset == final_offset); +} + +errcode_t ext2fs_dir_iterate2(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct dir_context ctx; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + ctx.dir = dir; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + retval = ext2fs_block_iterate2(fs, dir, 0, 0, + ext2fs_process_dir_block, &ctx); + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +struct xlate { + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *real_private; +}; + +static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry EXT2FS_ATTR((unused)), + struct ext2_dir_entry *dirent, int offset, + int blocksize, char *buf, void *priv_data) +{ + struct xlate *xl = (struct xlate *) priv_data; + + return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); +} + +extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct xlate xl; + + xl.real_private = priv_data; + xl.func = func; + + return ext2fs_dir_iterate2(fs, dir, flags, block_buf, + xlate_func, &xl); +} + + +/* + * Helper function which is private to this module. Used by + * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() + */ +int ext2fs_process_dir_block(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct dir_context *ctx = (struct dir_context *) priv_data; + unsigned int offset = 0; + unsigned int next_real_entry = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + int entry, size; + struct ext2_dir_entry *dirent; + + if (blockcnt < 0) + return 0; + + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf); + if (ctx->errcode) + return BLOCK_ABORT; + + while (offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (ctx->buf + offset); + if (((offset + dirent->rec_len) > fs->blocksize) || + (dirent->rec_len < 8) || + ((dirent->rec_len % 4) != 0) || + (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + ret = (ctx->func)(ctx->dir, + (next_real_entry > offset) ? + DIRENT_DELETED_FILE : entry, + dirent, offset, + fs->blocksize, ctx->buf, + ctx->priv_data); + if (entry < DIRENT_OTHER_FILE) + entry++; + + if (ret & DIRENT_CHANGED) + changed++; + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (next_real_entry == offset) + next_real_entry += dirent->rec_len; + + if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { + size = ((dirent->name_len & 0xFF) + 11) & ~3; + + if (dirent->rec_len != size) { + unsigned int final_offset; + + final_offset = offset + dirent->rec_len; + offset += size; + while (offset < final_offset && + !ext2fs_validate_entry(ctx->buf, + offset, + final_offset)) + offset += 4; + continue; + } + } + offset += dirent->rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c b/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c new file mode 100644 index 000000000..f9c5a104b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c @@ -0,0 +1,132 @@ +/* vi: set sw=4 ts=4: */ +/* + * dirblock.c --- directory block routines. + * + * Copyright (C) 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags EXT2FS_ATTR((unused))) +{ + errcode_t retval; + char *p, *end; + struct ext2_dir_entry *dirent; + unsigned int name_len, rec_len; +#if BB_BIG_ENDIAN + unsigned int do_swap; +#endif + + retval = io_channel_read_blk(fs->io, block, 1, buf); + if (retval) + return retval; +#if BB_BIG_ENDIAN + do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES| + EXT2_FLAG_SWAP_BYTES_READ)) != 0; +#endif + p = (char *) buf; + end = (char *) buf + fs->blocksize; + while (p < end-8) { + dirent = (struct ext2_dir_entry *) p; +#if BB_BIG_ENDIAN + if (do_swap) { + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); + } +#endif + name_len = dirent->name_len; +#ifdef WORDS_BIGENDIAN + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + rec_len = dirent->rec_len; + if ((rec_len < 8) || (rec_len % 4)) { + rec_len = 8; + retval = EXT2_ET_DIR_CORRUPTED; + } + if (((name_len & 0xFF) + 8) > dirent->rec_len) + retval = EXT2_ET_DIR_CORRUPTED; + p += rec_len; + } + return retval; +} + +errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, + void *buf) +{ + return ext2fs_read_dir_block2(fs, block, buf, 0); +} + + +errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, + void *inbuf, int flags EXT2FS_ATTR((unused))) +{ +#if BB_BIG_ENDIAN + int do_swap = 0; + errcode_t retval; + char *p, *end; + char *buf = NULL; + struct ext2_dir_entry *dirent; + + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) + do_swap = 1; + +#ifndef WORDS_BIGENDIAN + if (!do_swap) + return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); +#endif + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + memcpy(buf, inbuf, fs->blocksize); + p = buf; + end = buf + fs->blocksize; + while (p < end) { + dirent = (struct ext2_dir_entry *) p; + if ((dirent->rec_len < 8) || + (dirent->rec_len % 4)) { + ext2fs_free_mem(&buf); + return EXT2_ET_DIR_CORRUPTED; + } + p += dirent->rec_len; + if (do_swap) { + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); + } +#ifdef WORDS_BIGENDIAN + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + } + retval = io_channel_write_blk(fs->io, block, 1, buf); + ext2fs_free_mem(&buf); + return retval; +#else + return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); +#endif +} + + +errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, + void *inbuf) +{ + return ext2fs_write_dir_block2(fs, block, inbuf, 0); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c b/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c new file mode 100644 index 000000000..09e34be3b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c @@ -0,0 +1,234 @@ +/* vi: set sw=4 ts=4: */ +/* + * dirhash.c -- Calculate the hash of a directory entry + * + * Copyright (c) 2001 Daniel Phillips + * + * Copyright (c) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Keyed 32-bit hash function using TEA in a Davis-Meyer function + * H0 = Key + * Hi = E Mi(Hi-1) + Hi-1 + * + * (see Applied Cryptography, 2nd edition, p448). + * + * Jeremy Fitzhardinge 1998 + * + * This code is made available under the terms of the GPL + */ +#define DELTA 0x9E3779B9 + +static void TEA_transform(__u32 buf[4], __u32 const in[]) +{ + __u32 sum = 0; + __u32 b0 = buf[0], b1 = buf[1]; + __u32 a = in[0], b = in[1], c = in[2], d = in[3]; + int n = 16; + + do { + sum += DELTA; + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); + } while (--n); + + buf[0] += b0; + buf[1] += b1; +} + +/* F, G and H are basic MD4 functions: selection, majority, parity */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The generic round function. The application is so specific that + * we don't bother protecting all the arguments with parens, as is generally + * good macro practice, in favor of extra legibility. + * Rotation is separate from addition to prevent recomputation + */ +#define ROUND(f, a, b, c, d, x, s) \ + (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) +#define K1 0 +#define K2 013240474631UL +#define K3 015666365641UL + +/* + * Basic cut-down MD4 transform. Returns only 32 bits of result. + */ +static void halfMD4Transform (__u32 buf[4], __u32 const in[]) +{ + __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ + ROUND(F, a, b, c, d, in[0] + K1, 3); + ROUND(F, d, a, b, c, in[1] + K1, 7); + ROUND(F, c, d, a, b, in[2] + K1, 11); + ROUND(F, b, c, d, a, in[3] + K1, 19); + ROUND(F, a, b, c, d, in[4] + K1, 3); + ROUND(F, d, a, b, c, in[5] + K1, 7); + ROUND(F, c, d, a, b, in[6] + K1, 11); + ROUND(F, b, c, d, a, in[7] + K1, 19); + + /* Round 2 */ + ROUND(G, a, b, c, d, in[1] + K2, 3); + ROUND(G, d, a, b, c, in[3] + K2, 5); + ROUND(G, c, d, a, b, in[5] + K2, 9); + ROUND(G, b, c, d, a, in[7] + K2, 13); + ROUND(G, a, b, c, d, in[0] + K2, 3); + ROUND(G, d, a, b, c, in[2] + K2, 5); + ROUND(G, c, d, a, b, in[4] + K2, 9); + ROUND(G, b, c, d, a, in[6] + K2, 13); + + /* Round 3 */ + ROUND(H, a, b, c, d, in[3] + K3, 3); + ROUND(H, d, a, b, c, in[7] + K3, 9); + ROUND(H, c, d, a, b, in[2] + K3, 11); + ROUND(H, b, c, d, a, in[6] + K3, 15); + ROUND(H, a, b, c, d, in[1] + K3, 3); + ROUND(H, d, a, b, c, in[5] + K3, 9); + ROUND(H, c, d, a, b, in[0] + K3, 11); + ROUND(H, b, c, d, a, in[4] + K3, 15); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#undef ROUND +#undef F +#undef G +#undef H +#undef K1 +#undef K2 +#undef K3 + +/* The old legacy hash */ +static ext2_dirhash_t dx_hack_hash (const char *name, int len) +{ + __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; + while (len--) { + __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373)); + + if (hash & 0x80000000) hash -= 0x7fffffff; + hash1 = hash0; + hash0 = hash; + } + return (hash0 << 1); +} + +static void str2hashbuf(const char *msg, int len, __u32 *buf, int num) +{ + __u32 pad, val; + int i; + + pad = (__u32)len | ((__u32)len << 8); + pad |= pad << 16; + + val = pad; + if (len > num*4) + len = num * 4; + for (i=0; i < len; i++) { + if ((i % 4) == 0) + val = pad; + val = msg[i] + (val << 8); + if ((i % 4) == 3) { + *buf++ = val; + val = pad; + num--; + } + } + if (--num >= 0) + *buf++ = val; + while (--num >= 0) + *buf++ = pad; +} + +/* + * Returns the hash of a filename. If len is 0 and name is NULL, then + * this function can be used to test whether or not a hash version is + * supported. + * + * The seed is an 4 longword (32 bits) "secret" which can be used to + * uniquify a hash. If the seed is all zero's, then some default seed + * may be used. + * + * A particular hash version specifies whether or not the seed is + * represented, and whether or not the returned hash is 32 bits or 64 + * bits. 32 bit hashes will return 0 for the minor hash. + */ +errcode_t ext2fs_dirhash(int version, const char *name, int len, + const __u32 *seed, + ext2_dirhash_t *ret_hash, + ext2_dirhash_t *ret_minor_hash) +{ + __u32 hash; + __u32 minor_hash = 0; + const char *p; + int i; + __u32 in[8], buf[4]; + + /* Initialize the default seed for the hash checksum functions */ + buf[0] = 0x67452301; + buf[1] = 0xefcdab89; + buf[2] = 0x98badcfe; + buf[3] = 0x10325476; + + /* Check to see if the seed is all zero's */ + if (seed) { + for (i=0; i < 4; i++) { + if (seed[i]) + break; + } + if (i < 4) + memcpy(buf, seed, sizeof(buf)); + } + + switch (version) { + case EXT2_HASH_LEGACY: + hash = dx_hack_hash(name, len); + break; + case EXT2_HASH_HALF_MD4: + p = name; + while (len > 0) { + str2hashbuf(p, len, in, 8); + halfMD4Transform(buf, in); + len -= 32; + p += 32; + } + minor_hash = buf[2]; + hash = buf[1]; + break; + case EXT2_HASH_TEA: + p = name; + while (len > 0) { + str2hashbuf(p, len, in, 4); + TEA_transform(buf, in); + len -= 16; + p += 16; + } + hash = buf[0]; + minor_hash = buf[1]; + break; + default: + *ret_hash = 0; + return EXT2_ET_DIRHASH_UNSUPP; + } + *ret_hash = hash & ~1; + if (ret_minor_hash) + *ret_minor_hash = minor_hash; + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c b/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c new file mode 100644 index 000000000..d1879377a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * dupfs.c --- duplicate a ext2 filesystem handle + * + * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) +{ + ext2_filsys fs; + errcode_t retval; + + EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + *fs = *src; + fs->device_name = 0; + fs->super = 0; + fs->orig_super = 0; + fs->group_desc = 0; + fs->inode_map = 0; + fs->block_map = 0; + fs->badblocks = 0; + fs->dblist = 0; + + io_channel_bumpcount(fs->io); + if (fs->icache) + fs->icache->refcount++; + + retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); + if (retval) + goto errout; + strcpy(fs->device_name, src->device_name); + + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); + if (retval) + goto errout; + memcpy(fs->super, src->super, SUPERBLOCK_SIZE); + + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); + if (retval) + goto errout; + memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); + + retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, + &fs->group_desc); + if (retval) + goto errout; + memcpy(fs->group_desc, src->group_desc, + (size_t) fs->desc_blocks * fs->blocksize); + + if (src->inode_map) { + retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); + if (retval) + goto errout; + } + if (src->block_map) { + retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); + if (retval) + goto errout; + } + if (src->badblocks) { + retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); + if (retval) + goto errout; + } + if (src->dblist) { + retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); + if (retval) + goto errout; + } + *dest = fs; + return 0; +errout: + ext2fs_free(fs); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h b/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h new file mode 100644 index 000000000..a598d0111 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * e2image.h --- header file describing the ext2 image format + * + * Copyright (C) 2000 Theodore Ts'o. + * + * Note: this uses the POSIX IO interfaces, unlike most of the other + * functions in this library. So sue me. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + + +struct ext2_image_hdr { + __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */ + char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */ + char fs_hostname[64];/* Hostname of machine of image */ + char fs_netaddr[32]; /* Network address */ + __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */ + __u32 fs_device; /* Device number of image */ + char fs_device_name[64]; /* Device name */ + char fs_uuid[16]; /* UUID of filesystem */ + __u32 fs_blocksize; /* Block size of the filesystem */ + __u32 fs_reserved[8]; + + __u32 image_device; /* Device number of image file */ + __u32 image_inode; /* Inode number of image file */ + __u32 image_time; /* Time of image creation */ + __u32 image_reserved[8]; + + __u32 offset_super; /* Byte offset of the sb and descriptors */ + __u32 offset_inode; /* Byte offset of the inode table */ + __u32 offset_inodemap; /* Byte offset of the inode bitmaps */ + __u32 offset_blockmap; /* Byte offset of the inode bitmaps */ + __u32 offset_reserved[8]; +}; diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c b/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c new file mode 100644 index 000000000..8a29ae584 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c @@ -0,0 +1,127 @@ +/* vi: set sw=4 ts=4: */ +/* + * expand.c --- expand an ext2fs directory + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct expand_dir_struct { + int done; + int newblocks; + errcode_t err; +}; + +static int expand_dir_proc(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; + blk_t new_blk; + static blk_t last_blk = 0; + char *block; + errcode_t retval; + + if (*blocknr) { + last_blk = *blocknr; + return 0; + } + retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt > 0) { + retval = ext2fs_new_dir_block(fs, 0, 0, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + es->done = 1; + retval = ext2fs_write_dir_block(fs, new_blk, block); + } else { + retval = ext2fs_get_mem(fs->blocksize, &block); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + memset(block, 0, fs->blocksize); + retval = io_channel_write_blk(fs->io, new_blk, 1, block); + } + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + ext2fs_free_mem(&block); + *blocknr = new_blk; + ext2fs_block_alloc_stats(fs, new_blk, +1); + es->newblocks++; + + if (es->done) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; +} + +errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir) +{ + errcode_t retval; + struct expand_dir_struct es; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if (!fs->block_map) + return EXT2_ET_NO_BLOCK_BITMAP; + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + es.done = 0; + es.err = 0; + es.newblocks = 0; + + retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, + 0, expand_dir_proc, &es); + + if (es.err) + return es.err; + if (!es.done) + return EXT2_ET_EXPAND_DIR_ERR; + + /* + * Update the size and block count fields in the inode. + */ + retval = ext2fs_read_inode(fs, dir, &inode); + if (retval) + return retval; + + inode.i_size += fs->blocksize; + inode.i_blocks += (fs->blocksize / 512) * es.newblocks; + + retval = ext2fs_write_inode(fs, dir, &inode); + if (retval) + return retval; + + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h new file mode 100644 index 000000000..ead352810 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h @@ -0,0 +1,116 @@ +/* vi: set sw=4 ts=4: */ +/* + * ext2_err.h: + * This file is automatically generated; please do not edit it. + */ + +#define EXT2_ET_BASE (2133571328L) +#define EXT2_ET_MAGIC_EXT2FS_FILSYS (2133571329L) +#define EXT2_ET_MAGIC_BADBLOCKS_LIST (2133571330L) +#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE (2133571331L) +#define EXT2_ET_MAGIC_INODE_SCAN (2133571332L) +#define EXT2_ET_MAGIC_IO_CHANNEL (2133571333L) +#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL (2133571334L) +#define EXT2_ET_MAGIC_IO_MANAGER (2133571335L) +#define EXT2_ET_MAGIC_BLOCK_BITMAP (2133571336L) +#define EXT2_ET_MAGIC_INODE_BITMAP (2133571337L) +#define EXT2_ET_MAGIC_GENERIC_BITMAP (2133571338L) +#define EXT2_ET_MAGIC_TEST_IO_CHANNEL (2133571339L) +#define EXT2_ET_MAGIC_DBLIST (2133571340L) +#define EXT2_ET_MAGIC_ICOUNT (2133571341L) +#define EXT2_ET_MAGIC_PQ_IO_CHANNEL (2133571342L) +#define EXT2_ET_MAGIC_EXT2_FILE (2133571343L) +#define EXT2_ET_MAGIC_E2IMAGE (2133571344L) +#define EXT2_ET_MAGIC_INODE_IO_CHANNEL (2133571345L) +#define EXT2_ET_MAGIC_RESERVED_9 (2133571346L) +#define EXT2_ET_BAD_MAGIC (2133571347L) +#define EXT2_ET_REV_TOO_HIGH (2133571348L) +#define EXT2_ET_RO_FILSYS (2133571349L) +#define EXT2_ET_GDESC_READ (2133571350L) +#define EXT2_ET_GDESC_WRITE (2133571351L) +#define EXT2_ET_GDESC_BAD_BLOCK_MAP (2133571352L) +#define EXT2_ET_GDESC_BAD_INODE_MAP (2133571353L) +#define EXT2_ET_GDESC_BAD_INODE_TABLE (2133571354L) +#define EXT2_ET_INODE_BITMAP_WRITE (2133571355L) +#define EXT2_ET_INODE_BITMAP_READ (2133571356L) +#define EXT2_ET_BLOCK_BITMAP_WRITE (2133571357L) +#define EXT2_ET_BLOCK_BITMAP_READ (2133571358L) +#define EXT2_ET_INODE_TABLE_WRITE (2133571359L) +#define EXT2_ET_INODE_TABLE_READ (2133571360L) +#define EXT2_ET_NEXT_INODE_READ (2133571361L) +#define EXT2_ET_UNEXPECTED_BLOCK_SIZE (2133571362L) +#define EXT2_ET_DIR_CORRUPTED (2133571363L) +#define EXT2_ET_SHORT_READ (2133571364L) +#define EXT2_ET_SHORT_WRITE (2133571365L) +#define EXT2_ET_DIR_NO_SPACE (2133571366L) +#define EXT2_ET_NO_INODE_BITMAP (2133571367L) +#define EXT2_ET_NO_BLOCK_BITMAP (2133571368L) +#define EXT2_ET_BAD_INODE_NUM (2133571369L) +#define EXT2_ET_BAD_BLOCK_NUM (2133571370L) +#define EXT2_ET_EXPAND_DIR_ERR (2133571371L) +#define EXT2_ET_TOOSMALL (2133571372L) +#define EXT2_ET_BAD_BLOCK_MARK (2133571373L) +#define EXT2_ET_BAD_BLOCK_UNMARK (2133571374L) +#define EXT2_ET_BAD_BLOCK_TEST (2133571375L) +#define EXT2_ET_BAD_INODE_MARK (2133571376L) +#define EXT2_ET_BAD_INODE_UNMARK (2133571377L) +#define EXT2_ET_BAD_INODE_TEST (2133571378L) +#define EXT2_ET_FUDGE_BLOCK_BITMAP_END (2133571379L) +#define EXT2_ET_FUDGE_INODE_BITMAP_END (2133571380L) +#define EXT2_ET_BAD_IND_BLOCK (2133571381L) +#define EXT2_ET_BAD_DIND_BLOCK (2133571382L) +#define EXT2_ET_BAD_TIND_BLOCK (2133571383L) +#define EXT2_ET_NEQ_BLOCK_BITMAP (2133571384L) +#define EXT2_ET_NEQ_INODE_BITMAP (2133571385L) +#define EXT2_ET_BAD_DEVICE_NAME (2133571386L) +#define EXT2_ET_MISSING_INODE_TABLE (2133571387L) +#define EXT2_ET_CORRUPT_SUPERBLOCK (2133571388L) +#define EXT2_ET_BAD_GENERIC_MARK (2133571389L) +#define EXT2_ET_BAD_GENERIC_UNMARK (2133571390L) +#define EXT2_ET_BAD_GENERIC_TEST (2133571391L) +#define EXT2_ET_SYMLINK_LOOP (2133571392L) +#define EXT2_ET_CALLBACK_NOTHANDLED (2133571393L) +#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE (2133571394L) +#define EXT2_ET_UNSUPP_FEATURE (2133571395L) +#define EXT2_ET_RO_UNSUPP_FEATURE (2133571396L) +#define EXT2_ET_LLSEEK_FAILED (2133571397L) +#define EXT2_ET_NO_MEMORY (2133571398L) +#define EXT2_ET_INVALID_ARGUMENT (2133571399L) +#define EXT2_ET_BLOCK_ALLOC_FAIL (2133571400L) +#define EXT2_ET_INODE_ALLOC_FAIL (2133571401L) +#define EXT2_ET_NO_DIRECTORY (2133571402L) +#define EXT2_ET_TOO_MANY_REFS (2133571403L) +#define EXT2_ET_FILE_NOT_FOUND (2133571404L) +#define EXT2_ET_FILE_RO (2133571405L) +#define EXT2_ET_DB_NOT_FOUND (2133571406L) +#define EXT2_ET_DIR_EXISTS (2133571407L) +#define EXT2_ET_UNIMPLEMENTED (2133571408L) +#define EXT2_ET_CANCEL_REQUESTED (2133571409L) +#define EXT2_ET_FILE_TOO_BIG (2133571410L) +#define EXT2_ET_JOURNAL_NOT_BLOCK (2133571411L) +#define EXT2_ET_NO_JOURNAL_SB (2133571412L) +#define EXT2_ET_JOURNAL_TOO_SMALL (2133571413L) +#define EXT2_ET_JOURNAL_UNSUPP_VERSION (2133571414L) +#define EXT2_ET_LOAD_EXT_JOURNAL (2133571415L) +#define EXT2_ET_NO_JOURNAL (2133571416L) +#define EXT2_ET_DIRHASH_UNSUPP (2133571417L) +#define EXT2_ET_BAD_EA_BLOCK_NUM (2133571418L) +#define EXT2_ET_TOO_MANY_INODES (2133571419L) +#define EXT2_ET_NOT_IMAGE_FILE (2133571420L) +#define EXT2_ET_RES_GDT_BLOCKS (2133571421L) +#define EXT2_ET_RESIZE_INODE_CORRUPT (2133571422L) +#define EXT2_ET_SET_BMAP_NO_IND (2133571423L) + +#if 0 +extern const struct error_table et_ext2_error_table; +extern void initialize_ext2_error_table(void); + +/* For compatibility with Heimdal */ +extern void initialize_ext2_error_table_r(struct et_list **list); + +#define ERROR_TABLE_BASE_ext2 (2133571328L) + +/* for compatibility with older versions... */ +#define init_ext2_err_tbl initialize_ext2_error_table +#define ext2_err_base ERROR_TABLE_BASE_ext2 +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h new file mode 100644 index 000000000..ca309c0d0 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + File: linux/ext2_ext_attr.h + + On-disk format of extended attributes for the ext2 filesystem. + + (C) 2000 Andreas Gruenbacher, +*/ + +/* Magic value in attribute blocks */ +#define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000 +#define EXT2_EXT_ATTR_MAGIC 0xEA020000 + +/* Maximum number of references to one attribute block */ +#define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 + +struct ext2_ext_attr_header { + __u32 h_magic; /* magic number for identification */ + __u32 h_refcount; /* reference count */ + __u32 h_blocks; /* number of disk blocks used */ + __u32 h_hash; /* hash value of all attributes */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct ext2_ext_attr_entry { + __u8 e_name_len; /* length of name */ + __u8 e_name_index; /* attribute name index */ + __u16 e_value_offs; /* offset in disk block of value */ + __u32 e_value_block; /* disk block attribute is stored on (n/i) */ + __u32 e_value_size; /* size of attribute value */ + __u32 e_hash; /* hash value of name and value */ +}; + +#define EXT2_EXT_ATTR_PAD_BITS 2 +#define EXT2_EXT_ATTR_PAD (1<e_name_len)) ) +#define EXT2_EXT_ATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) +#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL) +#define EXT2_EXT_ATTR_NAME(entry) \ + (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry)) +#define EXT2_XATTR_LEN(name_len) \ + (((name_len) + EXT2_EXT_ATTR_ROUND + \ + sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND) +#define EXT2_XATTR_SIZE(size) \ + (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h new file mode 100644 index 000000000..6f4f70862 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h @@ -0,0 +1,569 @@ +/* vi: set sw=4 ts=4: */ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ +#ifndef LINUX_EXT2_FS_H +#define LINUX_EXT2_FS_H 1 + +#include "ext2_types.h" /* Changed from linux/types.h */ + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT2_JOURNAL_INO 8 /* Journal inode */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 32000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) + +/* + * Macro-instructions used to manage fragments + */ +#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE +#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) +# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; +}; + +/* + * Data structures used by the directory indexing feature + * + * Note: all of the multibyte integer fields are little endian. + */ + +/* + * Note: dx_root_info is laid out so that if it should somehow get + * overlaid by a dirent the two low bits of the hash version will be + * zero. Therefore, the hash version mod 4 should never be 0. + * Sincerely, the paranoia department. + */ +struct ext2_dx_root_info { + __u32 reserved_zero; + __u8 hash_version; /* 0 now, 1 at release */ + __u8 info_length; /* 8 */ + __u8 indirect_levels; + __u8 unused_flags; +}; + +#define EXT2_HASH_LEGACY 0 +#define EXT2_HASH_HALF_MD4 1 +#define EXT2_HASH_TEA 2 + +#define EXT2_HASH_FLAG_INCOMPAT 0x1 + +struct ext2_dx_entry { + __u32 hash; + __u32 block; +}; + +struct ext2_dx_countlimit { + __u16 limit; + __u16 count; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) +/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ +#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) +#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 +#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ + +/* + * ioctl commands + */ +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_reserved1; + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + struct { + __u32 m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + struct { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +/* + * Permanent part of an large inode on the disk + */ +struct ext2_inode_large { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_reserved1; + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + struct { + __u32 m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + struct { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ + __u16 i_extra_isize; + __u16 i_pad1; +}; + +#define i_size_high i_dir_acl + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +/* + * Structure of the super block + */ +struct ext2_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; /* Default type of journal backup */ + __u16 s_reserved_word_pad; + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock group */ + __u32 s_mkfs_time; /* When the filesystem was created */ + __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ + __u32 s_reserved[172]; /* Padding to the end of the block */ +}; + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Journal inode backup types + */ +#define EXT3_JNL_BACKUP_BLOCKS 1 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 + + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h new file mode 100644 index 000000000..1900a7639 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * io.h --- the I/O manager abstraction + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ +#ifndef EXT2FS_EXT2_IO_H +#define EXT2FS_EXT2_IO_H 1 + +/* + * ext2_loff_t is defined here since unix_io.c needs it. + */ +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +/* llseek.c */ +/* ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); */ +#ifdef CONFIG_LFS +# define ext2fs_llseek lseek64 +#else +# define ext2fs_llseek lseek +#endif + +typedef struct struct_io_manager *io_manager; +typedef struct struct_io_channel *io_channel; + +#define CHANNEL_FLAGS_WRITETHROUGH 0x01 + +struct struct_io_channel { + errcode_t magic; + io_manager manager; + char *name; + int block_size; + errcode_t (*read_error)(io_channel channel, + unsigned long block, + int count, + void *data, + size_t size, + int actual_bytes_read, + errcode_t error); + errcode_t (*write_error)(io_channel channel, + unsigned long block, + int count, + const void *data, + size_t size, + int actual_bytes_written, + errcode_t error); + int refcount; + int flags; + int reserved[14]; + void *private_data; + void *app_data; +}; + +struct struct_io_manager { + errcode_t magic; + const char *name; + errcode_t (*open)(const char *name, int flags, io_channel *channel); + errcode_t (*close)(io_channel channel); + errcode_t (*set_blksize)(io_channel channel, int blksize); + errcode_t (*read_blk)(io_channel channel, unsigned long block, + int count, void *data); + errcode_t (*write_blk)(io_channel channel, unsigned long block, + int count, const void *data); + errcode_t (*flush)(io_channel channel); + errcode_t (*write_byte)(io_channel channel, unsigned long offset, + int count, const void *data); + errcode_t (*set_option)(io_channel channel, const char *option, + const char *arg); + int reserved[14]; +}; + +#define IO_FLAG_RW 1 + +/* + * Convenience functions.... + */ +#define io_channel_close(c) ((c)->manager->close((c))) +#define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s)) +#define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) +#define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) +#define io_channel_flush(c) ((c)->manager->flush((c))) +#define io_channel_bumpcount(c) ((c)->refcount++) + +/* io_manager.c */ +extern errcode_t io_channel_set_options(io_channel channel, + const char *options); +extern errcode_t io_channel_write_byte(io_channel channel, + unsigned long offset, + int count, const void *data); + +/* unix_io.c */ +extern io_manager unix_io_manager; + +/* test_io.c */ +extern io_manager test_io_manager, test_io_backing_manager; +extern void (*test_io_cb_read_blk) + (unsigned long block, int count, errcode_t err); +extern void (*test_io_cb_write_blk) + (unsigned long block, int count, errcode_t err); +extern void (*test_io_cb_set_blksize) + (int blksize, errcode_t err); + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h new file mode 100644 index 000000000..2c1196b8b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h @@ -0,0 +1,2 @@ +/* vi: set sw=4 ts=4: */ +#include diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h new file mode 100644 index 000000000..9f772016f --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h @@ -0,0 +1,922 @@ +/* vi: set sw=4 ts=4: */ +/* + * ext2fs.h --- ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ +#ifndef EXT2FS_EXT2FS_H +#define EXT2FS_EXT2FS_H 1 + + +#define EXT2FS_ATTR(x) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Where the master copy of the superblock is located, and how big + * superblocks are supposed to be. We define SUPERBLOCK_SIZE because + * the size of the superblock structure is not necessarily trustworthy + * (some versions have the padding set up so that the superblock is + * 1032 bytes long). + */ +#define SUPERBLOCK_OFFSET 1024 +#define SUPERBLOCK_SIZE 1024 + +/* + * The last ext2fs revision level that this version of the library is + * able to support. + */ +#define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include + +#include "ext2_types.h" +#include "ext2_fs.h" + +typedef __u32 ext2_ino_t; +typedef __u32 blk_t; +typedef __u32 dgrp_t; +typedef __u32 ext2_off_t; +typedef __s64 e2_blkcnt_t; +typedef __u32 ext2_dirhash_t; + +#include "ext2_io.h" +#include "ext2_err.h" + +typedef struct struct_ext2_filsys *ext2_filsys; + +struct ext2fs_struct_generic_bitmap { + errcode_t magic; + ext2_filsys fs; + __u32 start, end; + __u32 real_end; + char * description; + char * bitmap; + errcode_t base_error_code; + __u32 reserved[7]; +}; + +#define EXT2FS_MARK_ERROR 0 +#define EXT2FS_UNMARK_ERROR 1 +#define EXT2FS_TEST_ERROR 2 + +typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap; +typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap; +typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap; + +#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s) + +/* + * badblocks list definitions + */ + +typedef struct ext2_struct_u32_list *ext2_badblocks_list; +typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate; + +typedef struct ext2_struct_u32_list *ext2_u32_list; +typedef struct ext2_struct_u32_iterate *ext2_u32_iterate; + +/* old */ +typedef struct ext2_struct_u32_list *badblocks_list; +typedef struct ext2_struct_u32_iterate *badblocks_iterate; + +#define BADBLOCKS_FLAG_DIRTY 1 + +/* + * ext2_dblist structure and abstractions (see dblist.c) + */ +struct ext2_db_entry { + ext2_ino_t ino; + blk_t blk; + int blockcnt; +}; + +typedef struct ext2_struct_dblist *ext2_dblist; + +#define DBLIST_ABORT 1 + +/* + * ext2_fileio definitions + */ + +#define EXT2_FILE_WRITE 0x0001 +#define EXT2_FILE_CREATE 0x0002 + +#define EXT2_FILE_MASK 0x00FF + +#define EXT2_FILE_BUF_DIRTY 0x4000 +#define EXT2_FILE_BUF_VALID 0x2000 + +typedef struct ext2_file *ext2_file_t; + +#define EXT2_SEEK_SET 0 +#define EXT2_SEEK_CUR 1 +#define EXT2_SEEK_END 2 + +/* + * Flags for the ext2_filsys structure and for ext2fs_open() + */ +#define EXT2_FLAG_RW 0x01 +#define EXT2_FLAG_CHANGED 0x02 +#define EXT2_FLAG_DIRTY 0x04 +#define EXT2_FLAG_VALID 0x08 +#define EXT2_FLAG_IB_DIRTY 0x10 +#define EXT2_FLAG_BB_DIRTY 0x20 +#define EXT2_FLAG_SWAP_BYTES 0x40 +#define EXT2_FLAG_SWAP_BYTES_READ 0x80 +#define EXT2_FLAG_SWAP_BYTES_WRITE 0x100 +#define EXT2_FLAG_MASTER_SB_ONLY 0x200 +#define EXT2_FLAG_FORCE 0x400 +#define EXT2_FLAG_SUPER_ONLY 0x800 +#define EXT2_FLAG_JOURNAL_DEV_OK 0x1000 +#define EXT2_FLAG_IMAGE_FILE 0x2000 + +/* + * Special flag in the ext2 inode i_flag field that means that this is + * a new inode. (So that ext2_write_inode() can clear extra fields.) + */ +#define EXT2_NEW_INODE_FL 0x80000000 + +/* + * Flags for mkjournal + * + * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock + */ +#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 + +struct struct_ext2_filsys { + errcode_t magic; + io_channel io; + int flags; + char * device_name; + struct ext2_super_block * super; + unsigned int blocksize; + int fragsize; + dgrp_t group_desc_count; + unsigned long desc_blocks; + struct ext2_group_desc * group_desc; + int inode_blocks_per_group; + ext2fs_inode_bitmap inode_map; + ext2fs_block_bitmap block_map; + errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); + errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); + errcode_t (*write_bitmaps)(ext2_filsys fs); + errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode); + errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode); + ext2_badblocks_list badblocks; + ext2_dblist dblist; + __u32 stride; /* for mke2fs */ + struct ext2_super_block * orig_super; + struct ext2_image_hdr * image_header; + __u32 umask; + /* + * Reserved for future expansion + */ + __u32 reserved[8]; + + /* + * Reserved for the use of the calling application. + */ + void * priv_data; + + /* + * Inode cache + */ + struct ext2_inode_cache *icache; + io_channel image_io; +}; + +#include "bitops.h" + +/* + * Return flags for the block iterator functions + */ +#define BLOCK_CHANGED 1 +#define BLOCK_ABORT 2 +#define BLOCK_ERROR 4 + +/* + * Block interate flags + * + * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator + * function should be called on blocks where the block number is zero. + * This is used by ext2fs_expand_dir() to be able to add a new block + * to an inode. It can also be used for programs that want to be able + * to deal with files that contain "holes". + * + * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the + * indirect, doubly indirect, etc. blocks should be called after all + * of the blocks containined in the indirect blocks are processed. + * This is useful if you are going to be deallocating blocks from an + * inode. + * + * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be + * called for data blocks only. + * + * BLOCK_FLAG_NO_LARGE is for internal use only. It informs + * ext2fs_block_iterate2 that large files won't be accepted. + */ +#define BLOCK_FLAG_APPEND 1 +#define BLOCK_FLAG_HOLE 1 +#define BLOCK_FLAG_DEPTH_TRAVERSE 2 +#define BLOCK_FLAG_DATA_ONLY 4 + +#define BLOCK_FLAG_NO_LARGE 0x1000 + +/* + * Magic "block count" return values for the block iterator function. + */ +#define BLOCK_COUNT_IND (-1) +#define BLOCK_COUNT_DIND (-2) +#define BLOCK_COUNT_TIND (-3) +#define BLOCK_COUNT_TRANSLATOR (-4) + +#if 0 +/* + * Flags for ext2fs_move_blocks + */ +#define EXT2_BMOVE_GET_DBLIST 0x0001 +#define EXT2_BMOVE_DEBUG 0x0002 +#endif + +/* + * Flags for directory block reading and writing functions + */ +#define EXT2_DIRBLOCK_V2_STRUCT 0x0001 + +/* + * Return flags for the directory iterator functions + */ +#define DIRENT_CHANGED 1 +#define DIRENT_ABORT 2 +#define DIRENT_ERROR 3 + +/* + * Directory iterator flags + */ + +#define DIRENT_FLAG_INCLUDE_EMPTY 1 +#define DIRENT_FLAG_INCLUDE_REMOVED 2 + +#define DIRENT_DOT_FILE 1 +#define DIRENT_DOT_DOT_FILE 2 +#define DIRENT_OTHER_FILE 3 +#define DIRENT_DELETED_FILE 4 + +/* + * Inode scan definitions + */ +typedef struct ext2_struct_inode_scan *ext2_inode_scan; + +/* + * ext2fs_scan flags + */ +#define EXT2_SF_CHK_BADBLOCKS 0x0001 +#define EXT2_SF_BAD_INODE_BLK 0x0002 +#define EXT2_SF_BAD_EXTRA_BYTES 0x0004 +#define EXT2_SF_SKIP_MISSING_ITABLE 0x0008 + +/* + * ext2fs_check_if_mounted flags + */ +#define EXT2_MF_MOUNTED 1 +#define EXT2_MF_ISROOT 2 +#define EXT2_MF_READONLY 4 +#define EXT2_MF_SWAP 8 +#define EXT2_MF_BUSY 16 + +/* + * Ext2/linux mode flags. We define them here so that we don't need + * to depend on the OS's sys/stat.h, since we may be compiling on a + * non-Linux system. + */ +#define LINUX_S_IFMT 00170000 +#define LINUX_S_IFSOCK 0140000 +#define LINUX_S_IFLNK 0120000 +#define LINUX_S_IFREG 0100000 +#define LINUX_S_IFBLK 0060000 +#define LINUX_S_IFDIR 0040000 +#define LINUX_S_IFCHR 0020000 +#define LINUX_S_IFIFO 0010000 +#define LINUX_S_ISUID 0004000 +#define LINUX_S_ISGID 0002000 +#define LINUX_S_ISVTX 0001000 + +#define LINUX_S_IRWXU 00700 +#define LINUX_S_IRUSR 00400 +#define LINUX_S_IWUSR 00200 +#define LINUX_S_IXUSR 00100 + +#define LINUX_S_IRWXG 00070 +#define LINUX_S_IRGRP 00040 +#define LINUX_S_IWGRP 00020 +#define LINUX_S_IXGRP 00010 + +#define LINUX_S_IRWXO 00007 +#define LINUX_S_IROTH 00004 +#define LINUX_S_IWOTH 00002 +#define LINUX_S_IXOTH 00001 + +#define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK) +#define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG) +#define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR) +#define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR) +#define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK) +#define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO) +#define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK) + +/* + * ext2 size of an inode + */ +#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32)) + +/* + * ext2_icount_t abstraction + */ +#define EXT2_ICOUNT_OPT_INCREMENT 0x01 + +typedef struct ext2_icount *ext2_icount_t; + +/* + * Flags for ext2fs_bmap + */ +#define BMAP_ALLOC 0x0001 +#define BMAP_SET 0x0002 + +/* + * Flags for imager.c functions + */ +#define IMAGER_FLAG_INODEMAP 1 +#define IMAGER_FLAG_SPARSEWRITE 2 + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + + +/* + * For ext2 compression support + */ +#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff) +#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR) + +/* + * Features supported by this version of the library + */ +#define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ + EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ + EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ + EXT2_FEATURE_COMPAT_RESIZE_INODE|\ + EXT2_FEATURE_COMPAT_DIR_INDEX|\ + EXT2_FEATURE_COMPAT_EXT_ATTR) + +/* This #ifdef is temporary until compression is fully supported */ +#ifdef ENABLE_COMPRESSION +#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL +/* If the below warning bugs you, then have + `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your + environment at configure time. */ + #warning "Compression support is experimental" +#endif +#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT2_FEATURE_INCOMPAT_COMPRESSION|\ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ + EXT2_FEATURE_INCOMPAT_META_BG|\ + EXT3_FEATURE_INCOMPAT_RECOVER) +#else +#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ + EXT2_FEATURE_INCOMPAT_META_BG|\ + EXT3_FEATURE_INCOMPAT_RECOVER) +#endif +#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE) +/* + * function prototypes + */ + +/* alloc.c */ +extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, + ext2fs_inode_bitmap map, ext2_ino_t *ret); +extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, + ext2fs_block_bitmap map, blk_t *ret); +extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, + blk_t finish, int num, + ext2fs_block_bitmap map, + blk_t *ret); +extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, + char *block_buf, blk_t *ret); + +/* alloc_sb.c */ +extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, + dgrp_t group, + ext2fs_block_bitmap bmap); + +/* alloc_stats.c */ +void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse); +void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, + int inuse, int isdir); +void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse); + +/* alloc_tables.c */ +extern errcode_t ext2fs_allocate_tables(ext2_filsys fs); +extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, + ext2fs_block_bitmap bmap); + +/* badblocks.c */ +extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size); +extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk); +extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk); +extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk); +extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, + ext2_u32_iterate *ret); +extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk); +extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter); +extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest); +extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2); + +extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, + int size); +extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, + blk_t blk); +extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb, + blk_t blk); +extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk); +extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk); +extern errcode_t + ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret); +extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, + blk_t *blk); +extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter); +extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, + ext2_badblocks_list *dest); +extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1, + ext2_badblocks_list bb2); +extern int ext2fs_u32_list_count(ext2_u32_list bb); + +/* bb_compat */ +extern errcode_t badblocks_list_create(badblocks_list *ret, int size); +extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk); +extern int badblocks_list_test(badblocks_list bb, blk_t blk); +extern errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret); +extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk); +extern void badblocks_list_iterate_end(badblocks_iterate iter); +extern void badblocks_list_free(badblocks_list bb); + +/* bb_inode.c */ +extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs, + ext2_badblocks_list bb_list); + +/* bitmaps.c */ +extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); +extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); +extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start, + __u32 end, + __u32 real_end, + const char *descr, + ext2fs_generic_bitmap *ret); +extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_block_bitmap *ret); +extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, + const char *descr, + ext2fs_inode_bitmap *ret); +extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, + ext2_ino_t end, ext2_ino_t *oend); +extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, + blk_t end, blk_t *oend); +extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap); +extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap); +extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs); +extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs); + +/* block.c */ +extern errcode_t ext2fs_block_iterate(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *priv_data), + void *priv_data); +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); + +/* bmap.c */ +extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char *block_buf, int bmap_flags, + blk_t block, blk_t *phys_blk); + + +#if 0 +/* bmove.c */ +extern errcode_t ext2fs_move_blocks(ext2_filsys fs, + ext2fs_block_bitmap reserve, + ext2fs_block_bitmap alloc_map, + int flags); +#endif + +/* check_desc.c */ +extern errcode_t ext2fs_check_desc(ext2_filsys fs); + +/* closefs.c */ +extern errcode_t ext2fs_close(ext2_filsys fs); +extern errcode_t ext2fs_flush(ext2_filsys fs); +extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); +extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, + dgrp_t group, + blk_t *ret_super_blk, + blk_t *ret_old_desc_blk, + blk_t *ret_new_desc_blk, + int *ret_meta_bg); +extern void ext2fs_update_dynamic_rev(ext2_filsys fs); + +/* cmp_bitmaps.c */ +extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, + ext2fs_block_bitmap bm2); +extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, + ext2fs_inode_bitmap bm2); + +/* dblist.c */ + +extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); +extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); +extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, + blk_t blk, int blockcnt); +extern void ext2fs_dblist_sort(ext2_dblist dblist, + int (*sortfunc)(const void *, + const void *)); +extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, + blk_t blk, int blockcnt); +extern errcode_t ext2fs_copy_dblist(ext2_dblist src, + ext2_dblist *dest); +extern int ext2fs_dblist_count(ext2_dblist dblist); + +/* dblist_dir.c */ +extern errcode_t + ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); + +/* dirblock.c */ +extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags); +extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, + void *buf, int flags); + +/* dirhash.c */ +extern errcode_t ext2fs_dirhash(int version, const char *name, int len, + const __u32 *seed, + ext2_dirhash_t *ret_hash, + ext2_dirhash_t *ret_minor_hash); + + +/* dir_iterate.c */ +extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); +extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, + ext2_ino_t dir, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data); + +/* dupfs.c */ +extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); + +/* expanddir.c */ +extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); + +/* ext_attr.c */ +extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); +extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, + void *buf); +extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, + char *block_buf, + int adjust, __u32 *newcount); + +/* fileio.c */ +extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + int flags, ext2_file_t *ret); +extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, + int flags, ext2_file_t *ret); +extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file); +extern errcode_t ext2fs_file_close(ext2_file_t file); +extern errcode_t ext2fs_file_flush(ext2_file_t file); +extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf, + unsigned int wanted, unsigned int *got); +extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, + unsigned int nbytes, unsigned int *written); +extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, + int whence, __u64 *ret_pos); +extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, + int whence, ext2_off_t *ret_pos); +errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size); +extern ext2_off_t ext2fs_file_get_size(ext2_file_t file); +extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size); + +/* finddev.c */ +extern char *ext2fs_find_block_device(dev_t device); + +/* flushb.c */ +extern errcode_t ext2fs_sync_device(int fd, int flushb); + +/* freefs.c */ +extern void ext2fs_free(ext2_filsys fs); +extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap); +extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap); +extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap); +extern void ext2fs_free_dblist(ext2_dblist dblist); +extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb); +extern void ext2fs_u32_list_free(ext2_u32_list bb); + +/* getsize.c */ +extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks); + +/* getsectsize.c */ +errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize); + +/* imager.c */ +extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags); +extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags); + +/* ind_block.c */ +errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf); +errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf); + +/* initialize.c */ +extern errcode_t ext2fs_initialize(const char *name, int flags, + struct ext2_super_block *param, + io_manager manager, ext2_filsys *ret_fs); + +/* icount.c */ +extern void ext2fs_free_icount(ext2_icount_t icount); +extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t hint, ext2_icount_t *ret); +extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t *ret); +extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret); +extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, + __u16 count); +extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); +errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); + +/* inode.c */ +extern errcode_t ext2fs_flush_icache(ext2_filsys fs); +extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, + ext2_ino_t *ino, + struct ext2_inode *inode, + int bufsize); +extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, + ext2_inode_scan *ret_scan); +extern void ext2fs_close_inode_scan(ext2_inode_scan scan); +extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode); +extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group); +extern void ext2fs_set_inode_callback + (ext2_inode_scan scan, + errcode_t (*done_group)(ext2_filsys fs, + dgrp_t group, + void * priv_data), + void *done_group_data); +extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags); +extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, + int bufsize); +extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, + int bufsize); +extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode); +extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); +extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino); + +/* inode_io.c */ +extern io_manager inode_io_manager; +extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, + char **name); +extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char **name); + +/* ismounted.c */ +extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags); +extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, + char *mtpt, int mtlen); + +/* namei.c */ +extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, + int namelen, char *buf, ext2_ino_t *inode); +extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode); +errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode); +extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + ext2_ino_t inode, ext2_ino_t *res_inode); + +/* native.c */ +int ext2fs_native_flag(void); + +/* newdir.c */ +extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, + ext2_ino_t parent_ino, char **block); + +/* mkdir.c */ +extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, + const char *name); + +/* mkjournal.c */ +extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 size, int flags, + char **ret_jsb); +extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, + ext2_filsys journal_dev); +extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, + int flags); + +/* openfs.c */ +extern errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs); +extern errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs); +extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, + dgrp_t i); +errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); +errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); +errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); + +/* get_pathname.c */ +extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, + char **name); + +/* link.c */ +errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags); +errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags); + +/* read_bb.c */ +extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, + ext2_badblocks_list *bb_list); + +/* read_bb_file.c */ +extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void *priv_data, + void (*invalid)(ext2_filsys fs, + blk_t blk, + char *badstr, + void *priv_data)); +extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void (*invalid)(ext2_filsys fs, + blk_t blk)); + +/* res_gdt.c */ +extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); + +/* rs_bitmap.c */ +extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, + __u32 new_real_end, + ext2fs_generic_bitmap bmap); +extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_inode_bitmap bmap); +extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_block_bitmap bmap); +extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, + ext2fs_generic_bitmap *dest); + +/* swapfs.c */ +extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, + int has_header); +extern void ext2fs_swap_super(struct ext2_super_block * super); +extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); +extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, + struct ext2_inode_large *f, int hostorder, + int bufsize); +extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t, + struct ext2_inode *f, int hostorder); + +/* valid_blk.c */ +extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode); + +/* version.c */ +extern int ext2fs_parse_version_string(const char *ver_string); +extern int ext2fs_get_library_version(const char **ver_string, + const char **date_string); + +/* write_bb_file.c */ +extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, + unsigned int flags, + FILE *f); + + +/* inline functions */ +extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); +extern errcode_t ext2fs_free_mem(void *ptr); +extern errcode_t ext2fs_resize_mem(unsigned long old_size, + unsigned long size, void *ptr); +extern void ext2fs_mark_super_dirty(ext2_filsys fs); +extern void ext2fs_mark_changed(ext2_filsys fs); +extern int ext2fs_test_changed(ext2_filsys fs); +extern void ext2fs_mark_valid(ext2_filsys fs); +extern void ext2fs_unmark_valid(ext2_filsys fs); +extern int ext2fs_test_valid(ext2_filsys fs); +extern void ext2fs_mark_ib_dirty(ext2_filsys fs); +extern void ext2fs_mark_bb_dirty(ext2_filsys fs); +extern int ext2fs_test_ib_dirty(ext2_filsys fs); +extern int ext2fs_test_bb_dirty(ext2_filsys fs); +extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk); +extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino); +extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs, + struct ext2_inode *inode); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h new file mode 100644 index 000000000..7a02e9a8e --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h @@ -0,0 +1,87 @@ +/* vi: set sw=4 ts=4: */ +/* + * ext2fsP.h --- private header file for ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include "ext2fs.h" + +/* + * Badblocks list + */ +struct ext2_struct_u32_list { + int magic; + int num; + int size; + __u32 *list; + int badblocks_flags; +}; + +struct ext2_struct_u32_iterate { + int magic; + ext2_u32_list bb; + int ptr; +}; + + +/* + * Directory block iterator definition + */ +struct ext2_struct_dblist { + int magic; + ext2_filsys fs; + ext2_ino_t size; + ext2_ino_t count; + int sorted; + struct ext2_db_entry * list; +}; + +/* + * For directory iterators + */ +struct dir_context { + ext2_ino_t dir; + int flags; + char *buf; + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *priv_data; + errcode_t errcode; +}; + +/* + * Inode cache structure + */ +struct ext2_inode_cache { + void * buffer; + blk_t buffer_blk; + int cache_last; + int cache_size; + int refcount; + struct ext2_inode_cache_ent *cache; +}; + +struct ext2_inode_cache_ent { + ext2_ino_t ino; + struct ext2_inode inode; +}; + +/* Function prototypes */ + +extern int ext2fs_process_dir_block(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block, + int ref_offset, + void *priv_data); diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c new file mode 100644 index 000000000..7d37d232d --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c @@ -0,0 +1,365 @@ +/* vi: set sw=4 ts=4: */ +/* + * ext2fs.h --- ext2fs + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include "ext2fs.h" +#include "bitops.h" +#include + +/* + * Allocate memory + */ +errcode_t ext2fs_get_mem(unsigned long size, void *ptr) +{ + void **pp = (void **)ptr; + + *pp = malloc(size); + if (!*pp) + return EXT2_ET_NO_MEMORY; + return 0; +} + +/* + * Free memory + */ +errcode_t ext2fs_free_mem(void *ptr) +{ + void **pp = (void **)ptr; + + free(*pp); + *pp = 0; + return 0; +} + +/* + * Resize memory + */ +errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size, + unsigned long size, void *ptr) +{ + void *p; + + /* Use "memcpy" for pointer assignments here to avoid problems + * with C99 strict type aliasing rules. */ + memcpy(&p, ptr, sizeof (p)); + p = xrealloc(p, size); + memcpy(ptr, &p, sizeof (p)); + return 0; +} + +/* + * Mark a filesystem superblock as dirty + */ +void ext2fs_mark_super_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Mark a filesystem as changed + */ +void ext2fs_mark_changed(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_CHANGED; +} + +/* + * Check to see if a filesystem has changed + */ +int ext2fs_test_changed(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_CHANGED); +} + +/* + * Mark a filesystem as valid + */ +void ext2fs_mark_valid(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_VALID; +} + +/* + * Mark a filesystem as NOT valid + */ +void ext2fs_unmark_valid(ext2_filsys fs) +{ + fs->flags &= ~EXT2_FLAG_VALID; +} + +/* + * Check to see if a filesystem is valid + */ +int ext2fs_test_valid(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_VALID); +} + +/* + * Mark the inode bitmap as dirty + */ +void ext2fs_mark_ib_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Mark the block bitmap as dirty + */ +void ext2fs_mark_bb_dirty(ext2_filsys fs) +{ + fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; +} + +/* + * Check to see if a filesystem's inode bitmap is dirty + */ +int ext2fs_test_ib_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_IB_DIRTY); +} + +/* + * Check to see if a filesystem's block bitmap is dirty + */ +int ext2fs_test_bb_dirty(ext2_filsys fs) +{ + return (fs->flags & EXT2_FLAG_BB_DIRTY); +} + +/* + * Return the group # of a block + */ +int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) +{ + return (blk - fs->super->s_first_data_block) / + fs->super->s_blocks_per_group; +} + +/* + * Return the group # of an inode number + */ +int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino) +{ + return (ino - 1) / fs->super->s_inodes_per_group; +} + +blk_t ext2fs_inode_data_blocks(ext2_filsys fs, + struct ext2_inode *inode) +{ + return inode->i_blocks - + (inode->i_file_acl ? fs->blocksize >> 9 : 0); +} + + + + + + + + + +__u16 ext2fs_swab16(__u16 val) +{ + return (val >> 8) | (val << 8); +} + +__u32 ext2fs_swab32(__u32 val) +{ + return ((val>>24) | ((val>>8)&0xFF00) | + ((val<<8)&0xFF0000) | (val<<24)); +} + +int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); + +int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno) +{ + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno); + return 0; + } + return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap); +} + +int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) + bitmap, + block); +} + +int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + block); +} + +int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, + inode); +} + +void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + ext2fs_set_bit(block - bitmap->start, bitmap->bitmap); +} + +void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap); +} + +int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, + blk_t block) +{ + return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap); +} + +void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap); +} + +void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap); +} + +int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, + ext2_ino_t inode) +{ + return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap); +} + +blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) +{ + return bitmap->start; +} + +ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) +{ + return bitmap->start; +} + +blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) +{ + return bitmap->end; +} + +ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) +{ + return bitmap->end; +} + +int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, + block, bitmap->description); + return 0; + } + for (i=0; i < num; i++) { + if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) + return 0; + } + return 1; +} + +int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + for (i=0; i < num; i++) { + if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) + return 0; + } + return 1; +} + +void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); +} + +void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + for (i=0; i < num; i++) + ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); +} + +void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); +} + +void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + for (i=0; i < num; i++) + ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c b/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c new file mode 100644 index 000000000..a2ba12d45 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c @@ -0,0 +1,101 @@ +/* vi: set sw=4 ts=4: */ +/* + * ext_attr.c --- extended attribute blocks + * + * Copyright (C) 2001 Andreas Gruenbacher, + * + * Copyright (C) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2_ext_attr.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) +{ + errcode_t retval; + + retval = io_channel_read_blk(fs->io, block, 1, buf); + if (retval) + return retval; +#if BB_BIG_ENDIAN + if ((fs->flags & (EXT2_FLAG_SWAP_BYTES| + EXT2_FLAG_SWAP_BYTES_READ)) != 0) + ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); +#endif + return 0; +} + +errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) +{ + errcode_t retval; + char *write_buf; + char *buf = NULL; + + if (BB_BIG_ENDIAN && ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + write_buf = buf; + ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); + } else + write_buf = (char *) inbuf; + retval = io_channel_write_blk(fs->io, block, 1, write_buf); + if (buf) + ext2fs_free_mem(&buf); + if (!retval) + ext2fs_mark_changed(fs); + return retval; +} + +/* + * This function adjusts the reference count of the EA block. + */ +errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + errcode_t retval; + struct ext2_ext_attr_header *header; + char *buf = NULL; + + if ((blk >= fs->super->s_blocks_count) || + (blk < fs->super->s_first_data_block)) + return EXT2_ET_BAD_EA_BLOCK_NUM; + + if (!block_buf) { + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + block_buf = buf; + } + + retval = ext2fs_read_ext_attr(fs, blk, block_buf); + if (retval) + goto errout; + + header = (struct ext2_ext_attr_header *) block_buf; + header->h_refcount += adjust; + if (newcount) + *newcount = header->h_refcount; + + retval = ext2fs_write_ext_attr(fs, blk, block_buf); + if (retval) + goto errout; + +errout: + if (buf) + ext2fs_free_mem(&buf); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c b/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c new file mode 100644 index 000000000..5a5ad3ef8 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c @@ -0,0 +1,377 @@ +/* vi: set sw=4 ts=4: */ +/* + * fileio.c --- Simple file I/O routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct ext2_file { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode inode; + int flags; + __u64 pos; + blk_t blockno; + blk_t physblock; + char *buf; +}; + +#define BMAP_BUFFER (file->buf + fs->blocksize) + +errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + int flags, ext2_file_t *ret) +{ + ext2_file_t file; + errcode_t retval; + + /* + * Don't let caller create or open a file for writing if the + * filesystem is read-only. + */ + if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) && + !(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); + if (retval) + return retval; + + memset(file, 0, sizeof(struct ext2_file)); + file->magic = EXT2_ET_MAGIC_EXT2_FILE; + file->fs = fs; + file->ino = ino; + file->flags = flags & EXT2_FILE_MASK; + + if (inode) { + memcpy(&file->inode, inode, sizeof(struct ext2_inode)); + } else { + retval = ext2fs_read_inode(fs, ino, &file->inode); + if (retval) + goto fail; + } + + retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf); + if (retval) + goto fail; + + *ret = file; + return 0; + +fail: + ext2fs_free_mem(&file->buf); + ext2fs_free_mem(&file); + return retval; +} + +errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, + int flags, ext2_file_t *ret) +{ + return ext2fs_file_open2(fs, ino, NULL, flags, ret); +} + +/* + * This function returns the filesystem handle of a file from the structure + */ +ext2_filsys ext2fs_file_get_fs(ext2_file_t file) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return 0; + return file->fs; +} + +/* + * This function flushes the dirty block buffer out to disk if + * necessary. + */ +errcode_t ext2fs_file_flush(ext2_file_t file) +{ + errcode_t retval; + ext2_filsys fs; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + if (!(file->flags & EXT2_FILE_BUF_VALID) || + !(file->flags & EXT2_FILE_BUF_DIRTY)) + return 0; + + /* + * OK, the physical block hasn't been allocated yet. + * Allocate it. + */ + if (!file->physblock) { + retval = ext2fs_bmap(fs, file->ino, &file->inode, + BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, + file->blockno, &file->physblock); + if (retval) + return retval; + } + + retval = io_channel_write_blk(fs->io, file->physblock, + 1, file->buf); + if (retval) + return retval; + + file->flags &= ~EXT2_FILE_BUF_DIRTY; + + return retval; +} + +/* + * This function synchronizes the file's block buffer and the current + * file position, possibly invalidating block buffer if necessary + */ +static errcode_t sync_buffer_position(ext2_file_t file) +{ + blk_t b; + errcode_t retval; + + b = file->pos / file->fs->blocksize; + if (b != file->blockno) { + retval = ext2fs_file_flush(file); + if (retval) + return retval; + file->flags &= ~EXT2_FILE_BUF_VALID; + } + file->blockno = b; + return 0; +} + +/* + * This function loads the file's block buffer with valid data from + * the disk as necessary. + * + * If dontfill is true, then skip initializing the buffer since we're + * going to be replacing its entire contents anyway. If set, then the + * function basically only sets file->physblock and EXT2_FILE_BUF_VALID + */ +#define DONTFILL 1 +static errcode_t load_buffer(ext2_file_t file, int dontfill) +{ + ext2_filsys fs = file->fs; + errcode_t retval; + + if (!(file->flags & EXT2_FILE_BUF_VALID)) { + retval = ext2fs_bmap(fs, file->ino, &file->inode, + BMAP_BUFFER, 0, file->blockno, + &file->physblock); + if (retval) + return retval; + if (!dontfill) { + if (file->physblock) { + retval = io_channel_read_blk(fs->io, + file->physblock, + 1, file->buf); + if (retval) + return retval; + } else + memset(file->buf, 0, fs->blocksize); + } + file->flags |= EXT2_FILE_BUF_VALID; + } + return 0; +} + + +errcode_t ext2fs_file_close(ext2_file_t file) +{ + errcode_t retval; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + retval = ext2fs_file_flush(file); + + ext2fs_free_mem(&file->buf); + ext2fs_free_mem(&file); + + return retval; +} + + +errcode_t ext2fs_file_read(ext2_file_t file, void *buf, + unsigned int wanted, unsigned int *got) +{ + ext2_filsys fs; + errcode_t retval = 0; + unsigned int start, c, count = 0; + __u64 left; + char *ptr = (char *) buf; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { + retval = sync_buffer_position(file); + if (retval) + goto fail; + retval = load_buffer(file, 0); + if (retval) + goto fail; + + start = file->pos % fs->blocksize; + c = fs->blocksize - start; + if (c > wanted) + c = wanted; + left = EXT2_I_SIZE(&file->inode) - file->pos; + if (c > left) + c = left; + + memcpy(ptr, file->buf+start, c); + file->pos += c; + ptr += c; + count += c; + wanted -= c; + } + +fail: + if (got) + *got = count; + return retval; +} + + +errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, + unsigned int nbytes, unsigned int *written) +{ + ext2_filsys fs; + errcode_t retval = 0; + unsigned int start, c, count = 0; + const char *ptr = (const char *) buf; + + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + fs = file->fs; + + if (!(file->flags & EXT2_FILE_WRITE)) + return EXT2_ET_FILE_RO; + + while (nbytes > 0) { + retval = sync_buffer_position(file); + if (retval) + goto fail; + + start = file->pos % fs->blocksize; + c = fs->blocksize - start; + if (c > nbytes) + c = nbytes; + + /* + * We only need to do a read-modify-update cycle if + * we're doing a partial write. + */ + retval = load_buffer(file, (c == fs->blocksize)); + if (retval) + goto fail; + + file->flags |= EXT2_FILE_BUF_DIRTY; + memcpy(file->buf+start, ptr, c); + file->pos += c; + ptr += c; + count += c; + nbytes -= c; + } + +fail: + if (written) + *written = count; + return retval; +} + +errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, + int whence, __u64 *ret_pos) +{ + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + if (whence == EXT2_SEEK_SET) + file->pos = offset; + else if (whence == EXT2_SEEK_CUR) + file->pos += offset; + else if (whence == EXT2_SEEK_END) + file->pos = EXT2_I_SIZE(&file->inode) + offset; + else + return EXT2_ET_INVALID_ARGUMENT; + + if (ret_pos) + *ret_pos = file->pos; + + return 0; +} + +errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, + int whence, ext2_off_t *ret_pos) +{ + __u64 loffset, ret_loffset; + errcode_t retval; + + loffset = offset; + retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); + if (ret_pos) + *ret_pos = (ext2_off_t) ret_loffset; + return retval; +} + + +/* + * This function returns the size of the file, according to the inode + */ +errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) +{ + if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) + return EXT2_ET_MAGIC_EXT2_FILE; + *ret_size = EXT2_I_SIZE(&file->inode); + return 0; +} + +/* + * This function returns the size of the file, according to the inode + */ +ext2_off_t ext2fs_file_get_size(ext2_file_t file) +{ + __u64 size; + + if (ext2fs_file_get_lsize(file, &size)) + return 0; + if ((size >> 32) != 0) + return 0; + return size; +} + +/* + * This function sets the size of the file, truncating it if necessary + * + * XXX still need to call truncate + */ +errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) +{ + errcode_t retval; + EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); + + file->inode.i_size = size; + file->inode.i_size_high = 0; + if (file->ino) { + retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); + if (retval) + return retval; + } + + /* + * XXX truncate inode if necessary + */ + + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c b/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c new file mode 100644 index 000000000..e9e83fd7a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c @@ -0,0 +1,199 @@ +/* vi: set sw=4 ts=4: */ +/* + * finddev.c -- this routine attempts to find a particular device in + * /dev + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct dir_list { + char *name; + struct dir_list *next; +}; + +/* + * This function adds an entry to the directory list + */ +static void add_to_dirlist(const char *name, struct dir_list **list) +{ + struct dir_list *dp; + + dp = xmalloc(sizeof(struct dir_list)); + dp->name = xmalloc(strlen(name)+1); + strcpy(dp->name, name); + dp->next = *list; + *list = dp; +} + +/* + * This function frees a directory list + */ +static void free_dirlist(struct dir_list **list) +{ + struct dir_list *dp, *next; + + for (dp = *list; dp; dp = next) { + next = dp->next; + free(dp->name); + free(dp); + } + *list = 0; +} + +static int scan_dir(char *dir_name, dev_t device, struct dir_list **list, + char **ret_path) +{ + DIR *dir; + struct dirent *dp; + char path[1024], *cp; + int dirlen; + struct stat st; + + dirlen = strlen(dir_name); + if ((dir = opendir(dir_name)) == NULL) + return errno; + dp = readdir(dir); + while (dp) { + if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path)) + goto skip_to_next; + if (dp->d_name[0] == '.' && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) + goto skip_to_next; + sprintf(path, "%s/%s", dir_name, dp->d_name); + if (stat(path, &st) < 0) + goto skip_to_next; + if (S_ISDIR(st.st_mode)) + add_to_dirlist(path, list); + if (S_ISBLK(st.st_mode) && st.st_rdev == device) { + cp = xmalloc(strlen(path)+1); + strcpy(cp, path); + *ret_path = cp; + goto success; + } + skip_to_next: + dp = readdir(dir); + } +success: + closedir(dir); + return 0; +} + +/* + * This function finds the pathname to a block device with a given + * device number. It returns a pointer to allocated memory to the + * pathname on success, and NULL on failure. + */ +char *ext2fs_find_block_device(dev_t device) +{ + struct dir_list *list = NULL, *new_list = NULL; + struct dir_list *current; + char *ret_path = NULL; + + /* + * Add the starting directories to search... + */ + add_to_dirlist("/devices", &list); + add_to_dirlist("/devfs", &list); + add_to_dirlist("/dev", &list); + + while (list) { + current = list; + list = list->next; +#ifdef DEBUG + printf("Scanning directory %s\n", current->name); +#endif + scan_dir(current->name, device, &new_list, &ret_path); + free(current->name); + free(current); + if (ret_path) + break; + /* + * If we're done checking at this level, descend to + * the next level of subdirectories. (breadth-first) + */ + if (list == 0) { + list = new_list; + new_list = 0; + } + } + free_dirlist(&list); + free_dirlist(&new_list); + return ret_path; +} + + +#ifdef DEBUG +int main(int argc, char** argv) +{ + char *devname, *tmp; + int major, minor; + dev_t device; + const char *errmsg = "Cannot parse %s: %s\n"; + + if ((argc != 2) && (argc != 3)) { + fprintf(stderr, "Usage: %s device_number\n", argv[0]); + fprintf(stderr, "\t: %s major minor\n", argv[0]); + exit(1); + } + if (argc == 2) { + device = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "device number", argv[1]); + exit(1); + } + } else { + major = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "major number", argv[1]); + exit(1); + } + minor = strtoul(argv[2], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "minor number", argv[2]); + exit(1); + } + device = makedev(major, minor); + printf("Looking for device 0x%04x (%d:%d)\n", device, + major, minor); + } + devname = ext2fs_find_block_device(device); + if (devname) { + printf("Found device! %s\n", devname); + free(devname); + } else { + printf("Cannot find device.\n"); + } + return 0; +} + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c b/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c new file mode 100644 index 000000000..45ed76512 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c @@ -0,0 +1,83 @@ +/* vi: set sw=4 ts=4: */ +/* + * flushb.c --- Hides system-dependent information for both syncing a + * device to disk and to flush any buffers from disk cache. + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#include /* This may define BLKFLSBUF */ +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since + * not all portable header file does so for us. This really should be + * fixed in the glibc header files. (Recent glibcs appear to define + * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be + * defined anywhere portable.) Until then.... + */ +#ifdef __linux__ +#ifndef BLKFLSBUF +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#endif +#ifndef FDFLUSH +#define FDFLUSH _IO(2,0x4b) /* flush floppy disk */ +#endif +#endif + +/* + * This function will sync a device/file, and optionally attempt to + * flush the buffer cache. The latter is basically only useful for + * system benchmarks and for torturing systems in burn-in tests. :) + */ +errcode_t ext2fs_sync_device(int fd, int flushb) +{ + /* + * We always sync the device in case we're running on old + * kernels for which we can lose data if we don't. (There + * still is a race condition for those kernels, but this + * reduces it greatly.) + */ + if (fsync (fd) == -1) + return errno; + + if (flushb) { + +#ifdef BLKFLSBUF + if (ioctl (fd, BLKFLSBUF, 0) == 0) + return 0; +#else +#ifdef __GNUC__ +# warning BLKFLSBUF not defined +#endif /* __GNUC__ */ +#endif +#ifdef FDFLUSH + ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */ +#else +#ifdef __GNUC__ +# warning FDFLUSH not defined +#endif /* __GNUC__ */ +#endif + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c b/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c new file mode 100644 index 000000000..0c5d48b11 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c @@ -0,0 +1,127 @@ +/* vi: set sw=4 ts=4: */ +/* + * freefs.c --- free an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); + +void ext2fs_free(ext2_filsys fs) +{ + if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) + return; + if (fs->image_io != fs->io) { + if (fs->image_io) + io_channel_close(fs->image_io); + } + if (fs->io) { + io_channel_close(fs->io); + } + ext2fs_free_mem(&fs->device_name); + ext2fs_free_mem(&fs->super); + ext2fs_free_mem(&fs->orig_super); + ext2fs_free_mem(&fs->group_desc); + ext2fs_free_block_bitmap(fs->block_map); + ext2fs_free_inode_bitmap(fs->inode_map); + + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + + ext2fs_free_dblist(fs->dblist); + + if (fs->icache) + ext2fs_free_inode_cache(fs->icache); + + fs->magic = 0; + + ext2fs_free_mem(&fs); +} + +void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap) +{ + if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP)) + return; + + bitmap->magic = 0; + ext2fs_free_mem(&bitmap->description); + ext2fs_free_mem(&bitmap->bitmap); + ext2fs_free_mem(&bitmap); +} + +void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap) +{ + if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP)) + return; + + bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; + ext2fs_free_generic_bitmap(bitmap); +} + +void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap) +{ + if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP)) + return; + + bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; + ext2fs_free_generic_bitmap(bitmap); +} + +/* + * Free the inode cache structure + */ +static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) +{ + if (--icache->refcount) + return; + ext2fs_free_mem(&icache->buffer); + ext2fs_free_mem(&icache->cache); + icache->buffer_blk = 0; + ext2fs_free_mem(&icache); +} + +/* + * This procedure frees a badblocks list. + */ +void ext2fs_u32_list_free(ext2_u32_list bb) +{ + if (!bb || bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return; + + ext2fs_free_mem(&bb->list); + ext2fs_free_mem(&bb); +} + +void ext2fs_badblocks_list_free(ext2_badblocks_list bb) +{ + ext2fs_u32_list_free((ext2_u32_list) bb); +} + + +/* + * Free a directory block list + */ +void ext2fs_free_dblist(ext2_dblist dblist) +{ + if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) + return; + + ext2fs_free_mem(&dblist->list); + if (dblist->fs && dblist->fs->dblist == dblist) + dblist->fs->dblist = 0; + dblist->magic = 0; + ext2fs_free_mem(&dblist); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c b/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c new file mode 100644 index 000000000..d0869c919 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * gen_bitmap.c --- Generic bitmap routines that used to be inlined. + * + * Copyright (C) 2001 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno) +{ + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); + return 0; + } + return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap); +} + +int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno) +{ + if ((bitno < bitmap->start) || (bitno > bitmap->end)) { + ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); + return 0; + } + return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c b/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c new file mode 100644 index 000000000..2bb1cc25e --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * get_pathname.c --- do directry/inode -> name translation + * + * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * ext2fs_get_pathname(fs, dir, ino, name) + * + * This function translates takes two inode numbers into a + * string, placing the result in . is the containing + * directory inode, and is the inode number itself. If + * is zero, then ext2fs_get_pathname will return pathname + * of the directory . + * + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct get_pathname_struct { + ext2_ino_t search_ino; + ext2_ino_t parent; + char *name; + errcode_t errcode; +}; + +#ifdef __TURBOC__ +# pragma argsused +#endif +static int get_pathname_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct get_pathname_struct *gp; + errcode_t retval; + + gp = (struct get_pathname_struct *) priv_data; + + if (((dirent->name_len & 0xFF) == 2) && + !strncmp(dirent->name, "..", 2)) + gp->parent = dirent->inode; + if (dirent->inode == gp->search_ino) { + retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1, + &gp->name); + if (retval) { + gp->errcode = retval; + return DIRENT_ABORT; + } + strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF)); + gp->name[dirent->name_len & 0xFF] = '\0'; + return DIRENT_ABORT; + } + return 0; +} + +static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, + ext2_ino_t ino, int maxdepth, + char *buf, char **name) +{ + struct get_pathname_struct gp; + char *parent_name, *ret; + errcode_t retval; + + if (dir == ino) { + retval = ext2fs_get_mem(2, name); + if (retval) + return retval; + strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : "."); + return 0; + } + + if (!dir || (maxdepth < 0)) { + retval = ext2fs_get_mem(4, name); + if (retval) + return retval; + strcpy(*name, "..."); + return 0; + } + + gp.search_ino = ino; + gp.parent = 0; + gp.name = 0; + gp.errcode = 0; + + retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp); + if (retval) + goto cleanup; + if (gp.errcode) { + retval = gp.errcode; + goto cleanup; + } + + retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1, + buf, &parent_name); + if (retval) + goto cleanup; + if (!ino) { + *name = parent_name; + return 0; + } + + if (gp.name) + retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2, + &ret); + else + retval = ext2fs_get_mem(strlen(parent_name)+5, &ret); + if (retval) + goto cleanup; + + ret[0] = 0; + if (parent_name[1]) + strcat(ret, parent_name); + strcat(ret, "/"); + if (gp.name) + strcat(ret, gp.name); + else + strcat(ret, "???"); + *name = ret; + ext2fs_free_mem(&parent_name); + retval = 0; + +cleanup: + ext2fs_free_mem(&gp.name); + return retval; +} + +errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, + char **name) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + if (dir == ino) + ino = 0; + retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name); + ext2fs_free_mem(&buf); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c new file mode 100644 index 000000000..163ec65e5 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c @@ -0,0 +1,58 @@ +/* vi: set sw=4 ts=4: */ +/* + * getsectsize.c --- get the sector size of a device. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_LINUX_FD_H +#include +#include +#endif + +#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) +#define BLKSSZGET _IO(0x12,104)/* get block device sector size */ +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Returns the number of blocks in a partition + */ +errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize) +{ + int fd; + +#ifdef CONFIG_LFS + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef BLKSSZGET + if (ioctl(fd, BLKSSZGET, sectsize) >= 0) { + close(fd); + return 0; + } +#endif + *sectsize = 0; + close(fd); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c new file mode 100644 index 000000000..ee4bbb7b0 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c @@ -0,0 +1,291 @@ +/* vi: set sw=4 ts=4: */ +/* + * getsize.c --- get the size of a partition. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2003 VMware, Inc. + * + * Windows version of ext2fs_get_device_size by Chris Li, VMware. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_SYS_DISKLABEL_H +#include +#endif +#ifdef HAVE_SYS_DISK_H +#ifdef HAVE_SYS_QUEUE_H +#include /* for LIST_HEAD */ +#endif +#include +#endif +#ifdef __linux__ +#include +#endif + +#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#endif + +#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +#endif + +#ifdef APPLE_DARWIN +#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif /* APPLE_DARWIN */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__CYGWIN__) || defined(WIN32) +#include +#include + +#if (_WIN32_WINNT >= 0x0500) +#define HAVE_GET_FILE_SIZE_EX 1 +#endif + +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + HANDLE dev; + PARTITION_INFORMATION pi; + DISK_GEOMETRY gi; + DWORD retbytes; +#ifdef HAVE_GET_FILE_SIZE_EX + LARGE_INTEGER filesize; +#else + DWORD filesize; +#endif /* HAVE_GET_FILE_SIZE_EX */ + + dev = CreateFile(file, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE , + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (dev == INVALID_HANDLE_VALUE) + return EBADF; + if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, + &pi, sizeof(PARTITION_INFORMATION), + &pi, sizeof(PARTITION_INFORMATION), + &retbytes, NULL)) { + + *retblocks = pi.PartitionLength.QuadPart / blocksize; + + } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, + &gi, sizeof(DISK_GEOMETRY), + &gi, sizeof(DISK_GEOMETRY), + &retbytes, NULL)) { + + *retblocks = gi.BytesPerSector * + gi.SectorsPerTrack * + gi.TracksPerCylinder * + gi.Cylinders.QuadPart / blocksize; + +#ifdef HAVE_GET_FILE_SIZE_EX + } else if (GetFileSizeEx(dev, &filesize)) { + *retblocks = filesize.QuadPart / blocksize; + } +#else + } else { + filesize = GetFileSize(dev, NULL); + if (INVALID_FILE_SIZE != filesize) { + *retblocks = filesize / blocksize; + } + } +#endif /* HAVE_GET_FILE_SIZE_EX */ + + CloseHandle(dev); + return 0; +} + +#else + +static int valid_offset (int fd, ext2_loff_t offset) +{ + char ch; + + if (ext2fs_llseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +/* + * Returns the number of blocks in a partition + */ +errcode_t ext2fs_get_device_size(const char *file, int blocksize, + blk_t *retblocks) +{ + int fd; + int valid_blkgetsize64 = 1; +#ifdef __linux__ + struct utsname ut; +#endif + unsigned long long size64; + unsigned long size; + ext2_loff_t high, low; +#ifdef FDGETPRM + struct floppy_struct this_floppy; +#endif +#ifdef HAVE_SYS_DISKLABEL_H + int part; + struct disklabel lab; + struct partition *pp; + char ch; +#endif /* HAVE_SYS_DISKLABEL_H */ + +#ifdef CONFIG_LFS + fd = open64(file, O_RDONLY); +#else + fd = open(file, O_RDONLY); +#endif + if (fd < 0) + return errno; + +#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { + if ((sizeof(*retblocks) < sizeof(unsigned long long)) + && ((size64 / (blocksize / 512)) > 0xFFFFFFFF)) + return EFBIG; + close(fd); + *retblocks = size64 / (blocksize / 512); + return 0; + } +#endif + +#ifdef BLKGETSIZE64 +#ifdef __linux__ + uname(&ut); + if ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.')) + valid_blkgetsize64 = 0; +#endif + if (valid_blkgetsize64 && + ioctl(fd, BLKGETSIZE64, &size64) >= 0) { + if ((sizeof(*retblocks) < sizeof(unsigned long long)) + && ((size64 / blocksize) > 0xFFFFFFFF)) + return EFBIG; + close(fd); + *retblocks = size64 / blocksize; + return 0; + } +#endif + +#ifdef BLKGETSIZE + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + close(fd); + *retblocks = size / (blocksize / 512); + return 0; + } +#endif + +#ifdef FDGETPRM + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + close(fd); + *retblocks = this_floppy.size / (blocksize / 512); + return 0; + } +#endif + +#ifdef HAVE_SYS_DISKLABEL_H +#if defined(DIOCGMEDIASIZE) + { + off_t ms; + u_int bs; + if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { + *retblocks = ms / blocksize; + return 0; + } + } +#elif defined(DIOCGDINFO) + /* old disklabel interface */ + part = strlen(file) - 1; + if (part >= 0) { + ch = file[part]; + if (isdigit(ch)) + part = 0; + else if (ch >= 'a' && ch <= 'h') + part = ch - 'a'; + else + part = -1; + } + if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { + pp = &lab.d_partitions[part]; + if (pp->p_size) { + close(fd); + *retblocks = pp->p_size / (blocksize / 512); + return 0; + } + } +#endif /* defined(DIOCG*) */ +#endif /* HAVE_SYS_DISKLABEL_H */ + + /* + * OK, we couldn't figure it out by using a specialized ioctl, + * which is generally the best way. So do binary search to + * find the size of the partition. + */ + low = 0; + for (high = 1024; valid_offset (fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const ext2_loff_t mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + close(fd); + size64 = low + 1; + if ((sizeof(*retblocks) < sizeof(unsigned long long)) + && ((size64 / blocksize) > 0xFFFFFFFF)) + return EFBIG; + *retblocks = size64 / blocksize; + return 0; +} + +#endif /* WIN32 */ + +#ifdef DEBUG +int main(int argc, char **argv) +{ + blk_t blocks; + int retval; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + retval = ext2fs_get_device_size(argv[1], 1024, &blocks); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_get_device_size"); + exit(1); + } + printf("Device %s has %d 1k blocks.\n", argv[1], blocks); + exit(0); +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/icount.c b/e2fsprogs/old_e2fsprogs/ext2fs/icount.c new file mode 100644 index 000000000..7ab5f51f4 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/icount.c @@ -0,0 +1,467 @@ +/* vi: set sw=4 ts=4: */ +/* + * icount.c --- an efficient inode count abstraction + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * The data storage strategy used by icount relies on the observation + * that most inode counts are either zero (for non-allocated inodes), + * one (for most files), and only a few that are two or more + * (directories and files that are linked to more than one directory). + * + * Also, e2fsck tends to load the icount data sequentially. + * + * So, we use an inode bitmap to indicate which inodes have a count of + * one, and then use a sorted list to store the counts for inodes + * which are greater than one. + * + * We also use an optional bitmap to indicate which inodes are already + * in the sorted list, to speed up the use of this abstraction by + * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, + * so this extra bitmap avoids searching the sorted list to see if a + * particular inode is on the sorted list already. + */ + +struct ext2_icount_el { + ext2_ino_t ino; + __u16 count; +}; + +struct ext2_icount { + errcode_t magic; + ext2fs_inode_bitmap single; + ext2fs_inode_bitmap multiple; + ext2_ino_t count; + ext2_ino_t size; + ext2_ino_t num_inodes; + ext2_ino_t cursor; + struct ext2_icount_el *list; +}; + +void ext2fs_free_icount(ext2_icount_t icount) +{ + if (!icount) + return; + + icount->magic = 0; + ext2fs_free_mem(&icount->list); + ext2fs_free_inode_bitmap(icount->single); + ext2fs_free_inode_bitmap(icount->multiple); + ext2fs_free_mem(&icount); +} + +errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, + ext2_icount_t hint, ext2_icount_t *ret) +{ + ext2_icount_t icount; + errcode_t retval; + size_t bytes; + ext2_ino_t i; + + if (hint) { + EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); + if (hint->size > size) + size = (size_t) hint->size; + } + + retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); + if (retval) + return retval; + memset(icount, 0, sizeof(struct ext2_icount)); + + retval = ext2fs_allocate_inode_bitmap(fs, 0, + &icount->single); + if (retval) + goto errout; + + if (flags & EXT2_ICOUNT_OPT_INCREMENT) { + retval = ext2fs_allocate_inode_bitmap(fs, 0, + &icount->multiple); + if (retval) + goto errout; + } else + icount->multiple = 0; + + if (size) { + icount->size = size; + } else { + /* + * Figure out how many special case inode counts we will + * have. We know we will need one for each directory; + * we also need to reserve some extra room for file links + */ + retval = ext2fs_get_num_dirs(fs, &icount->size); + if (retval) + goto errout; + icount->size += fs->super->s_inodes_count / 50; + } + + bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); + retval = ext2fs_get_mem(bytes, &icount->list); + if (retval) + goto errout; + memset(icount->list, 0, bytes); + + icount->magic = EXT2_ET_MAGIC_ICOUNT; + icount->count = 0; + icount->cursor = 0; + icount->num_inodes = fs->super->s_inodes_count; + + /* + * Populate the sorted list with those entries which were + * found in the hint icount (since those are ones which will + * likely need to be in the sorted list this time around). + */ + if (hint) { + for (i=0; i < hint->count; i++) + icount->list[i].ino = hint->list[i].ino; + icount->count = hint->count; + } + + *ret = icount; + return 0; + +errout: + ext2fs_free_icount(icount); + return retval; +} + +errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, + unsigned int size, + ext2_icount_t *ret) +{ + return ext2fs_create_icount2(fs, flags, size, 0, ret); +} + +/* + * insert_icount_el() --- Insert a new entry into the sorted list at a + * specified position. + */ +static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, + ext2_ino_t ino, int pos) +{ + struct ext2_icount_el *el; + errcode_t retval; + ext2_ino_t new_size = 0; + int num; + + if (icount->count >= icount->size) { + if (icount->count) { + new_size = icount->list[(unsigned)icount->count-1].ino; + new_size = (ext2_ino_t) (icount->count * + ((float) icount->num_inodes / new_size)); + } + if (new_size < (icount->size + 100)) + new_size = icount->size + 100; + retval = ext2fs_resize_mem((size_t) icount->size * + sizeof(struct ext2_icount_el), + (size_t) new_size * + sizeof(struct ext2_icount_el), + &icount->list); + if (retval) + return 0; + icount->size = new_size; + } + num = (int) icount->count - pos; + if (num < 0) + return 0; /* should never happen */ + if (num) { + memmove(&icount->list[pos+1], &icount->list[pos], + sizeof(struct ext2_icount_el) * num); + } + icount->count++; + el = &icount->list[pos]; + el->count = 0; + el->ino = ino; + return el; +} + +/* + * get_icount_el() --- given an inode number, try to find icount + * information in the sorted list. If the create flag is set, + * and we can't find an entry, create one in the sorted list. + */ +static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, + ext2_ino_t ino, int create) +{ + float range; + int low, high, mid; + ext2_ino_t lowval, highval; + + if (!icount || !icount->list) + return 0; + + if (create && ((icount->count == 0) || + (ino > icount->list[(unsigned)icount->count-1].ino))) { + return insert_icount_el(icount, ino, (unsigned) icount->count); + } + if (icount->count == 0) + return 0; + + if (icount->cursor >= icount->count) + icount->cursor = 0; + if (ino == icount->list[icount->cursor].ino) + return &icount->list[icount->cursor++]; + low = 0; + high = (int) icount->count-1; + while (low <= high) { + if (low == high) + mid = low; + else { + /* Interpolate for efficiency */ + lowval = icount->list[low].ino; + highval = icount->list[high].ino; + + if (ino < lowval) + range = 0; + else if (ino > highval) + range = 1; + else + range = ((float) (ino - lowval)) / + (highval - lowval); + mid = low + ((int) (range * (high-low))); + } + if (ino == icount->list[mid].ino) { + icount->cursor = mid+1; + return &icount->list[mid]; + } + if (ino < icount->list[mid].ino) + high = mid-1; + else + low = mid+1; + } + /* + * If we need to create a new entry, it should be right at + * low (where high will be left at low-1). + */ + if (create) + return insert_icount_el(icount, ino, low); + return 0; +} + +errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) +{ + errcode_t ret = 0; + unsigned int i; + const char *bad = "bad icount"; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (icount->count > icount->size) { + fprintf(out, "%s: count > size\n", bad); + return EXT2_ET_INVALID_ARGUMENT; + } + for (i=1; i < icount->count; i++) { + if (icount->list[i-1].ino >= icount->list[i].ino) { + fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", + bad, i-1, icount->list[i-1].ino, + i, icount->list[i].ino); + ret = EXT2_ET_INVALID_ARGUMENT; + } + } + return ret; +} + +errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) +{ + struct ext2_icount_el *el; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + if (ext2fs_test_inode_bitmap(icount->single, ino)) { + *ret = 1; + return 0; + } + if (icount->multiple && + !ext2fs_test_inode_bitmap(icount->multiple, ino)) { + *ret = 0; + return 0; + } + el = get_icount_el(icount, ino, 0); + if (!el) { + *ret = 0; + return 0; + } + *ret = el->count; + return 0; +} + +errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + struct ext2_icount_el *el; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + if (ext2fs_test_inode_bitmap(icount->single, ino)) { + /* + * If the existing count is 1, then we know there is + * no entry in the list. + */ + el = get_icount_el(icount, ino, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + ext2fs_unmark_inode_bitmap(icount->single, ino); + el->count = 2; + } else if (icount->multiple) { + /* + * The count is either zero or greater than 1; if the + * inode is set in icount->multiple, then there should + * be an entry in the list, so find it using + * get_icount_el(). + */ + if (ext2fs_test_inode_bitmap(icount->multiple, ino)) { + el = get_icount_el(icount, ino, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + el->count++; + } else { + /* + * The count was zero; mark the single bitmap + * and return. + */ + zero_count: + ext2fs_mark_inode_bitmap(icount->single, ino); + if (ret) + *ret = 1; + return 0; + } + } else { + /* + * The count is either zero or greater than 1; try to + * find an entry in the list to determine which. + */ + el = get_icount_el(icount, ino, 0); + if (!el) { + /* No entry means the count was zero */ + goto zero_count; + } + el = get_icount_el(icount, ino, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + el->count++; + } + if (icount->multiple) + ext2fs_mark_inode_bitmap(icount->multiple, ino); + if (ret) + *ret = el->count; + return 0; +} + +errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + struct ext2_icount_el *el; + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (ext2fs_test_inode_bitmap(icount->single, ino)) { + ext2fs_unmark_inode_bitmap(icount->single, ino); + if (icount->multiple) + ext2fs_unmark_inode_bitmap(icount->multiple, ino); + else { + el = get_icount_el(icount, ino, 0); + if (el) + el->count = 0; + } + if (ret) + *ret = 0; + return 0; + } + + if (icount->multiple && + !ext2fs_test_inode_bitmap(icount->multiple, ino)) + return EXT2_ET_INVALID_ARGUMENT; + + el = get_icount_el(icount, ino, 0); + if (!el || el->count == 0) + return EXT2_ET_INVALID_ARGUMENT; + + el->count--; + if (el->count == 1) + ext2fs_mark_inode_bitmap(icount->single, ino); + if ((el->count == 0) && icount->multiple) + ext2fs_unmark_inode_bitmap(icount->multiple, ino); + + if (ret) + *ret = el->count; + return 0; +} + +errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, + __u16 count) +{ + struct ext2_icount_el *el; + + if (!ino || (ino > icount->num_inodes)) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); + + if (count == 1) { + ext2fs_mark_inode_bitmap(icount->single, ino); + if (icount->multiple) + ext2fs_unmark_inode_bitmap(icount->multiple, ino); + return 0; + } + if (count == 0) { + ext2fs_unmark_inode_bitmap(icount->single, ino); + if (icount->multiple) { + /* + * If the icount->multiple bitmap is enabled, + * we can just clear both bitmaps and we're done + */ + ext2fs_unmark_inode_bitmap(icount->multiple, ino); + } else { + el = get_icount_el(icount, ino, 0); + if (el) + el->count = 0; + } + return 0; + } + + /* + * Get the icount element + */ + el = get_icount_el(icount, ino, 1); + if (!el) + return EXT2_ET_NO_MEMORY; + el->count = count; + ext2fs_unmark_inode_bitmap(icount->single, ino); + if (icount->multiple) + ext2fs_mark_inode_bitmap(icount->multiple, ino); + return 0; +} + +ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) +{ + if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) + return 0; + + return icount->size; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/imager.c b/e2fsprogs/old_e2fsprogs/ext2fs/imager.c new file mode 100644 index 000000000..0f9cfcfaf --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/imager.c @@ -0,0 +1,377 @@ +/* vi: set sw=4 ts=4: */ +/* + * image.c --- writes out the critical parts of the filesystem as a + * flat file. + * + * Copyright (C) 2000 Theodore Ts'o. + * + * Note: this uses the POSIX IO interfaces, unlike most of the other + * functions in this library. So sue me. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef HAVE_TYPE_SSIZE_T +typedef int ssize_t; +#endif + +/* + * This function returns 1 if the specified block is all zeros + */ +static int check_zero_block(char *buf, int blocksize) +{ + char *cp = buf; + int left = blocksize; + + while (left > 0) { + if (*cp++) + return 0; + left--; + } + return 1; +} + +/* + * Write the inode table out as a single block. + */ +#define BUF_BLOCKS 32 + +errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) +{ + unsigned int group, left, c, d; + char *buf, *cp; + blk_t blk; + ssize_t actual; + errcode_t retval; + + buf = xmalloc(fs->blocksize * BUF_BLOCKS); + + for (group = 0; group < fs->group_desc_count; group++) { + blk = fs->group_desc[(unsigned)group].bg_inode_table; + if (!blk) + return EXT2_ET_MISSING_INODE_TABLE; + left = fs->inode_blocks_per_group; + while (left) { + c = BUF_BLOCKS; + if (c > left) + c = left; + retval = io_channel_read_blk(fs->io, blk, c, buf); + if (retval) + goto errout; + cp = buf; + while (c) { + if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { + d = c; + goto skip_sparse; + } + /* Skip zero blocks */ + if (check_zero_block(cp, fs->blocksize)) { + c--; + blk++; + left--; + cp += fs->blocksize; + lseek(fd, fs->blocksize, SEEK_CUR); + continue; + } + /* Find non-zero blocks */ + for (d=1; d < c; d++) { + if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) + break; + } + skip_sparse: + actual = write(fd, cp, fs->blocksize * d); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * d)) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + blk += d; + left -= d; + cp += fs->blocksize * d; + c -= d; + } + } + } + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Read in the inode table and stuff it into place + */ +errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + unsigned int group, c, left; + char *buf; + blk_t blk; + ssize_t actual; + errcode_t retval; + + buf = xmalloc(fs->blocksize * BUF_BLOCKS); + + for (group = 0; group < fs->group_desc_count; group++) { + blk = fs->group_desc[(unsigned)group].bg_inode_table; + if (!blk) { + retval = EXT2_ET_MISSING_INODE_TABLE; + goto errout; + } + left = fs->inode_blocks_per_group; + while (left) { + c = BUF_BLOCKS; + if (c > left) + c = left; + actual = read(fd, buf, fs->blocksize * c); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * c)) { + retval = EXT2_ET_SHORT_READ; + goto errout; + } + retval = io_channel_write_blk(fs->io, blk, c, buf); + if (retval) + goto errout; + + blk += c; + left -= c; + } + } + retval = ext2fs_flush_icache(fs); + +errout: + free(buf); + return retval; +} + +/* + * Write out superblock and group descriptors + */ +errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + char *buf, *cp; + ssize_t actual; + errcode_t retval; + + buf = xmalloc(fs->blocksize); + + /* + * Write out the superblock + */ + memset(buf, 0, fs->blocksize); + memcpy(buf, fs->super, SUPERBLOCK_SIZE); + actual = write(fd, buf, fs->blocksize); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) fs->blocksize) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + + /* + * Now write out the block group descriptors + */ + cp = (char *) fs->group_desc; + actual = write(fd, cp, fs->blocksize * fs->desc_blocks); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Read the superblock and group descriptors and overwrite them. + */ +errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, + int flags EXT2FS_ATTR((unused))) +{ + char *buf; + ssize_t actual, size; + errcode_t retval; + + size = fs->blocksize * (fs->group_desc_count + 1); + buf = xmalloc(size); + + /* + * Read it all in. + */ + actual = read(fd, buf, size); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != size) { + retval = EXT2_ET_SHORT_READ; + goto errout; + } + + /* + * Now copy in the superblock and group descriptors + */ + memcpy(fs->super, buf, SUPERBLOCK_SIZE); + + memcpy(fs->group_desc, buf + fs->blocksize, + fs->blocksize * fs->group_desc_count); + + retval = 0; + +errout: + free(buf); + return retval; +} + +/* + * Write the block/inode bitmaps. + */ +errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) +{ + char *ptr; + int c, size; + char zero_buf[1024]; + ssize_t actual; + errcode_t retval; + + if (flags & IMAGER_FLAG_INODEMAP) { + if (!fs->inode_map) { + retval = ext2fs_read_inode_bitmap(fs); + if (retval) + return retval; + } + ptr = fs->inode_map->bitmap; + size = (EXT2_INODES_PER_GROUP(fs->super) / 8); + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + return retval; + } + ptr = fs->block_map->bitmap; + size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + } + size = size * fs->group_desc_count; + + actual = write(fd, ptr, size); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != size) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + size = size % fs->blocksize; + memset(zero_buf, 0, sizeof(zero_buf)); + if (size) { + size = fs->blocksize - size; + while (size) { + c = size; + if (c > (int) sizeof(zero_buf)) + c = sizeof(zero_buf); + actual = write(fd, zero_buf, c); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != c) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + size -= c; + } + } + retval = 0; +errout: + return retval; +} + + +/* + * Read the block/inode bitmaps. + */ +errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) +{ + char *ptr, *buf = NULL; + int size; + ssize_t actual; + errcode_t retval; + + if (flags & IMAGER_FLAG_INODEMAP) { + if (!fs->inode_map) { + retval = ext2fs_read_inode_bitmap(fs); + if (retval) + return retval; + } + ptr = fs->inode_map->bitmap; + size = (EXT2_INODES_PER_GROUP(fs->super) / 8); + } else { + if (!fs->block_map) { + retval = ext2fs_read_block_bitmap(fs); + if (retval) + return retval; + } + ptr = fs->block_map->bitmap; + size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + } + size = size * fs->group_desc_count; + + buf = xmalloc(size); + + actual = read(fd, buf, size); + if (actual == -1) { + retval = errno; + goto errout; + } + if (actual != size) { + retval = EXT2_ET_SHORT_WRITE; + goto errout; + } + memcpy(ptr, buf, size); + + retval = 0; +errout: + free(buf); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c b/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c new file mode 100644 index 000000000..a1038300b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * ind_block.c --- indirect block I/O routines + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf) +{ + errcode_t retval; +#if BB_BIG_ENDIAN + blk_t *block_nr; + int i; + int limit = fs->blocksize >> 2; +#endif + + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) && + (fs->io != fs->image_io)) + memset(buf, 0, fs->blocksize); + else { + retval = io_channel_read_blk(fs->io, blk, 1, buf); + if (retval) + return retval; + } +#if BB_BIG_ENDIAN + if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) { + block_nr = (blk_t *) buf; + for (i = 0; i < limit; i++, block_nr++) + *block_nr = ext2fs_swab32(*block_nr); + } +#endif + return 0; +} + +errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf) +{ +#if BB_BIG_ENDIAN + blk_t *block_nr; + int i; + int limit = fs->blocksize >> 2; +#endif + + if (fs->flags & EXT2_FLAG_IMAGE_FILE) + return 0; + +#if BB_BIG_ENDIAN + if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) { + block_nr = (blk_t *) buf; + for (i = 0; i < limit; i++, block_nr++) + *block_nr = ext2fs_swab32(*block_nr); + } +#endif + return io_channel_write_blk(fs->io, blk, 1, buf); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c b/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c new file mode 100644 index 000000000..ef1d34379 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c @@ -0,0 +1,388 @@ +/* vi: set sw=4 ts=4: */ +/* + * initialize.c --- initialize a filesystem handle given superblock + * parameters. Used by mke2fs when initializing a filesystem. + * + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#if defined(__linux__) && defined(EXT2_OS_LINUX) +#define CREATOR_OS EXT2_OS_LINUX +#else +#if defined(__GNU__) && defined(EXT2_OS_HURD) +#define CREATOR_OS EXT2_OS_HURD +#else +#if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) +#define CREATOR_OS EXT2_OS_FREEBSD +#else +#if defined(LITES) && defined(EXT2_OS_LITES) +#define CREATOR_OS EXT2_OS_LITES +#else +#define CREATOR_OS EXT2_OS_LINUX /* by default */ +#endif /* defined(LITES) && defined(EXT2_OS_LITES) */ +#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ +#endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ +#endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ + +/* + * Note we override the kernel include file's idea of what the default + * check interval (never) should be. It's a good idea to check at + * least *occasionally*, specially since servers will never rarely get + * to reboot, since Linux is so robust these days. :-) + * + * 180 days (six months) seems like a good value. + */ +#ifdef EXT2_DFL_CHECKINTERVAL +#undef EXT2_DFL_CHECKINTERVAL +#endif +#define EXT2_DFL_CHECKINTERVAL (86400L * 180L) + +/* + * Calculate the number of GDT blocks to reserve for online filesystem growth. + * The absolute maximum number of GDT blocks we can reserve is determined by + * the number of block pointers that can fit into a single block. + */ +static int calc_reserved_gdt_blocks(ext2_filsys fs) +{ + struct ext2_super_block *sb = fs->super; + unsigned long bpg = sb->s_blocks_per_group; + unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc); + unsigned long max_blocks = 0xffffffff; + unsigned long rsv_groups; + int rsv_gdb; + + /* We set it at 1024x the current filesystem size, or + * the upper block count limit (2^32), whichever is lower. + */ + if (sb->s_blocks_count < max_blocks / 1024) + max_blocks = sb->s_blocks_count * 1024; + rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg; + rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks; + if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) + rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); +#ifdef RES_GDT_DEBUG + printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %lu\n", + max_blocks, rsv_groups, rsv_gdb); +#endif + + return rsv_gdb; +} + +errcode_t ext2fs_initialize(const char *name, int flags, + struct ext2_super_block *param, + io_manager manager, ext2_filsys *ret_fs) +{ + ext2_filsys fs; + errcode_t retval; + struct ext2_super_block *super; + int frags_per_block; + unsigned int rem; + unsigned int overhead = 0; + blk_t group_block; + unsigned int ipg; + dgrp_t i; + blk_t numblocks; + int rsv_gdt; + char *buf; + + if (!param || !param->s_blocks_count) + return EXT2_ET_INVALID_ARGUMENT; + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; + fs->flags = flags | EXT2_FLAG_RW; + fs->umask = 022; +#ifdef WORDS_BIGENDIAN + fs->flags |= EXT2_FLAG_SWAP_BYTES; +#endif + retval = manager->open(name, IO_FLAG_RW, &fs->io); + if (retval) + goto cleanup; + fs->image_io = fs->io; + fs->io->app_data = fs; + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + + strcpy(fs->device_name, name); + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); + if (retval) + goto cleanup; + fs->super = super; + + memset(super, 0, SUPERBLOCK_SIZE); + +#define set_field(field, default) (super->field = param->field ? \ + param->field : (default)) + + super->s_magic = EXT2_SUPER_MAGIC; + super->s_state = EXT2_VALID_FS; + + set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ + set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ + set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); + set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); + set_field(s_errors, EXT2_ERRORS_DEFAULT); + set_field(s_feature_compat, 0); + set_field(s_feature_incompat, 0); + set_field(s_feature_ro_compat, 0); + set_field(s_first_meta_bg, 0); + if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } + + set_field(s_rev_level, EXT2_GOOD_OLD_REV); + if (super->s_rev_level >= EXT2_DYNAMIC_REV) { + set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); + set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); + } + + set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); + super->s_mkfs_time = super->s_lastcheck = time(NULL); + + super->s_creator_os = CREATOR_OS; + + fs->blocksize = EXT2_BLOCK_SIZE(super); + fs->fragsize = EXT2_FRAG_SIZE(super); + frags_per_block = fs->blocksize / fs->fragsize; + + /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ + set_field(s_blocks_per_group, fs->blocksize * 8); + if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) + super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); + super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; + + super->s_blocks_count = param->s_blocks_count; + super->s_r_blocks_count = param->s_r_blocks_count; + if (super->s_r_blocks_count >= param->s_blocks_count) { + retval = EXT2_ET_INVALID_ARGUMENT; + goto cleanup; + } + + /* + * If we're creating an external journal device, we don't need + * to bother with the rest. + */ + if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + fs->group_desc_count = 0; + ext2fs_mark_super_dirty(fs); + *ret_fs = fs; + return 0; + } + +retry: + fs->group_desc_count = (super->s_blocks_count - + super->s_first_data_block + + EXT2_BLOCKS_PER_GROUP(super) - 1) + / EXT2_BLOCKS_PER_GROUP(super); + if (fs->group_desc_count == 0) { + retval = EXT2_ET_TOOSMALL; + goto cleanup; + } + fs->desc_blocks = (fs->group_desc_count + + EXT2_DESC_PER_BLOCK(super) - 1) + / EXT2_DESC_PER_BLOCK(super); + + i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; + set_field(s_inodes_count, super->s_blocks_count / i); + + /* + * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so + * that we have enough inodes for the filesystem(!) + */ + if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) + super->s_inodes_count = EXT2_FIRST_INODE(super)+1; + + /* + * There should be at least as many inodes as the user + * requested. Figure out how many inodes per group that + * should be. But make sure that we don't allocate more than + * one bitmap's worth of inodes each group. + */ + ipg = (super->s_inodes_count + fs->group_desc_count - 1) / + fs->group_desc_count; + if (ipg > fs->blocksize * 8) { + if (super->s_blocks_per_group >= 256) { + /* Try again with slightly different parameters */ + super->s_blocks_per_group -= 8; + super->s_blocks_count = param->s_blocks_count; + super->s_frags_per_group = super->s_blocks_per_group * + frags_per_block; + goto retry; + } else + return EXT2_ET_TOO_MANY_INODES; + } + + if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) + ipg = EXT2_MAX_INODES_PER_GROUP(super); + + super->s_inodes_per_group = ipg; + if (super->s_inodes_count > ipg * fs->group_desc_count) + super->s_inodes_count = ipg * fs->group_desc_count; + + /* + * Make sure the number of inodes per group completely fills + * the inode table blocks in the descriptor. If not, add some + * additional inodes/group. Waste not, want not... + */ + fs->inode_blocks_per_group = (((super->s_inodes_per_group * + EXT2_INODE_SIZE(super)) + + EXT2_BLOCK_SIZE(super) - 1) / + EXT2_BLOCK_SIZE(super)); + super->s_inodes_per_group = ((fs->inode_blocks_per_group * + EXT2_BLOCK_SIZE(super)) / + EXT2_INODE_SIZE(super)); + /* + * Finally, make sure the number of inodes per group is a + * multiple of 8. This is needed to simplify the bitmap + * splicing code. + */ + super->s_inodes_per_group &= ~7; + fs->inode_blocks_per_group = (((super->s_inodes_per_group * + EXT2_INODE_SIZE(super)) + + EXT2_BLOCK_SIZE(super) - 1) / + EXT2_BLOCK_SIZE(super)); + + /* + * adjust inode count to reflect the adjusted inodes_per_group + */ + super->s_inodes_count = super->s_inodes_per_group * + fs->group_desc_count; + super->s_free_inodes_count = super->s_inodes_count; + + /* + * check the number of reserved group descriptor table blocks + */ + if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) + rsv_gdt = calc_reserved_gdt_blocks(fs); + else + rsv_gdt = 0; + set_field(s_reserved_gdt_blocks, rsv_gdt); + if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { + retval = EXT2_ET_RES_GDT_BLOCKS; + goto cleanup; + } + + /* + * Overhead is the number of bookkeeping blocks per group. It + * includes the superblock backup, the group descriptor + * backups, the inode bitmap, the block bitmap, and the inode + * table. + */ + + overhead = (int) (2 + fs->inode_blocks_per_group); + + if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) + overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; + + /* This can only happen if the user requested too many inodes */ + if (overhead > super->s_blocks_per_group) + return EXT2_ET_TOO_MANY_INODES; + + /* + * See if the last group is big enough to support the + * necessary data structures. If not, we need to get rid of + * it. + */ + rem = ((super->s_blocks_count - super->s_first_data_block) % + super->s_blocks_per_group); + if ((fs->group_desc_count == 1) && rem && (rem < overhead)) + return EXT2_ET_TOOSMALL; + if (rem && (rem < overhead+50)) { + super->s_blocks_count -= rem; + goto retry; + } + + /* + * At this point we know how big the filesystem will be. So + * we can do any and all allocations that depend on the block + * count. + */ + + retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); + if (retval) + goto cleanup; + + sprintf(buf, "block bitmap for %s", fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); + if (retval) + goto cleanup; + + sprintf(buf, "inode bitmap for %s", fs->device_name); + retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); + if (retval) + goto cleanup; + + ext2fs_free_mem(&buf); + + retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, + &fs->group_desc); + if (retval) + goto cleanup; + + memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); + + /* + * Reserve the superblock and group descriptors for each + * group, and fill in the correct group statistics for group. + * Note that although the block bitmap, inode bitmap, and + * inode table have not been allocated (and in fact won't be + * by this routine), they are accounted for nevertheless. + */ + group_block = super->s_first_data_block; + super->s_free_blocks_count = 0; + for (i = 0; i < fs->group_desc_count; i++) { + numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); + + super->s_free_blocks_count += numblocks; + fs->group_desc[i].bg_free_blocks_count = numblocks; + fs->group_desc[i].bg_free_inodes_count = + fs->super->s_inodes_per_group; + fs->group_desc[i].bg_used_dirs_count = 0; + + group_block += super->s_blocks_per_group; + } + + ext2fs_mark_super_dirty(fs); + ext2fs_mark_bb_dirty(fs); + ext2fs_mark_ib_dirty(fs); + + io_channel_set_blksize(fs->io, fs->blocksize); + + *ret_fs = fs; + return 0; +cleanup: + ext2fs_free(fs); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/inline.c b/e2fsprogs/old_e2fsprogs/ext2fs/inline.c new file mode 100644 index 000000000..7457b9396 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/inline.c @@ -0,0 +1,32 @@ +/* vi: set sw=4 ts=4: */ +/* + * inline.c --- Includes the inlined functions defined in the header + * files as standalone functions, in case the application program + * is compiled with inlining turned off. + * + * Copyright (C) 1993, 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#define INCLUDE_INLINE_FUNCS +#include "ext2fs.h" diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/inode.c b/e2fsprogs/old_e2fsprogs/ext2fs/inode.c new file mode 100644 index 000000000..7a1d5c94b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/inode.c @@ -0,0 +1,766 @@ +/* vi: set sw=4 ts=4: */ +/* + * inode.c --- utility routines to read and write inodes + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fsP.h" +#include "e2image.h" + +struct ext2_struct_inode_scan { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t current_inode; + blk_t current_block; + dgrp_t current_group; + ext2_ino_t inodes_left; + blk_t blocks_left; + dgrp_t groups_left; + blk_t inode_buffer_blocks; + char * inode_buffer; + int inode_size; + char * ptr; + int bytes_left; + char *temp_buffer; + errcode_t (*done_group)(ext2_filsys fs, + dgrp_t group, + void * priv_data); + void * done_group_data; + int bad_block_ptr; + int scan_flags; + int reserved[6]; +}; + +/* + * This routine flushes the icache, if it exists. + */ +errcode_t ext2fs_flush_icache(ext2_filsys fs) +{ + int i; + + if (!fs->icache) + return 0; + + for (i=0; i < fs->icache->cache_size; i++) + fs->icache->cache[i].ino = 0; + + fs->icache->buffer_blk = 0; + return 0; +} + +static errcode_t create_icache(ext2_filsys fs) +{ + errcode_t retval; + + if (fs->icache) + return 0; + retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); + if (retval) + return retval; + + memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); + retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); + if (retval) { + ext2fs_free_mem(&fs->icache); + return retval; + } + fs->icache->buffer_blk = 0; + fs->icache->cache_last = -1; + fs->icache->cache_size = 4; + fs->icache->refcount = 1; + retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent) + * fs->icache->cache_size, + &fs->icache->cache); + if (retval) { + ext2fs_free_mem(&fs->icache->buffer); + ext2fs_free_mem(&fs->icache); + return retval; + } + ext2fs_flush_icache(fs); + return 0; +} + +errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, + ext2_inode_scan *ret_scan) +{ + ext2_inode_scan scan; + errcode_t retval; + errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * If fs->badblocks isn't set, then set it --- since the inode + * scanning functions require it. + */ + if (fs->badblocks == 0) { + /* + * Temporarly save fs->get_blocks and set it to zero, + * for compatibility with old e2fsck's. + */ + save_get_blocks = fs->get_blocks; + fs->get_blocks = 0; + retval = ext2fs_read_bb_inode(fs, &fs->badblocks); + if (retval) { + ext2fs_badblocks_list_free(fs->badblocks); + fs->badblocks = 0; + } + fs->get_blocks = save_get_blocks; + } + + retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); + if (retval) + return retval; + memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); + + scan->magic = EXT2_ET_MAGIC_INODE_SCAN; + scan->fs = fs; + scan->inode_size = EXT2_INODE_SIZE(fs->super); + scan->bytes_left = 0; + scan->current_group = 0; + scan->groups_left = fs->group_desc_count - 1; + scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; + scan->current_block = scan->fs-> + group_desc[scan->current_group].bg_inode_table; + scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); + scan->blocks_left = scan->fs->inode_blocks_per_group; + retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks * + fs->blocksize), + &scan->inode_buffer); + scan->done_group = 0; + scan->done_group_data = 0; + scan->bad_block_ptr = 0; + if (retval) { + ext2fs_free_mem(&scan); + return retval; + } + retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); + if (retval) { + ext2fs_free_mem(&scan->inode_buffer); + ext2fs_free_mem(&scan); + return retval; + } + if (scan->fs->badblocks && scan->fs->badblocks->num) + scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; + *ret_scan = scan; + return 0; +} + +void ext2fs_close_inode_scan(ext2_inode_scan scan) +{ + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return; + + ext2fs_free_mem(&scan->inode_buffer); + scan->inode_buffer = NULL; + ext2fs_free_mem(&scan->temp_buffer); + scan->temp_buffer = NULL; + ext2fs_free_mem(&scan); +} + +void ext2fs_set_inode_callback(ext2_inode_scan scan, + errcode_t (*done_group)(ext2_filsys fs, + dgrp_t group, + void * priv_data), + void *done_group_data) +{ + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return; + + scan->done_group = done_group; + scan->done_group_data = done_group_data; +} + +int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, + int clear_flags) +{ + int old_flags; + + if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) + return 0; + + old_flags = scan->scan_flags; + scan->scan_flags &= ~clear_flags; + scan->scan_flags |= set_flags; + return old_flags; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * get ready to read in a new blockgroup. + */ +static errcode_t get_next_blockgroup(ext2_inode_scan scan) +{ + scan->current_group++; + scan->groups_left--; + + scan->current_block = scan->fs-> + group_desc[scan->current_group].bg_inode_table; + + scan->current_inode = scan->current_group * + EXT2_INODES_PER_GROUP(scan->fs->super); + + scan->bytes_left = 0; + scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); + scan->blocks_left = scan->fs->inode_blocks_per_group; + return 0; +} + +errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, + int group) +{ + scan->current_group = group - 1; + scan->groups_left = scan->fs->group_desc_count - group; + return get_next_blockgroup(scan); +} + +/* + * This function is called by get_next_blocks() to check for bad + * blocks in the inode table. + * + * This function assumes that badblocks_list->list is sorted in + * increasing order. + */ +static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, + blk_t *num_blocks) +{ + blk_t blk = scan->current_block; + badblocks_list bb = scan->fs->badblocks; + + /* + * If the inode table is missing, then obviously there are no + * bad blocks. :-) + */ + if (blk == 0) + return 0; + + /* + * If the current block is greater than the bad block listed + * in the bad block list, then advance the pointer until this + * is no longer the case. If we run out of bad blocks, then + * we don't need to do any more checking! + */ + while (blk > bb->list[scan->bad_block_ptr]) { + if (++scan->bad_block_ptr >= bb->num) { + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + } + + /* + * If the current block is equal to the bad block listed in + * the bad block list, then handle that one block specially. + * (We could try to handle runs of bad blocks, but that + * only increases CPU efficiency by a small amount, at the + * expense of a huge expense of code complexity, and for an + * uncommon case at that.) + */ + if (blk == bb->list[scan->bad_block_ptr]) { + scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; + *num_blocks = 1; + if (++scan->bad_block_ptr >= bb->num) + scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; + return 0; + } + + /* + * If there is a bad block in the range that we're about to + * read in, adjust the number of blocks to read so that we we + * don't read in the bad block. (Then the next block to read + * will be the bad block, which is handled in the above case.) + */ + if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) + *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); + + return 0; +} + +/* + * This function is called by ext2fs_get_next_inode when it needs to + * read in more blocks from the current blockgroup's inode table. + */ +static errcode_t get_next_blocks(ext2_inode_scan scan) +{ + blk_t num_blocks; + errcode_t retval; + + /* + * Figure out how many blocks to read; we read at most + * inode_buffer_blocks, and perhaps less if there aren't that + * many blocks left to read. + */ + num_blocks = scan->inode_buffer_blocks; + if (num_blocks > scan->blocks_left) + num_blocks = scan->blocks_left; + + /* + * If the past block "read" was a bad block, then mark the + * left-over extra bytes as also being bad. + */ + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { + if (scan->bytes_left) + scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; + scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; + } + + /* + * Do inode bad block processing, if necessary. + */ + if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { + retval = check_for_inode_bad_blocks(scan, &num_blocks); + if (retval) + return retval; + } + + if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || + (scan->current_block == 0)) { + memset(scan->inode_buffer, 0, + (size_t) num_blocks * scan->fs->blocksize); + } else { + retval = io_channel_read_blk(scan->fs->io, + scan->current_block, + (int) num_blocks, + scan->inode_buffer); + if (retval) + return EXT2_ET_NEXT_INODE_READ; + } + scan->ptr = scan->inode_buffer; + scan->bytes_left = num_blocks * scan->fs->blocksize; + + scan->blocks_left -= num_blocks; + if (scan->current_block) + scan->current_block += num_blocks; + return 0; +} + +errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode, int bufsize) +{ + errcode_t retval; + int extra_bytes = 0; + + EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); + + /* + * Do we need to start reading a new block group? + */ + if (scan->inodes_left <= 0) { + force_new_group: + if (scan->done_group) { + retval = (scan->done_group) + (scan->fs, scan->current_group, + scan->done_group_data); + if (retval) + return retval; + } + if (scan->groups_left <= 0) { + *ino = 0; + return 0; + } + retval = get_next_blockgroup(scan); + if (retval) + return retval; + } + /* + * This is done outside the above if statement so that the + * check can be done for block group #0. + */ + if (scan->current_block == 0) { + if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { + goto force_new_group; + } else + return EXT2_ET_MISSING_INODE_TABLE; + } + + + /* + * Have we run out of space in the inode buffer? If so, we + * need to read in more blocks. + */ + if (scan->bytes_left < scan->inode_size) { + memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); + extra_bytes = scan->bytes_left; + + retval = get_next_blocks(scan); + if (retval) + return retval; +#if 0 + /* + * XXX test Need check for used inode somehow. + * (Note: this is hard.) + */ + if (is_empty_scan(scan)) + goto force_new_group; +#endif + } + + retval = 0; + if (extra_bytes) { + memcpy(scan->temp_buffer+extra_bytes, scan->ptr, + scan->inode_size - extra_bytes); + scan->ptr += scan->inode_size - extra_bytes; + scan->bytes_left -= scan->inode_size - extra_bytes; + +#if BB_BIG_ENDIAN + if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || + (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->temp_buffer, + 0, bufsize); + else +#endif + *inode = *((struct ext2_inode *) scan->temp_buffer); + if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; + } else { +#if BB_BIG_ENDIAN + if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) || + (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) + ext2fs_swap_inode_full(scan->fs, + (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) scan->ptr, + 0, bufsize); + else +#endif + memcpy(inode, scan->ptr, bufsize); + scan->ptr += scan->inode_size; + scan->bytes_left -= scan->inode_size; + if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) + retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; + } + + scan->inodes_left--; + scan->current_inode++; + *ino = scan->current_inode; + return retval; +} + +errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, + struct ext2_inode *inode) +{ + return ext2fs_get_next_inode_full(scan, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * Functions to read and write a single inode. + */ +errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + unsigned long group, block, block_nr, offset; + char *ptr; + errcode_t retval; + int clen, i, inodes_per_block, length; + io_channel io; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* Check to see if user has an override function */ + if (fs->read_inode) { + retval = (fs->read_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + /* Create inode cache if not present */ + if (!fs->icache) { + retval = create_icache(fs); + if (retval) + return retval; + } + /* Check to see if it's in the inode cache */ + if (bufsize == sizeof(struct ext2_inode)) { + /* only old good inode can be retrieve from the cache */ + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + *inode = fs->icache->cache[i].inode; + return 0; + } + } + } + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); + block_nr = fs->image_header->offset_inode / fs->blocksize; + block_nr += (ino - 1) / inodes_per_block; + offset = ((ino - 1) % inodes_per_block) * + EXT2_INODE_SIZE(fs->super); + io = fs->image_io; + } else { + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!fs->group_desc[(unsigned)group].bg_inode_table) + return EXT2_ET_MISSING_INODE_TABLE; + block_nr = fs->group_desc[(unsigned)group].bg_inode_table + + block; + io = fs->io; + } + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); + + length = EXT2_INODE_SIZE(fs->super); + if (bufsize < length) + length = bufsize; + + ptr = (char *) inode; + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (block_nr != fs->icache->buffer_blk) { + retval = io_channel_read_blk(io, block_nr, 1, + fs->icache->buffer); + if (retval) + return retval; + fs->icache->buffer_blk = block_nr; + } + + memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, + clen); + + offset = 0; + length -= clen; + ptr += clen; + block_nr++; + } + +#if BB_BIG_ENDIAN + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, + (struct ext2_inode_large *) inode, + 0, length); +#endif + + /* Update the inode cache */ + fs->icache->cache_last = (fs->icache->cache_last + 1) % + fs->icache->cache_size; + fs->icache->cache[fs->icache->cache_last].ino = ino; + fs->icache->cache[fs->icache->cache_last].inode = *inode; + + return 0; +} + +errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode) +{ + return ext2fs_read_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode * inode, int bufsize) +{ + unsigned long group, block, block_nr, offset; + errcode_t retval = 0; + struct ext2_inode_large temp_inode, *w_inode; + char *ptr; + int clen, i, length; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* Check to see if user provided an override function */ + if (fs->write_inode) { + retval = (fs->write_inode)(fs, ino, inode); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + + /* Check to see if the inode cache needs to be updated */ + if (fs->icache) { + for (i=0; i < fs->icache->cache_size; i++) { + if (fs->icache->cache[i].ino == ino) { + fs->icache->cache[i].inode = *inode; + break; + } + } + } else { + retval = create_icache(fs); + if (retval) + return retval; + } + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + if ((ino == 0) || (ino > fs->super->s_inodes_count)) + return EXT2_ET_BAD_INODE_NUM; + + length = bufsize; + if (length < EXT2_INODE_SIZE(fs->super)) + length = EXT2_INODE_SIZE(fs->super); + + if (length > (int) sizeof(struct ext2_inode_large)) { + w_inode = xmalloc(length); + } else + w_inode = &temp_inode; + memset(w_inode, 0, length); + +#if BB_BIG_ENDIAN + if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) + ext2fs_swap_inode_full(fs, w_inode, + (struct ext2_inode_large *) inode, + 1, bufsize); + else +#endif + memcpy(w_inode, inode, bufsize); + + group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); + offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * + EXT2_INODE_SIZE(fs->super); + block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); + if (!fs->group_desc[(unsigned) group].bg_inode_table) + return EXT2_ET_MISSING_INODE_TABLE; + block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block; + + offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); + + length = EXT2_INODE_SIZE(fs->super); + if (length > bufsize) + length = bufsize; + + ptr = (char *) w_inode; + + while (length) { + clen = length; + if ((offset + length) > fs->blocksize) + clen = fs->blocksize - offset; + + if (fs->icache->buffer_blk != block_nr) { + retval = io_channel_read_blk(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + fs->icache->buffer_blk = block_nr; + } + + + memcpy((char *) fs->icache->buffer + (unsigned) offset, + ptr, clen); + + retval = io_channel_write_blk(fs->io, block_nr, 1, + fs->icache->buffer); + if (retval) + goto errout; + + offset = 0; + ptr += clen; + length -= clen; + block_nr++; + } + + fs->flags |= EXT2_FLAG_CHANGED; +errout: + if (w_inode && w_inode != &temp_inode) + free(w_inode); + return retval; +} + +errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); +} + +/* + * This function should be called when writing a new inode. It makes + * sure that extra part of large inodes is initialized properly. + */ +errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode) +{ + struct ext2_inode *buf; + int size = EXT2_INODE_SIZE(fs->super); + struct ext2_inode_large *large_inode; + + if (size == sizeof(struct ext2_inode)) + return ext2fs_write_inode_full(fs, ino, inode, + sizeof(struct ext2_inode)); + + buf = xmalloc(size); + + memset(buf, 0, size); + *buf = *inode; + + large_inode = (struct ext2_inode_large *) buf; + large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - + EXT2_GOOD_OLD_INODE_SIZE; + + return ext2fs_write_inode_full(fs, ino, buf, size); +} + + +errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) +{ + struct ext2_inode inode; + int i; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (ino > fs->super->s_inodes_count) + return EXT2_ET_BAD_INODE_NUM; + + if (fs->get_blocks) { + if (!(*fs->get_blocks)(fs, ino, blocks)) + return 0; + } + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + for (i=0; i < EXT2_N_BLOCKS; i++) + blocks[i] = inode.i_block[i]; + return 0; +} + +errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) +{ + struct ext2_inode inode; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (ino > fs->super->s_inodes_count) + return EXT2_ET_BAD_INODE_NUM; + + if (fs->check_directory) { + retval = (fs->check_directory)(fs, ino); + if (retval != EXT2_ET_CALLBACK_NOTHANDLED) + return retval; + } + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + return retval; + if (!LINUX_S_ISDIR(inode.i_mode)) + return EXT2_ET_NO_DIRECTORY; + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c b/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c new file mode 100644 index 000000000..b861d5ff6 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c @@ -0,0 +1,270 @@ +/* vi: set sw=4 ts=4: */ +/* + * inode_io.c --- This is allows an inode in an ext2 filesystem image + * to be accessed via the I/O manager interface. + * + * Copyright (C) 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + +struct inode_private_data { + int magic; + char name[32]; + ext2_file_t file; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode inode; + int flags; + struct inode_private_data *next; +}; + +#define CHANNEL_HAS_INODE 0x8000 + +static struct inode_private_data *top_intern; +static int ino_unique = 0; + +static errcode_t inode_open(const char *name, int flags, io_channel *channel); +static errcode_t inode_close(io_channel channel); +static errcode_t inode_set_blksize(io_channel channel, int blksize); +static errcode_t inode_read_blk(io_channel channel, unsigned long block, + int count, void *data); +static errcode_t inode_write_blk(io_channel channel, unsigned long block, + int count, const void *data); +static errcode_t inode_flush(io_channel channel); +static errcode_t inode_write_byte(io_channel channel, unsigned long offset, + int size, const void *data); + +static struct struct_io_manager struct_inode_manager = { + EXT2_ET_MAGIC_IO_MANAGER, + "Inode I/O Manager", + inode_open, + inode_close, + inode_set_blksize, + inode_read_blk, + inode_write_blk, + inode_flush, + inode_write_byte +}; + +io_manager inode_io_manager = &struct_inode_manager; + +errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, + char **name) +{ + struct inode_private_data *data; + errcode_t retval; + + if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data), + &data))) + return retval; + data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL; + sprintf(data->name, "%u:%d", ino, ino_unique++); + data->file = 0; + data->fs = fs; + data->ino = ino; + data->flags = 0; + if (inode) { + memcpy(&data->inode, inode, sizeof(struct ext2_inode)); + data->flags |= CHANNEL_HAS_INODE; + } + data->next = top_intern; + top_intern = data; + *name = data->name; + return 0; +} + +errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, + char **name) +{ + return ext2fs_inode_io_intern2(fs, ino, NULL, name); +} + + +static errcode_t inode_open(const char *name, int flags, io_channel *channel) +{ + io_channel io = NULL; + struct inode_private_data *prev, *data = NULL; + errcode_t retval; + int open_flags; + + if (name == 0) + return EXT2_ET_BAD_DEVICE_NAME; + + for (data = top_intern, prev = NULL; data; + prev = data, data = data->next) + if (strcmp(name, data->name) == 0) + break; + if (!data) + return ENOENT; + if (prev) + prev->next = data->next; + else + top_intern = data->next; + + retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); + if (retval) + goto cleanup; + memset(io, 0, sizeof(struct struct_io_channel)); + + io->magic = EXT2_ET_MAGIC_IO_CHANNEL; + io->manager = inode_io_manager; + retval = ext2fs_get_mem(strlen(name)+1, &io->name); + if (retval) + goto cleanup; + + strcpy(io->name, name); + io->private_data = data; + io->block_size = 1024; + io->read_error = 0; + io->write_error = 0; + io->refcount = 1; + + open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0; + retval = ext2fs_file_open2(data->fs, data->ino, + (data->flags & CHANNEL_HAS_INODE) ? + &data->inode : 0, open_flags, + &data->file); + if (retval) + goto cleanup; + + *channel = io; + return 0; + +cleanup: + if (data) { + ext2fs_free_mem(&data); + } + if (io) + ext2fs_free_mem(&io); + return retval; +} + +static errcode_t inode_close(io_channel channel) +{ + struct inode_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if (--channel->refcount > 0) + return 0; + + retval = ext2fs_file_close(data->file); + + ext2fs_free_mem(&channel->private_data); + if (channel->name) + ext2fs_free_mem(&channel->name); + ext2fs_free_mem(&channel); + return retval; +} + +static errcode_t inode_set_blksize(io_channel channel, int blksize) +{ + struct inode_private_data *data; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + channel->block_size = blksize; + return 0; +} + + +static errcode_t inode_read_blk(io_channel channel, unsigned long block, + int count, void *buf) +{ + struct inode_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, + block * channel->block_size, + EXT2_SEEK_SET, 0))) + return retval; + + count = (count < 0) ? -count : (count * channel->block_size); + + return ext2fs_file_read(data->file, buf, count, 0); +} + +static errcode_t inode_write_blk(io_channel channel, unsigned long block, + int count, const void *buf) +{ + struct inode_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, + block * channel->block_size, + EXT2_SEEK_SET, 0))) + return retval; + + count = (count < 0) ? -count : (count * channel->block_size); + + return ext2fs_file_write(data->file, buf, count, 0); +} + +static errcode_t inode_write_byte(io_channel channel, unsigned long offset, + int size, const void *buf) +{ + struct inode_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + if ((retval = ext2fs_file_lseek(data->file, offset, + EXT2_SEEK_SET, 0))) + return retval; + + return ext2fs_file_write(data->file, buf, size, 0); +} + +/* + * Flush data buffers to disk. + */ +static errcode_t inode_flush(io_channel channel) +{ + struct inode_private_data *data; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct inode_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); + + return ext2fs_file_flush(data->file); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c b/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c new file mode 100644 index 000000000..b47038602 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * io_manager.c --- the I/O manager abstraction + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t io_channel_set_options(io_channel channel, const char *opts) +{ + errcode_t retval = 0; + char *next, *ptr, *options, *arg; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (!opts) + return 0; + + if (!channel->manager->set_option) + return EXT2_ET_INVALID_ARGUMENT; + + options = malloc(strlen(opts)+1); + if (!options) + return EXT2_ET_NO_MEMORY; + strcpy(options, opts); + ptr = options; + + while (ptr && *ptr) { + next = strchr(ptr, '&'); + if (next) + *next++ = 0; + + arg = strchr(ptr, '='); + if (arg) + *arg++ = 0; + + retval = (channel->manager->set_option)(channel, ptr, arg); + if (retval) + break; + ptr = next; + } + free(options); + return retval; +} + +errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, + int count, const void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->write_byte) + return channel->manager->write_byte(channel, offset, + count, data); + + return EXT2_ET_UNIMPLEMENTED; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/irel.h b/e2fsprogs/old_e2fsprogs/ext2fs/irel.h new file mode 100644 index 000000000..91d1d89d5 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/irel.h @@ -0,0 +1,115 @@ +/* vi: set sw=4 ts=4: */ +/* + * irel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +struct ext2_inode_reference { + blk_t block; + __u16 offset; +}; + +struct ext2_inode_relocate_entry { + ext2_ino_t new; + ext2_ino_t orig; + __u16 flags; + __u16 max_refs; +}; + +typedef struct ext2_inode_relocation_table *ext2_irel; + +struct ext2_inode_relocation_table { + __u32 magic; + char *name; + ext2_ino_t current; + void *priv_data; + + /* + * Add an inode relocation entry. + */ + errcode_t (*put)(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); + /* + * Get an inode relocation entry. + */ + errcode_t (*get)(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); + + /* + * Get an inode relocation entry by its original inode number + */ + errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); + + /* + * Initialize for iterating over the inode relocation entries. + */ + errcode_t (*start_iter)(ext2_irel irel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); + + /* + * Add an inode reference (i.e., note the fact that a + * particular block/offset contains a reference to an inode) + */ + errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref); + + /* + * Initialize for iterating over the inode references for a + * particular inode. + */ + errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino); + + /* + * The iterator function for the inode references for an + * inode. The references for only one inode can be interator + * over at a time, as the iterator state is stored in ext2_irel. + */ + errcode_t (*next_ref)(ext2_irel irel, + struct ext2_inode_reference *ref); + + /* + * Move the inode relocation table from one inode number to + * another. Note that the inode references also must move. + */ + errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); + + /* + * Remove an inode relocation entry, along with all of the + * inode references. + */ + errcode_t (*delete)(ext2_irel irel, ext2_ino_t old); + + /* + * Free the inode relocation table. + */ + errcode_t (*free)(ext2_irel irel); +}; + +errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, + ext2_irel *irel); + +#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent)) +#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent)) +#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \ + ((irel)->get_by_orig((irel), orig, old, ent)) +#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel))) +#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent)) +#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref)) +#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino)) +#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref)) +#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new)) +#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old)) +#define ext2fs_irel_free(irel) ((irel)->free((irel))) diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c b/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c new file mode 100644 index 000000000..c871b1891 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c @@ -0,0 +1,367 @@ +/* vi: set sw=4 ts=4: */ +/* + * irel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "irel.h" + +static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_start_iter(ext2_irel irel); +static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent); +static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref); +static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino); +static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref); +static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); +static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old); +static errcode_t ima_free(ext2_irel irel); + +/* + * This data structure stores the array of inode references; there is + * a structure for each inode. + */ +struct inode_reference_entry { + __u16 num; + struct ext2_inode_reference *refs; +}; + +struct irel_ma { + __u32 magic; + ext2_ino_t max_inode; + ext2_ino_t ref_current; + int ref_iter; + ext2_ino_t *orig_map; + struct ext2_inode_relocate_entry *entries; + struct inode_reference_entry *ref_entries; +}; + +errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, + ext2_irel *new_irel) +{ + ext2_irel irel = 0; + errcode_t retval; + struct irel_ma *ma = 0; + size_t size; + + *new_irel = 0; + + /* + * Allocate memory structures + */ + retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table), + &irel); + if (retval) + goto errout; + memset(irel, 0, sizeof(struct ext2_inode_relocation_table)); + + retval = ext2fs_get_mem(strlen(name)+1, &irel->name); + if (retval) + goto errout; + strcpy(irel->name, name); + + retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma); + if (retval) + goto errout; + memset(ma, 0, sizeof(struct irel_ma)); + irel->priv_data = ma; + + size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1)); + retval = ext2fs_get_mem(size, &ma->orig_map); + if (retval) + goto errout; + memset(ma->orig_map, 0, size); + + size = (size_t) (sizeof(struct ext2_inode_relocate_entry) * + (max_inode+1)); + retval = ext2fs_get_mem(size, &ma->entries); + if (retval) + goto errout; + memset(ma->entries, 0, size); + + size = (size_t) (sizeof(struct inode_reference_entry) * + (max_inode+1)); + retval = ext2fs_get_mem(size, &ma->ref_entries); + if (retval) + goto errout; + memset(ma->ref_entries, 0, size); + ma->max_inode = max_inode; + + /* + * Fill in the irel data structure + */ + irel->put = ima_put; + irel->get = ima_get; + irel->get_by_orig = ima_get_by_orig; + irel->start_iter = ima_start_iter; + irel->next = ima_next; + irel->add_ref = ima_add_ref; + irel->start_iter_ref = ima_start_iter_ref; + irel->next_ref = ima_next_ref; + irel->move = ima_move; + irel->delete = ima_delete; + irel->free = ima_free; + + *new_irel = irel; + return 0; + +errout: + ima_free(irel); + return retval; +} + +static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent) +{ + struct inode_reference_entry *ref_ent; + struct irel_ma *ma; + errcode_t retval; + size_t size, old_size; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + + /* + * Force the orig field to the correct value; the application + * program shouldn't be messing with this field. + */ + if (ma->entries[(unsigned) old].new == 0) + ent->orig = old; + else + ent->orig = ma->entries[(unsigned) old].orig; + + /* + * If max_refs has changed, reallocate the refs array + */ + ref_ent = ma->ref_entries + (unsigned) old; + if (ref_ent->refs && ent->max_refs != + ma->entries[(unsigned) old].max_refs) { + size = (sizeof(struct ext2_inode_reference) * ent->max_refs); + old_size = (sizeof(struct ext2_inode_reference) * + ma->entries[(unsigned) old].max_refs); + retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs); + if (retval) + return retval; + } + + ma->entries[(unsigned) old] = *ent; + ma->orig_map[(unsigned) ent->orig] = old; + return 0; +} + +static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + *ent = ma->entries[(unsigned) old]; + return 0; +} + +static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + ext2_ino_t ino; + + ma = irel->priv_data; + if (orig > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + ino = ma->orig_map[(unsigned) orig]; + if (ino == 0) + return ENOENT; + *old = ino; + *ent = ma->entries[(unsigned) ino]; + return 0; +} + +static errcode_t ima_start_iter(ext2_irel irel) +{ + irel->current = 0; + return 0; +} + +static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, + struct ext2_inode_relocate_entry *ent) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + while (++irel->current < ma->max_inode) { + if (ma->entries[(unsigned) irel->current].new == 0) + continue; + *old = irel->current; + *ent = ma->entries[(unsigned) irel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, + struct ext2_inode_reference *ref) +{ + struct irel_ma *ma; + size_t size; + struct inode_reference_entry *ref_ent; + struct ext2_inode_relocate_entry *ent; + errcode_t retval; + + ma = irel->priv_data; + if (ino > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + + ref_ent = ma->ref_entries + (unsigned) ino; + ent = ma->entries + (unsigned) ino; + + /* + * If the inode reference array doesn't exist, create it. + */ + if (ref_ent->refs == 0) { + size = (size_t) ((sizeof(struct ext2_inode_reference) * + ent->max_refs)); + retval = ext2fs_get_mem(size, &ref_ent->refs); + if (retval) + return retval; + memset(ref_ent->refs, 0, size); + ref_ent->num = 0; + } + + if (ref_ent->num >= ent->max_refs) + return EXT2_ET_TOO_MANY_REFS; + + ref_ent->refs[(unsigned) ref_ent->num++] = *ref; + return 0; +} + +static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (ino > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) ino].new == 0) + return ENOENT; + ma->ref_current = ino; + ma->ref_iter = 0; + return 0; +} + +static errcode_t ima_next_ref(ext2_irel irel, + struct ext2_inode_reference *ref) +{ + struct irel_ma *ma; + struct inode_reference_entry *ref_ent; + + ma = irel->priv_data; + + ref_ent = ma->ref_entries + ma->ref_current; + + if ((ref_ent->refs == NULL) || + (ma->ref_iter >= ref_ent->num)) { + ref->block = 0; + ref->offset = 0; + return 0; + } + *ref = ref_ent->refs[ma->ref_iter++]; + return 0; +} + + +static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if ((old > ma->max_inode) || (new > ma->max_inode)) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + + ma->entries[(unsigned) new] = ma->entries[(unsigned) old]; + ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs); + ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old]; + + ma->entries[(unsigned) old].new = 0; + ma->ref_entries[(unsigned) old].num = 0; + ma->ref_entries[(unsigned) old].refs = 0; + + ma->orig_map[ma->entries[new].orig] = new; + return 0; +} + +static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old) +{ + struct irel_ma *ma; + + ma = irel->priv_data; + if (old > ma->max_inode) + return EXT2_ET_INVALID_ARGUMENT; + if (ma->entries[(unsigned) old].new == 0) + return ENOENT; + + ma->entries[old].new = 0; + ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs); + ma->orig_map[ma->entries[(unsigned) old].orig] = 0; + + ma->ref_entries[(unsigned) old].num = 0; + ma->ref_entries[(unsigned) old].refs = 0; + return 0; +} + +static errcode_t ima_free(ext2_irel irel) +{ + struct irel_ma *ma; + ext2_ino_t ino; + + if (!irel) + return 0; + + ma = irel->priv_data; + + if (ma) { + ext2fs_free_mem(&ma->orig_map); + ext2fs_free_mem(&ma->entries); + if (ma->ref_entries) { + for (ino = 0; ino <= ma->max_inode; ino++) { + ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs); + } + ext2fs_free_mem(&ma->ref_entries); + } + ext2fs_free_mem(&ma); + } + ext2fs_free_mem(&irel->name); + ext2fs_free_mem(&irel); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c b/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c new file mode 100644 index 000000000..f5f6f31cd --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c @@ -0,0 +1,357 @@ +/* vi: set sw=4 ts=4: */ +/* + * ismounted.c --- Check to see if the filesystem was mounted + * + * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_LINUX_FD_H +#include +#endif +#ifdef HAVE_MNTENT_H +#include +#endif +#ifdef HAVE_GETMNTINFO +#include +#include +#include +#endif /* HAVE_GETMNTINFO */ +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifdef HAVE_MNTENT_H +/* + * Helper function which checks a file in /etc/mtab format to see if a + * filesystem is mounted. Returns an error if the file doesn't exist + * or can't be opened. + */ +static errcode_t check_mntent_file(const char *mtab_file, const char *file, + int *mount_flags, char *mtpt, int mtlen) +{ + struct mntent *mnt; + struct stat st_buf; + errcode_t retval = 0; + dev_t file_dev=0, file_rdev=0; + ino_t file_ino=0; + FILE *f; + int fd; + + *mount_flags = 0; + if ((f = setmntent (mtab_file, "r")) == NULL) + return errno; + if (stat(file, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + file_rdev = st_buf.st_rdev; +#endif /* __GNU__ */ + } else { + file_dev = st_buf.st_dev; + file_ino = st_buf.st_ino; + } + } + while ((mnt = getmntent (f)) != NULL) { + if (strcmp(file, mnt->mnt_fsname) == 0) + break; + if (stat(mnt->mnt_fsname, &st_buf) == 0) { + if (S_ISBLK(st_buf.st_mode)) { +#ifndef __GNU__ + if (file_rdev && (file_rdev == st_buf.st_rdev)) + break; +#endif /* __GNU__ */ + } else { + if (file_dev && ((file_dev == st_buf.st_dev) && + (file_ino == st_buf.st_ino))) + break; + } + } + } + + if (mnt == 0) { +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + /* + * Do an extra check to see if this is the root device. We + * can't trust /etc/mtab, and /proc/mounts will only list + * /dev/root for the root filesystem. Argh. Instead we + * check if the given device has the same major/minor number + * as the device that the root directory is on. + */ + if (file_rdev && stat("/", &st_buf) == 0) { + if (st_buf.st_dev == file_rdev) { + *mount_flags = EXT2_MF_MOUNTED; + if (mtpt) + strncpy(mtpt, "/", mtlen); + goto is_root; + } + } +#endif /* __GNU__ */ + goto errout; + } +#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ + /* Validate the entry in case /etc/mtab is out of date */ + /* + * We need to be paranoid, because some broken distributions + * (read: Slackware) don't initialize /etc/mtab before checking + * all of the non-root filesystems on the disk. + */ + if (stat(mnt->mnt_dir, &st_buf) < 0) { + retval = errno; + if (retval == ENOENT) { +#ifdef DEBUG + printf("Bogus entry in %s! (%s does not exist)\n", + mtab_file, mnt->mnt_dir); +#endif /* DEBUG */ + retval = 0; + } + goto errout; + } + if (file_rdev && (st_buf.st_dev != file_rdev)) { +#ifdef DEBUG + printf("Bogus entry in %s! (%s not mounted on %s)\n", + mtab_file, file, mnt->mnt_dir); +#endif /* DEBUG */ + goto errout; + } +#endif /* __GNU__ */ + *mount_flags = EXT2_MF_MOUNTED; + +#ifdef MNTOPT_RO + /* Check to see if the ro option is set */ + if (hasmntopt(mnt, MNTOPT_RO)) + *mount_flags |= EXT2_MF_READONLY; +#endif + + if (mtpt) + strncpy(mtpt, mnt->mnt_dir, mtlen); + /* + * Check to see if we're referring to the root filesystem. + * If so, do a manual check to see if we can open /etc/mtab + * read/write, since if the root is mounted read/only, the + * contents of /etc/mtab may not be accurate. + */ + if (LONE_CHAR(mnt->mnt_dir, '/')) { +is_root: +#define TEST_FILE "/.ismount-test-file" + *mount_flags |= EXT2_MF_ISROOT; + fd = open(TEST_FILE, O_RDWR|O_CREAT); + if (fd < 0) { + if (errno == EROFS) + *mount_flags |= EXT2_MF_READONLY; + } else + close(fd); + (void) unlink(TEST_FILE); + } + retval = 0; +errout: + endmntent (f); + return retval; +} + +static errcode_t check_mntent(const char *file, int *mount_flags, + char *mtpt, int mtlen) +{ + errcode_t retval; + +#ifdef DEBUG + retval = check_mntent_file("/tmp/mtab", file, mount_flags, + mtpt, mtlen); + if (retval == 0) + return 0; +#endif /* DEBUG */ +#ifdef __linux__ + retval = check_mntent_file("/proc/mounts", file, mount_flags, + mtpt, mtlen); + if (retval == 0 && (*mount_flags != 0)) + return 0; +#endif /* __linux__ */ +#if defined(MOUNTED) || defined(_PATH_MOUNTED) +#ifndef MOUNTED +#define MOUNTED _PATH_MOUNTED +#endif /* MOUNTED */ + retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); + return retval; +#else + *mount_flags = 0; + return 0; +#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ +} + +#else +#if defined(HAVE_GETMNTINFO) + +static errcode_t check_getmntinfo(const char *file, int *mount_flags, + char *mtpt, int mtlen) +{ + struct statfs *mp; + int len, n; + const char *s1; + char *s2; + + n = getmntinfo(&mp, MNT_NOWAIT); + if (n == 0) + return errno; + + len = sizeof(_PATH_DEV) - 1; + s1 = file; + if (strncmp(_PATH_DEV, s1, len) == 0) + s1 += len; + + *mount_flags = 0; + while (--n >= 0) { + s2 = mp->f_mntfromname; + if (strncmp(_PATH_DEV, s2, len) == 0) { + s2 += len - 1; + *s2 = 'r'; + } + if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { + *mount_flags = EXT2_MF_MOUNTED; + break; + } + ++mp; + } + if (mtpt) + strncpy(mtpt, mp->f_mntonname, mtlen); + return 0; +} +#endif /* HAVE_GETMNTINFO */ +#endif /* HAVE_MNTENT_H */ + +/* + * Check to see if we're dealing with the swap device. + */ +static int is_swap_device(const char *file) +{ + FILE *f; + char buf[1024], *cp; + dev_t file_dev; + struct stat st_buf; + int ret = 0; + + file_dev = 0; +#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ + if ((stat(file, &st_buf) == 0) && + S_ISBLK(st_buf.st_mode)) + file_dev = st_buf.st_rdev; +#endif /* __GNU__ */ + + if (!(f = fopen_for_read("/proc/swaps"))) + return 0; + /* Skip the first line */ + fgets(buf, sizeof(buf), f); + while (!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) + break; + if ((cp = strchr(buf, ' ')) != NULL) + *cp = 0; + if ((cp = strchr(buf, '\t')) != NULL) + *cp = 0; + if (strcmp(buf, file) == 0) { + ret++; + break; + } +#ifndef __GNU__ + if (file_dev && (stat(buf, &st_buf) == 0) && + S_ISBLK(st_buf.st_mode) && + file_dev == st_buf.st_rdev) { + ret++; + break; + } +#endif /* __GNU__ */ + } + fclose(f); + return ret; +} + + +/* + * ext2fs_check_mount_point() returns 1 if the device is mounted, 0 + * otherwise. If mtpt is non-NULL, the directory where the device is + * mounted is copied to where mtpt is pointing, up to mtlen + * characters. + */ +#ifdef __TURBOC__ +# pragma argsused +#endif +errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, + char *mtpt, int mtlen) +{ + if (is_swap_device(device)) { + *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; + strncpy(mtpt, "", mtlen); + return 0; + } +#ifdef HAVE_MNTENT_H + return check_mntent(device, mount_flags, mtpt, mtlen); +#else +#ifdef HAVE_GETMNTINFO + return check_getmntinfo(device, mount_flags, mtpt, mtlen); +#else +#ifdef __GNUC__ + #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" +#endif + *mount_flags = 0; + return 0; +#endif /* HAVE_GETMNTINFO */ +#endif /* HAVE_MNTENT_H */ +} + +/* + * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, + * EXT2_MF_READONLY, and EXT2_MF_ROOT + * + */ +errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) +{ + return ext2fs_check_mount_point(file, mount_flags, NULL, 0); +} + +#ifdef DEBUG +int main(int argc, char **argv) +{ + int retval, mount_flags; + char mntpt[80]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s device\n", argv[0]); + exit(1); + } + + mntpt[0] = 0; + retval = ext2fs_check_mount_point(argv[1], &mount_flags, + mntpt, sizeof(mntpt)); + if (retval) { + com_err(argv[0], retval, + "while calling ext2fs_check_if_mounted"); + exit(1); + } + printf("Device %s reports flags %02x\n", argv[1], mount_flags); + if (mount_flags & EXT2_MF_BUSY) + printf("\t%s is apparently in use.\n", argv[1]); + if (mount_flags & EXT2_MF_MOUNTED) + printf("\t%s is mounted.\n", argv[1]); + if (mount_flags & EXT2_MF_SWAP) + printf("\t%s is a swap device.\n", argv[1]); + if (mount_flags & EXT2_MF_READONLY) + printf("\t%s is read-only.\n", argv[1]); + if (mount_flags & EXT2_MF_ISROOT) + printf("\t%s is the root filesystem.\n", argv[1]); + if (mntpt[0]) + printf("\t%s is mounted on %s.\n", argv[1], mntpt); + exit(0); +} +#endif /* DEBUG */ diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h b/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h new file mode 100644 index 000000000..17c586a29 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h @@ -0,0 +1,63 @@ +/* vi: set sw=4 ts=4: */ +/* + * jfs_dat.h --- stripped down header file which only contains the JFS + * on-disk data structures + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK 3 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ +} journal_block_tag_t; + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +/* + * The journal superblock + */ +typedef struct journal_superblock_s +{ + journal_header_t s_header; + + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ +} journal_superblock_t; diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h b/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h new file mode 100644 index 000000000..853d97ac7 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h @@ -0,0 +1,235 @@ +/* vi: set sw=4 ts=4: */ +/* + * linux/include/linux/jbd.h + * + * Written by Stephen C. Tweedie + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * Definitions for transaction data structures for the buffer cache + * filesystem journaling support. + */ +#ifndef LINUX_JBD_H +#define LINUX_JBD_H 1 + +#include +#include +#include "ext2fs.h" + +/* + * Standard header for all descriptor blocks: + */ + +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + +/* + * This is the global e2fsck structure. + */ +typedef struct e2fsck_struct *e2fsck_t; + + +struct inode { + e2fsck_t i_ctx; + ext2_ino_t i_ino; + struct ext2_inode i_ext2; +}; + + +/* + * The journal superblock. All fields are in big-endian byte order. + */ +typedef struct journal_superblock_s +{ +/* 0x0000 */ + journal_header_t s_header; + +/* 0x000C */ + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + +/* 0x0018 */ + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +/* 0x0020 */ + /* Error value, as set by journal_abort(). */ + __s32 s_errno; + +/* 0x0024 */ + /* Remaining fields are only valid in a version-2 superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ +/* 0x0030 */ + __u8 s_uuid[16]; /* 128-bit uuid for journal */ + +/* 0x0040 */ + __u32 s_nr_users; /* Nr of filesystems sharing log */ + + __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ + +/* 0x0048 */ + __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ + __u32 s_max_trans_data; /* Limit of data blocks per trans. */ + +/* 0x0050 */ + __u32 s_padding[44]; + +/* 0x0100 */ + __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ +/* 0x0400 */ +} journal_superblock_t; + + +extern int journal_blocks_per_page(struct inode *inode); +extern int jbd_blocks_per_page(struct inode *inode); + +#define JFS_MIN_JOURNAL_BLOCKS 1024 + + +/* + * Internal structures used by the logging mechanism: + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK_V1 3 +#define JFS_SUPERBLOCK_V2 4 +#define JFS_REVOKE_BLOCK 5 + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ +} journal_block_tag_t; + +/* + * The revoke descriptor: used on disk to describe a series of blocks to + * be revoked from the log + */ +typedef struct journal_revoke_header_s +{ + journal_header_t r_header; + int r_count; /* Count of bytes used in the block */ +} journal_revoke_header_t; + + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + + + +#define JFS_HAS_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) +#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) +#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 + +/* Features known to this kernel version: */ +#define JFS_KNOWN_COMPAT_FEATURES 0 +#define JFS_KNOWN_ROCOMPAT_FEATURES 0 +#define JFS_KNOWN_INCOMPAT_FEATURES JFS_FEATURE_INCOMPAT_REVOKE + +/* Comparison functions for transaction IDs: perform comparisons using + * modulo arithmetic so that they work over sequence number wraps. */ + + +/* + * Definitions which augment the buffer_head layer + */ + +/* journaling buffer types */ +#define BJ_None 0 /* Not journaled */ +#define BJ_SyncData 1 /* Normal data: flush before commit */ +#define BJ_AsyncData 2 /* writepage data: wait on it before commit */ +#define BJ_Metadata 3 /* Normal journaled metadata */ +#define BJ_Forget 4 /* Buffer superceded by this transaction */ +#define BJ_IO 5 /* Buffer is for temporary IO use */ +#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ +#define BJ_LogCtl 7 /* Buffer contains log descriptors */ +#define BJ_Reserved 8 /* Buffer is reserved for access by journal */ +#define BJ_Types 9 + + +struct kdev_s { + e2fsck_t k_ctx; + int k_dev; +}; + +typedef struct kdev_s *kdev_t; +typedef unsigned int tid_t; + +struct journal_s +{ + unsigned long j_flags; + int j_errno; + struct buffer_head * j_sb_buffer; + struct journal_superblock_s *j_superblock; + int j_format_version; + unsigned long j_head; + unsigned long j_tail; + unsigned long j_free; + unsigned long j_first, j_last; + kdev_t j_dev; + kdev_t j_fs_dev; + int j_blocksize; + unsigned int j_blk_offset; + unsigned int j_maxlen; + struct inode * j_inode; + tid_t j_tail_sequence; + tid_t j_transaction_sequence; + __u8 j_uuid[16]; + struct jbd_revoke_table_s *j_revoke; +}; + +typedef struct journal_s journal_t; + +extern int journal_recover (journal_t *journal); +extern int journal_skip_recovery (journal_t *); + +/* Primary revoke support */ +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +extern void journal_destroy_revoke(journal_t *); + + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h b/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h new file mode 100644 index 000000000..d80716a45 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h @@ -0,0 +1,113 @@ +/* vi: set sw=4 ts=4: */ +#ifndef LINUX_LIST_H +#define LINUX_LIST_H 1 + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = { &name, &name } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define __inline__ +#endif + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * Insert a new entry after the specified head.. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Insert a new entry at the tail + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * Splice in "list" into "head" + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/link.c b/e2fsprogs/old_e2fsprogs/ext2fs/link.c new file mode 100644 index 000000000..08b2e9673 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/link.c @@ -0,0 +1,135 @@ +/* vi: set sw=4 ts=4: */ +/* + * link.c --- create links in a ext2fs directory + * + * Copyright (C) 1993, 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct link_struct { + const char *name; + int namelen; + ext2_ino_t inode; + int flags; + int done; + struct ext2_super_block *sb; +}; + +static int link_proc(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data) +{ + struct link_struct *ls = (struct link_struct *) priv_data; + struct ext2_dir_entry *next; + int rec_len, min_rec_len; + int ret = 0; + + rec_len = EXT2_DIR_REC_LEN(ls->namelen); + + /* + * See if the following directory entry (if any) is unused; + * if so, absorb it into this one. + */ + next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); + if ((offset + dirent->rec_len < blocksize - 8) && + (next->inode == 0) && + (offset + dirent->rec_len + next->rec_len <= blocksize)) { + dirent->rec_len += next->rec_len; + ret = DIRENT_CHANGED; + } + + /* + * If the directory entry is used, see if we can split the + * directory entry to make room for the new name. If so, + * truncate it and return. + */ + if (dirent->inode) { + min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); + if (dirent->rec_len < (min_rec_len + rec_len)) + return ret; + rec_len = dirent->rec_len - min_rec_len; + dirent->rec_len = min_rec_len; + next = (struct ext2_dir_entry *) (buf + offset + + dirent->rec_len); + next->inode = 0; + next->name_len = 0; + next->rec_len = rec_len; + return DIRENT_CHANGED; + } + + /* + * If we get this far, then the directory entry is not used. + * See if we can fit the request entry in. If so, do it. + */ + if (dirent->rec_len < rec_len) + return ret; + dirent->inode = ls->inode; + dirent->name_len = ls->namelen; + strncpy(dirent->name, ls->name, ls->namelen); + if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) + dirent->name_len |= (ls->flags & 0x7) << 8; + + ls->done++; + return DIRENT_ABORT|DIRENT_CHANGED; +} + +/* + * Note: the low 3 bits of the flags field are used as the directory + * entry filetype. + */ +#ifdef __TURBOC__ +# pragma argsused +#endif +errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, + ext2_ino_t ino, int flags) +{ + errcode_t retval; + struct link_struct ls; + struct ext2_inode inode; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + ls.name = name; + ls.namelen = name ? strlen(name) : 0; + ls.inode = ino; + ls.flags = flags; + ls.done = 0; + ls.sb = fs->super; + + retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, + 0, link_proc, &ls); + if (retval) + return retval; + + if (!ls.done) + return EXT2_ET_DIR_NO_SPACE; + + if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) + return retval; + + if (inode.i_flags & EXT2_INDEX_FL) { + inode.i_flags &= ~EXT2_INDEX_FL; + if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) + return retval; + } + + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c b/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c new file mode 100644 index 000000000..b2e8de8ec --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c @@ -0,0 +1,68 @@ +/* vi: set sw=4 ts=4: */ +/* + * lookup.c --- ext2fs directory lookup operations + * + * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct lookup_struct { + const char *name; + int len; + ext2_ino_t *inode; + int found; +}; + +#ifdef __TURBOC__ +# pragma argsused +#endif +static int lookup_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct lookup_struct *ls = (struct lookup_struct *) priv_data; + + if (ls->len != (dirent->name_len & 0xFF)) + return 0; + if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) + return 0; + *ls->inode = dirent->inode; + ls->found++; + return DIRENT_ABORT; +} + + +errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, + int namelen, char *buf, ext2_ino_t *inode) +{ + errcode_t retval; + struct lookup_struct ls; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ls.name = name; + ls.len = namelen; + ls.inode = inode; + ls.found = 0; + + retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); + if (retval) + return retval; + + return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c b/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c new file mode 100644 index 000000000..a86ac8e93 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkdir.c --- make a directory in the filesystem + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef EXT2_FT_DIR +#define EXT2_FT_DIR 2 +#endif + +errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, + const char *name) +{ + errcode_t retval; + struct ext2_inode parent_inode, inode; + ext2_ino_t ino = inum; + ext2_ino_t scratch_ino; + blk_t blk; + char *block = NULL; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + /* + * Allocate an inode, if necessary + */ + if (!ino) { + retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, + 0, &ino); + if (retval) + goto cleanup; + } + + /* + * Allocate a data block for the directory + */ + retval = ext2fs_new_block(fs, 0, 0, &blk); + if (retval) + goto cleanup; + + /* + * Create a scratch template for the directory + */ + retval = ext2fs_new_dir_block(fs, ino, parent, &block); + if (retval) + goto cleanup; + + /* + * Get the parent's inode, if necessary + */ + if (parent != ino) { + retval = ext2fs_read_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } else + memset(&parent_inode, 0, sizeof(parent_inode)); + + /* + * Create the inode structure.... + */ + memset(&inode, 0, sizeof(struct ext2_inode)); + inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask); + inode.i_uid = inode.i_gid = 0; + inode.i_blocks = fs->blocksize / 512; + inode.i_block[0] = blk; + inode.i_links_count = 2; + inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL); + inode.i_size = fs->blocksize; + + /* + * Write out the inode and inode data block + */ + retval = ext2fs_write_dir_block(fs, blk, block); + if (retval) + goto cleanup; + retval = ext2fs_write_new_inode(fs, ino, &inode); + if (retval) + goto cleanup; + + /* + * Link the directory into the filesystem hierarchy + */ + if (name) { + retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, + &scratch_ino); + if (!retval) { + retval = EXT2_ET_DIR_EXISTS; + name = 0; + goto cleanup; + } + if (retval != EXT2_ET_FILE_NOT_FOUND) + goto cleanup; + retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); + if (retval) + goto cleanup; + } + + /* + * Update parent inode's counts + */ + if (parent != ino) { + parent_inode.i_links_count++; + retval = ext2fs_write_inode(fs, parent, &parent_inode); + if (retval) + goto cleanup; + } + + /* + * Update accounting.... + */ + ext2fs_block_alloc_stats(fs, blk, +1); + ext2fs_inode_alloc_stats2(fs, ino, +1, 1); + +cleanup: + ext2fs_free_mem(&block); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c b/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c new file mode 100644 index 000000000..748d9abc7 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c @@ -0,0 +1,426 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkjournal.c --- make a journal for a filesystem + * + * Copyright (C) 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif + +#include "ext2_fs.h" +#include "../e2p/e2p.h" +#include "../e2fsck.h" +#include "ext2fs.h" +#include "kernel-jbd.h" + +/* + * This function automatically sets up the journal superblock and + * returns it as an allocated block. + */ +errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, + __u32 size, int flags, + char **ret_jsb) +{ + errcode_t retval; + journal_superblock_t *jsb; + + if (size < 1024) + return EXT2_ET_JOURNAL_TOO_SMALL; + + if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) + return retval; + + memset (jsb, 0, fs->blocksize); + + jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); + if (flags & EXT2_MKJOURNAL_V1_SUPER) + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); + else + jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); + jsb->s_blocksize = htonl(fs->blocksize); + jsb->s_maxlen = htonl(size); + jsb->s_nr_users = htonl(1); + jsb->s_first = htonl(1); + jsb->s_sequence = htonl(1); + memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); + /* + * If we're creating an external journal device, we need to + * adjust these fields. + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + jsb->s_nr_users = 0; + if (fs->blocksize == 1024) + jsb->s_first = htonl(3); + else + jsb->s_first = htonl(2); + } + + *ret_jsb = (char *) jsb; + return 0; +} + +/* + * This function writes a journal using POSIX routines. It is used + * for creating external journals and creating journals on live + * filesystems. + */ +static errcode_t write_journal_file(ext2_filsys fs, char *filename, + blk_t size, int flags) +{ + errcode_t retval; + char *buf = NULL; + int fd, ret_size; + blk_t i; + + if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) + return retval; + + /* Open the device or journal file */ + if ((fd = open(filename, O_WRONLY)) < 0) { + retval = errno; + goto errout; + } + + /* Write the superblock out */ + retval = EXT2_ET_SHORT_WRITE; + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + memset(buf, 0, fs->blocksize); + + for (i = 1; i < size; i++) { + ret_size = write(fd, buf, fs->blocksize); + if (ret_size < 0) { + retval = errno; + goto errout; + } + if (ret_size != (int) fs->blocksize) + goto errout; + } + close(fd); + + retval = 0; +errout: + ext2fs_free_mem(&buf); + return retval; +} + +/* + * Helper function for creating the journal using direct I/O routines + */ +struct mkjournal_struct { + int num_blocks; + int newblocks; + char *buf; + errcode_t err; +}; + +static int mkjournal_proc(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; + blk_t new_blk; + static blk_t last_blk = 0; + errcode_t retval; + + if (*blocknr) { + last_blk = *blocknr; + return 0; + } + retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + if (blockcnt > 0) + es->num_blocks--; + + es->newblocks++; + retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); + + if (blockcnt == 0) + memset(es->buf, 0, fs->blocksize); + + if (retval) { + es->err = retval; + return BLOCK_ABORT; + } + *blocknr = new_blk; + last_blk = new_blk; + ext2fs_block_alloc_stats(fs, new_blk, +1); + + if (es->num_blocks == 0) + return (BLOCK_CHANGED | BLOCK_ABORT); + else + return BLOCK_CHANGED; +} + +/* + * This function creates a journal using direct I/O routines. + */ +static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, + blk_t size, int flags) +{ + char *buf; + errcode_t retval; + struct ext2_inode inode; + struct mkjournal_struct es; + + if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) + return retval; + + if ((retval = ext2fs_read_bitmaps(fs))) + return retval; + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + return retval; + + if (inode.i_blocks > 0) + return EEXIST; + + es.num_blocks = size; + es.newblocks = 0; + es.buf = buf; + es.err = 0; + + retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND, + 0, mkjournal_proc, &es); + if (es.err) { + retval = es.err; + goto errout; + } + + if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) + goto errout; + + inode.i_size += fs->blocksize * size; + inode.i_blocks += (fs->blocksize / 512) * es.newblocks; + inode.i_mtime = inode.i_ctime = time(NULL); + inode.i_links_count = 1; + inode.i_mode = LINUX_S_IFREG | 0600; + + if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) + goto errout; + retval = 0; + + memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); + fs->super->s_jnl_blocks[16] = inode.i_size; + fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; + ext2fs_mark_super_dirty(fs); + +errout: + ext2fs_free_mem(&buf); + return retval; +} + +/* + * This function adds a journal device to a filesystem + */ +errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) +{ + struct stat st; + errcode_t retval; + char buf[1024]; + journal_superblock_t *jsb; + int start; + __u32 i, nr_users; + + /* Make sure the device exists and is a block device */ + if (stat(journal_dev->device_name, &st) < 0) + return errno; + + if (!S_ISBLK(st.st_mode)) + return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ + + /* Get the journal superblock */ + start = 1; + if (journal_dev->blocksize == 1024) + start++; + if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf))) + return retval; + + jsb = (journal_superblock_t *) buf; + if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || + (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) + return EXT2_ET_NO_JOURNAL_SB; + + if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) + return EXT2_ET_UNEXPECTED_BLOCK_SIZE; + + /* Check and see if this filesystem has already been added */ + nr_users = ntohl(jsb->s_nr_users); + for (i=0; i < nr_users; i++) { + if (memcmp(fs->super->s_uuid, + &jsb->s_users[i*16], 16) == 0) + break; + } + if (i >= nr_users) { + memcpy(&jsb->s_users[nr_users*16], + fs->super->s_uuid, 16); + jsb->s_nr_users = htonl(nr_users+1); + } + + /* Writeback the journal superblock */ + if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf))) + return retval; + + fs->super->s_journal_inum = 0; + fs->super->s_journal_dev = st.st_rdev; + memcpy(fs->super->s_journal_uuid, jsb->s_uuid, + sizeof(fs->super->s_journal_uuid)); + fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + ext2fs_mark_super_dirty(fs); + return 0; +} + +/* + * This function adds a journal inode to a filesystem, using either + * POSIX routines if the filesystem is mounted, or using direct I/O + * functions if it is not. + */ +errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) +{ + errcode_t retval; + ext2_ino_t journal_ino; + struct stat st; + char jfile[1024]; + int fd, mount_flags, f; + + retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, + jfile, sizeof(jfile)-10); + if (retval) + return retval; + + if (mount_flags & EXT2_MF_MOUNTED) { + strcat(jfile, "/.journal"); + + /* + * If .../.journal already exists, make sure any + * immutable or append-only flags are cleared. + */ +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + (void) chflags (jfile, 0); +#else +#if HAVE_EXT2_IOCTLS + fd = open(jfile, O_RDONLY); + if (fd >= 0) { + f = 0; + ioctl(fd, EXT2_IOC_SETFLAGS, &f); + close(fd); + } +#endif +#endif + + /* Create the journal file */ + if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) + return errno; + + if ((retval = write_journal_file(fs, jfile, size, flags))) + goto errout; + + /* Get inode number of the journal file */ + if (fstat(fd, &st) < 0) + goto errout; + +#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) + retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); +#else +#if HAVE_EXT2_IOCTLS + f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; + retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); +#endif +#endif + if (retval) + goto errout; + + close(fd); + journal_ino = st.st_ino; + } else { + journal_ino = EXT2_JOURNAL_INO; + if ((retval = write_journal_inode(fs, journal_ino, + size, flags))) + return retval; + } + + fs->super->s_journal_inum = journal_ino; + fs->super->s_journal_dev = 0; + memset(fs->super->s_journal_uuid, 0, + sizeof(fs->super->s_journal_uuid)); + fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; + + ext2fs_mark_super_dirty(fs); + return 0; +errout: + close(fd); + return retval; +} + +#ifdef DEBUG +main(int argc, char **argv) +{ + errcode_t retval; + char *device_name; + ext2_filsys fs; + + if (argc < 2) { + fprintf(stderr, "Usage: %s filesystem\n", argv[0]); + exit(1); + } + device_name = argv[1]; + + retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, + unix_io_manager, &fs); + if (retval) { + com_err(argv[0], retval, "while opening %s", device_name); + exit(1); + } + + retval = ext2fs_add_journal_inode(fs, 1024); + if (retval) { + com_err(argv[0], retval, "while adding journal to %s", + device_name); + exit(1); + } + retval = ext2fs_flush(fs); + if (retval) { + printf("Warning, had trouble writing out superblocks.\n"); + } + ext2fs_close(fs); + exit(0); +} +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/namei.c b/e2fsprogs/old_e2fsprogs/ext2fs/namei.c new file mode 100644 index 000000000..18244613c --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/namei.c @@ -0,0 +1,204 @@ +/* vi: set sw=4 ts=4: */ +/* + * namei.c --- ext2fs directory lookup operations + * + * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +/* #define NAMEI_DEBUG */ + +#include "ext2_fs.h" +#include "ext2fs.h" + +static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, + const char *pathname, size_t pathlen, int follow, + int link_count, char *buf, ext2_ino_t *res_inode); + +static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, + ext2_ino_t inode, int link_count, + char *buf, ext2_ino_t *res_inode) +{ + char *pathname; + char *buffer = NULL; + errcode_t retval; + struct ext2_inode ei; + +#ifdef NAMEI_DEBUG + printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", + root, dir, inode, link_count); + +#endif + retval = ext2fs_read_inode (fs, inode, &ei); + if (retval) return retval; + if (!LINUX_S_ISLNK (ei.i_mode)) { + *res_inode = inode; + return 0; + } + if (link_count++ > 5) { + return EXT2_ET_SYMLINK_LOOP; + } + if (ext2fs_inode_data_blocks(fs, &ei)) { + retval = ext2fs_get_mem(fs->blocksize, &buffer); + if (retval) + return retval; + retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer); + if (retval) { + ext2fs_free_mem(&buffer); + return retval; + } + pathname = buffer; + } else + pathname = (char *)&(ei.i_block[0]); + retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, + link_count, buf, res_inode); + ext2fs_free_mem(&buffer); + return retval; +} + +/* + * This routine interprets a pathname in the context of the current + * directory and the root directory, and returns the inode of the + * containing directory, and a pointer to the filename of the file + * (pointing into the pathname) and the length of the filename. + */ +static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, + const char *pathname, int pathlen, + int link_count, char *buf, + const char **name, int *namelen, + ext2_ino_t *res_inode) +{ + char c; + const char *thisname; + int len; + ext2_ino_t inode; + errcode_t retval; + + if ((c = *pathname) == '/') { + dir = root; + pathname++; + pathlen--; + } + while (1) { + thisname = pathname; + for (len=0; --pathlen >= 0;len++) { + c = *(pathname++); + if (c == '/') + break; + } + if (pathlen < 0) + break; + retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode); + if (retval) return retval; + retval = follow_link (fs, root, dir, inode, + link_count, buf, &dir); + if (retval) return retval; + } + *name = thisname; + *namelen = len; + *res_inode = dir; + return 0; +} + +static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, + const char *pathname, size_t pathlen, int follow, + int link_count, char *buf, ext2_ino_t *res_inode) +{ + const char *basename; + int namelen; + ext2_ino_t dir, inode; + errcode_t retval; + +#ifdef NAMEI_DEBUG + printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", + root, base, pathlen, pathname, link_count); +#endif + retval = dir_namei(fs, root, base, pathname, pathlen, + link_count, buf, &basename, &namelen, &dir); + if (retval) return retval; + if (!namelen) { /* special case: '/usr/' etc */ + *res_inode=dir; + return 0; + } + retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode); + if (retval) + return retval; + if (follow) { + retval = follow_link(fs, root, dir, inode, link_count, + buf, &inode); + if (retval) + return retval; + } +#ifdef NAMEI_DEBUG + printf("open_namei: (link_count=%d) returns %lu\n", + link_count, inode); +#endif + *res_inode = inode; + return 0; +} + +errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0, + buf, inode); + + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + const char *name, ext2_ino_t *inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0, + buf, inode); + + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, + ext2_ino_t inode, ext2_ino_t *res_inode) +{ + char *buf; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + + retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode); + + ext2fs_free_mem(&buf); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c b/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c new file mode 100644 index 000000000..9f156626d --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * newdir.c --- create a new directory block + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +#ifndef EXT2_FT_DIR +#define EXT2_FT_DIR 2 +#endif + +/* + * Create new directory block + */ +errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, + ext2_ino_t parent_ino, char **block) +{ + struct ext2_dir_entry *dir = NULL; + errcode_t retval; + char *buf; + int rec_len; + int filetype = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return retval; + memset(buf, 0, fs->blocksize); + dir = (struct ext2_dir_entry *) buf; + dir->rec_len = fs->blocksize; + + if (dir_ino) { + if (fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE) + filetype = EXT2_FT_DIR << 8; + /* + * Set up entry for '.' + */ + dir->inode = dir_ino; + dir->name_len = 1 | filetype; + dir->name[0] = '.'; + rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1); + dir->rec_len = EXT2_DIR_REC_LEN(1); + + /* + * Set up entry for '..' + */ + dir = (struct ext2_dir_entry *) (buf + dir->rec_len); + dir->rec_len = rec_len; + dir->inode = parent_ino; + dir->name_len = 2 | filetype; + dir->name[0] = '.'; + dir->name[1] = '.'; + } + *block = buf; + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c b/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c new file mode 100644 index 000000000..1b271196b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c @@ -0,0 +1,330 @@ +/* vi: set sw=4 ts=4: */ +/* + * openfs.c --- open an ext2 filesystem + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" + + +#include "ext2fs.h" +#include "e2image.h" + +blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) +{ + int bg; + int has_super = 0; + int ret_blk; + + if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || + (i < fs->super->s_first_meta_bg)) + return (group_block + i + 1); + + bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i; + if (ext2fs_bg_has_super(fs, bg)) + has_super = 1; + ret_blk = (fs->super->s_first_data_block + has_super + + (bg * fs->super->s_blocks_per_group)); + /* + * If group_block is not the normal value, we're trying to use + * the backup group descriptors and superblock --- so use the + * alternate location of the second block group in the + * metablock group. Ideally we should be testing each bg + * descriptor block individually for correctness, but we don't + * have the infrastructure in place to do that. + */ + if (group_block != fs->super->s_first_data_block && + ((ret_blk + fs->super->s_blocks_per_group) < + fs->super->s_blocks_count)) + ret_blk += fs->super->s_blocks_per_group; + return ret_blk; +} + +errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs) +{ + return ext2fs_open2(name, 0, flags, superblock, block_size, + manager, ret_fs); +} + +/* + * Note: if superblock is non-zero, block-size must also be non-zero. + * Superblock and block_size can be zero to use the default size. + * + * Valid flags for ext2fs_open() + * + * EXT2_FLAG_RW - Open the filesystem for read/write. + * EXT2_FLAG_FORCE - Open the filesystem even if some of the + * features aren't supported. + * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device + */ +errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs) +{ + ext2_filsys fs; + errcode_t retval; + unsigned long i; + int groups_per_block, blocks_per_group; + blk_t group_block, blk; + char *dest, *cp; +#if BB_BIG_ENDIAN + int j; + struct ext2_group_desc *gdp; +#endif + + EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); + + retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); + if (retval) + return retval; + + memset(fs, 0, sizeof(struct struct_ext2_filsys)); + fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; + fs->flags = flags; + fs->umask = 022; + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + strcpy(fs->device_name, name); + cp = strchr(fs->device_name, '?'); + if (!io_options && cp) { + *cp++ = 0; + io_options = cp; + } + + retval = manager->open(fs->device_name, + (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, + &fs->io); + if (retval) + goto cleanup; + if (io_options && + (retval = io_channel_set_options(fs->io, io_options))) + goto cleanup; + fs->image_io = fs->io; + fs->io->app_data = fs; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); + if (retval) + goto cleanup; + if (flags & EXT2_FLAG_IMAGE_FILE) { + retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), + &fs->image_header); + if (retval) + goto cleanup; + retval = io_channel_read_blk(fs->io, 0, + -(int)sizeof(struct ext2_image_hdr), + fs->image_header); + if (retval) + goto cleanup; + if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) + return EXT2_ET_MAGIC_E2IMAGE; + superblock = 1; + block_size = fs->image_header->fs_blocksize; + } + + /* + * If the user specifies a specific block # for the + * superblock, then he/she must also specify the block size! + * Otherwise, read the master superblock located at offset + * SUPERBLOCK_OFFSET from the start of the partition. + * + * Note: we only save a backup copy of the superblock if we + * are reading the superblock from the primary superblock location. + */ + if (superblock) { + if (!block_size) { + retval = EXT2_ET_INVALID_ARGUMENT; + goto cleanup; + } + io_channel_set_blksize(fs->io, block_size); + group_block = superblock; + fs->orig_super = 0; + } else { + io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); + superblock = 1; + group_block = 0; + retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); + if (retval) + goto cleanup; + } + retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, + fs->super); + if (retval) + goto cleanup; + if (fs->orig_super) + memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); + +#if BB_BIG_ENDIAN + if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) || + (fs->flags & EXT2_FLAG_SWAP_BYTES)) { + fs->flags |= EXT2_FLAG_SWAP_BYTES; + + ext2fs_swap_super(fs->super); + } +#endif + + if (fs->super->s_magic != EXT2_SUPER_MAGIC) { + retval = EXT2_ET_BAD_MAGIC; + goto cleanup; + } + if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { + retval = EXT2_ET_REV_TOO_HIGH; + goto cleanup; + } + + /* + * Check for feature set incompatibility + */ + if (!(flags & EXT2_FLAG_FORCE)) { + if (fs->super->s_feature_incompat & + ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + if ((flags & EXT2_FLAG_RW) && + (fs->super->s_feature_ro_compat & + ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { + retval = EXT2_ET_RO_UNSUPP_FEATURE; + goto cleanup; + } + if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && + (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + retval = EXT2_ET_UNSUPP_FEATURE; + goto cleanup; + } + } + + fs->blocksize = EXT2_BLOCK_SIZE(fs->super); + if (fs->blocksize == 0) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->fragsize = EXT2_FRAG_SIZE(fs->super); + fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group * + EXT2_INODE_SIZE(fs->super) + + EXT2_BLOCK_SIZE(fs->super) - 1) / + EXT2_BLOCK_SIZE(fs->super)); + if (block_size) { + if (block_size != fs->blocksize) { + retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; + goto cleanup; + } + } + /* + * Set the blocksize to the filesystem's blocksize. + */ + io_channel_set_blksize(fs->io, fs->blocksize); + + /* + * If this is an external journal device, don't try to read + * the group descriptors, because they're not there. + */ + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + fs->group_desc_count = 0; + *ret_fs = fs; + return 0; + } + + /* + * Read group descriptors + */ + blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); + if (blocks_per_group == 0 || + blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || + fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) { + retval = EXT2_ET_CORRUPT_SUPERBLOCK; + goto cleanup; + } + fs->group_desc_count = (fs->super->s_blocks_count - + fs->super->s_first_data_block + + blocks_per_group - 1) / blocks_per_group; + fs->desc_blocks = (fs->group_desc_count + + EXT2_DESC_PER_BLOCK(fs->super) - 1) + / EXT2_DESC_PER_BLOCK(fs->super); + retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize, + &fs->group_desc); + if (retval) + goto cleanup; + if (!group_block) + group_block = fs->super->s_first_data_block; + dest = (char *) fs->group_desc; + groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc); + for (i = 0; i < fs->desc_blocks; i++) { + blk = ext2fs_descriptor_block_loc(fs, group_block, i); + retval = io_channel_read_blk(fs->io, blk, 1, dest); + if (retval) + goto cleanup; +#if BB_BIG_ENDIAN + if (fs->flags & EXT2_FLAG_SWAP_BYTES) { + gdp = (struct ext2_group_desc *) dest; + for (j=0; j < groups_per_block; j++) + ext2fs_swap_group_desc(gdp++); + } +#endif + dest += fs->blocksize; + } + + *ret_fs = fs; + return 0; +cleanup: + ext2fs_free(fs); + return retval; +} + +/* + * Set/get the filesystem data I/O channel. + * + * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true. + */ +errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + if (old_io) { + *old_io = (fs->image_io == fs->io) ? 0 : fs->io; + } + return 0; +} + +errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + fs->io = new_io ? new_io : fs->image_io; + return 0; +} + +errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) +{ + if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) + return EXT2_ET_NOT_IMAGE_FILE; + fs->io = fs->image_io = new_io; + fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | + EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; + fs->flags &= ~EXT2_FLAG_IMAGE_FILE; + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c b/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c new file mode 100644 index 000000000..ce77bc9f6 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c @@ -0,0 +1,96 @@ +/* vi: set sw=4 ts=4: */ +/* + * read_bb --- read the bad blocks inode + * + * Copyright (C) 1994 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct read_bb_record { + ext2_badblocks_list bb_list; + errcode_t err; +}; + +/* + * Helper function for ext2fs_read_bb_inode() + */ +#ifdef __TURBOC__ +# pragma argsused +#endif +static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, + e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct read_bb_record *rb = (struct read_bb_record *) priv_data; + + if (blockcnt < 0) + return 0; + + if ((*block_nr < fs->super->s_first_data_block) || + (*block_nr >= fs->super->s_blocks_count)) + return 0; /* Ignore illegal blocks */ + + rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); + if (rb->err) + return BLOCK_ABORT; + return 0; +} + +/* + * Reads the current bad blocks from the bad blocks inode. + */ +errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list) +{ + errcode_t retval; + struct read_bb_record rb; + struct ext2_inode inode; + blk_t numblocks; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!*bb_list) { + retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); + if (retval) + return retval; + if (inode.i_blocks < 500) + numblocks = (inode.i_blocks / + (fs->blocksize / 512)) + 20; + else + numblocks = 500; + retval = ext2fs_badblocks_list_create(bb_list, numblocks); + if (retval) + return retval; + } + + rb.bb_list = *bb_list; + rb.err = 0; + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0, + mark_bad_block, &rb); + if (retval) + return retval; + + return rb.err; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c b/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c new file mode 100644 index 000000000..bf1fc328b --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c @@ -0,0 +1,96 @@ +/* vi: set sw=4 ts=4: */ +/* + * read_bb_file.c --- read a list of bad blocks from a FILE * + * + * Copyright (C) 1994, 1995, 2000 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Reads a list of bad blocks from a FILE * + */ +errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void *priv_data, + void (*invalid)(ext2_filsys fs, + blk_t blk, + char *badstr, + void *priv_data)) +{ + errcode_t retval; + blk_t blockno; + int count; + char buf[128]; + + if (fs) + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!*bb_list) { + retval = ext2fs_badblocks_list_create(bb_list, 10); + if (retval) + return retval; + } + + while (!feof (f)) { + if (fgets(buf, sizeof(buf), f) == NULL) + break; + count = sscanf(buf, "%u", &blockno); + if (count <= 0) + continue; + if (fs && + ((blockno < fs->super->s_first_data_block) || + (blockno >= fs->super->s_blocks_count))) { + if (invalid) + (invalid)(fs, blockno, buf, priv_data); + continue; + } + retval = ext2fs_badblocks_list_add(*bb_list, blockno); + if (retval) + return retval; + } + return 0; +} + +static void call_compat_invalid(ext2_filsys fs, blk_t blk, + char *badstr EXT2FS_ATTR((unused)), + void *priv_data) +{ + void (*invalid)(ext2_filsys, blk_t); + + invalid = (void (*)(ext2_filsys, blk_t)) priv_data; + if (invalid) + invalid(fs, blk); +} + + +/* + * Reads a list of bad blocks from a FILE * + */ +errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, + ext2_badblocks_list *bb_list, + void (*invalid)(ext2_filsys fs, blk_t blk)) +{ + return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid, + call_compat_invalid); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c b/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c new file mode 100644 index 000000000..403463a90 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c @@ -0,0 +1,220 @@ +/* vi: set sw=4 ts=4: */ +/* + * res_gdt.c --- reserve blocks for growing the group descriptor table + * during online resizing. + * + * Copyright (C) 2002 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +static unsigned int list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} + +/* + * This code assumes that the reserved blocks have already been marked in-use + * during ext2fs_initialize(), so that they are not allocated for other + * uses before we can add them to the resize inode (which has to come + * after the creation of the inode table). + */ +errcode_t ext2fs_create_resize_inode(ext2_filsys fs) +{ + errcode_t retval, retval2; + struct ext2_super_block *sb; + struct ext2_inode inode; + __u32 *dindir_buf, *gdt_buf; + int rsv_add; + unsigned long long apb, inode_size; + blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; + int dindir_dirty = 0, inode_dirty = 0; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + sb = fs->super; + + retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf); + if (retval) + goto out_free; + gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); + + retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); + if (retval) + goto out_free; + + /* Maximum possible file size (we donly use the dindirect blocks) */ + apb = EXT2_ADDR_PER_BLOCK(sb); + rsv_add = fs->blocksize / 512; + if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { +#ifdef RES_GDT_DEBUG + printf("reading GDT dindir %u\n", dindir_blk); +#endif + retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); + if (retval) + goto out_inode; + } else { + blk_t goal = 3 + sb->s_reserved_gdt_blocks + + fs->desc_blocks + fs->inode_blocks_per_group; + + retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); + if (retval) + goto out_free; + inode.i_mode = LINUX_S_IFREG | 0600; + inode.i_links_count = 1; + inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; + inode.i_blocks = rsv_add; + memset(dindir_buf, 0, fs->blocksize); +#ifdef RES_GDT_DEBUG + printf("allocated GDT dindir %u\n", dindir_blk); +#endif + dindir_dirty = inode_dirty = 1; + inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; + inode_size *= fs->blocksize; + inode.i_size = inode_size & 0xFFFFFFFF; + inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; + if (inode.i_size_high) { + sb->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_LARGE_FILE; + } + inode.i_ctime = time(NULL); + } + + for (rsv_off = 0, gdt_off = fs->desc_blocks, + gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; + rsv_off < sb->s_reserved_gdt_blocks; + rsv_off++, gdt_off++, gdt_blk++) { + unsigned int three = 1, five = 5, seven = 7; + unsigned int grp, last = 0; + int gdt_dirty = 0; + + gdt_off %= apb; + if (!dindir_buf[gdt_off]) { + /* FIXME XXX XXX + blk_t new_blk; + + retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); + if (retval) + goto out_free; + if (new_blk != gdt_blk) { + // XXX free block + retval = -1; // XXX + } + */ + gdt_dirty = dindir_dirty = inode_dirty = 1; + memset(gdt_buf, 0, fs->blocksize); + dindir_buf[gdt_off] = gdt_blk; + inode.i_blocks += rsv_add; +#ifdef RES_GDT_DEBUG + printf("added primary GDT block %u at %u[%u]\n", + gdt_blk, dindir_blk, gdt_off); +#endif + } else if (dindir_buf[gdt_off] == gdt_blk) { +#ifdef RES_GDT_DEBUG + printf("reading primary GDT block %u\n", gdt_blk); +#endif + retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); + if (retval) + goto out_dindir; + } else { +#ifdef RES_GDT_DEBUG + printf("bad primary GDT %u != %u at %u[%u]\n", + dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); +#endif + retval = EXT2_ET_RESIZE_INODE_CORRUPT; + goto out_dindir; + } + + while ((grp = list_backups(fs, &three, &five, &seven)) < + fs->group_desc_count) { + blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; + + if (!gdt_buf[last]) { +#ifdef RES_GDT_DEBUG + printf("added backup GDT %u grp %u@%u[%u]\n", + expect, grp, gdt_blk, last); +#endif + gdt_buf[last] = expect; + inode.i_blocks += rsv_add; + gdt_dirty = inode_dirty = 1; + } else if (gdt_buf[last] != expect) { +#ifdef RES_GDT_DEBUG + printf("bad backup GDT %u != %u at %u[%u]\n", + gdt_buf[last], expect, gdt_blk, last); +#endif + retval = EXT2_ET_RESIZE_INODE_CORRUPT; + goto out_dindir; + } + last++; + } + if (gdt_dirty) { +#ifdef RES_GDT_DEBUG + printf("writing primary GDT block %u\n", gdt_blk); +#endif + retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); + if (retval) + goto out_dindir; + } + } + +out_dindir: + if (dindir_dirty) { + retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); + if (!retval) + retval = retval2; + } +out_inode: +#ifdef RES_GDT_DEBUG + printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, + inode.i_size); +#endif + if (inode_dirty) { + inode.i_atime = inode.i_mtime = time(NULL); + retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode); + if (!retval) + retval = retval2; + } +out_free: + ext2fs_free_mem((void *)&dindir_buf); + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c b/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c new file mode 100644 index 000000000..32e87b77a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * rs_bitmap.c --- routine for changing the size of a bitmap + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_generic_bitmap bmap) +{ + errcode_t retval; + size_t size, new_size; + __u32 bitno; + + if (!bmap) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP); + + /* + * If we're expanding the bitmap, make sure all of the new + * parts of the bitmap are zero. + */ + if (new_end > bmap->end) { + bitno = bmap->real_end; + if (bitno > new_end) + bitno = new_end; + for (; bitno > bmap->end; bitno--) + ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap); + } + if (new_real_end == bmap->real_end) { + bmap->end = new_end; + return 0; + } + + size = ((bmap->real_end - bmap->start) / 8) + 1; + new_size = ((new_real_end - bmap->start) / 8) + 1; + + if (size != new_size) { + retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap); + if (retval) + return retval; + } + if (new_size > size) + memset(bmap->bitmap + size, 0, new_size - size); + + bmap->end = new_end; + bmap->real_end = new_real_end; + return 0; +} + +errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_inode_bitmap bmap) +{ + errcode_t retval; + + if (!bmap) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP); + + bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; + retval = ext2fs_resize_generic_bitmap(new_end, new_real_end, + bmap); + bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP; + return retval; +} + +errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, + ext2fs_block_bitmap bmap) +{ + errcode_t retval; + + if (!bmap) + return EXT2_ET_INVALID_ARGUMENT; + + EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP); + + bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP; + retval = ext2fs_resize_generic_bitmap(new_end, new_real_end, + bmap); + bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP; + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c b/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c new file mode 100644 index 000000000..bba432679 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c @@ -0,0 +1,294 @@ +/* vi: set sw=4 ts=4: */ +/* + * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. + * + * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "e2image.h" + +#if defined(__powerpc__) && BB_BIG_ENDIAN +/* + * On the PowerPC, the big-endian variant of the ext2 filesystem + * has its bitmaps stored as 32-bit words with bit 0 as the LSB + * of each word. Thus a bitmap with only bit 0 set would be, as + * a string of bytes, 00 00 00 01 00 ... + * To cope with this, we byte-reverse each word of a bitmap if + * we have a big-endian filesystem, that is, if we are *not* + * byte-swapping other word-sized numbers. + */ +#define EXT2_BIG_ENDIAN_BITMAPS +#endif + +#ifdef EXT2_BIG_ENDIAN_BITMAPS +static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes) +{ + __u32 *p = (__u32 *) bitmap; + int n; + + for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) + *p = ext2fs_swab32(*p); +} +#endif + +errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) +{ + dgrp_t i; + size_t nbytes; + errcode_t retval; + char * inode_bitmap = fs->inode_map->bitmap; + char * bitmap_block = NULL; + blk_t blk; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + if (!inode_bitmap) + return 0; + nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); + + retval = ext2fs_get_mem(fs->blocksize, &bitmap_block); + if (retval) + return retval; + memset(bitmap_block, 0xff, fs->blocksize); + for (i = 0; i < fs->group_desc_count; i++) { + memcpy(bitmap_block, inode_bitmap, nbytes); + blk = fs->group_desc[i].bg_inode_bitmap; + if (blk) { +#ifdef EXT2_BIG_ENDIAN_BITMAPS + if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) + ext2fs_swap_bitmap(fs, bitmap_block, nbytes); +#endif + retval = io_channel_write_blk(fs->io, blk, 1, + bitmap_block); + if (retval) + return EXT2_ET_INODE_BITMAP_WRITE; + } + inode_bitmap += nbytes; + } + fs->flags &= ~EXT2_FLAG_IB_DIRTY; + ext2fs_free_mem(&bitmap_block); + return 0; +} + +errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) +{ + dgrp_t i; + unsigned int j; + int nbytes; + unsigned int nbits; + errcode_t retval; + char * block_bitmap = fs->block_map->bitmap; + char * bitmap_block = NULL; + blk_t blk; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + if (!block_bitmap) + return 0; + nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + retval = ext2fs_get_mem(fs->blocksize, &bitmap_block); + if (retval) + return retval; + memset(bitmap_block, 0xff, fs->blocksize); + for (i = 0; i < fs->group_desc_count; i++) { + memcpy(bitmap_block, block_bitmap, nbytes); + if (i == fs->group_desc_count - 1) { + /* Force bitmap padding for the last group */ + nbits = ((fs->super->s_blocks_count + - fs->super->s_first_data_block) + % EXT2_BLOCKS_PER_GROUP(fs->super)); + if (nbits) + for (j = nbits; j < fs->blocksize * 8; j++) + ext2fs_set_bit(j, bitmap_block); + } + blk = fs->group_desc[i].bg_block_bitmap; + if (blk) { +#ifdef EXT2_BIG_ENDIAN_BITMAPS + if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) + ext2fs_swap_bitmap(fs, bitmap_block, nbytes); +#endif + retval = io_channel_write_blk(fs->io, blk, 1, + bitmap_block); + if (retval) + return EXT2_ET_BLOCK_BITMAP_WRITE; + } + block_bitmap += nbytes; + } + fs->flags &= ~EXT2_FLAG_BB_DIRTY; + ext2fs_free_mem(&bitmap_block); + return 0; +} + +static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) +{ + dgrp_t i; + char *block_bitmap = NULL, *inode_bitmap = NULL; + char *buf; + errcode_t retval; + int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8; + int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8; + blk_t blk; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + fs->write_bitmaps = ext2fs_write_bitmaps; + + retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); + if (retval) + return retval; + if (do_block) { + ext2fs_free_block_bitmap(fs->block_map); + sprintf(buf, "block bitmap for %s", fs->device_name); + retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); + if (retval) + goto cleanup; + block_bitmap = fs->block_map->bitmap; + } + if (do_inode) { + ext2fs_free_inode_bitmap(fs->inode_map); + sprintf(buf, "inode bitmap for %s", fs->device_name); + retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); + if (retval) + goto cleanup; + inode_bitmap = fs->inode_map->bitmap; + } + ext2fs_free_mem(&buf); + + if (fs->flags & EXT2_FLAG_IMAGE_FILE) { + if (inode_bitmap) { + blk = (fs->image_header->offset_inodemap / + fs->blocksize); + retval = io_channel_read_blk(fs->image_io, blk, + -(inode_nbytes * fs->group_desc_count), + inode_bitmap); + if (retval) + goto cleanup; + } + if (block_bitmap) { + blk = (fs->image_header->offset_blockmap / + fs->blocksize); + retval = io_channel_read_blk(fs->image_io, blk, + -(block_nbytes * fs->group_desc_count), + block_bitmap); + if (retval) + goto cleanup; + } + return 0; + } + + for (i = 0; i < fs->group_desc_count; i++) { + if (block_bitmap) { + blk = fs->group_desc[i].bg_block_bitmap; + if (blk) { + retval = io_channel_read_blk(fs->io, blk, + -block_nbytes, block_bitmap); + if (retval) { + retval = EXT2_ET_BLOCK_BITMAP_READ; + goto cleanup; + } +#ifdef EXT2_BIG_ENDIAN_BITMAPS + if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) + ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes); +#endif + } else + memset(block_bitmap, 0, block_nbytes); + block_bitmap += block_nbytes; + } + if (inode_bitmap) { + blk = fs->group_desc[i].bg_inode_bitmap; + if (blk) { + retval = io_channel_read_blk(fs->io, blk, + -inode_nbytes, inode_bitmap); + if (retval) { + retval = EXT2_ET_INODE_BITMAP_READ; + goto cleanup; + } +#ifdef EXT2_BIG_ENDIAN_BITMAPS + if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) || + (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))) + ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes); +#endif + } else + memset(inode_bitmap, 0, inode_nbytes); + inode_bitmap += inode_nbytes; + } + } + return 0; + +cleanup: + if (do_block) { + ext2fs_free_mem(&fs->block_map); + } + if (do_inode) { + ext2fs_free_mem(&fs->inode_map); + } + ext2fs_free_mem(&buf); + return retval; +} + +errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs) +{ + return read_bitmaps(fs, 1, 0); +} + +errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) +{ + return read_bitmaps(fs, 0, 1); +} + +errcode_t ext2fs_read_bitmaps(ext2_filsys fs) +{ + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (fs->inode_map && fs->block_map) + return 0; + + return read_bitmaps(fs, !fs->inode_map, !fs->block_map); +} + +errcode_t ext2fs_write_bitmaps(ext2_filsys fs) +{ + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (fs->block_map && ext2fs_test_bb_dirty(fs)) { + retval = ext2fs_write_block_bitmap(fs); + if (retval) + return retval; + } + if (fs->inode_map && ext2fs_test_ib_dirty(fs)) { + retval = ext2fs_write_inode_bitmap(fs); + if (retval) + return retval; + } + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c b/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c new file mode 100644 index 000000000..b3d3071e9 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c @@ -0,0 +1,79 @@ +/* vi: set sw=4 ts=4: */ +/* + * sparse.c --- find the groups in an ext2 filesystem with metadata backups + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * Copyright (C) 2002 Andreas Dilger. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fsP.h" + +static int test_root(int a, int b) +{ + if (a == 0) + return 1; + while (1) { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } +} + +int ext2fs_bg_has_super(ext2_filsys fs, int group_block) +{ + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) + return 1; + + if (test_root(group_block, 3) || (test_root(group_block, 5)) || + test_root(group_block, 7)) + return 1; + + return 0; +} + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, + unsigned int *five, unsigned int *seven) +{ + unsigned int *min = three; + int mult = 3; + unsigned int ret; + + if (!(fs->super->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c b/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c new file mode 100644 index 000000000..07b757abd --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c @@ -0,0 +1,234 @@ +/* vi: set sw=4 ts=4: */ +/* + * swapfs.c --- swap ext2 filesystem data structures + * + * Copyright (C) 1995, 1996, 2002 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" +#include "ext2_ext_attr.h" + +#if BB_BIG_ENDIAN +void ext2fs_swap_super(struct ext2_super_block * sb) +{ + int i; + sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count); + sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count); + sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count); + sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count); + sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count); + sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block); + sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size); + sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size); + sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group); + sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group); + sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group); + sb->s_mtime = ext2fs_swab32(sb->s_mtime); + sb->s_wtime = ext2fs_swab32(sb->s_wtime); + sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count); + sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count); + sb->s_magic = ext2fs_swab16(sb->s_magic); + sb->s_state = ext2fs_swab16(sb->s_state); + sb->s_errors = ext2fs_swab16(sb->s_errors); + sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level); + sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck); + sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval); + sb->s_creator_os = ext2fs_swab32(sb->s_creator_os); + sb->s_rev_level = ext2fs_swab32(sb->s_rev_level); + sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid); + sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid); + sb->s_first_ino = ext2fs_swab32(sb->s_first_ino); + sb->s_inode_size = ext2fs_swab16(sb->s_inode_size); + sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr); + sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat); + sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat); + sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat); + sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap); + sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks); + sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum); + sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev); + sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan); + sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts); + sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg); + sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time); + for (i=0; i < 4; i++) + sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); + for (i=0; i < 17; i++) + sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]); +} + +void ext2fs_swap_group_desc(struct ext2_group_desc *gdp) +{ + gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap); + gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap); + gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table); + gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count); + gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count); + gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count); +} + +void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header) +{ + struct ext2_ext_attr_header *from_header = + (struct ext2_ext_attr_header *)from; + struct ext2_ext_attr_header *to_header = + (struct ext2_ext_attr_header *)to; + struct ext2_ext_attr_entry *from_entry, *to_entry; + char *from_end = (char *)from_header + bufsize; + int n; + + if (to_header != from_header) + memcpy(to_header, from_header, bufsize); + + from_entry = (struct ext2_ext_attr_entry *)from_header; + to_entry = (struct ext2_ext_attr_entry *)to_header; + + if (has_header) { + to_header->h_magic = ext2fs_swab32(from_header->h_magic); + to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); + to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); + for (n=0; n<4; n++) + to_header->h_reserved[n] = + ext2fs_swab32(from_header->h_reserved[n]); + from_entry = (struct ext2_ext_attr_entry *)(from_header+1); + to_entry = (struct ext2_ext_attr_entry *)(to_header+1); + } + + while ((char *)from_entry < from_end && *(__u32 *)from_entry) { + to_entry->e_value_offs = + ext2fs_swab16(from_entry->e_value_offs); + to_entry->e_value_block = + ext2fs_swab32(from_entry->e_value_block); + to_entry->e_value_size = + ext2fs_swab32(from_entry->e_value_size); + from_entry = EXT2_EXT_ATTR_NEXT(from_entry); + to_entry = EXT2_EXT_ATTR_NEXT(to_entry); + } +} + +void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, + struct ext2_inode_large *f, int hostorder, + int bufsize) +{ + unsigned i; + int islnk = 0; + __u32 *eaf, *eat; + + if (hostorder && LINUX_S_ISLNK(f->i_mode)) + islnk = 1; + t->i_mode = ext2fs_swab16(f->i_mode); + if (!hostorder && LINUX_S_ISLNK(t->i_mode)) + islnk = 1; + t->i_uid = ext2fs_swab16(f->i_uid); + t->i_size = ext2fs_swab32(f->i_size); + t->i_atime = ext2fs_swab32(f->i_atime); + t->i_ctime = ext2fs_swab32(f->i_ctime); + t->i_mtime = ext2fs_swab32(f->i_mtime); + t->i_dtime = ext2fs_swab32(f->i_dtime); + t->i_gid = ext2fs_swab16(f->i_gid); + t->i_links_count = ext2fs_swab16(f->i_links_count); + t->i_blocks = ext2fs_swab32(f->i_blocks); + t->i_flags = ext2fs_swab32(f->i_flags); + t->i_file_acl = ext2fs_swab32(f->i_file_acl); + t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); + if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) { + for (i = 0; i < EXT2_N_BLOCKS; i++) + t->i_block[i] = ext2fs_swab32(f->i_block[i]); + } else if (t != f) { + for (i = 0; i < EXT2_N_BLOCKS; i++) + t->i_block[i] = f->i_block[i]; + } + t->i_generation = ext2fs_swab32(f->i_generation); + t->i_faddr = ext2fs_swab32(f->i_faddr); + + switch (fs->super->s_creator_os) { + case EXT2_OS_LINUX: + t->osd1.linux1.l_i_reserved1 = + ext2fs_swab32(f->osd1.linux1.l_i_reserved1); + t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag; + t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize; + t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1); + t->osd2.linux2.l_i_uid_high = + ext2fs_swab16 (f->osd2.linux2.l_i_uid_high); + t->osd2.linux2.l_i_gid_high = + ext2fs_swab16 (f->osd2.linux2.l_i_gid_high); + t->osd2.linux2.l_i_reserved2 = + ext2fs_swab32(f->osd2.linux2.l_i_reserved2); + break; + case EXT2_OS_HURD: + t->osd1.hurd1.h_i_translator = + ext2fs_swab32 (f->osd1.hurd1.h_i_translator); + t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag; + t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize; + t->osd2.hurd2.h_i_mode_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high); + t->osd2.hurd2.h_i_uid_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high); + t->osd2.hurd2.h_i_gid_high = + ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high); + t->osd2.hurd2.h_i_author = + ext2fs_swab32 (f->osd2.hurd2.h_i_author); + break; + case EXT2_OS_MASIX: + t->osd1.masix1.m_i_reserved1 = + ext2fs_swab32(f->osd1.masix1.m_i_reserved1); + t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag; + t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize; + t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1); + t->osd2.masix2.m_i_reserved2[0] = + ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]); + t->osd2.masix2.m_i_reserved2[1] = + ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]); + break; + } + + if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16))) + return; /* no i_extra_isize field */ + + t->i_extra_isize = ext2fs_swab16(f->i_extra_isize); + if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) - + sizeof(struct ext2_inode)) { + /* this is error case: i_extra_size is too large */ + return; + } + + i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32); + if (bufsize < (int) i) + return; /* no space for EA magic */ + + eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) + + f->i_extra_isize); + + if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC) + return; /* it seems no magic here */ + + eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) + + f->i_extra_isize); + *eat = ext2fs_swab32(*eaf); + + /* convert EA(s) */ + ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1), + bufsize - sizeof(struct ext2_inode) - + t->i_extra_isize - sizeof(__u32), 0); +} + +void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t, + struct ext2_inode *f, int hostorder) +{ + ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t, + (struct ext2_inode_large *) f, hostorder, + sizeof(struct ext2_inode)); +} + +#endif diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c b/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c new file mode 100644 index 000000000..3d40d9a97 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c @@ -0,0 +1,380 @@ +/* vi: set sw=4 ts=4: */ +/* + * test_io.c --- This is the Test I/O interface. + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + +struct test_private_data { + int magic; + io_channel real; + int flags; + FILE *outfile; + unsigned long block; + int read_abort_count, write_abort_count; + void (*read_blk)(unsigned long block, int count, errcode_t err); + void (*write_blk)(unsigned long block, int count, errcode_t err); + void (*set_blksize)(int blksize, errcode_t err); + void (*write_byte)(unsigned long block, int count, errcode_t err); +}; + +static errcode_t test_open(const char *name, int flags, io_channel *channel); +static errcode_t test_close(io_channel channel); +static errcode_t test_set_blksize(io_channel channel, int blksize); +static errcode_t test_read_blk(io_channel channel, unsigned long block, + int count, void *data); +static errcode_t test_write_blk(io_channel channel, unsigned long block, + int count, const void *data); +static errcode_t test_flush(io_channel channel); +static errcode_t test_write_byte(io_channel channel, unsigned long offset, + int count, const void *buf); +static errcode_t test_set_option(io_channel channel, const char *option, + const char *arg); + +static struct struct_io_manager struct_test_manager = { + EXT2_ET_MAGIC_IO_MANAGER, + "Test I/O Manager", + test_open, + test_close, + test_set_blksize, + test_read_blk, + test_write_blk, + test_flush, + test_write_byte, + test_set_option +}; + +io_manager test_io_manager = &struct_test_manager; + +/* + * These global variable can be set by the test program as + * necessary *before* calling test_open + */ +io_manager test_io_backing_manager = 0; +void (*test_io_cb_read_blk) + (unsigned long block, int count, errcode_t err) = 0; +void (*test_io_cb_write_blk) + (unsigned long block, int count, errcode_t err) = 0; +void (*test_io_cb_set_blksize) + (int blksize, errcode_t err) = 0; +void (*test_io_cb_write_byte) + (unsigned long block, int count, errcode_t err) = 0; + +/* + * Test flags + */ +#define TEST_FLAG_READ 0x01 +#define TEST_FLAG_WRITE 0x02 +#define TEST_FLAG_SET_BLKSIZE 0x04 +#define TEST_FLAG_FLUSH 0x08 +#define TEST_FLAG_DUMP 0x10 +#define TEST_FLAG_SET_OPTION 0x20 + +static void test_dump_block(io_channel channel, + struct test_private_data *data, + unsigned long block, const void *buf) +{ + const unsigned char *cp; + FILE *f = data->outfile; + int i; + unsigned long cksum = 0; + + for (i=0, cp = buf; i < channel->block_size; i++, cp++) { + cksum += *cp; + } + fprintf(f, "Contents of block %lu, checksum %08lu:\n", block, cksum); + for (i=0, cp = buf; i < channel->block_size; i++, cp++) { + if ((i % 16) == 0) + fprintf(f, "%04x: ", i); + fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' '); + } +} + +static void test_abort(io_channel channel, unsigned long block) +{ + struct test_private_data *data; + FILE *f; + + data = (struct test_private_data *) channel->private_data; + f = data->outfile; + test_flush(channel); + + fprintf(f, "Aborting due to I/O to block %lu\n", block); + fflush(f); + abort(); +} + +static errcode_t test_open(const char *name, int flags, io_channel *channel) +{ + io_channel io = NULL; + struct test_private_data *data = NULL; + errcode_t retval; + char *value; + + if (name == 0) + return EXT2_ET_BAD_DEVICE_NAME; + retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); + if (retval) + return retval; + memset(io, 0, sizeof(struct struct_io_channel)); + io->magic = EXT2_ET_MAGIC_IO_CHANNEL; + retval = ext2fs_get_mem(sizeof(struct test_private_data), &data); + if (retval) { + retval = EXT2_ET_NO_MEMORY; + goto cleanup; + } + io->manager = test_io_manager; + retval = ext2fs_get_mem(strlen(name)+1, &io->name); + if (retval) + goto cleanup; + + strcpy(io->name, name); + io->private_data = data; + io->block_size = 1024; + io->read_error = 0; + io->write_error = 0; + io->refcount = 1; + + memset(data, 0, sizeof(struct test_private_data)); + data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL; + if (test_io_backing_manager) { + retval = test_io_backing_manager->open(name, flags, + &data->real); + if (retval) + goto cleanup; + } else + data->real = 0; + data->read_blk = test_io_cb_read_blk; + data->write_blk = test_io_cb_write_blk; + data->set_blksize = test_io_cb_set_blksize; + data->write_byte = test_io_cb_write_byte; + + data->outfile = NULL; + if ((value = getenv("TEST_IO_LOGFILE")) != NULL) + data->outfile = fopen_for_write(value); + if (!data->outfile) + data->outfile = stderr; + + data->flags = 0; + if ((value = getenv("TEST_IO_FLAGS")) != NULL) + data->flags = strtoul(value, NULL, 0); + + data->block = 0; + if ((value = getenv("TEST_IO_BLOCK")) != NULL) + data->block = strtoul(value, NULL, 0); + + data->read_abort_count = 0; + if ((value = getenv("TEST_IO_READ_ABORT")) != NULL) + data->read_abort_count = strtoul(value, NULL, 0); + + data->write_abort_count = 0; + if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL) + data->write_abort_count = strtoul(value, NULL, 0); + + *channel = io; + return 0; + +cleanup: + ext2fs_free_mem(&io); + ext2fs_free_mem(&data); + return retval; +} + +static errcode_t test_close(io_channel channel) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + if (--channel->refcount > 0) + return 0; + + if (data->real) + retval = io_channel_close(data->real); + + if (data->outfile && data->outfile != stderr) + fclose(data->outfile); + + ext2fs_free_mem(&channel->private_data); + ext2fs_free_mem(&channel->name); + ext2fs_free_mem(&channel); + return retval; +} + +static errcode_t test_set_blksize(io_channel channel, int blksize) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + if (data->real) + retval = io_channel_set_blksize(data->real, blksize); + if (data->set_blksize) + data->set_blksize(blksize, retval); + if (data->flags & TEST_FLAG_SET_BLKSIZE) + fprintf(data->outfile, + "Test_io: set_blksize(%d) returned %s\n", + blksize, retval ? error_message(retval) : "OK"); + channel->block_size = blksize; + return retval; +} + + +static errcode_t test_read_blk(io_channel channel, unsigned long block, + int count, void *buf) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + if (data->real) + retval = io_channel_read_blk(data->real, block, count, buf); + if (data->read_blk) + data->read_blk(block, count, retval); + if (data->flags & TEST_FLAG_READ) + fprintf(data->outfile, + "Test_io: read_blk(%lu, %d) returned %s\n", + block, count, retval ? error_message(retval) : "OK"); + if (data->block && data->block == block) { + if (data->flags & TEST_FLAG_DUMP) + test_dump_block(channel, data, block, buf); + if (--data->read_abort_count == 0) + test_abort(channel, block); + } + return retval; +} + +static errcode_t test_write_blk(io_channel channel, unsigned long block, + int count, const void *buf) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + if (data->real) + retval = io_channel_write_blk(data->real, block, count, buf); + if (data->write_blk) + data->write_blk(block, count, retval); + if (data->flags & TEST_FLAG_WRITE) + fprintf(data->outfile, + "Test_io: write_blk(%lu, %d) returned %s\n", + block, count, retval ? error_message(retval) : "OK"); + if (data->block && data->block == block) { + if (data->flags & TEST_FLAG_DUMP) + test_dump_block(channel, data, block, buf); + if (--data->write_abort_count == 0) + test_abort(channel, block); + } + return retval; +} + +static errcode_t test_write_byte(io_channel channel, unsigned long offset, + int count, const void *buf) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + if (data->real && data->real->manager->write_byte) + retval = io_channel_write_byte(data->real, offset, count, buf); + if (data->write_byte) + data->write_byte(offset, count, retval); + if (data->flags & TEST_FLAG_WRITE) + fprintf(data->outfile, + "Test_io: write_byte(%lu, %d) returned %s\n", + offset, count, retval ? error_message(retval) : "OK"); + return retval; +} + +/* + * Flush data buffers to disk. + */ +static errcode_t test_flush(io_channel channel) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + if (data->real) + retval = io_channel_flush(data->real); + + if (data->flags & TEST_FLAG_FLUSH) + fprintf(data->outfile, "Test_io: flush() returned %s\n", + retval ? error_message(retval) : "OK"); + + return retval; +} + +static errcode_t test_set_option(io_channel channel, const char *option, + const char *arg) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + + if (data->flags & TEST_FLAG_SET_OPTION) + fprintf(data->outfile, "Test_io: set_option(%s, %s) ", + option, arg); + if (data->real && data->real->manager->set_option) { + retval = (data->real->manager->set_option)(data->real, + option, arg); + if (data->flags & TEST_FLAG_SET_OPTION) + fprintf(data->outfile, "returned %s\n", + retval ? error_message(retval) : "OK"); + } else { + if (data->flags & TEST_FLAG_SET_OPTION) + fprintf(data->outfile, "not implemented\n"); + } + return retval; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c b/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c new file mode 100644 index 000000000..474f07340 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c @@ -0,0 +1,703 @@ +/* vi: set sw=4 ts=4: */ +/* + * unix_io.c --- This is the Unix (well, really POSIX) implementation + * of the I/O manager. + * + * Implements a one-block write-through cache. + * + * Includes support for Windows NT support under Cygwin. + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#include +#include +#ifdef __linux__ +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * For checking structure magic numbers... + */ + +#define EXT2_CHECK_MAGIC(struct, code) \ + if ((struct)->magic != (code)) return (code) + +struct unix_cache { + char *buf; + unsigned long block; + int access_time; + unsigned dirty:1; + unsigned in_use:1; +}; + +#define CACHE_SIZE 8 +#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ +#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ + +struct unix_private_data { + int magic; + int dev; + int flags; + int access_time; + ext2_loff_t offset; + struct unix_cache cache[CACHE_SIZE]; +}; + +static errcode_t unix_open(const char *name, int flags, io_channel *channel); +static errcode_t unix_close(io_channel channel); +static errcode_t unix_set_blksize(io_channel channel, int blksize); +static errcode_t unix_read_blk(io_channel channel, unsigned long block, + int count, void *data); +static errcode_t unix_write_blk(io_channel channel, unsigned long block, + int count, const void *data); +static errcode_t unix_flush(io_channel channel); +static errcode_t unix_write_byte(io_channel channel, unsigned long offset, + int size, const void *data); +static errcode_t unix_set_option(io_channel channel, const char *option, + const char *arg); + +static void reuse_cache(io_channel channel, struct unix_private_data *data, + struct unix_cache *cache, unsigned long block); + +/* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel + * does not know buffered block devices - everything is raw. */ +#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#define NEED_BOUNCE_BUFFER +#else +#undef NEED_BOUNCE_BUFFER +#endif + +static struct struct_io_manager struct_unix_manager = { + EXT2_ET_MAGIC_IO_MANAGER, + "Unix I/O Manager", + unix_open, + unix_close, + unix_set_blksize, + unix_read_blk, + unix_write_blk, + unix_flush, +#ifdef NEED_BOUNCE_BUFFER + 0, +#else + unix_write_byte, +#endif + unix_set_option +}; + +io_manager unix_io_manager = &struct_unix_manager; + +/* + * Here are the raw I/O functions + */ +#ifndef NEED_BOUNCE_BUFFER +static errcode_t raw_read_blk(io_channel channel, + struct unix_private_data *data, + unsigned long block, + int count, void *buf) +{ + errcode_t retval; + ssize_t size; + ext2_loff_t location; + int actual = 0; + + size = (count < 0) ? -count : count * channel->block_size; + location = ((ext2_loff_t) block * channel->block_size) + data->offset; + if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { + retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; + goto error_out; + } + actual = read(data->dev, buf, size); + if (actual != size) { + if (actual < 0) + actual = 0; + retval = EXT2_ET_SHORT_READ; + goto error_out; + } + return 0; + +error_out: + memset((char *) buf+actual, 0, size-actual); + if (channel->read_error) + retval = (channel->read_error)(channel, block, count, buf, + size, actual, retval); + return retval; +} +#else /* NEED_BOUNCE_BUFFER */ +/* + * Windows and FreeBSD block devices only allow sector alignment IO in offset and size + */ +static errcode_t raw_read_blk(io_channel channel, + struct unix_private_data *data, + unsigned long block, + int count, void *buf) +{ + errcode_t retval; + size_t size, alignsize, fragment; + ext2_loff_t location; + int total = 0, actual; +#define BLOCKALIGN 512 + char sector[BLOCKALIGN]; + + size = (count < 0) ? -count : count * channel->block_size; + location = ((ext2_loff_t) block * channel->block_size) + data->offset; +#ifdef DEBUG + printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n", + count, size, block, channel->block_size, location); +#endif + if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { + retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; + goto error_out; + } + fragment = size % BLOCKALIGN; + alignsize = size - fragment; + if (alignsize) { + actual = read(data->dev, buf, alignsize); + if (actual != alignsize) + goto short_read; + } + if (fragment) { + actual = read(data->dev, sector, BLOCKALIGN); + if (actual != BLOCKALIGN) + goto short_read; + memcpy(buf+alignsize, sector, fragment); + } + return 0; + +short_read: + if (actual>0) + total += actual; + retval = EXT2_ET_SHORT_READ; + +error_out: + memset((char *) buf+total, 0, size-actual); + if (channel->read_error) + retval = (channel->read_error)(channel, block, count, buf, + size, actual, retval); + return retval; +} +#endif + +static errcode_t raw_write_blk(io_channel channel, + struct unix_private_data *data, + unsigned long block, + int count, const void *buf) +{ + ssize_t size; + ext2_loff_t location; + int actual = 0; + errcode_t retval; + + if (count == 1) + size = channel->block_size; + else { + if (count < 0) + size = -count; + else + size = count * channel->block_size; + } + + location = ((ext2_loff_t) block * channel->block_size) + data->offset; + if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { + retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; + goto error_out; + } + + actual = write(data->dev, buf, size); + if (actual != size) { + retval = EXT2_ET_SHORT_WRITE; + goto error_out; + } + return 0; + +error_out: + if (channel->write_error) + retval = (channel->write_error)(channel, block, count, buf, + size, actual, retval); + return retval; +} + + +/* + * Here we implement the cache functions + */ + +/* Allocate the cache buffers */ +static errcode_t alloc_cache(io_channel channel, + struct unix_private_data *data) +{ + errcode_t retval; + struct unix_cache *cache; + int i; + + data->access_time = 0; + for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + cache->block = 0; + cache->access_time = 0; + cache->dirty = 0; + cache->in_use = 0; + if ((retval = ext2fs_get_mem(channel->block_size, + &cache->buf))) + return retval; + } + return 0; +} + +/* Free the cache buffers */ +static void free_cache(struct unix_private_data *data) +{ + struct unix_cache *cache; + int i; + + data->access_time = 0; + for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + cache->block = 0; + cache->access_time = 0; + cache->dirty = 0; + cache->in_use = 0; + ext2fs_free_mem(&cache->buf); + cache->buf = 0; + } +} + +#ifndef NO_IO_CACHE +/* + * Try to find a block in the cache. If the block is not found, and + * eldest is a non-zero pointer, then fill in eldest with the cache + * entry to that should be reused. + */ +static struct unix_cache *find_cached_block(struct unix_private_data *data, + unsigned long block, + struct unix_cache **eldest) +{ + struct unix_cache *cache, *unused_cache, *oldest_cache; + int i; + + unused_cache = oldest_cache = 0; + for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + if (!cache->in_use) { + if (!unused_cache) + unused_cache = cache; + continue; + } + if (cache->block == block) { + cache->access_time = ++data->access_time; + return cache; + } + if (!oldest_cache || + (cache->access_time < oldest_cache->access_time)) + oldest_cache = cache; + } + if (eldest) + *eldest = (unused_cache) ? unused_cache : oldest_cache; + return 0; +} + +/* + * Reuse a particular cache entry for another block. + */ +static void reuse_cache(io_channel channel, struct unix_private_data *data, + struct unix_cache *cache, unsigned long block) +{ + if (cache->dirty && cache->in_use) + raw_write_blk(channel, data, cache->block, 1, cache->buf); + + cache->in_use = 1; + cache->dirty = 0; + cache->block = block; + cache->access_time = ++data->access_time; +} + +/* + * Flush all of the blocks in the cache + */ +static errcode_t flush_cached_blocks(io_channel channel, + struct unix_private_data *data, + int invalidate) + +{ + struct unix_cache *cache; + errcode_t retval, retval2; + int i; + + retval2 = 0; + for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + if (!cache->in_use) + continue; + + if (invalidate) + cache->in_use = 0; + + if (!cache->dirty) + continue; + + retval = raw_write_blk(channel, data, + cache->block, 1, cache->buf); + if (retval) + retval2 = retval; + else + cache->dirty = 0; + } + return retval2; +} +#endif /* NO_IO_CACHE */ + +static errcode_t unix_open(const char *name, int flags, io_channel *channel) +{ + io_channel io = NULL; + struct unix_private_data *data = NULL; + errcode_t retval; + int open_flags; + struct stat st; +#ifdef __linux__ + struct utsname ut; +#endif + + if (name == 0) + return EXT2_ET_BAD_DEVICE_NAME; + retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); + if (retval) + return retval; + memset(io, 0, sizeof(struct struct_io_channel)); + io->magic = EXT2_ET_MAGIC_IO_CHANNEL; + retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); + if (retval) + goto cleanup; + + io->manager = unix_io_manager; + retval = ext2fs_get_mem(strlen(name)+1, &io->name); + if (retval) + goto cleanup; + + strcpy(io->name, name); + io->private_data = data; + io->block_size = 1024; + io->read_error = 0; + io->write_error = 0; + io->refcount = 1; + + memset(data, 0, sizeof(struct unix_private_data)); + data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; + + if ((retval = alloc_cache(io, data))) + goto cleanup; + + open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; +#ifdef CONFIG_LFS + data->dev = open64(io->name, open_flags); +#else + data->dev = open(io->name, open_flags); +#endif + if (data->dev < 0) { + retval = errno; + goto cleanup; + } + +#ifdef __linux__ +#undef RLIM_INFINITY +#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) +#define RLIM_INFINITY ((unsigned long)(~0UL>>1)) +#else +#define RLIM_INFINITY (~0UL) +#endif + /* + * Work around a bug in 2.4.10-2.4.18 kernels where writes to + * block devices are wrongly getting hit by the filesize + * limit. This workaround isn't perfect, since it won't work + * if glibc wasn't built against 2.2 header files. (Sigh.) + * + */ + if ((flags & IO_FLAG_RW) && + (uname(&ut) == 0) && + ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] == '4') && (ut.release[3] == '.') && + (ut.release[4] == '1') && (ut.release[5] >= '0') && + (ut.release[5] < '8')) && + (fstat(data->dev, &st) == 0) && + (S_ISBLK(st.st_mode))) { + struct rlimit rlim; + + rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; + setrlimit(RLIMIT_FSIZE, &rlim); + getrlimit(RLIMIT_FSIZE, &rlim); + if (((unsigned long) rlim.rlim_cur) < + ((unsigned long) rlim.rlim_max)) { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_FSIZE, &rlim); + } + } +#endif + *channel = io; + return 0; + +cleanup: + if (data) { + free_cache(data); + ext2fs_free_mem(&data); + } + ext2fs_free_mem(&io); + return retval; +} + +static errcode_t unix_close(io_channel channel) +{ + struct unix_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + + if (--channel->refcount > 0) + return 0; + +#ifndef NO_IO_CACHE + retval = flush_cached_blocks(channel, data, 0); +#endif + + if (close(data->dev) < 0) + retval = errno; + free_cache(data); + + ext2fs_free_mem(&channel->private_data); + ext2fs_free_mem(&channel->name); + ext2fs_free_mem(&channel); + return retval; +} + +static errcode_t unix_set_blksize(io_channel channel, int blksize) +{ + struct unix_private_data *data; + errcode_t retval; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + + if (channel->block_size != blksize) { +#ifndef NO_IO_CACHE + if ((retval = flush_cached_blocks(channel, data, 0))) + return retval; +#endif + + channel->block_size = blksize; + free_cache(data); + if ((retval = alloc_cache(channel, data))) + return retval; + } + return 0; +} + + +static errcode_t unix_read_blk(io_channel channel, unsigned long block, + int count, void *buf) +{ + struct unix_private_data *data; + struct unix_cache *cache, *reuse[READ_DIRECT_SIZE]; + errcode_t retval; + char *cp; + int i, j; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + +#ifdef NO_IO_CACHE + return raw_read_blk(channel, data, block, count, buf); +#else + /* + * If we're doing an odd-sized read or a very large read, + * flush out the cache and then do a direct read. + */ + if (count < 0 || count > WRITE_DIRECT_SIZE) { + if ((retval = flush_cached_blocks(channel, data, 0))) + return retval; + return raw_read_blk(channel, data, block, count, buf); + } + + cp = buf; + while (count > 0) { + /* If it's in the cache, use it! */ + if ((cache = find_cached_block(data, block, &reuse[0]))) { +#ifdef DEBUG + printf("Using cached block %d\n", block); +#endif + memcpy(cp, cache->buf, channel->block_size); + count--; + block++; + cp += channel->block_size; + continue; + } + /* + * Find the number of uncached blocks so we can do a + * single read request + */ + for (i=1; i < count; i++) + if (find_cached_block(data, block+i, &reuse[i])) + break; +#ifdef DEBUG + printf("Reading %d blocks starting at %d\n", i, block); +#endif + if ((retval = raw_read_blk(channel, data, block, i, cp))) + return retval; + + /* Save the results in the cache */ + for (j=0; j < i; j++) { + count--; + cache = reuse[j]; + reuse_cache(channel, data, cache, block++); + memcpy(cache->buf, cp, channel->block_size); + cp += channel->block_size; + } + } + return 0; +#endif /* NO_IO_CACHE */ +} + +static errcode_t unix_write_blk(io_channel channel, unsigned long block, + int count, const void *buf) +{ + struct unix_private_data *data; + struct unix_cache *cache, *reuse; + errcode_t retval = 0; + const char *cp; + int writethrough; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + +#ifdef NO_IO_CACHE + return raw_write_blk(channel, data, block, count, buf); +#else + /* + * If we're doing an odd-sized write or a very large write, + * flush out the cache completely and then do a direct write. + */ + if (count < 0 || count > WRITE_DIRECT_SIZE) { + if ((retval = flush_cached_blocks(channel, data, 1))) + return retval; + return raw_write_blk(channel, data, block, count, buf); + } + + /* + * For a moderate-sized multi-block write, first force a write + * if we're in write-through cache mode, and then fill the + * cache with the blocks. + */ + writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; + if (writethrough) + retval = raw_write_blk(channel, data, block, count, buf); + + cp = buf; + while (count > 0) { + cache = find_cached_block(data, block, &reuse); + if (!cache) { + cache = reuse; + reuse_cache(channel, data, cache, block); + } + memcpy(cache->buf, cp, channel->block_size); + cache->dirty = !writethrough; + count--; + block++; + cp += channel->block_size; + } + return retval; +#endif /* NO_IO_CACHE */ +} + +static errcode_t unix_write_byte(io_channel channel, unsigned long offset, + int size, const void *buf) +{ + struct unix_private_data *data; + errcode_t retval = 0; + ssize_t actual; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + +#ifndef NO_IO_CACHE + /* + * Flush out the cache completely + */ + if ((retval = flush_cached_blocks(channel, data, 1))) + return retval; +#endif + + if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) + return errno; + + actual = write(data->dev, buf, size); + if (actual != size) + return EXT2_ET_SHORT_WRITE; + + return 0; +} + +/* + * Flush data buffers to disk. + */ +static errcode_t unix_flush(io_channel channel) +{ + struct unix_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + +#ifndef NO_IO_CACHE + retval = flush_cached_blocks(channel, data, 0); +#endif + fsync(data->dev); + return retval; +} + +static errcode_t unix_set_option(io_channel channel, const char *option, + const char *arg) +{ + struct unix_private_data *data; + unsigned long tmp; + char *end; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + + if (!strcmp(option, "offset")) { + if (!arg) + return EXT2_ET_INVALID_ARGUMENT; + + tmp = strtoul(arg, &end, 0); + if (*end) + return EXT2_ET_INVALID_ARGUMENT; + data->offset = tmp; + return 0; + } + return EXT2_ET_INVALID_ARGUMENT; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c b/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c new file mode 100644 index 000000000..71a9ffcb9 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * unlink.c --- delete links in a ext2fs directory + * + * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +struct link_struct { + const char *name; + int namelen; + ext2_ino_t inode; + int flags; + struct ext2_dir_entry *prev; + int done; +}; + +#ifdef __TURBOC__ +# pragma argsused +#endif +static int unlink_proc(struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct link_struct *ls = (struct link_struct *) priv_data; + struct ext2_dir_entry *prev; + + prev = ls->prev; + ls->prev = dirent; + + if (ls->name) { + if ((dirent->name_len & 0xFF) != ls->namelen) + return 0; + if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) + return 0; + } + if (ls->inode) { + if (dirent->inode != ls->inode) + return 0; + } else { + if (!dirent->inode) + return 0; + } + + if (prev) + prev->rec_len += dirent->rec_len; + else + dirent->inode = 0; + ls->done++; + return DIRENT_ABORT|DIRENT_CHANGED; +} + +#ifdef __TURBOC__ + #pragma argsused +#endif +errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, + const char *name, ext2_ino_t ino, + int flags EXT2FS_ATTR((unused))) +{ + errcode_t retval; + struct link_struct ls; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if (!name && !ino) + return EXT2_ET_INVALID_ARGUMENT; + + if (!(fs->flags & EXT2_FLAG_RW)) + return EXT2_ET_RO_FILSYS; + + ls.name = name; + ls.namelen = name ? strlen(name) : 0; + ls.inode = ino; + ls.flags = 0; + ls.done = 0; + ls.prev = 0; + + retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, + 0, unlink_proc, &ls); + if (retval) + return retval; + + return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c b/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c new file mode 100644 index 000000000..8ed77ae2a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c @@ -0,0 +1,57 @@ +/* vi: set sw=4 ts=4: */ +/* + * valid_blk.c --- does the inode have valid blocks? + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +/* + * This function returns 1 if the inode's block entries actually + * contain block entries. + */ +int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode) +{ + /* + * Only directories, regular files, and some symbolic links + * have valid block entries. + */ + if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && + !LINUX_S_ISLNK(inode->i_mode)) + return 0; + + /* + * If the symbolic link is a "fast symlink", then the symlink + * target is stored in the block entries. + */ + if (LINUX_S_ISLNK (inode->i_mode)) { + if (inode->i_file_acl == 0) { + /* With no EA block, we can rely on i_blocks */ + if (inode->i_blocks == 0) + return 0; + } else { + /* With an EA block, life gets more tricky */ + if (inode->i_size >= EXT2_N_BLOCKS*4) + return 1; /* definitely using i_block[] */ + if (inode->i_size > 4 && inode->i_block[1] == 0) + return 1; /* definitely using i_block[] */ + return 0; /* Probably a fast symlink */ + } + } + return 1; +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/version.c b/e2fsprogs/old_e2fsprogs/ext2fs/version.c new file mode 100644 index 000000000..d2981e867 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/version.c @@ -0,0 +1,51 @@ +/* vi: set sw=4 ts=4: */ +/* + * version.c --- Return the version of the ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +static const char *lib_version = E2FSPROGS_VERSION; +static const char *lib_date = E2FSPROGS_DATE; + +int ext2fs_parse_version_string(const char *ver_string) +{ + const char *cp; + int version = 0; + + for (cp = ver_string; *cp; cp++) { + if (*cp == '.') + continue; + if (!isdigit(*cp)) + break; + version = (version * 10) + (*cp - '0'); + } + return version; +} + + +int ext2fs_get_library_version(const char **ver_string, + const char **date_string) +{ + if (ver_string) + *ver_string = lib_version; + if (date_string) + *date_string = lib_date; + + return ext2fs_parse_version_string(lib_version); +} diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c b/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c new file mode 100644 index 000000000..5b19eefa0 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c @@ -0,0 +1,35 @@ +/* vi: set sw=4 ts=4: */ +/* + * write_bb_file.c --- write a list of bad blocks to a FILE * + * + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, + unsigned int flags EXT2FS_ATTR((unused)), + FILE *f) +{ + badblocks_iterate bb_iter; + blk_t blk; + errcode_t retval; + + retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); + if (retval) + return retval; + + while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) { + fprintf(f, "%d\n", blk); + } + ext2fs_badblocks_list_iterate_end(bb_iter); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/fsck.c b/e2fsprogs/old_e2fsprogs/fsck.c new file mode 100644 index 000000000..3a0743bb1 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/fsck.c @@ -0,0 +1,1376 @@ +/* vi: set sw=4 ts=4: */ +/* + * pfsck --- A generic, parallelizing front-end for the fsck program. + * It will automatically try to run fsck programs in parallel if the + * devices are on separate spindles. It is based on the same ideas as + * the generic front end for fsck by David Engel and Fred van Kempen, + * but it has been completely rewritten from scratch to support + * parallel execution. + * + * Written by Theodore Ts'o, + * + * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: + * o Changed -t fstype to behave like with mount when -A (all file + * systems) or -M (like mount) is specified. + * o fsck looks if it can find the fsck.type program to decide + * if it should ignore the fs type. This way more fsck programs + * can be added without changing this front-end. + * o -R flag skip root file system. + * + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsck.h" +#include "blkid/blkid.h" + +#include "e2fsbb.h" + +#include "libbb.h" + +#ifndef _PATH_MNTTAB +#define _PATH_MNTTAB "/etc/fstab" +#endif + +/* + * fsck.h + */ + +#ifndef DEFAULT_FSTYPE +#define DEFAULT_FSTYPE "ext2" +#endif + +#define MAX_DEVICES 32 +#define MAX_ARGS 32 + +/* + * Internal structure for mount tabel entries. + */ + +struct fs_info { + char *device; + char *mountpt; + char *type; + char *opts; + int freq; + int passno; + int flags; + struct fs_info *next; +}; + +#define FLAG_DONE 1 +#define FLAG_PROGRESS 2 + +/* + * Structure to allow exit codes to be stored + */ +struct fsck_instance { + int pid; + int flags; + int exit_status; + time_t start_time; + char * prog; + char * type; + char * device; + char * base_device; + struct fsck_instance *next; +}; + +/* + * base_device.c + * + * Return the "base device" given a particular device; this is used to + * assure that we only fsck one partition on a particular drive at any + * one time. Otherwise, the disk heads will be seeking all over the + * place. If the base device cannot be determined, return NULL. + * + * The base_device() function returns an allocated string which must + * be freed. + * + */ + + +#ifdef CONFIG_FEATURE_DEVFS +/* + * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3 + * pathames. + */ +static const char *const devfs_hier[] = { + "host", "bus", "target", "lun", 0 +}; +#endif + +static char *base_device(const char *device) +{ + char *str, *cp; +#ifdef CONFIG_FEATURE_DEVFS + const char *const *hier; + const char *disk; + int len; +#endif + + cp = str = xstrdup(device); + + /* Skip over /dev/; if it's not present, give up. */ + if (strncmp(cp, "/dev/", 5) != 0) + goto errout; + cp += 5; + + /* + * For md devices, we treat them all as if they were all + * on one disk, since we don't know how to parallelize them. + */ + if (cp[0] == 'm' && cp[1] == 'd') { + *(cp+2) = 0; + return str; + } + + /* Handle DAC 960 devices */ + if (strncmp(cp, "rd/", 3) == 0) { + cp += 3; + if (cp[0] != 'c' || cp[2] != 'd' || + !isdigit(cp[1]) || !isdigit(cp[3])) + goto errout; + *(cp+4) = 0; + return str; + } + + /* Now let's handle /dev/hd* and /dev/sd* devices.... */ + if ((cp[0] == 'h' || cp[0] == 's') && (cp[1] == 'd')) { + cp += 2; + /* If there's a single number after /dev/hd, skip it */ + if (isdigit(*cp)) + cp++; + /* What follows must be an alpha char, or give up */ + if (!isalpha(*cp)) + goto errout; + *(cp + 1) = 0; + return str; + } + +#ifdef CONFIG_FEATURE_DEVFS + /* Now let's handle devfs (ugh) names */ + len = 0; + if (strncmp(cp, "ide/", 4) == 0) + len = 4; + if (strncmp(cp, "scsi/", 5) == 0) + len = 5; + if (len) { + cp += len; + /* + * Now we proceed down the expected devfs hierarchy. + * i.e., .../host1/bus2/target3/lun4/... + * If we don't find the expected token, followed by + * some number of digits at each level, abort. + */ + for (hier = devfs_hier; *hier; hier++) { + len = strlen(*hier); + if (strncmp(cp, *hier, len) != 0) + goto errout; + cp += len; + while (*cp != '/' && *cp != 0) { + if (!isdigit(*cp)) + goto errout; + cp++; + } + cp++; + } + *(cp - 1) = 0; + return str; + } + + /* Now handle devfs /dev/disc or /dev/disk names */ + disk = 0; + if (strncmp(cp, "discs/", 6) == 0) + disk = "disc"; + else if (strncmp(cp, "disks/", 6) == 0) + disk = "disk"; + if (disk) { + cp += 6; + if (strncmp(cp, disk, 4) != 0) + goto errout; + cp += 4; + while (*cp != '/' && *cp != 0) { + if (!isdigit(*cp)) + goto errout; + cp++; + } + *cp = 0; + return str; + } +#endif + +errout: + free(str); + return NULL; +} + + +static const char *const ignored_types[] = { + "ignore", + "iso9660", + "nfs", + "proc", + "sw", + "swap", + "tmpfs", + "devpts", + NULL +}; + +static const char *const really_wanted[] = { + "minix", + "ext2", + "ext3", + "jfs", + "reiserfs", + "xiafs", + "xfs", + NULL +}; + +#define BASE_MD "/dev/md" + +/* + * Global variables for options + */ +static char *devices[MAX_DEVICES]; +static char *args[MAX_ARGS]; +static int num_devices, num_args; + +static int verbose; +static int doall; +static int noexecute; +static int serialize; +static int skip_root; +static int like_mount; +static int notitle; +static int parallel_root; +static int progress; +static int progress_fd; +static int force_all_parallel; +static int num_running; +static int max_running; +static volatile int cancel_requested; +static int kill_sent; +static char *fstype; +static struct fs_info *filesys_info, *filesys_last; +static struct fsck_instance *instance_list; +static char *fsck_path; +static blkid_cache cache; + +static char *string_copy(const char *s) +{ + char *ret; + + if (!s) + return 0; + ret = xstrdup(s); + return ret; +} + +static int string_to_int(const char *s) +{ + long l; + char *p; + + l = strtol(s, &p, 0); + if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX) + return -1; + else + return (int) l; +} + +static char *skip_over_blank(char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + while (*cp && !isspace(*cp)) + cp++; + return cp; +} + +static void strip_line(char *line) +{ + char *p; + + while (*line) { + p = line + strlen(line) - 1; + if ((*p == '\n') || (*p == '\r')) + *p = 0; + else + break; + } +} + +static char *parse_word(char **buf) +{ + char *word, *next; + + word = *buf; + if (*word == 0) + return 0; + + word = skip_over_blank(word); + next = skip_over_word(word); + if (*next) + *next++ = 0; + *buf = next; + return word; +} + +static void parse_escape(char *word) +{ + char *q, c; + const char *p; + + if (!word) + return; + + strcpy_and_process_escape_sequences(word, word); +} + +static void free_instance(struct fsck_instance *i) +{ + if (i->prog) + free(i->prog); + if (i->device) + free(i->device); + if (i->base_device) + free(i->base_device); + free(i); +} + +static struct fs_info *create_fs_device(const char *device, const char *mntpnt, + const char *type, const char *opts, + int freq, int passno) +{ + struct fs_info *fs; + + fs = xmalloc(sizeof(struct fs_info)); + + fs->device = string_copy(device); + fs->mountpt = string_copy(mntpnt); + fs->type = string_copy(type); + fs->opts = string_copy(opts ? opts : ""); + fs->freq = freq; + fs->passno = passno; + fs->flags = 0; + fs->next = NULL; + + if (!filesys_info) + filesys_info = fs; + else + filesys_last->next = fs; + filesys_last = fs; + + return fs; +} + + + +static int parse_fstab_line(char *line, struct fs_info **ret_fs) +{ + char *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp; + struct fs_info *fs; + + *ret_fs = 0; + strip_line(line); + if ((cp = strchr(line, '#'))) + *cp = 0; /* Ignore everything after the comment char */ + cp = line; + + device = parse_word(&cp); + mntpnt = parse_word(&cp); + type = parse_word(&cp); + opts = parse_word(&cp); + freq = parse_word(&cp); + passno = parse_word(&cp); + + if (!device) + return 0; /* Allow blank lines */ + + if (!mntpnt || !type) + return -1; + + parse_escape(device); + parse_escape(mntpnt); + parse_escape(type); + parse_escape(opts); + parse_escape(freq); + parse_escape(passno); + + dev = blkid_get_devname(cache, device, NULL); + if (dev) + device = dev; + + if (strchr(type, ',')) + type = 0; + + fs = create_fs_device(device, mntpnt, type ? type : "auto", opts, + freq ? atoi(freq) : -1, + passno ? atoi(passno) : -1); + if (dev) + free(dev); + + if (!fs) + return -1; + *ret_fs = fs; + return 0; +} + +static void interpret_type(struct fs_info *fs) +{ + char *t; + + if (strcmp(fs->type, "auto") != 0) + return; + t = blkid_get_tag_value(cache, "TYPE", fs->device); + if (t) { + free(fs->type); + fs->type = t; + } +} + +/* + * Load the filesystem database from /etc/fstab + */ +static void load_fs_info(const char *filename) +{ + FILE *f; + char buf[1024]; + int lineno = 0; + int old_fstab = 1; + struct fs_info *fs; + + if ((f = fopen_or_warn(filename, "r")) == NULL) { + return; + } + while (!feof(f)) { + lineno++; + if (!fgets(buf, sizeof(buf), f)) + break; + buf[sizeof(buf)-1] = 0; + if (parse_fstab_line(buf, &fs) < 0) { + bb_error_msg("WARNING: bad format " + "on line %d of %s\n", lineno, filename); + continue; + } + if (!fs) + continue; + if (fs->passno < 0) + fs->passno = 0; + else + old_fstab = 0; + } + + fclose(f); + + if (old_fstab) { + fputs("\007\007\007" + "WARNING: Your /etc/fstab does not contain the fsck passno\n" + " field. I will kludge around things for you, but you\n" + " should fix your /etc/fstab file as soon as you can.\n\n", stderr); + + for (fs = filesys_info; fs; fs = fs->next) { + fs->passno = 1; + } + } +} + +/* Lookup filesys in /etc/fstab and return the corresponding entry. */ +static struct fs_info *lookup(char *filesys) +{ + struct fs_info *fs; + + /* No filesys name given. */ + if (filesys == NULL) + return NULL; + + for (fs = filesys_info; fs; fs = fs->next) { + if (!strcmp(filesys, fs->device) || + (fs->mountpt && !strcmp(filesys, fs->mountpt))) + break; + } + + return fs; +} + +/* Find fsck program for a given fs type. */ +static char *find_fsck(char *type) +{ + char *s; + const char *tpl; + char *p = string_copy(fsck_path); + struct stat st; + + /* Are we looking for a program or just a type? */ + tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s"); + + for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) { + s = xasprintf(tpl, s, type); + if (stat(s, &st) == 0) break; + free(s); + } + free(p); + return s; +} + +static int progress_active(void) +{ + struct fsck_instance *inst; + + for (inst = instance_list; inst; inst = inst->next) { + if (inst->flags & FLAG_DONE) + continue; + if (inst->flags & FLAG_PROGRESS) + return 1; + } + return 0; +} + +/* + * Execute a particular fsck program, and link it into the list of + * child processes we are waiting for. + */ +static int execute(const char *type, const char *device, const char *mntpt, + int interactive) +{ + char *s, *argv[80]; + char *prog; + int argc, i; + struct fsck_instance *inst, *p; + pid_t pid; + + inst = xzalloc(sizeof(struct fsck_instance)); + + prog = xasprintf("fsck.%s", type); + argv[0] = prog; + argc = 1; + + for (i=0; i flags |= FLAG_PROGRESS; + } + } + + argv[argc++] = string_copy(device); + argv[argc] = 0; + + s = find_fsck(prog); + if (s == NULL) { + bb_error_msg("%s: not found", prog); + return ENOENT; + } + + if (verbose || noexecute) { + printf("[%s (%d) -- %s] ", s, num_running, + mntpt ? mntpt : device); + for (i=0; i < argc; i++) + printf("%s ", argv[i]); + bb_putchar('\n'); + } + + /* Fork and execute the correct program. */ + if (noexecute) + pid = -1; + else if ((pid = fork()) < 0) { + perror("vfork"+1); + return errno; + } else if (pid == 0) { + if (!interactive) + close(0); + (void) execv(s, argv); + bb_simple_perror_msg_and_die(argv[0]); + } + + for (i = 1; i < argc; i++) + free(argv[i]); + + free(s); + inst->pid = pid; + inst->prog = prog; + inst->type = string_copy(type); + inst->device = string_copy(device); + inst->base_device = base_device(device); + inst->start_time = time(0); + inst->next = NULL; + + /* + * Find the end of the list, so we add the instance on at the end. + */ + for (p = instance_list; p && p->next; p = p->next); + + if (p) + p->next = inst; + else + instance_list = inst; + + return 0; +} + +/* + * Send a signal to all outstanding fsck child processes + */ +static int kill_all(int signum) +{ + struct fsck_instance *inst; + int n = 0; + + for (inst = instance_list; inst; inst = inst->next) { + if (inst->flags & FLAG_DONE) + continue; + kill(inst->pid, signum); + n++; + } + return n; +} + +/* + * Wait for one child process to exit; when it does, unlink it from + * the list of executing child processes, and return it. + */ +static struct fsck_instance *wait_one(int flags) +{ + int status; + int sig; + struct fsck_instance *inst, *inst2, *prev; + pid_t pid; + + if (!instance_list) + return NULL; + + if (noexecute) { + inst = instance_list; + prev = 0; +#ifdef RANDOM_DEBUG + while (inst->next && (random() & 1)) { + prev = inst; + inst = inst->next; + } +#endif + inst->exit_status = 0; + goto ret_inst; + } + + /* + * gcc -Wall fails saving throw against stupidity + * (inst and prev are thought to be uninitialized variables) + */ + inst = prev = NULL; + + do { + pid = waitpid(-1, &status, flags); + if (cancel_requested && !kill_sent) { + kill_all(SIGTERM); + kill_sent++; + } + if ((pid == 0) && (flags & WNOHANG)) + return NULL; + if (pid < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + if (errno == ECHILD) { + bb_error_msg("wait: no more child process?!?"); + return NULL; + } + perror("wait"); + continue; + } + for (prev = 0, inst = instance_list; + inst; + prev = inst, inst = inst->next) { + if (inst->pid == pid) + break; + } + } while (!inst); + + if (WIFEXITED(status)) + status = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) { + sig = WTERMSIG(status); + if (sig == SIGINT) { + status = EXIT_UNCORRECTED; + } else { + printf("Warning... %s for device %s exited " + "with signal %d.\n", + inst->prog, inst->device, sig); + status = EXIT_ERROR; + } + } else { + printf("%s %s: status is %x, should never happen.\n", + inst->prog, inst->device, status); + status = EXIT_ERROR; + } + inst->exit_status = status; + if (progress && (inst->flags & FLAG_PROGRESS) && + !progress_active()) { + for (inst2 = instance_list; inst2; inst2 = inst2->next) { + if (inst2->flags & FLAG_DONE) + continue; + if (strcmp(inst2->type, "ext2") && + strcmp(inst2->type, "ext3")) + continue; + /* + * If we've just started the fsck, wait a tiny + * bit before sending the kill, to give it + * time to set up the signal handler + */ + if (inst2->start_time < time(0)+2) { + if (fork() == 0) { + sleep(1); + kill(inst2->pid, SIGUSR1); + exit(0); + } + } else + kill(inst2->pid, SIGUSR1); + inst2->flags |= FLAG_PROGRESS; + break; + } + } +ret_inst: + if (prev) + prev->next = inst->next; + else + instance_list = inst->next; + if (verbose > 1) + printf("Finished with %s (exit status %d)\n", + inst->device, inst->exit_status); + num_running--; + return inst; +} + +#define FLAG_WAIT_ALL 0 +#define FLAG_WAIT_ATLEAST_ONE 1 +/* + * Wait until all executing child processes have exited; return the + * logical OR of all of their exit code values. + */ +static int wait_many(int flags) +{ + struct fsck_instance *inst; + int global_status = 0; + int wait_flags = 0; + + while ((inst = wait_one(wait_flags))) { + global_status |= inst->exit_status; + free_instance(inst); +#ifdef RANDOM_DEBUG + if (noexecute && (flags & WNOHANG) && !(random() % 3)) + break; +#endif + if (flags & FLAG_WAIT_ATLEAST_ONE) + wait_flags = WNOHANG; + } + return global_status; +} + +/* + * Run the fsck program on a particular device + * + * If the type is specified using -t, and it isn't prefixed with "no" + * (as in "noext2") and only one filesystem type is specified, then + * use that type regardless of what is specified in /etc/fstab. + * + * If the type isn't specified by the user, then use either the type + * specified in /etc/fstab, or DEFAULT_FSTYPE. + */ +static void fsck_device(struct fs_info *fs, int interactive) +{ + const char *type; + int retval; + + interpret_type(fs); + + if (strcmp(fs->type, "auto") != 0) + type = fs->type; + else if (fstype && strncmp(fstype, "no", 2) && + strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) && + !strchr(fstype, ',')) + type = fstype; + else + type = DEFAULT_FSTYPE; + + num_running++; + retval = execute(type, fs->device, fs->mountpt, interactive); + if (retval) { + bb_error_msg("error %d while executing fsck.%s for %s", + retval, type, fs->device); + num_running--; + } +} + + +/* + * Deal with the fsck -t argument. + */ +struct fs_type_compile { + char **list; + int *type; + int negate; +} fs_type_compiled; + +#define FS_TYPE_NORMAL 0 +#define FS_TYPE_OPT 1 +#define FS_TYPE_NEGOPT 2 + +static const char fs_type_syntax_error[] = +"Either all or none of the filesystem types passed to -t must be prefixed\n" + "with 'no' or '!'."; + +static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp) +{ + char *cp, *list, *s; + int num = 2; + int negate, first_negate = 1; + + if (fs_type) { + for (cp=fs_type; *cp; cp++) { + if (*cp == ',') + num++; + } + } + + cmp->list = xzalloc(num * sizeof(char *)); + cmp->type = xzalloc(num * sizeof(int)); + cmp->negate = 0; + + if (!fs_type) + return; + + list = string_copy(fs_type); + num = 0; + s = strtok(list, ","); + while (s) { + negate = 0; + if (strncmp(s, "no", 2) == 0) { + s += 2; + negate = 1; + } else if (*s == '!') { + s++; + negate = 1; + } + if (strcmp(s, "loop") == 0) + /* loop is really short-hand for opts=loop */ + goto loop_special_case; + else if (strncmp(s, "opts=", 5) == 0) { + s += 5; + loop_special_case: + cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT; + } else { + if (first_negate) { + cmp->negate = negate; + first_negate = 0; + } + if ((negate && !cmp->negate) || + (!negate && cmp->negate)) { + bb_error_msg_and_die("%s", fs_type_syntax_error); + } + } + cmp->list[num++] = string_copy(s); + s = strtok(NULL, ","); + } + free(list); +} + +/* + * This function returns true if a particular option appears in a + * comma-delimited options list + */ +static int opt_in_list(char *opt, char *optlist) +{ + char *list, *s; + + if (!optlist) + return 0; + list = string_copy(optlist); + + s = strtok(list, ","); + while (s) { + if (strcmp(s, opt) == 0) { + free(list); + return 1; + } + s = strtok(NULL, ","); + } + free(list); + return 0; +} + +/* See if the filesystem matches the criteria given by the -t option */ +static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp) +{ + int n, ret = 0, checked_type = 0; + char *cp; + + if (cmp->list == 0 || cmp->list[0] == 0) + return 1; + + for (n=0; (cp = cmp->list[n]); n++) { + switch (cmp->type[n]) { + case FS_TYPE_NORMAL: + checked_type++; + if (strcmp(cp, fs->type) == 0) { + ret = 1; + } + break; + case FS_TYPE_NEGOPT: + if (opt_in_list(cp, fs->opts)) + return 0; + break; + case FS_TYPE_OPT: + if (!opt_in_list(cp, fs->opts)) + return 0; + break; + } + } + if (checked_type == 0) + return 1; + return (cmp->negate ? !ret : ret); +} + +/* Check if we should ignore this filesystem. */ +static int ignore(struct fs_info *fs) +{ + int wanted; + char *s; + + /* + * If the pass number is 0, ignore it. + */ + if (fs->passno == 0) + return 1; + + interpret_type(fs); + + /* + * If a specific fstype is specified, and it doesn't match, + * ignore it. + */ + if (!fs_match(fs, &fs_type_compiled)) return 1; + + /* Are we ignoring this type? */ + if (index_in_str_array(ignored_types, fs->type) >= 0) + return 1; + + /* Do we really really want to check this fs? */ + wanted = index_in_str_array(really_wanted, fs->type) >= 0; + + /* See if the program is available. */ + s = find_fsck(fs->type); + if (s == NULL) { + if (wanted) + bb_error_msg("can't check %s: fsck.%s not found", + fs->device, fs->type); + return 1; + } + free(s); + + /* We can and want to check this file system type. */ + return 0; +} + +/* + * Returns TRUE if a partition on the same disk is already being + * checked. + */ +static int device_already_active(char *device) +{ + struct fsck_instance *inst; + char *base; + + if (force_all_parallel) + return 0; + +#ifdef BASE_MD + /* Don't check a soft raid disk with any other disk */ + if (instance_list && + (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) || + !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))) + return 1; +#endif + + base = base_device(device); + /* + * If we don't know the base device, assume that the device is + * already active if there are any fsck instances running. + */ + if (!base) + return (instance_list != 0); + for (inst = instance_list; inst; inst = inst->next) { + if (!inst->base_device || !strcmp(base, inst->base_device)) { + free(base); + return 1; + } + } + free(base); + return 0; +} + +/* Check all file systems, using the /etc/fstab table. */ +static int check_all(void) +{ + struct fs_info *fs = NULL; + int status = EXIT_OK; + int not_done_yet = 1; + int passno = 1; + int pass_done; + + if (verbose) + fputs("Checking all file systems.\n", stdout); + + /* + * Do an initial scan over the filesystem; mark filesystems + * which should be ignored as done, and resolve any "auto" + * filesystem types (done as a side-effect of calling ignore()). + */ + for (fs = filesys_info; fs; fs = fs->next) { + if (ignore(fs)) + fs->flags |= FLAG_DONE; + } + + /* + * Find and check the root filesystem. + */ + if (!parallel_root) { + for (fs = filesys_info; fs; fs = fs->next) { + if (LONE_CHAR(fs->mountpt, '/')) + break; + } + if (fs) { + if (!skip_root && !ignore(fs)) { + fsck_device(fs, 1); + status |= wait_many(FLAG_WAIT_ALL); + if (status > EXIT_NONDESTRUCT) + return status; + } + fs->flags |= FLAG_DONE; + } + } + /* + * This is for the bone-headed user who enters the root + * filesystem twice. Skip root will skep all root entries. + */ + if (skip_root) + for (fs = filesys_info; fs; fs = fs->next) + if (LONE_CHAR(fs->mountpt, '/')) + fs->flags |= FLAG_DONE; + + while (not_done_yet) { + not_done_yet = 0; + pass_done = 1; + + for (fs = filesys_info; fs; fs = fs->next) { + if (cancel_requested) + break; + if (fs->flags & FLAG_DONE) + continue; + /* + * If the filesystem's pass number is higher + * than the current pass number, then we don't + * do it yet. + */ + if (fs->passno > passno) { + not_done_yet++; + continue; + } + /* + * If a filesystem on a particular device has + * already been spawned, then we need to defer + * this to another pass. + */ + if (device_already_active(fs->device)) { + pass_done = 0; + continue; + } + /* + * Spawn off the fsck process + */ + fsck_device(fs, serialize); + fs->flags |= FLAG_DONE; + + /* + * Only do one filesystem at a time, or if we + * have a limit on the number of fsck's extant + * at one time, apply that limit. + */ + if (serialize || + (max_running && (num_running >= max_running))) { + pass_done = 0; + break; + } + } + if (cancel_requested) + break; + if (verbose > 1) + printf("--waiting-- (pass %d)\n", passno); + status |= wait_many(pass_done ? FLAG_WAIT_ALL : + FLAG_WAIT_ATLEAST_ONE); + if (pass_done) { + if (verbose > 1) + printf("----------------------------------\n"); + passno++; + } else + not_done_yet++; + } + if (cancel_requested && !kill_sent) { + kill_all(SIGTERM); + kill_sent++; + } + status |= wait_many(FLAG_WAIT_ATLEAST_ONE); + return status; +} + +static void signal_cancel(int sig FSCK_ATTR((unused))) +{ + cancel_requested++; +} + +static void PRS(int argc, char **argv) +{ + int i, j; + char *arg, *dev, *tmp = NULL; + char options[128]; + int opt = 0; + int opts_for_fsck = 0; + struct sigaction sa; + + /* + * Set up signal action + */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = signal_cancel; + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + + num_devices = 0; + num_args = 0; + instance_list = 0; + + for (i=1; i < argc; i++) { + arg = argv[i]; + if (!arg) + continue; + if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) { + if (num_devices >= MAX_DEVICES) { + bb_error_msg_and_die("too many devices"); + } + dev = blkid_get_devname(cache, arg, NULL); + if (!dev && strchr(arg, '=')) { + /* + * Check to see if we failed because + * /proc/partitions isn't found. + */ + if (access("/proc/partitions", R_OK) < 0) { + bb_perror_msg_and_die("can't open /proc/partitions " + "(is /proc mounted?)"); + } + /* + * Check to see if this is because + * we're not running as root + */ + if (geteuid()) + bb_error_msg_and_die( + "must be root to scan for matching filesystems: %s\n", arg); + else + bb_error_msg_and_die( + "can't find matching filesystem: %s", arg); + } + devices[num_devices++] = dev ? dev : string_copy(arg); + continue; + } + if (arg[0] != '-' || opts_for_fsck) { + if (num_args >= MAX_ARGS) { + bb_error_msg_and_die("too many arguments"); + } + args[num_args++] = string_copy(arg); + continue; + } + for (j=1; arg[j]; j++) { + if (opts_for_fsck) { + options[++opt] = arg[j]; + continue; + } + switch (arg[j]) { + case 'A': + doall++; + break; + case 'C': + progress++; + if (arg[j+1]) { + progress_fd = string_to_int(arg+j+1); + if (progress_fd < 0) + progress_fd = 0; + else + goto next_arg; + } else if ((i+1) < argc + && argv[i+1][0] != '-') { + progress_fd = string_to_int(argv[i]); + if (progress_fd < 0) + progress_fd = 0; + else { + goto next_arg; + i++; + } + } + break; + case 'V': + verbose++; + break; + case 'N': + noexecute++; + break; + case 'R': + skip_root++; + break; + case 'T': + notitle++; + break; + case 'M': + like_mount++; + break; + case 'P': + parallel_root++; + break; + case 's': + serialize++; + break; + case 't': + tmp = 0; + if (fstype) + bb_show_usage(); + if (arg[j+1]) + tmp = arg+j+1; + else if ((i+1) < argc) + tmp = argv[++i]; + else + bb_show_usage(); + fstype = string_copy(tmp); + compile_fs_type(fstype, &fs_type_compiled); + goto next_arg; + case '-': + opts_for_fsck++; + break; + case '?': + bb_show_usage(); + break; + default: + options[++opt] = arg[j]; + break; + } + } + next_arg: + if (opt) { + options[0] = '-'; + options[++opt] = '\0'; + if (num_args >= MAX_ARGS) { + bb_error_msg("too many arguments"); + } + args[num_args++] = string_copy(options); + opt = 0; + } + } + if (getenv("FSCK_FORCE_ALL_PARALLEL")) + force_all_parallel++; + if ((tmp = getenv("FSCK_MAX_INST"))) + max_running = atoi(tmp); +} + +int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fsck_main(int argc, char **argv) +{ + int i, status = 0; + int interactive = 0; + const char *fstab; + struct fs_info *fs; + + setvbuf(stdout, NULL, _IONBF, BUFSIZ); + setvbuf(stderr, NULL, _IONBF, BUFSIZ); + + blkid_get_cache(&cache, NULL); + PRS(argc, argv); + + if (!notitle) + printf("fsck %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); + + fstab = getenv("FSTAB_FILE"); + if (!fstab) + fstab = _PATH_MNTTAB; + load_fs_info(fstab); + + fsck_path = e2fs_set_sbin_path(); + + if ((num_devices == 1) || (serialize)) + interactive = 1; + + /* If -A was specified ("check all"), do that! */ + if (doall) + return check_all(); + + if (num_devices == 0) { + serialize++; + interactive++; + return check_all(); + } + for (i = 0; i < num_devices; i++) { + if (cancel_requested) { + if (!kill_sent) { + kill_all(SIGTERM); + kill_sent++; + } + break; + } + fs = lookup(devices[i]); + if (!fs) { + fs = create_fs_device(devices[i], 0, "auto", + 0, -1, -1); + if (!fs) + continue; + } + fsck_device(fs, interactive); + if (serialize || + (max_running && (num_running >= max_running))) { + struct fsck_instance *inst; + + inst = wait_one(0); + if (inst) { + status |= inst->exit_status; + free_instance(inst); + } + if (verbose > 1) + printf("----------------------------------\n"); + } + } + status |= wait_many(FLAG_WAIT_ALL); + blkid_put_cache(cache); + return status; +} diff --git a/e2fsprogs/old_e2fsprogs/fsck.h b/e2fsprogs/old_e2fsprogs/fsck.h new file mode 100644 index 000000000..2ca2af7da --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/fsck.h @@ -0,0 +1,16 @@ +/* vi: set sw=4 ts=4: */ +/* + * fsck.h + */ + +#define FSCK_ATTR(x) __attribute__(x) + +#define EXIT_OK 0 +#define EXIT_NONDESTRUCT 1 +#define EXIT_DESTRUCT 2 +#define EXIT_UNCORRECTED 4 +#define EXIT_ERROR 8 +#define EXIT_USAGE 16 +#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ + +extern char *e2fs_set_sbin_path(void); diff --git a/e2fsprogs/old_e2fsprogs/lsattr.c b/e2fsprogs/old_e2fsprogs/lsattr.c new file mode 100644 index 000000000..9e0e4cb60 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/lsattr.c @@ -0,0 +1,129 @@ +/* vi: set sw=4 ts=4: */ +/* + * lsattr.c - List file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * This file can be redistributed under the terms of the GNU General + * Public License + */ + +/* + * History: + * 93/10/30 - Creation + * 93/11/13 - Replace stat() calls by lstat() to avoid loops + * 94/02/27 - Integrated in Ted's distribution + * 98/12/29 - Display version info only when -V specified (G M Sipe) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ext2fs/ext2_fs.h" +#include "e2fsbb.h" +#include "e2p/e2p.h" + +#define OPT_RECUR 1 +#define OPT_ALL 2 +#define OPT_DIRS_OPT 4 +#define OPT_PF_LONG 8 +#define OPT_GENERATION 16 +static int flags; + +static void list_attributes(const char *name) +{ + unsigned long fsflags; + unsigned long generation; + + if (fgetflags(name, &fsflags) == -1) + goto read_err; + if (flags & OPT_GENERATION) { + if (fgetversion(name, &generation) == -1) + goto read_err; + printf("%5lu ", generation); + } + + if (flags & OPT_PF_LONG) { + printf("%-28s ", name); + print_e2flags(stdout, fsflags, PFOPT_LONG); + bb_putchar('\n'); + } else { + print_e2flags(stdout, fsflags, 0); + printf(" %s\n", name); + } + + return; +read_err: + bb_perror_msg("reading %s", name); +} + +static int lsattr_dir_proc(const char *, struct dirent *, void *); + +static void lsattr_args(const char *name) +{ + struct stat st; + + if (lstat(name, &st) == -1) { + bb_perror_msg("stating %s", name); + } else { + if (S_ISDIR(st.st_mode) && !(flags & OPT_DIRS_OPT)) + iterate_on_dir(name, lsattr_dir_proc, NULL); + else + list_attributes(name); + } +} + +static int lsattr_dir_proc(const char *dir_name, struct dirent *de, + void *private) +{ + struct stat st; + char *path; + + path = concat_path_file(dir_name, de->d_name); + + if (lstat(path, &st) == -1) + bb_perror_msg(path); + else { + if (de->d_name[0] != '.' || (flags & OPT_ALL)) { + list_attributes(path); + if (S_ISDIR(st.st_mode) && (flags & OPT_RECUR) && + (de->d_name[0] != '.' && (de->d_name[1] != '\0' || + (de->d_name[1] != '.' && de->d_name[2] != '\0')))) { + printf("\n%s:\n", path); + iterate_on_dir(path, lsattr_dir_proc, NULL); + bb_putchar('\n'); + } + } + } + + free(path); + + return 0; +} + +int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lsattr_main(int argc, char **argv) +{ + int i; + + flags = getopt32(argv, "Radlv"); + + if (optind > argc - 1) + lsattr_args("."); + else + for (i = optind; i < argc; i++) + lsattr_args(argv[i]); + + return EXIT_SUCCESS; +} diff --git a/e2fsprogs/old_e2fsprogs/mke2fs.c b/e2fsprogs/old_e2fsprogs/mke2fs.c new file mode 100644 index 000000000..7555650a2 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/mke2fs.c @@ -0,0 +1,1335 @@ +/* vi: set sw=4 ts=4: */ +/* + * mke2fs.c - Make a ext2fs filesystem. + * + * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + * 2003, 2004, 2005 by Theodore Ts'o. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +/* Usage: mke2fs [options] device + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "e2fsbb.h" +#include "ext2fs/ext2_fs.h" +#include "uuid/uuid.h" +#include "e2p/e2p.h" +#include "ext2fs/ext2fs.h" +#include "util.h" + +#define STRIDE_LENGTH 8 + +#ifndef __sparc__ +#define ZAP_BOOTBLOCK +#endif + +static const char * device_name; + +/* Command line options */ +static int cflag; +static int quiet; +static int super_only; +static int force; +static int noaction; +static int journal_size; +static int journal_flags; +static const char *bad_blocks_filename; +static __u32 fs_stride; + +static struct ext2_super_block param; +static char *creator_os; +static char *volume_label; +static char *mount_dir; +static char *journal_device = NULL; +static int sync_kludge; /* Set using the MKE2FS_SYNC env. option */ + +static int sys_page_size = 4096; +static int linux_version_code = 0; + +static int int_log2(int arg) +{ + int l = 0; + + arg >>= 1; + while (arg) { + l++; + arg >>= 1; + } + return l; +} + +static int int_log10(unsigned int arg) +{ + int l; + + for (l = 0; arg; l++) + arg = arg / 10; + return l; +} + +/* + * This function sets the default parameters for a filesystem + * + * The type is specified by the user. The size is the maximum size + * (in megabytes) for which a set of parameters applies, with a size + * of zero meaning that it is the default parameter for the type. + * Note that order is important in the table below. + */ +#define DEF_MAX_BLOCKSIZE -1 +static const char default_str[] = "default"; +struct mke2fs_defaults { + const char *type; + int size; + int blocksize; + int inode_ratio; +}; + +static const struct mke2fs_defaults settings[] = { + { default_str, 0, 4096, 8192 }, + { default_str, 512, 1024, 4096 }, + { default_str, 3, 1024, 8192 }, + { "journal", 0, 4096, 8192 }, + { "news", 0, 4096, 4096 }, + { "largefile", 0, 4096, 1024 * 1024 }, + { "largefile4", 0, 4096, 4096 * 1024 }, + { 0, 0, 0, 0}, +}; + +static void set_fs_defaults(const char *fs_type, + struct ext2_super_block *super, + int blocksize, int sector_size, + int *inode_ratio) +{ + int megs; + int ratio = 0; + const struct mke2fs_defaults *p; + int use_bsize = 1024; + + megs = super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024; + if (inode_ratio) + ratio = *inode_ratio; + if (!fs_type) + fs_type = default_str; + for (p = settings; p->type; p++) { + if ((strcmp(p->type, fs_type) != 0) && + (strcmp(p->type, default_str) != 0)) + continue; + if ((p->size != 0) && (megs > p->size)) + continue; + if (ratio == 0) + *inode_ratio = p->inode_ratio < blocksize ? + blocksize : p->inode_ratio; + use_bsize = p->blocksize; + } + if (blocksize <= 0) { + if (use_bsize == DEF_MAX_BLOCKSIZE) { + use_bsize = sys_page_size; + if ((linux_version_code < (2*65536 + 6*256)) && + (use_bsize > 4096)) + use_bsize = 4096; + } + if (sector_size && use_bsize < sector_size) + use_bsize = sector_size; + if ((blocksize < 0) && (use_bsize < (-blocksize))) + use_bsize = -blocksize; + blocksize = use_bsize; + super->s_blocks_count /= blocksize / 1024; + } + super->s_log_frag_size = super->s_log_block_size = + int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); +} + + +/* + * Helper function for read_bb_file and test_disk + */ +static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk) +{ + bb_error_msg("Bad block %u out of range; ignored", blk); +} + +/* + * Busybox stuff + */ +static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...) +{ + va_list ap; + + if (retval) { + va_start(ap, fmt); + fprintf(stderr, "\nCould not "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(EXIT_FAILURE); + } +} + +static void mke2fs_verbose(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +static void mke2fs_verbose(const char *fmt, ...) +{ + va_list ap; + + if (!quiet) { + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + fflush(stdout); + va_end(ap); + } +} + +static void mke2fs_verbose_done(void) +{ + mke2fs_verbose("done\n"); +} + +static void mke2fs_warning_msg(int retval, char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); +static void mke2fs_warning_msg(int retval, char *fmt, ... ) +{ + va_list ap; + + if (retval) { + va_start(ap, fmt); + fprintf(stderr, "\nWarning: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + } +} + +/* + * Reads the bad blocks list from a file + */ +static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list, + const char *bad_blocks_file) +{ + FILE *f; + errcode_t retval; + + f = xfopen_for_read(bad_blocks_file); + retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block); + fclose (f); + mke2fs_error_msg_and_die(retval, "read bad blocks from list"); +} + +/* + * Runs the badblocks program to test the disk + */ +static void test_disk(ext2_filsys fs, badblocks_list *bb_list) +{ + FILE *f; + errcode_t retval; + char buf[1024]; + + sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize, + quiet ? "" : "-s ", (cflag > 1) ? "-w " : "", + fs->device_name, fs->super->s_blocks_count); + mke2fs_verbose("Running command: %s\n", buf); + f = popen(buf, "r"); + if (!f) { + bb_perror_msg_and_die("can't run '%s'", buf); + } + retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block); + pclose(f); + mke2fs_error_msg_and_die(retval, "read bad blocks from program"); +} + +static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list) +{ + dgrp_t i; + blk_t j; + unsigned must_be_good; + blk_t blk; + badblocks_iterate bb_iter; + errcode_t retval; + blk_t group_block; + int group; + int group_bad; + + if (!bb_list) + return; + + /* + * The primary superblock and group descriptors *must* be + * good; if not, abort. + */ + must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks; + for (i = fs->super->s_first_data_block; i <= must_be_good; i++) { + if (ext2fs_badblocks_list_test(bb_list, i)) { + bb_error_msg_and_die( + "Block %d in primary superblock/group descriptor area bad\n" + "Blocks %d through %d must be good in order to build a filesystem\n" + "Aborting ...", i, fs->super->s_first_data_block, must_be_good); + } + } + + /* + * See if any of the bad blocks are showing up in the backup + * superblocks and/or group descriptors. If so, issue a + * warning and adjust the block counts appropriately. + */ + group_block = fs->super->s_first_data_block + + fs->super->s_blocks_per_group; + + for (i = 1; i < fs->group_desc_count; i++) { + group_bad = 0; + for (j=0; j < fs->desc_blocks+1; j++) { + if (ext2fs_badblocks_list_test(bb_list, + group_block + j)) { + mke2fs_warning_msg(!group_bad, + "the backup superblock/group descriptors at block %d contain\n" + "bad blocks\n", group_block); + group_bad++; + group = ext2fs_group_of_blk(fs, group_block+j); + fs->group_desc[group].bg_free_blocks_count++; + fs->super->s_free_blocks_count++; + } + } + group_block += fs->super->s_blocks_per_group; + } + + /* + * Mark all the bad blocks as used... + */ + retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); + mke2fs_error_msg_and_die(retval, "mark bad blocks as used"); + + while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) + ext2fs_mark_block_bitmap(fs->block_map, blk); + ext2fs_badblocks_list_iterate_end(bb_iter); +} + +/* + * These functions implement a generalized progress meter. + */ +struct progress_struct { + char format[20]; + char backup[80]; + __u32 max; + int skip_progress; +}; + +static void progress_init(struct progress_struct *progress, + const char *label,__u32 max) +{ + int i; + + memset(progress, 0, sizeof(struct progress_struct)); + if (quiet) + return; + + /* + * Figure out how many digits we need + */ + i = int_log10(max); + sprintf(progress->format, "%%%dd/%%%dld", i, i); + memset(progress->backup, '\b', sizeof(progress->backup)-1); + progress->backup[sizeof(progress->backup)-1] = 0; + if ((2*i)+1 < (int) sizeof(progress->backup)) + progress->backup[(2*i)+1] = 0; + progress->max = max; + + progress->skip_progress = 0; + if (getenv("MKE2FS_SKIP_PROGRESS")) + progress->skip_progress++; + + fputs(label, stdout); + fflush(stdout); +} + +static void progress_update(struct progress_struct *progress, __u32 val) +{ + if ((progress->format[0] == 0) || progress->skip_progress) + return; + printf(progress->format, val, progress->max); + fputs(progress->backup, stdout); +} + +static void progress_close(struct progress_struct *progress) +{ + if (progress->format[0] == 0) + return; + printf("%-28s\n", "done"); +} + + +/* + * Helper function which zeros out _num_ blocks starting at _blk_. In + * case of an error, the details of the error is returned via _ret_blk_ + * and _ret_count_ if they are non-NULL pointers. Returns 0 on + * success, and an error code on an error. + * + * As a special case, if the first argument is NULL, then it will + * attempt to free the static zeroizing buffer. (This is to keep + * programs that check for memory leaks happy.) + */ +static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num, + struct progress_struct *progress, + blk_t *ret_blk, int *ret_count) +{ + int j, count, next_update, next_update_incr; + static char *buf; + errcode_t retval; + + /* If fs is null, clean up the static buffer and return */ + if (!fs) { + if (buf) { + free(buf); + buf = 0; + } + return 0; + } + /* Allocate the zeroizing buffer if necessary */ + if (!buf) { + buf = xzalloc(fs->blocksize * STRIDE_LENGTH); + } + /* OK, do the write loop */ + next_update = 0; + next_update_incr = num / 100; + if (next_update_incr < 1) + next_update_incr = 1; + for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) { + count = num - j; + if (count > STRIDE_LENGTH) + count = STRIDE_LENGTH; + retval = io_channel_write_blk(fs->io, blk, count, buf); + if (retval) { + if (ret_count) + *ret_count = count; + if (ret_blk) + *ret_blk = blk; + return retval; + } + if (progress && j > next_update) { + next_update += num / 100; + progress_update(progress, blk); + } + } + return 0; +} + +static void write_inode_tables(ext2_filsys fs) +{ + errcode_t retval; + blk_t blk; + dgrp_t i; + int num; + struct progress_struct progress; + + if (quiet) + memset(&progress, 0, sizeof(progress)); + else + progress_init(&progress, "Writing inode tables: ", + fs->group_desc_count); + + for (i = 0; i < fs->group_desc_count; i++) { + progress_update(&progress, i); + + blk = fs->group_desc[i].bg_inode_table; + num = fs->inode_blocks_per_group; + + retval = zero_blocks(fs, blk, num, 0, &blk, &num); + mke2fs_error_msg_and_die(retval, + "write %d blocks in inode table starting at %d.", + num, blk); + if (sync_kludge) { + if (sync_kludge == 1) + sync(); + else if ((i % sync_kludge) == 0) + sync(); + } + } + zero_blocks(0, 0, 0, 0, 0, 0); + progress_close(&progress); +} + +static void create_root_dir(ext2_filsys fs) +{ + errcode_t retval; + struct ext2_inode inode; + + retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); + mke2fs_error_msg_and_die(retval, "create root dir"); + if (geteuid()) { + retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode); + mke2fs_error_msg_and_die(retval, "read root inode"); + inode.i_uid = getuid(); + if (inode.i_uid) + inode.i_gid = getgid(); + retval = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode); + mke2fs_error_msg_and_die(retval, "set root inode ownership"); + } +} + +static void create_lost_and_found(ext2_filsys fs) +{ + errcode_t retval; + ext2_ino_t ino; + const char *name = "lost+found"; + int i = 1; + char *msg = "create"; + int lpf_size = 0; + + fs->umask = 077; + retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name); + if (retval) { + goto CREATE_LOST_AND_FOUND_ERROR; + } + + retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino); + if (retval) { + msg = "lookup"; + goto CREATE_LOST_AND_FOUND_ERROR; + } + + for (; i < EXT2_NDIR_BLOCKS; i++) { + if ((lpf_size += fs->blocksize) >= 16*1024) + break; + retval = ext2fs_expand_dir(fs, ino); + msg = "expand"; +CREATE_LOST_AND_FOUND_ERROR: + mke2fs_error_msg_and_die(retval, "%s %s", msg, name); + } +} + +static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list) +{ + errcode_t retval; + + ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO); + fs->group_desc[0].bg_free_inodes_count--; + fs->super->s_free_inodes_count--; + retval = ext2fs_update_bb_inode(fs, bb_list); + mke2fs_error_msg_and_die(retval, "set bad block inode"); +} + +static void reserve_inodes(ext2_filsys fs) +{ + ext2_ino_t i; + int group; + + for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) { + ext2fs_mark_inode_bitmap(fs->inode_map, i); + group = ext2fs_group_of_ino(fs, i); + fs->group_desc[group].bg_free_inodes_count--; + fs->super->s_free_inodes_count--; + } + ext2fs_mark_ib_dirty(fs); +} + +#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ +#define BSD_MAGICDISK (0x57455682UL) /* The disk magic number reversed */ +#define BSD_LABEL_OFFSET 64 + +static void zap_sector(ext2_filsys fs, int sect, int nsect) +{ + char *buf; + char *fmt = "could not %s %d"; + int retval; + unsigned int *magic; + + buf = xmalloc(512*nsect); + + if (sect == 0) { + /* Check for a BSD disklabel, and don't erase it if so */ + retval = io_channel_read_blk(fs->io, 0, -512, buf); + if (retval) + mke2fs_warning_msg(retval, fmt, "read block", 0); + else { + magic = (unsigned int *) (buf + BSD_LABEL_OFFSET); + if ((*magic == BSD_DISKMAGIC) || + (*magic == BSD_MAGICDISK)) + return; + } + } + + memset(buf, 0, 512*nsect); + io_channel_set_blksize(fs->io, 512); + retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf); + io_channel_set_blksize(fs->io, fs->blocksize); + free(buf); + mke2fs_warning_msg(retval, fmt, "erase sector", sect); +} + +static void create_journal_dev(ext2_filsys fs) +{ + struct progress_struct progress; + errcode_t retval; + char *buf; + char *fmt = "%s journal superblock"; + blk_t blk; + int count; + + retval = ext2fs_create_journal_superblock(fs, + fs->super->s_blocks_count, 0, &buf); + mke2fs_error_msg_and_die(retval, fmt, "init"); + if (quiet) + memset(&progress, 0, sizeof(progress)); + else + progress_init(&progress, "Zeroing journal device: ", + fs->super->s_blocks_count); + + retval = zero_blocks(fs, 0, fs->super->s_blocks_count, + &progress, &blk, &count); + mke2fs_error_msg_and_die(retval, "zero journal device (block %u, count %d)", + blk, count); + zero_blocks(0, 0, 0, 0, 0, 0); + + retval = io_channel_write_blk(fs->io, + fs->super->s_first_data_block+1, + 1, buf); + mke2fs_error_msg_and_die(retval, fmt, "write"); + progress_close(&progress); +} + +static void show_stats(ext2_filsys fs) +{ + struct ext2_super_block *s = fs->super; + char *os; + blk_t group_block; + dgrp_t i; + int need, col_left; + + mke2fs_warning_msg((param.s_blocks_count != s->s_blocks_count), + "%d blocks unused\n", param.s_blocks_count - s->s_blocks_count); + os = e2p_os2string(fs->super->s_creator_os); + printf( "Filesystem label=%.*s\n" + "OS type: %s\n" + "Block size=%u (log=%u)\n" + "Fragment size=%u (log=%u)\n" + "%u inodes, %u blocks\n" + "%u blocks (%2.2f%%) reserved for the super user\n" + "First data block=%u\n", + (int) sizeof(s->s_volume_name), + s->s_volume_name, + os, + fs->blocksize, s->s_log_block_size, + fs->fragsize, s->s_log_frag_size, + s->s_inodes_count, s->s_blocks_count, + s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count, + s->s_first_data_block); + free(os); + if (s->s_reserved_gdt_blocks) { + printf("Maximum filesystem blocks=%lu\n", + (s->s_reserved_gdt_blocks + fs->desc_blocks) * + (fs->blocksize / sizeof(struct ext2_group_desc)) * + s->s_blocks_per_group); + } + printf( "%u block group%s\n" + "%u blocks per group, %u fragments per group\n" + "%u inodes per group\n", + fs->group_desc_count, (fs->group_desc_count > 1) ? "s" : "", + s->s_blocks_per_group, s->s_frags_per_group, + s->s_inodes_per_group); + if (fs->group_desc_count == 1) { + bb_putchar('\n'); + return; + } + + printf("Superblock backups stored on blocks: "); + group_block = s->s_first_data_block; + col_left = 0; + for (i = 1; i < fs->group_desc_count; i++) { + group_block += s->s_blocks_per_group; + if (!ext2fs_bg_has_super(fs, i)) + continue; + if (i != 1) + printf(", "); + need = int_log10(group_block) + 2; + if (need > col_left) { + printf("\n\t"); + col_left = 72; + } + col_left -= need; + printf("%u", group_block); + } + puts("\n"); +} + +/* + * Set the S_CREATOR_OS field. Return true if OS is known, + * otherwise, 0. + */ +static int set_os(struct ext2_super_block *sb, char *os) +{ + if (isdigit (*os)) { + sb->s_creator_os = atoi(os); + return 1; + } + + if ((sb->s_creator_os = e2p_string2os(os)) >= 0) { + return 1; + } else if (!strcasecmp("GNU", os)) { + sb->s_creator_os = EXT2_OS_HURD; + return 1; + } + return 0; +} + +static void parse_extended_opts(struct ext2_super_block *sb_param, + const char *opts) +{ + char *buf, *token, *next, *p, *arg; + int r_usage = 0; + + buf = xstrdup(opts); + for (token = buf; token && *token; token = next) { + p = strchr(token, ','); + next = 0; + if (p) { + *p = 0; + next = p+1; + } + arg = strchr(token, '='); + if (arg) { + *arg = 0; + arg++; + } + if (strcmp(token, "stride") == 0) { + if (!arg) { + r_usage++; + continue; + } + fs_stride = strtoul(arg, &p, 0); + if (*p || (fs_stride == 0)) { + bb_error_msg("Invalid stride parameter: %s", arg); + r_usage++; + continue; + } + } else if (!strcmp(token, "resize")) { + unsigned long resize, bpg, rsv_groups; + unsigned long group_desc_count, desc_blocks; + unsigned int gdpb, blocksize; + int rsv_gdb; + + if (!arg) { + r_usage++; + continue; + } + + resize = parse_num_blocks(arg, + sb_param->s_log_block_size); + + if (resize == 0) { + bb_error_msg("Invalid resize parameter: %s", arg); + r_usage++; + continue; + } + if (resize <= sb_param->s_blocks_count) { + bb_error_msg("The resize maximum must be greater " + "than the filesystem size"); + r_usage++; + continue; + } + + blocksize = EXT2_BLOCK_SIZE(sb_param); + bpg = sb_param->s_blocks_per_group; + if (!bpg) + bpg = blocksize * 8; + gdpb = blocksize / sizeof(struct ext2_group_desc); + group_desc_count = (sb_param->s_blocks_count + + bpg - 1) / bpg; + desc_blocks = (group_desc_count + + gdpb - 1) / gdpb; + rsv_groups = (resize + bpg - 1) / bpg; + rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - + desc_blocks; + if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb_param)) + rsv_gdb = EXT2_ADDR_PER_BLOCK(sb_param); + + if (rsv_gdb > 0) { + sb_param->s_feature_compat |= + EXT2_FEATURE_COMPAT_RESIZE_INODE; + + sb_param->s_reserved_gdt_blocks = rsv_gdb; + } + } else + r_usage++; + } + if (r_usage) { + bb_error_msg_and_die( + "\nBad options specified.\n\n" + "Extended options are separated by commas, " + "and may take an argument which\n" + "\tis set off by an equals ('=') sign.\n\n" + "Valid extended options are:\n" + "\tstride=\n" + "\tresize=\n"); + } +} + +static __u32 ok_features[3] = { + EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_RESIZE_INODE | + EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */ + EXT2_FEATURE_INCOMPAT_FILETYPE| /* Incompat */ + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV| + EXT2_FEATURE_INCOMPAT_META_BG, + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER /* R/O compat */ +}; + +static int PRS(int argc, char **argv) +{ + int c; + int size; + char * tmp; + int blocksize = 0; + int inode_ratio = 0; + int inode_size = 0; + int reserved_ratio = 5; + int sector_size = 0; + int show_version_only = 0; + ext2_ino_t num_inodes = 0; + errcode_t retval; + char * extended_opts = NULL; + const char * fs_type = NULL; + blk_t dev_size; + long sysval; + + /* Update our PATH to include /sbin */ + e2fs_set_sbin_path(); + + tmp = getenv("MKE2FS_SYNC"); + if (tmp) + sync_kludge = atoi(tmp); + + /* Determine the system page size if possible */ +#if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) +#define _SC_PAGESIZE _SC_PAGE_SIZE +#endif +#ifdef _SC_PAGESIZE + sysval = sysconf(_SC_PAGESIZE); + if (sysval > 0) + sys_page_size = sysval; +#endif /* _SC_PAGESIZE */ + + setbuf(stdout, NULL); + setbuf(stderr, NULL); + memset(¶m, 0, sizeof(struct ext2_super_block)); + param.s_rev_level = 1; /* Create revision 1 filesystems now */ + param.s_feature_incompat |= EXT2_FEATURE_INCOMPAT_FILETYPE; + param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + +#ifdef __linux__ + linux_version_code = get_linux_version_code(); + if (linux_version_code && linux_version_code < KERNEL_VERSION(2,2,0)) { + param.s_rev_level = 0; + param.s_feature_incompat = 0; + param.s_feature_compat = 0; + param.s_feature_ro_compat = 0; + } +#endif + + /* If called as mkfs.ext3, create a journal inode */ + if (last_char_is(applet_name, '3')) + journal_size = -1; + + while ((c = getopt (argc, argv, + "b:cE:f:g:i:jl:m:no:qr:R:s:tvI:J:ST:FL:M:N:O:V")) != EOF) { + switch (c) { + case 'b': + blocksize = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE); + mke2fs_warning_msg((blocksize > 4096), + "blocksize %d not usable on most systems", + blocksize); + param.s_log_block_size = + int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); + break; + case 'c': /* Check for bad blocks */ + case 't': /* deprecated */ + cflag++; + break; + case 'f': + size = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE); + param.s_log_frag_size = + int_log2(size >> EXT2_MIN_BLOCK_LOG_SIZE); + mke2fs_warning_msg(1, "fragments not supported. Ignoring -f option"); + break; + case 'g': + param.s_blocks_per_group = xatou32(optarg); + if ((param.s_blocks_per_group % 8) != 0) { + bb_error_msg_and_die("blocks per group must be multiple of 8"); + } + break; + case 'i': + /* Huh? is "* 1024" correct? */ + inode_ratio = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE * 1024); + break; + case 'J': + parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg); + break; + case 'j': + param.s_feature_compat |= + EXT3_FEATURE_COMPAT_HAS_JOURNAL; + if (!journal_size) + journal_size = -1; + break; + case 'l': + bad_blocks_filename = optarg; + break; + case 'm': + reserved_ratio = xatou_range(optarg, 0, 50); + break; + case 'n': + noaction++; + break; + case 'o': + creator_os = optarg; + break; + case 'r': + param.s_rev_level = xatoi_positive(optarg); + if (param.s_rev_level == EXT2_GOOD_OLD_REV) { + param.s_feature_incompat = 0; + param.s_feature_compat = 0; + param.s_feature_ro_compat = 0; + } + break; + case 's': /* deprecated */ + if (xatou(optarg)) + param.s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + else + param.s_feature_ro_compat &= + ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + break; +#ifdef EXT2_DYNAMIC_REV + case 'I': + inode_size = xatoi_positive(optarg); + break; +#endif + case 'N': + num_inodes = xatoi_positive(optarg); + break; + case 'v': + quiet = 0; + break; + case 'q': + quiet = 1; + break; + case 'F': + force = 1; + break; + case 'L': + volume_label = optarg; + break; + case 'M': + mount_dir = optarg; + break; + case 'O': + if (!strcmp(optarg, "none")) { + param.s_feature_compat = 0; + param.s_feature_incompat = 0; + param.s_feature_ro_compat = 0; + break; + } + if (e2p_edit_feature(optarg, + ¶m.s_feature_compat, + ok_features)) { + bb_error_msg_and_die("Invalid filesystem option set: %s", optarg); + } + break; + case 'E': + case 'R': + extended_opts = optarg; + break; + case 'S': + super_only = 1; + break; + case 'T': + fs_type = optarg; + break; + case 'V': + /* Print version number and exit */ + show_version_only = 1; + quiet = 0; + break; + default: + bb_show_usage(); + } + } + if ((optind == argc) /*&& !show_version_only*/) + bb_show_usage(); + device_name = argv[optind++]; + + mke2fs_verbose("mke2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); + + if (show_version_only) { + return 0; + } + + /* + * If there's no blocksize specified and there is a journal + * device, use it to figure out the blocksize + */ + if (blocksize <= 0 && journal_device) { + ext2_filsys jfs; + io_manager io_ptr; + +#ifdef CONFIG_TESTIO_DEBUG + io_ptr = test_io_manager; + test_io_backing_manager = unix_io_manager; +#else + io_ptr = unix_io_manager; +#endif + retval = ext2fs_open(journal_device, + EXT2_FLAG_JOURNAL_DEV_OK, 0, + 0, io_ptr, &jfs); + mke2fs_error_msg_and_die(retval, "open journal device %s", journal_device); + if ((blocksize < 0) && (jfs->blocksize < (unsigned) (-blocksize))) { + bb_error_msg_and_die( + "Journal dev blocksize (%d) smaller than " + "minimum blocksize %d\n", jfs->blocksize, + -blocksize); + } + blocksize = jfs->blocksize; + param.s_log_block_size = + int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE); + ext2fs_close(jfs); + } + + if (blocksize > sys_page_size) { + mke2fs_warning_msg(1, "%d-byte blocks too big for system (max %d)", + blocksize, sys_page_size); + if (!force) { + proceed_question(); + } + bb_error_msg("Forced to continue"); + } + mke2fs_warning_msg(((blocksize > 4096) && + (param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)), + "some 2.4 kernels do not support " + "blocksizes greater than 4096 using ext3.\n" + "Use -b 4096 if this is an issue for you\n"); + + if (optind < argc) { + param.s_blocks_count = parse_num_blocks(argv[optind++], + param.s_log_block_size); + mke2fs_error_msg_and_die(!param.s_blocks_count, "invalid blocks count - %s", argv[optind - 1]); + } + if (optind < argc) + bb_show_usage(); + + if (param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + if (!fs_type) + fs_type = "journal"; + reserved_ratio = 0; + param.s_feature_incompat = EXT3_FEATURE_INCOMPAT_JOURNAL_DEV; + param.s_feature_compat = 0; + param.s_feature_ro_compat = 0; + } + if (param.s_rev_level == EXT2_GOOD_OLD_REV && + (param.s_feature_compat || param.s_feature_ro_compat || + param.s_feature_incompat)) + param.s_rev_level = 1; /* Create a revision 1 filesystem */ + + check_plausibility(device_name , force); + check_mount(device_name, force, "filesystem"); + + param.s_log_frag_size = param.s_log_block_size; + + if (noaction && param.s_blocks_count) { + dev_size = param.s_blocks_count; + retval = 0; + } else { + retry: + retval = ext2fs_get_device_size(device_name, + EXT2_BLOCK_SIZE(¶m), + &dev_size); + if ((retval == EFBIG) && + (blocksize == 0) && + (param.s_log_block_size == 0)) { + param.s_log_block_size = 2; + blocksize = 4096; + goto retry; + } + } + + mke2fs_error_msg_and_die((retval && (retval != EXT2_ET_UNIMPLEMENTED)),"determine filesystem size"); + + if (!param.s_blocks_count) { + if (retval == EXT2_ET_UNIMPLEMENTED) { + mke2fs_error_msg_and_die(1, + "determine device size; you " + "must specify\nthe size of the " + "filesystem"); + } else { + if (dev_size == 0) { + bb_error_msg_and_die( + "Device size reported to be zero. " + "Invalid partition specified, or\n\t" + "partition table wasn't reread " + "after running fdisk, due to\n\t" + "a modified partition being busy " + "and in use. You may need to reboot\n\t" + "to re-read your partition table.\n" + ); + } + param.s_blocks_count = dev_size; + if (sys_page_size > EXT2_BLOCK_SIZE(¶m)) + param.s_blocks_count &= ~((sys_page_size / + EXT2_BLOCK_SIZE(¶m))-1); + } + + } else if (!force && (param.s_blocks_count > dev_size)) { + bb_error_msg("Filesystem larger than apparent device size"); + proceed_question(); + } + + /* + * If the user asked for HAS_JOURNAL, then make sure a journal + * gets created. + */ + if ((param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + !journal_size) + journal_size = -1; + + /* Set first meta blockgroup via an environment variable */ + /* (this is mostly for debugging purposes) */ + if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) && + ((tmp = getenv("MKE2FS_FIRST_META_BG")))) + param.s_first_meta_bg = atoi(tmp); + + /* Get the hardware sector size, if available */ + retval = ext2fs_get_device_sectsize(device_name, §or_size); + mke2fs_error_msg_and_die(retval, "determine hardware sector size"); + + if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL) + sector_size = atoi(tmp); + + set_fs_defaults(fs_type, ¶m, blocksize, sector_size, &inode_ratio); + blocksize = EXT2_BLOCK_SIZE(¶m); + + if (extended_opts) + parse_extended_opts(¶m, extended_opts); + + /* Since sparse_super is the default, we would only have a problem + * here if it was explicitly disabled. + */ + if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) && + !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + bb_error_msg_and_die("reserved online resize blocks not supported " + "on non-sparse filesystem"); + } + + if (param.s_blocks_per_group) { + if (param.s_blocks_per_group < 256 || + param.s_blocks_per_group > 8 * (unsigned) blocksize) { + bb_error_msg_and_die("blocks per group count out of range"); + } + } + + if (!force && param.s_blocks_count >= (1 << 31)) { + bb_error_msg_and_die("Filesystem too large. No more than 2**31-1 blocks\n" + "\t (8TB using a blocksize of 4k) are currently supported."); + } + + if (inode_size) { + if (inode_size < EXT2_GOOD_OLD_INODE_SIZE || + inode_size > EXT2_BLOCK_SIZE(¶m) || + inode_size & (inode_size - 1)) { + bb_error_msg_and_die("invalid inode size %d (min %d/max %d)", + inode_size, EXT2_GOOD_OLD_INODE_SIZE, + blocksize); + } + mke2fs_warning_msg((inode_size != EXT2_GOOD_OLD_INODE_SIZE), + "%d-byte inodes not usable on most systems", + inode_size); + param.s_inode_size = inode_size; + } + + /* + * Calculate number of inodes based on the inode ratio + */ + param.s_inodes_count = num_inodes ? num_inodes : + ((__u64) param.s_blocks_count * blocksize) + / inode_ratio; + + /* + * Calculate number of blocks to reserve + */ + param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100; + return 1; +} + +static void mke2fs_clean_up(void) +{ + if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device); +} + +int mke2fs_main (int argc, char **argv); +int mke2fs_main (int argc, char **argv) +{ + errcode_t retval; + ext2_filsys fs; + badblocks_list bb_list = 0; + unsigned int i; + int val; + io_manager io_ptr; + + if (ENABLE_FEATURE_CLEAN_UP) + atexit(mke2fs_clean_up); + if (!PRS(argc, argv)) + return 0; + +#ifdef CONFIG_TESTIO_DEBUG + io_ptr = test_io_manager; + test_io_backing_manager = unix_io_manager; +#else + io_ptr = unix_io_manager; +#endif + + /* + * Initialize the superblock.... + */ + retval = ext2fs_initialize(device_name, 0, ¶m, + io_ptr, &fs); + mke2fs_error_msg_and_die(retval, "set up superblock"); + + /* + * Wipe out the old on-disk superblock + */ + if (!noaction) + zap_sector(fs, 2, 6); + + /* + * Generate a UUID for it... + */ + uuid_generate(fs->super->s_uuid); + + /* + * Initialize the directory index variables + */ + fs->super->s_def_hash_version = EXT2_HASH_TEA; + uuid_generate((unsigned char *) fs->super->s_hash_seed); + + /* + * Add "jitter" to the superblock's check interval so that we + * don't check all the filesystems at the same time. We use a + * kludgy hack of using the UUID to derive a random jitter value. + */ + for (i = 0, val = 0; i < sizeof(fs->super->s_uuid); i++) + val += fs->super->s_uuid[i]; + fs->super->s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT; + + /* + * Override the creator OS, if applicable + */ + if (creator_os && !set_os(fs->super, creator_os)) { + bb_error_msg_and_die("unknown os - %s", creator_os); + } + + /* + * For the Hurd, we will turn off filetype since it doesn't + * support it. + */ + if (fs->super->s_creator_os == EXT2_OS_HURD) + fs->super->s_feature_incompat &= + ~EXT2_FEATURE_INCOMPAT_FILETYPE; + + /* + * Set the volume label... + */ + if (volume_label) { + snprintf(fs->super->s_volume_name, sizeof(fs->super->s_volume_name), "%s", volume_label); + } + + /* + * Set the last mount directory + */ + if (mount_dir) { + snprintf(fs->super->s_last_mounted, sizeof(fs->super->s_last_mounted), "%s", mount_dir); + } + + if (!quiet || noaction) + show_stats(fs); + + if (noaction) + return 0; + + if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { + create_journal_dev(fs); + return (ext2fs_close(fs) ? 1 : 0); + } + + if (bad_blocks_filename) + read_bb_file(fs, &bb_list, bad_blocks_filename); + if (cflag) + test_disk(fs, &bb_list); + + handle_bad_blocks(fs, bb_list); + fs->stride = fs_stride; + retval = ext2fs_allocate_tables(fs); + mke2fs_error_msg_and_die(retval, "allocate filesystem tables"); + if (super_only) { + fs->super->s_state |= EXT2_ERROR_FS; + fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY); + } else { + /* rsv must be a power of two (64kB is MD RAID sb alignment) */ + unsigned int rsv = 65536 / fs->blocksize; + unsigned long blocks = fs->super->s_blocks_count; + unsigned long start; + blk_t ret_blk; + +#ifdef ZAP_BOOTBLOCK + zap_sector(fs, 0, 2); +#endif + + /* + * Wipe out any old MD RAID (or other) metadata at the end + * of the device. This will also verify that the device is + * as large as we think. Be careful with very small devices. + */ + start = (blocks & ~(rsv - 1)); + if (start > rsv) + start -= rsv; + if (start > 0) + retval = zero_blocks(fs, start, blocks - start, + NULL, &ret_blk, NULL); + + mke2fs_warning_msg(retval, "can't zero block %u at end of filesystem", ret_blk); + write_inode_tables(fs); + create_root_dir(fs); + create_lost_and_found(fs); + reserve_inodes(fs); + create_bad_block_inode(fs, bb_list); + if (fs->super->s_feature_compat & + EXT2_FEATURE_COMPAT_RESIZE_INODE) { + retval = ext2fs_create_resize_inode(fs); + mke2fs_error_msg_and_die(retval, "reserve blocks for online resize"); + } + } + + if (journal_device) { + make_journal_device(journal_device, fs, quiet, force); + } else if (journal_size) { + make_journal_blocks(fs, journal_size, journal_flags, quiet); + } + + mke2fs_verbose("Writing superblocks and filesystem accounting information: "); + retval = ext2fs_flush(fs); + mke2fs_warning_msg(retval, "had trouble writing out superblocks"); + mke2fs_verbose_done(); + if (!quiet && !getenv("MKE2FS_SKIP_CHECK_MSG")) + print_check_message(fs); + val = ext2fs_close(fs); + return (retval || val) ? 1 : 0; +} diff --git a/e2fsprogs/old_e2fsprogs/tune2fs.c b/e2fsprogs/old_e2fsprogs/tune2fs.c new file mode 100644 index 000000000..3c3f4afa5 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/tune2fs.c @@ -0,0 +1,710 @@ +/* vi: set sw=4 ts=4: */ +/* + * tune2fs.c - Change the file system parameters on an ext2 file system + * + * Copyright (C) 1992, 1993, 1994 Remy Card + * Laboratoire MASI, Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +/* + * History: + * 93/06/01 - Creation + * 93/10/31 - Added the -c option to change the maximal mount counts + * 93/12/14 - Added -l flag to list contents of superblock + * M.J.E. Mol (marcel@duteca.et.tudelft.nl) + * F.W. ten Wolde (franky@duteca.et.tudelft.nl) + * 93/12/29 - Added the -e option to change errors behavior + * 94/02/27 - Ported to use the ext2fs library + * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "e2fsbb.h" +#include "ext2fs/ext2_fs.h" +#include "ext2fs/ext2fs.h" +#include "uuid/uuid.h" +#include "e2p/e2p.h" +#include "ext2fs/kernel-jbd.h" +#include "util.h" +#include "blkid/blkid.h" + +#include "libbb.h" + +static char * device_name = NULL; +static char * new_label, *new_last_mounted, *new_UUID; +static char * io_options; +static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; +static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; +static time_t last_check_time; +static int print_label; +static int max_mount_count, mount_count, mount_flags; +static unsigned long interval, reserved_blocks; +static unsigned reserved_ratio; +static unsigned long resgid, resuid; +static unsigned short errors; +static int open_flag; +static char *features_cmd; +static char *mntopts_cmd; + +static int journal_size, journal_flags; +static char *journal_device = NULL; + +static const char *please_fsck = "Please run e2fsck on the filesystem\n"; + +static __u32 ok_features[3] = { + EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_DIR_INDEX, + EXT2_FEATURE_INCOMPAT_FILETYPE, + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER +}; + +/* + * Remove an external journal from the filesystem + */ +static void remove_journal_device(ext2_filsys fs) +{ + char *journal_path; + ext2_filsys jfs; + char buf[1024]; + journal_superblock_t *jsb; + int i, nr_users; + errcode_t retval; + int commit_remove_journal = 0; + io_manager io_ptr; + + if (f_flag) + commit_remove_journal = 1; /* force removal even if error */ + + uuid_unparse(fs->super->s_journal_uuid, buf); + journal_path = blkid_get_devname(NULL, "UUID", buf); + + if (!journal_path) { + journal_path = + ext2fs_find_block_device(fs->super->s_journal_dev); + if (!journal_path) + return; + } + + io_ptr = unix_io_manager; + retval = ext2fs_open(journal_path, EXT2_FLAG_RW| + EXT2_FLAG_JOURNAL_DEV_OK, 0, + fs->blocksize, io_ptr, &jfs); + if (retval) { + bb_error_msg("Failed to open external journal"); + goto no_valid_journal; + } + if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + bb_error_msg("%s is not a journal device", journal_path); + goto no_valid_journal; + } + + /* Get the journal superblock */ + if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) { + bb_error_msg("Failed to read journal superblock"); + goto no_valid_journal; + } + + jsb = (journal_superblock_t *) buf; + if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || + (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) { + bb_error_msg("Journal superblock not found!"); + goto no_valid_journal; + } + + /* Find the filesystem UUID */ + nr_users = ntohl(jsb->s_nr_users); + for (i=0; i < nr_users; i++) { + if (memcmp(fs->super->s_uuid, + &jsb->s_users[i*16], 16) == 0) + break; + } + if (i >= nr_users) { + bb_error_msg("Filesystem's UUID not found on journal device"); + commit_remove_journal = 1; + goto no_valid_journal; + } + nr_users--; + for (i=0; i < nr_users; i++) + memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16); + jsb->s_nr_users = htonl(nr_users); + + /* Write back the journal superblock */ + if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) { + bb_error_msg("Failed to write journal superblock"); + goto no_valid_journal; + } + + commit_remove_journal = 1; + +no_valid_journal: + if (commit_remove_journal == 0) + bb_error_msg_and_die("Journal NOT removed"); + fs->super->s_journal_dev = 0; + uuid_clear(fs->super->s_journal_uuid); + ext2fs_mark_super_dirty(fs); + puts("Journal removed"); + free(journal_path); +} + +/* Helper function for remove_journal_inode */ +static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, + int blockcnt EXT2FS_ATTR((unused)), + void *private EXT2FS_ATTR((unused))) +{ + blk_t block; + int group; + + block = *blocknr; + ext2fs_unmark_block_bitmap(fs->block_map,block); + group = ext2fs_group_of_blk(fs, block); + fs->group_desc[group].bg_free_blocks_count++; + fs->super->s_free_blocks_count++; + return 0; +} + +/* + * Remove the journal inode from the filesystem + */ +static void remove_journal_inode(ext2_filsys fs) +{ + struct ext2_inode inode; + errcode_t retval; + ino_t ino = fs->super->s_journal_inum; + char *msg = "to read"; + char *s = "journal inode"; + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + goto REMOVE_JOURNAL_INODE_ERROR; + if (ino == EXT2_JOURNAL_INO) { + retval = ext2fs_read_bitmaps(fs); + if (retval) { + msg = "to read bitmaps"; + s = ""; + goto REMOVE_JOURNAL_INODE_ERROR; + } + retval = ext2fs_block_iterate(fs, ino, 0, NULL, + release_blocks_proc, NULL); + if (retval) { + msg = "clearing"; + goto REMOVE_JOURNAL_INODE_ERROR; + } + memset(&inode, 0, sizeof(inode)); + ext2fs_mark_bb_dirty(fs); + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } else + inode.i_flags &= ~EXT2_IMMUTABLE_FL; + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + msg = "writing"; +REMOVE_JOURNAL_INODE_ERROR: + bb_error_msg_and_die("Failed %s %s", msg, s); + } + fs->super->s_journal_inum = 0; + ext2fs_mark_super_dirty(fs); +} + +/* + * Update the default mount options + */ +static void update_mntopts(ext2_filsys fs, char *mntopts) +{ + struct ext2_super_block *sb= fs->super; + + if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) + bb_error_msg_and_die("Invalid mount option set: %s", mntopts); + ext2fs_mark_super_dirty(fs); +} + +/* + * Update the feature set as provided by the user. + */ +static void update_feature_set(ext2_filsys fs, char *features) +{ + int sparse, old_sparse, filetype, old_filetype; + int journal, old_journal, dxdir, old_dxdir; + struct ext2_super_block *sb= fs->super; + __u32 old_compat, old_incompat, old_ro_compat; + + old_compat = sb->s_feature_compat; + old_ro_compat = sb->s_feature_ro_compat; + old_incompat = sb->s_feature_incompat; + + old_sparse = sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + old_filetype = sb->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE; + old_journal = sb->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL; + old_dxdir = sb->s_feature_compat & + EXT2_FEATURE_COMPAT_DIR_INDEX; + if (e2p_edit_feature(features, &sb->s_feature_compat, ok_features)) + bb_error_msg_and_die("Invalid filesystem option set: %s", features); + sparse = sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + filetype = sb->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE; + journal = sb->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL; + dxdir = sb->s_feature_compat & + EXT2_FEATURE_COMPAT_DIR_INDEX; + if (old_journal && !journal) { + if ((mount_flags & EXT2_MF_MOUNTED) && + !(mount_flags & EXT2_MF_READONLY)) { + bb_error_msg_and_die( + "The has_journal flag may only be " + "cleared when the filesystem is\n" + "unmounted or mounted " + "read-only"); + } + if (sb->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER) { + bb_error_msg_and_die( + "The needs_recovery flag is set. " + "%s before clearing the has_journal flag.", + please_fsck); + } + if (sb->s_journal_inum) { + remove_journal_inode(fs); + } + if (sb->s_journal_dev) { + remove_journal_device(fs); + } + } + if (journal && !old_journal) { + /* + * If adding a journal flag, let the create journal + * code below handle creating setting the flag and + * creating the journal. We supply a default size if + * necessary. + */ + if (!journal_size) + journal_size = -1; + sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; + } + if (dxdir && !old_dxdir) { + if (!sb->s_def_hash_version) + sb->s_def_hash_version = EXT2_HASH_TEA; + if (uuid_is_null((unsigned char *) sb->s_hash_seed)) + uuid_generate((unsigned char *) sb->s_hash_seed); + } + + if (sb->s_rev_level == EXT2_GOOD_OLD_REV && + (sb->s_feature_compat || sb->s_feature_ro_compat || + sb->s_feature_incompat)) + ext2fs_update_dynamic_rev(fs); + if ((sparse != old_sparse) || + (filetype != old_filetype)) { + sb->s_state &= ~EXT2_VALID_FS; + printf("\n%s\n", please_fsck); + } + if ((old_compat != sb->s_feature_compat) || + (old_ro_compat != sb->s_feature_ro_compat) || + (old_incompat != sb->s_feature_incompat)) + ext2fs_mark_super_dirty(fs); +} + +/* + * Add a journal to the filesystem. + */ +static void add_journal(ext2_filsys fs) +{ + if (fs->super->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL) { + bb_error_msg_and_die("The filesystem already has a journal"); + } + if (journal_device) { + make_journal_device(journal_device, fs, 0, 0); + } else if (journal_size) { + make_journal_blocks(fs, journal_size, journal_flags, 0); + /* + * If the filesystem wasn't mounted, we need to force + * the block group descriptors out. + */ + if ((mount_flags & EXT2_MF_MOUNTED) == 0) + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } + print_check_message(fs); +} + +/* + * Busybox stuff + */ +static char * x_blkid_get_devname(const char *token) +{ + char * dev_name; + + if (!(dev_name = blkid_get_devname(NULL, token, NULL))) + bb_error_msg_and_die("Unable to resolve '%s'", token); + return dev_name; +} + +#ifdef CONFIG_E2LABEL +static void parse_e2label_options(int argc, char ** argv) +{ + if ((argc < 2) || (argc > 3)) + bb_show_usage(); + io_options = strchr(argv[1], '?'); + if (io_options) + *io_options++ = 0; + device_name = x_blkid_get_devname(argv[1]); + if (argc == 3) { + open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK; + L_flag = 1; + new_label = argv[2]; + } else + print_label++; +} +#else +#define parse_e2label_options(x,y) +#endif + +static time_t parse_time(char *str) +{ + struct tm ts; + + if (strcmp(str, "now") == 0) { + return time(0); + } + memset(&ts, 0, sizeof(ts)); +#ifdef HAVE_STRPTIME + strptime(str, "%Y%m%d%H%M%S", &ts); +#else + sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon, + &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec); + ts.tm_year -= 1900; + ts.tm_mon -= 1; + if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 || + ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 || + ts.tm_min > 59 || ts.tm_sec > 61) + ts.tm_mday = 0; +#endif + if (ts.tm_mday == 0) { + bb_error_msg_and_die("can't parse date/time specifier: %s", str); + } + return mktime(&ts); +} + +static void parse_tune2fs_options(int argc, char **argv) +{ + int c; + char * tmp; + + printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); + while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF) + switch (c) + { + case 'c': + max_mount_count = xatou_range(optarg, 0, 16000); + if (max_mount_count == 0) + max_mount_count = -1; + c_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'C': + mount_count = xatou_range(optarg, 0, 16000); + C_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'e': + if (strcmp (optarg, "continue") == 0) + errors = EXT2_ERRORS_CONTINUE; + else if (strcmp (optarg, "remount-ro") == 0) + errors = EXT2_ERRORS_RO; + else if (strcmp (optarg, "panic") == 0) + errors = EXT2_ERRORS_PANIC; + else { + bb_error_msg_and_die("bad error behavior - %s", optarg); + } + e_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'f': /* Force */ + f_flag = 1; + break; + case 'g': + resgid = bb_strtoul(optarg, NULL, 10); + if (errno) + resgid = xgroup2gid(optarg); + g_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'i': + interval = strtoul(optarg, &tmp, 0); + switch (*tmp) { + case 's': + tmp++; + break; + case '\0': + case 'd': + case 'D': /* days */ + interval *= 86400; + if (*tmp != '\0') + tmp++; + break; + case 'm': + case 'M': /* months! */ + interval *= 86400 * 30; + tmp++; + break; + case 'w': + case 'W': /* weeks */ + interval *= 86400 * 7; + tmp++; + break; + } + if (*tmp || interval > (365 * 86400)) { + bb_error_msg_and_die("bad interval - %s", optarg); + } + i_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'j': + if (!journal_size) + journal_size = -1; + open_flag = EXT2_FLAG_RW; + break; + case 'J': + parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg); + open_flag = EXT2_FLAG_RW; + break; + case 'l': + l_flag = 1; + break; + case 'L': + new_label = optarg; + L_flag = 1; + open_flag = EXT2_FLAG_RW | + EXT2_FLAG_JOURNAL_DEV_OK; + break; + case 'm': + reserved_ratio = xatou_range(optarg, 0, 50); + m_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'M': + new_last_mounted = optarg; + M_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'o': + if (mntopts_cmd) { + bb_error_msg_and_die("-o may only be specified once"); + } + mntopts_cmd = optarg; + open_flag = EXT2_FLAG_RW; + break; + + case 'O': + if (features_cmd) { + bb_error_msg_and_die("-O may only be specified once"); + } + features_cmd = optarg; + open_flag = EXT2_FLAG_RW; + break; + case 'r': + reserved_blocks = xatoul(optarg); + r_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 's': + s_flag = atoi(optarg); + open_flag = EXT2_FLAG_RW; + break; + case 'T': + T_flag = 1; + last_check_time = parse_time(optarg); + open_flag = EXT2_FLAG_RW; + break; + case 'u': + resuid = bb_strtoul(optarg, NULL, 10); + if (errno) + resuid = xuname2uid(optarg); + u_flag = 1; + open_flag = EXT2_FLAG_RW; + break; + case 'U': + new_UUID = optarg; + U_flag = 1; + open_flag = EXT2_FLAG_RW | + EXT2_FLAG_JOURNAL_DEV_OK; + break; + default: + bb_show_usage(); + } + if (optind < argc - 1 || optind == argc) + bb_show_usage(); + if (!open_flag && !l_flag) + bb_show_usage(); + io_options = strchr(argv[optind], '?'); + if (io_options) + *io_options++ = 0; + device_name = x_blkid_get_devname(argv[optind]); +} + +static void tune2fs_clean_up(void) +{ + if (ENABLE_FEATURE_CLEAN_UP && device_name) free(device_name); + if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device); +} + +int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tune2fs_main(int argc, char **argv) +{ + errcode_t retval; + ext2_filsys fs; + struct ext2_super_block *sb; + io_manager io_ptr; + + if (ENABLE_FEATURE_CLEAN_UP) + atexit(tune2fs_clean_up); + + if (ENABLE_E2LABEL && (applet_name[0] == 'e')) /* e2label */ + parse_e2label_options(argc, argv); + else + parse_tune2fs_options(argc, argv); /* tune2fs */ + + io_ptr = unix_io_manager; + retval = ext2fs_open2(device_name, io_options, open_flag, + 0, 0, io_ptr, &fs); + if (retval) + bb_error_msg_and_die("No valid superblock on %s", device_name); + sb = fs->super; + if (print_label) { + /* For e2label emulation */ + printf("%.*s\n", (int) sizeof(sb->s_volume_name), + sb->s_volume_name); + return 0; + } + retval = ext2fs_check_if_mounted(device_name, &mount_flags); + if (retval) + bb_error_msg_and_die("can't determine if %s is mounted", device_name); + /* Normally we only need to write out the superblock */ + fs->flags |= EXT2_FLAG_SUPER_ONLY; + + if (c_flag) { + sb->s_max_mnt_count = max_mount_count; + ext2fs_mark_super_dirty(fs); + printf("Setting maximal mount count to %d\n", max_mount_count); + } + if (C_flag) { + sb->s_mnt_count = mount_count; + ext2fs_mark_super_dirty(fs); + printf("Setting current mount count to %d\n", mount_count); + } + if (e_flag) { + sb->s_errors = errors; + ext2fs_mark_super_dirty(fs); + printf("Setting error behavior to %d\n", errors); + } + if (g_flag) { + sb->s_def_resgid = resgid; + ext2fs_mark_super_dirty(fs); + printf("Setting reserved blocks gid to %lu\n", resgid); + } + if (i_flag) { + sb->s_checkinterval = interval; + ext2fs_mark_super_dirty(fs); + printf("Setting interval between check %lu seconds\n", interval); + } + if (m_flag) { + sb->s_r_blocks_count = (sb->s_blocks_count / 100) + * reserved_ratio; + ext2fs_mark_super_dirty(fs); + printf("Setting reserved blocks percentage to %u (%u blocks)\n", + reserved_ratio, sb->s_r_blocks_count); + } + if (r_flag) { + if (reserved_blocks >= sb->s_blocks_count/2) + bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks); + sb->s_r_blocks_count = reserved_blocks; + ext2fs_mark_super_dirty(fs); + printf("Setting reserved blocks count to %lu\n", reserved_blocks); + } + if (s_flag == 1) { + if (sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) + bb_error_msg("\nThe filesystem already has sparse superblocks"); + else { + sb->s_feature_ro_compat |= + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + sb->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); + printf("\nSparse superblock flag set. %s", please_fsck); + } + } + if (s_flag == 0) { + if (!(sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) + bb_error_msg("\nThe filesystem already has sparse superblocks disabled"); + else { + sb->s_feature_ro_compat &= + ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + sb->s_state &= ~EXT2_VALID_FS; + fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; + ext2fs_mark_super_dirty(fs); + printf("\nSparse superblock flag cleared. %s", please_fsck); + } + } + if (T_flag) { + sb->s_lastcheck = last_check_time; + ext2fs_mark_super_dirty(fs); + printf("Setting time filesystem last checked to %s\n", + ctime(&last_check_time)); + } + if (u_flag) { + sb->s_def_resuid = resuid; + ext2fs_mark_super_dirty(fs); + printf("Setting reserved blocks uid to %lu\n", resuid); + } + if (L_flag) { + if (strlen(new_label) > sizeof(sb->s_volume_name)) + bb_error_msg("Warning: label too long, truncating"); + memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name)); + safe_strncpy(sb->s_volume_name, new_label, + sizeof(sb->s_volume_name)); + ext2fs_mark_super_dirty(fs); + } + if (M_flag) { + memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted)); + safe_strncpy(sb->s_last_mounted, new_last_mounted, + sizeof(sb->s_last_mounted)); + ext2fs_mark_super_dirty(fs); + } + if (mntopts_cmd) + update_mntopts(fs, mntopts_cmd); + if (features_cmd) + update_feature_set(fs, features_cmd); + if (journal_size || journal_device) + add_journal(fs); + + if (U_flag) { + if ((strcasecmp(new_UUID, "null") == 0) || + (strcasecmp(new_UUID, "clear") == 0)) { + uuid_clear(sb->s_uuid); + } else if (strcasecmp(new_UUID, "time") == 0) { + uuid_generate_time(sb->s_uuid); + } else if (strcasecmp(new_UUID, "random") == 0) { + uuid_generate(sb->s_uuid); + } else if (uuid_parse(new_UUID, sb->s_uuid)) { + bb_error_msg_and_die("Invalid UUID format"); + } + ext2fs_mark_super_dirty(fs); + } + + if (l_flag) + list_super (sb); + return (ext2fs_close (fs) ? 1 : 0); +} diff --git a/e2fsprogs/old_e2fsprogs/util.c b/e2fsprogs/old_e2fsprogs/util.c new file mode 100644 index 000000000..a61abc2c6 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/util.c @@ -0,0 +1,263 @@ +/* vi: set sw=4 ts=4: */ +/* + * util.c --- helper functions used by tune2fs and mke2fs + * + * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include +#include +#include +#include +#include + +#include "e2fsbb.h" +#include "e2p/e2p.h" +#include "ext2fs/ext2_fs.h" +#include "ext2fs/ext2fs.h" +#include "blkid/blkid.h" +#include "util.h" + +void proceed_question(void) +{ + fputs("Proceed anyway? (y,n) ", stdout); + if (bb_ask_confirmation() == 0) + exit(1); +} + +void check_plausibility(const char *device, int force) +{ + int val; + struct stat s; + val = stat(device, &s); + if (force) + return; + if (val == -1) + bb_perror_msg_and_die("can't stat '%s'", device); + if (!S_ISBLK(s.st_mode)) { + printf("%s is not a block special device.\n", device); + proceed_question(); + return; + } + +#ifdef HAVE_LINUX_MAJOR_H +#ifndef MAJOR +#define MAJOR(dev) ((dev)>>8) +#define MINOR(dev) ((dev) & 0xff) +#endif +#ifndef SCSI_BLK_MAJOR +#ifdef SCSI_DISK0_MAJOR +#ifdef SCSI_DISK8_MAJOR +#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \ + ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \ + ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR)) +#else +#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \ + ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR)) +#endif /* defined(SCSI_DISK8_MAJOR) */ +#define SCSI_BLK_MAJOR(M) (SCSI_DISK_MAJOR((M)) || (M) == SCSI_CDROM_MAJOR) +#else +#define SCSI_BLK_MAJOR(M) ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR) +#endif /* defined(SCSI_DISK0_MAJOR) */ +#endif /* defined(SCSI_BLK_MAJOR) */ + if (((MAJOR(s.st_rdev) == HD_MAJOR && + MINOR(s.st_rdev)%64 == 0) || + (SCSI_BLK_MAJOR(MAJOR(s.st_rdev)) && + MINOR(s.st_rdev)%16 == 0))) { + printf("%s is entire device, not just one partition!\n", device); + proceed_question(); + } +#endif +} + +void check_mount(const char *device, int force, const char *type) +{ + errcode_t retval; + int mount_flags; + + retval = ext2fs_check_if_mounted(device, &mount_flags); + if (retval) { + bb_error_msg("can't determine if %s is mounted", device); + return; + } + if (mount_flags & EXT2_MF_MOUNTED) { + bb_error_msg("%s is mounted !", device); +force_check: + if (force) + bb_error_msg("badblocks forced anyways"); + else + bb_error_msg_and_die("it's not safe to run badblocks!"); + } + + if (mount_flags & EXT2_MF_BUSY) { + bb_error_msg("%s is apparently in use by the system", device); + goto force_check; + } +} + +void parse_journal_opts(char **journal_device, int *journal_flags, + int *journal_size, const char *opts) +{ + char *buf, *token, *next, *p, *arg; + int journal_usage = 0; + buf = xstrdup(opts); + for (token = buf; token && *token; token = next) { + p = strchr(token, ','); + next = 0; + if (p) { + *p = 0; + next = p+1; + } + arg = strchr(token, '='); + if (arg) { + *arg = 0; + arg++; + } + if (strcmp(token, "device") == 0) { + *journal_device = blkid_get_devname(NULL, arg, NULL); + if (!journal_device) { + journal_usage++; + continue; + } + } else if (strcmp(token, "size") == 0) { + if (!arg) { + journal_usage++; + continue; + } + (*journal_size) = strtoul(arg, &p, 0); + if (*p) + journal_usage++; + } else if (strcmp(token, "v1_superblock") == 0) { + (*journal_flags) |= EXT2_MKJOURNAL_V1_SUPER; + continue; + } else + journal_usage++; + } + if (journal_usage) + bb_error_msg_and_die( + "\nBad journal options specified.\n\n" + "Journal options are separated by commas, " + "and may take an argument which\n" + "\tis set off by an equals ('=') sign.\n\n" + "Valid journal options are:\n" + "\tsize=\n" + "\tdevice=\n\n" + "The journal size must be between " + "1024 and 102400 filesystem blocks.\n\n"); +} + +/* + * Determine the number of journal blocks to use, either via + * user-specified # of megabytes, or via some intelligently selected + * defaults. + * + * Find a reasonable journal file size (in blocks) given the number of blocks + * in the filesystem. For very small filesystems, it is not reasonable to + * have a journal that fills more than half of the filesystem. + */ +int figure_journal_size(int size, ext2_filsys fs) +{ + blk_t j_blocks; + + if (fs->super->s_blocks_count < 2048) { + bb_error_msg("Filesystem too small for a journal"); + return 0; + } + + if (size >= 0) { + j_blocks = size * 1024 / (fs->blocksize / 1024); + if (j_blocks < 1024 || j_blocks > 102400) + bb_error_msg_and_die("\nThe requested journal " + "size is %d blocks;\n it must be " + "between 1024 and 102400 blocks; Aborting", + j_blocks); + if (j_blocks > fs->super->s_free_blocks_count) + bb_error_msg_and_die("Journal size too big for filesystem"); + return j_blocks; + } + + if (fs->super->s_blocks_count < 32768) + j_blocks = 1024; + else if (fs->super->s_blocks_count < 256*1024) + j_blocks = 4096; + else if (fs->super->s_blocks_count < 512*1024) + j_blocks = 8192; + else if (fs->super->s_blocks_count < 1024*1024) + j_blocks = 16384; + else + j_blocks = 32768; + + return j_blocks; +} + +void print_check_message(ext2_filsys fs) +{ + printf("This filesystem will be automatically " + "checked every %d mounts or\n" + "%g days, whichever comes first. " + "Use tune2fs -c or -i to override.\n", + fs->super->s_max_mnt_count, + (double)fs->super->s_checkinterval / (3600 * 24)); +} + +void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force) +{ + errcode_t retval; + ext2_filsys jfs; + io_manager io_ptr; + + check_plausibility(journal_device, force); + check_mount(journal_device, force, "journal"); + io_ptr = unix_io_manager; + retval = ext2fs_open(journal_device, EXT2_FLAG_RW| + EXT2_FLAG_JOURNAL_DEV_OK, 0, + fs->blocksize, io_ptr, &jfs); + if (retval) + bb_error_msg_and_die("can't journal device %s", journal_device); + if (!quiet) + printf("Adding journal to device %s: ", journal_device); + fflush(stdout); + retval = ext2fs_add_journal_device(fs, jfs); + if (retval) + bb_error_msg_and_die("\nFailed to add journal to device %s", journal_device); + if (!quiet) + puts("done"); + ext2fs_close(jfs); +} + +void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet) +{ + unsigned long journal_blocks; + errcode_t retval; + + journal_blocks = figure_journal_size(journal_size, fs); + if (!journal_blocks) { + fs->super->s_feature_compat &= + ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; + return; + } + if (!quiet) + printf("Creating journal (%ld blocks): ", journal_blocks); + fflush(stdout); + retval = ext2fs_add_journal_inode(fs, journal_blocks, + journal_flags); + if (retval) + bb_error_msg_and_die("can't create journal"); + if (!quiet) + puts("done"); +} + +char *e2fs_set_sbin_path(void) +{ + char *oldpath = getenv("PATH"); + /* Update our PATH to include /sbin */ +#define PATH_SET "/sbin" + if (oldpath) + oldpath = xasprintf("%s:%s", PATH_SET, oldpath); + else + oldpath = PATH_SET; + putenv(oldpath); + return oldpath; +} diff --git a/e2fsprogs/old_e2fsprogs/util.h b/e2fsprogs/old_e2fsprogs/util.h new file mode 100644 index 000000000..80d241718 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/util.h @@ -0,0 +1,22 @@ +/* vi: set sw=4 ts=4: */ +/* + * util.h --- header file defining prototypes for helper functions + * used by tune2fs and mke2fs + * + * Copyright 2000 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +extern void proceed_question(void); +extern void check_plausibility(const char *device, int force); +extern void parse_journal_opts(char **, int *, int *, const char *opts); +extern void check_mount(const char *device, int force, const char *type); +extern int figure_journal_size(int size, ext2_filsys fs); +extern void print_check_message(ext2_filsys fs); +extern void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force); +extern void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet); +extern char *e2fs_set_sbin_path(void); diff --git a/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src b/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src new file mode 100644 index 000000000..b8c687d30 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src @@ -0,0 +1,16 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +NEEDED-$(CONFIG_E2FSCK) = y +NEEDED-$(CONFIG_FSCK) = y +NEEDED-$(CONFIG_MKE2FS) = y +NEEDED-$(CONFIG_TUNE2FS) = y + +lib-y:= + +INSERT +lib-$(NEEDED-y) += compare.o gen_uuid.o pack.o parse.o unpack.o unparse.o \ + uuid_time.o diff --git a/e2fsprogs/old_e2fsprogs/uuid/compare.c b/e2fsprogs/old_e2fsprogs/uuid/compare.c new file mode 100644 index 000000000..348ea7c1f --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/compare.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * compare.c --- compare whether or not two UUID's are the same + * + * Returns 0 if the two UUID's are different, and 1 if they are the same. + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include "uuidP.h" +#include + +#define UUCMP(u1,u2) if (u1 != u2) return (u1 < u2) ? -1 : 1; + +int uuid_compare(const uuid_t uu1, const uuid_t uu2) +{ + struct uuid uuid1, uuid2; + + uuid_unpack(uu1, &uuid1); + uuid_unpack(uu2, &uuid2); + + UUCMP(uuid1.time_low, uuid2.time_low); + UUCMP(uuid1.time_mid, uuid2.time_mid); + UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version); + UUCMP(uuid1.clock_seq, uuid2.clock_seq); + return memcmp(uuid1.node, uuid2.node, 6); +} diff --git a/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c b/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c new file mode 100644 index 000000000..4310c17db --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c @@ -0,0 +1,304 @@ +/* vi: set sw=4 ts=4: */ +/* + * gen_uuid.c --- generate a DCE-compatible uuid + * + * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#include +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NET_IF_DL_H +#include +#endif + +#include "uuidP.h" + +#ifdef HAVE_SRANDOM +#define srand(x) srandom(x) +#define rand() random() +#endif + +static int get_random_fd(void) +{ + struct timeval tv; + static int fd = -2; + int i; + + if (fd == -2) { + gettimeofday(&tv, 0); + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec); + } + /* Crank the random number generator a few times */ + gettimeofday(&tv, 0); + for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) + rand(); + return fd; +} + + +/* + * Generate a series of random bytes. Use /dev/urandom if possible, + * and if not, use srandom/random. + */ +static void get_random_bytes(void *buf, int nbytes) +{ + int i, n = nbytes, fd = get_random_fd(); + int lose_counter = 0; + unsigned char *cp = (unsigned char *) buf; + + if (fd >= 0) { + while (n > 0) { + i = read(fd, cp, n); + if (i <= 0) { + if (lose_counter++ > 16) + break; + continue; + } + n -= i; + cp += i; + lose_counter = 0; + } + } + + /* + * We do this all the time, but this is the only source of + * randomness if /dev/random/urandom is out to lunch. + */ + for (cp = buf, i = 0; i < nbytes; i++) + *cp++ ^= (rand() >> 7) & 0xFF; +} + +/* + * Get the ethernet hardware address, if we can find it... + */ +static int get_node_id(unsigned char *node_id) +{ +#ifdef HAVE_NET_IF_H + int sd; + struct ifreq ifr, *ifrp; + struct ifconf ifc; + char buf[1024]; + int n, i; + unsigned char *a; +#ifdef HAVE_NET_IF_DL_H + struct sockaddr_dl *sdlp; +#endif + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#ifdef HAVE_SA_LEN +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN*/ + + sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sd < 0) { + return -1; + } + memset(buf, 0, sizeof(buf)); + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { + close(sd); + return -1; + } + n = ifc.ifc_len; + for (i = 0; i < n; i+= ifreq_size(*ifrp) ) { + ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); + strncpy_IFNAMSIZ(ifr.ifr_name, ifrp->ifr_name); +#ifdef SIOCGIFHWADDR + if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) + continue; + a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; +#else +#ifdef SIOCGENADDR + if (ioctl(sd, SIOCGENADDR, &ifr) < 0) + continue; + a = (unsigned char *) ifr.ifr_enaddr; +#else +#ifdef HAVE_NET_IF_DL_H + sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr; + if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6)) + continue; + a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen]; +#else + /* + * XXX we don't have a way of getting the hardware + * address + */ + close(sd); + return 0; +#endif /* HAVE_NET_IF_DL_H */ +#endif /* SIOCGENADDR */ +#endif /* SIOCGIFHWADDR */ + if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) + continue; + if (node_id) { + memcpy(node_id, a, 6); + close(sd); + return 1; + } + } + close(sd); +#endif + return 0; +} + +/* Assume that the gettimeofday() has microsecond granularity */ +#define MAX_ADJUSTMENT 10 + +static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq) +{ + static int adjustment = 0; + static struct timeval last = {0, 0}; + static uint16_t clock_seq; + struct timeval tv; + unsigned long long clock_reg; + +try_again: + gettimeofday(&tv, 0); + if ((last.tv_sec == 0) && (last.tv_usec == 0)) { + get_random_bytes(&clock_seq, sizeof(clock_seq)); + clock_seq &= 0x3FFF; + last = tv; + last.tv_sec--; + } + if ((tv.tv_sec < last.tv_sec) || + ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec < last.tv_usec))) { + clock_seq = (clock_seq+1) & 0x3FFF; + adjustment = 0; + last = tv; + } else if ((tv.tv_sec == last.tv_sec) && + (tv.tv_usec == last.tv_usec)) { + if (adjustment >= MAX_ADJUSTMENT) + goto try_again; + adjustment++; + } else { + adjustment = 0; + last = tv; + } + + clock_reg = tv.tv_usec*10 + adjustment; + clock_reg += ((unsigned long long) tv.tv_sec)*10000000; + clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000; + + *clock_high = clock_reg >> 32; + *clock_low = clock_reg; + *ret_clock_seq = clock_seq; + return 0; +} + +void uuid_generate_time(uuid_t out) +{ + static unsigned char node_id[6]; + static int has_init = 0; + struct uuid uu; + uint32_t clock_mid; + + if (!has_init) { + if (get_node_id(node_id) <= 0) { + get_random_bytes(node_id, 6); + /* + * Set multicast bit, to prevent conflicts + * with IEEE 802 addresses obtained from + * network cards + */ + node_id[0] |= 0x01; + } + has_init = 1; + } + get_clock(&clock_mid, &uu.time_low, &uu.clock_seq); + uu.clock_seq |= 0x8000; + uu.time_mid = (uint16_t) clock_mid; + uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000; + memcpy(uu.node, node_id, 6); + uuid_pack(&uu, out); +} + +void uuid_generate_random(uuid_t out) +{ + uuid_t buf; + struct uuid uu; + + get_random_bytes(buf, sizeof(buf)); + uuid_unpack(buf, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; + uuid_pack(&uu, out); +} + +/* + * This is the generic front-end to uuid_generate_random and + * uuid_generate_time. It uses uuid_generate_random only if + * /dev/urandom is available, since otherwise we won't have + * high-quality randomness. + */ +void uuid_generate(uuid_t out) +{ + if (get_random_fd() >= 0) + uuid_generate_random(out); + else + uuid_generate_time(out); +} diff --git a/e2fsprogs/old_e2fsprogs/uuid/pack.c b/e2fsprogs/old_e2fsprogs/uuid/pack.c new file mode 100644 index 000000000..217cfce5d --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/pack.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * Internal routine for packing UUID's + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_pack(const struct uuid *uu, uuid_t ptr) +{ + uint32_t tmp; + unsigned char *out = ptr; + + tmp = uu->time_low; + out[3] = (unsigned char) tmp; + tmp >>= 8; + out[2] = (unsigned char) tmp; + tmp >>= 8; + out[1] = (unsigned char) tmp; + tmp >>= 8; + out[0] = (unsigned char) tmp; + + tmp = uu->time_mid; + out[5] = (unsigned char) tmp; + tmp >>= 8; + out[4] = (unsigned char) tmp; + + tmp = uu->time_hi_and_version; + out[7] = (unsigned char) tmp; + tmp >>= 8; + out[6] = (unsigned char) tmp; + + tmp = uu->clock_seq; + out[9] = (unsigned char) tmp; + tmp >>= 8; + out[8] = (unsigned char) tmp; + + memcpy(out+10, uu->node, 6); +} diff --git a/e2fsprogs/old_e2fsprogs/uuid/parse.c b/e2fsprogs/old_e2fsprogs/uuid/parse.c new file mode 100644 index 000000000..9a3f9cb92 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/parse.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * parse.c --- UUID parsing + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include +#include +#include + +#include "uuidP.h" + +int uuid_parse(const char *in, uuid_t uu) +{ + struct uuid uuid; + int i; + const char *cp; + char buf[3]; + + if (strlen(in) != 36) + return -1; + for (i=0, cp = in; i <= 36; i++,cp++) { + if ((i == 8) || (i == 13) || (i == 18) || + (i == 23)) { + if (*cp == '-') + continue; + else + return -1; + } + if (i== 36) + if (*cp == 0) + continue; + if (!isxdigit(*cp)) + return -1; + } + uuid.time_low = strtoul(in, NULL, 16); + uuid.time_mid = strtoul(in+9, NULL, 16); + uuid.time_hi_and_version = strtoul(in+14, NULL, 16); + uuid.clock_seq = strtoul(in+19, NULL, 16); + cp = in+24; + buf[2] = 0; + for (i=0; i < 6; i++) { + buf[0] = *cp++; + buf[1] = *cp++; + uuid.node[i] = strtoul(buf, NULL, 16); + } + + uuid_pack(&uuid, uu); + return 0; +} diff --git a/e2fsprogs/old_e2fsprogs/uuid/unpack.c b/e2fsprogs/old_e2fsprogs/uuid/unpack.c new file mode 100644 index 000000000..95d3aab4a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/unpack.c @@ -0,0 +1,63 @@ +/* vi: set sw=4 ts=4: */ +/* + * Internal routine for unpacking UUID + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include "uuidP.h" + +void uuid_unpack(const uuid_t in, struct uuid *uu) +{ + const uint8_t *ptr = in; + uint32_t tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_low = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_mid = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->time_hi_and_version = tmp; + + tmp = *ptr++; + tmp = (tmp << 8) | *ptr++; + uu->clock_seq = tmp; + + memcpy(uu->node, ptr, 6); +} diff --git a/e2fsprogs/old_e2fsprogs/uuid/unparse.c b/e2fsprogs/old_e2fsprogs/uuid/unparse.c new file mode 100644 index 000000000..d2948fe6d --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/unparse.c @@ -0,0 +1,77 @@ +/* vi: set sw=4 ts=4: */ +/* + * unparse.c -- convert a UUID to string + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include + +#include "uuidP.h" + +static const char *fmt_lower = + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + +static const char *fmt_upper = + "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"; + +#ifdef UUID_UNPARSE_DEFAULT_UPPER +#define FMT_DEFAULT fmt_upper +#else +#define FMT_DEFAULT fmt_lower +#endif + +static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + sprintf(out, fmt, + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5]); +} + +void uuid_unparse_lower(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_lower); +} + +void uuid_unparse_upper(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, fmt_upper); +} + +void uuid_unparse(const uuid_t uu, char *out) +{ + uuid_unparse_x(uu, out, FMT_DEFAULT); +} diff --git a/e2fsprogs/old_e2fsprogs/uuid/uuid.h b/e2fsprogs/old_e2fsprogs/uuid/uuid.h new file mode 100644 index 000000000..7a9706449 --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/uuid.h @@ -0,0 +1,103 @@ +/* vi: set sw=4 ts=4: */ +/* + * Public include file for the UUID library + * + * Copyright (C) 1996, 1997, 1998 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ +#ifndef UUID_UUID_H +#define UUID_UUID_H 1 + +#include +#include + +typedef unsigned char uuid_t[16]; + +/* UUID Variant definitions */ +#define UUID_VARIANT_NCS 0 +#define UUID_VARIANT_DCE 1 +#define UUID_VARIANT_MICROSOFT 2 +#define UUID_VARIANT_OTHER 3 + +/* UUID Type definitions */ +#define UUID_TYPE_DCE_TIME 1 +#define UUID_TYPE_DCE_RANDOM 4 + +/* Allow UUID constants to be defined */ +#ifdef __GNUC__ +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name UNUSED_PARAM = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#else +#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ + static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clear.c */ +/*void uuid_clear(uuid_t uu);*/ +#define uuid_clear(uu) memset(uu, 0, sizeof(uu)) + +/* compare.c */ +int uuid_compare(const uuid_t uu1, const uuid_t uu2); + +/* copy.c */ +/*void uuid_copy(uuid_t dst, const uuid_t src);*/ +#define uuid_copy(dst,src) memcpy(dst, src, sizeof(dst)) + +/* gen_uuid.c */ +void uuid_generate(uuid_t out); +void uuid_generate_random(uuid_t out); +void uuid_generate_time(uuid_t out); + +/* isnull.c */ +/*int uuid_is_null(const uuid_t uu);*/ +#define uuid_is_null(uu) (!memcmp(uu, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(uu))) + +/* parse.c */ +int uuid_parse(const char *in, uuid_t uu); + +/* unparse.c */ +void uuid_unparse(const uuid_t uu, char *out); +void uuid_unparse_lower(const uuid_t uu, char *out); +void uuid_unparse_upper(const uuid_t uu, char *out); + +/* uuid_time.c */ +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); +int uuid_type(const uuid_t uu); +int uuid_variant(const uuid_t uu); + +#ifdef __cplusplus +} +#endif + +#endif /* _UUID_UUID_H */ diff --git a/e2fsprogs/old_e2fsprogs/uuid/uuidP.h b/e2fsprogs/old_e2fsprogs/uuid/uuidP.h new file mode 100644 index 000000000..87041ef0a --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/uuidP.h @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * uuid.h -- private header file for uuids + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include + +#include "uuid.h" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint16_t clock_seq; + uint8_t node[6]; +}; + + +/* + * prototypes + */ +void uuid_pack(const struct uuid *uu, uuid_t ptr); +void uuid_unpack(const uuid_t in, struct uuid *uu); diff --git a/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c b/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c new file mode 100644 index 000000000..b6f73e6dc --- /dev/null +++ b/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c @@ -0,0 +1,161 @@ +/* vi: set sw=4 ts=4: */ +/* + * uuid_time.c --- Interpret the time field from a uuid. This program + * violates the UUID abstraction barrier by reaching into the guts + * of a UUID and interpreting it. + * + * Copyright (C) 1998, 1999 Theodore Ts'o. + * + * %Begin-Header% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "uuidP.h" + +time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) +{ + struct uuid uuid; + uint32_t high; + struct timeval tv; + unsigned long long clock_reg; + + uuid_unpack(uu, &uuid); + + high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16); + clock_reg = uuid.time_low | ((unsigned long long) high << 32); + + clock_reg -= (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000; + tv.tv_sec = clock_reg / 10000000; + tv.tv_usec = (clock_reg % 10000000) / 10; + + if (ret_tv) + *ret_tv = tv; + + return tv.tv_sec; +} + +int uuid_type(const uuid_t uu) +{ + struct uuid uuid; + + uuid_unpack(uu, &uuid); + return ((uuid.time_hi_and_version >> 12) & 0xF); +} + +int uuid_variant(const uuid_t uu) +{ + struct uuid uuid; + int var; + + uuid_unpack(uu, &uuid); + var = uuid.clock_seq; + + if ((var & 0x8000) == 0) + return UUID_VARIANT_NCS; + if ((var & 0x4000) == 0) + return UUID_VARIANT_DCE; + if ((var & 0x2000) == 0) + return UUID_VARIANT_MICROSOFT; + return UUID_VARIANT_OTHER; +} + +#ifdef DEBUG +static const char *variant_string(int variant) +{ + switch (variant) { + case UUID_VARIANT_NCS: + return "NCS"; + case UUID_VARIANT_DCE: + return "DCE"; + case UUID_VARIANT_MICROSOFT: + return "Microsoft"; + default: + return "Other"; + } +} + + +int +main(int argc, char **argv) +{ + uuid_t buf; + time_t time_reg; + struct timeval tv; + int type, variant; + + if (argc != 2) { + fprintf(stderr, "Usage: %s uuid\n", argv[0]); + exit(1); + } + if (uuid_parse(argv[1], buf)) { + fprintf(stderr, "Invalid UUID: %s\n", argv[1]); + exit(1); + } + variant = uuid_variant(buf); + type = uuid_type(buf); + time_reg = uuid_time(buf, &tv); + + printf("UUID variant is %d (%s)\n", variant, variant_string(variant)); + if (variant != UUID_VARIANT_DCE) { + printf("Warning: This program only knows how to interpret " + "DCE UUIDs.\n\tThe rest of the output is likely " + "to be incorrect!!\n"); + } + printf("UUID type is %d", type); + switch (type) { + case 1: + printf(" (time based)\n"); + break; + case 2: + printf(" (DCE)\n"); + break; + case 3: + printf(" (name-based)\n"); + break; + case 4: + printf(" (random)\n"); + break; + default: + bb_putchar('\n'); + } + if (type != 1) { + printf("Warning: not a time-based UUID, so UUID time " + "decoding will likely not work!\n"); + } + printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec, + ctime(&time_reg)); + + return 0; +} +#endif diff --git a/e2fsprogs/tune2fs.c b/e2fsprogs/tune2fs.c new file mode 100644 index 000000000..020bdaa33 --- /dev/null +++ b/e2fsprogs/tune2fs.c @@ -0,0 +1,107 @@ +/* vi: set sw=4 ts=4: */ +/* + * tune2fs: utility to modify EXT2 filesystem + * + * Busybox'ed (2009) by Vladimir Dronnikov + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include +#include + +// storage helpers +char BUG_wrong_field_size(void); +#define STORE_LE(field, value) \ +do { \ + if (sizeof(field) == 4) \ + field = SWAP_LE32(value); \ + else if (sizeof(field) == 2) \ + field = SWAP_LE16(value); \ + else if (sizeof(field) == 1) \ + field = (value); \ + else \ + BUG_wrong_field_size(); \ +} while (0) + +#define FETCH_LE32(field) \ + (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size()) + +//usage:#define tune2fs_trivial_usage +//usage: "[-c MAX_MOUNT_COUNT] " +////usage: "[-e errors-behavior] [-g group] " +//usage: "[-i DAYS] " +////usage: "[-j] [-J journal-options] [-l] [-s sparse-flag] " +////usage: "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] " +////usage: "[-r reserved-blocks-count] [-u user] " +//usage: "[-C MOUNT_COUNT] " +//usage: "[-L LABEL] " +////usage: "[-M last-mounted-dir] [-O [^]feature[,...]] " +////usage: "[-T last-check-time] [-U UUID] " +//usage: "BLOCKDEV" +//usage: +//usage:#define tune2fs_full_usage "\n\n" +//usage: "Adjust filesystem options on ext[23] filesystems" + +enum { + OPT_L = 1 << 0, // label + OPT_c = 1 << 1, // max mount count + OPT_i = 1 << 2, // check interval + OPT_C = 1 << 3, // current mount count +}; + +int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tune2fs_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opts; + const char *label, *str_c, *str_i, *str_C; + struct ext2_super_block *sb; + int fd; + + opt_complementary = "=1"; + opts = getopt32(argv, "L:c:i:C:", &label, &str_c, &str_i, &str_C); + if (!opts) + bb_show_usage(); + argv += optind; // argv[0] -- device + + // read superblock + fd = xopen(argv[0], O_RDWR); + xlseek(fd, 1024, SEEK_SET); + sb = xzalloc(1024); + xread(fd, sb, 1024); + + // mangle superblock + //STORE_LE(sb->s_wtime, time(NULL)); - why bother? + + if (opts & OPT_C) { + int n = xatoi_range(str_C, 1, 0xfffe); + STORE_LE(sb->s_mnt_count, (unsigned)n); + } + + // set the label + if (opts & OPT_L) + safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name)); + + if (opts & OPT_c) { + int n = xatoi_range(str_c, -1, 0xfffe); + if (n == 0) + n = -1; + STORE_LE(sb->s_max_mnt_count, (unsigned)n); + } + + if (opts & OPT_i) { + unsigned n = xatou_range(str_i, 0, (unsigned)0xffffffff / (24*60*60)) * 24*60*60; + STORE_LE(sb->s_checkinterval, n); + } + + // write superblock + xlseek(fd, 1024, SEEK_SET); + xwrite(fd, sb, 1024); + + if (ENABLE_FEATURE_CLEAN_UP) { + free(sb); + } + + xclose(fd); + return EXIT_SUCCESS; +} diff --git a/editors/Config.src b/editors/Config.src new file mode 100644 index 000000000..af1e1de5e --- /dev/null +++ b/editors/Config.src @@ -0,0 +1,78 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Editors" + +INSERT + +config AWK + bool "awk" + default y + help + Awk is used as a pattern scanning and processing language. This is + the BusyBox implementation of that programming language. + +config FEATURE_AWK_LIBM + bool "Enable math functions (requires libm)" + default y + depends on AWK + help + Enable math functions of the Awk programming language. + NOTE: This will require libm to be present for linking. + +config CMP + bool "cmp" + default y + help + cmp is used to compare two files and returns the result + to standard output. + +config DIFF + bool "diff" + default y + help + diff compares two files or directories and outputs the + differences between them in a form that can be given to + the patch command. + +config FEATURE_DIFF_LONG_OPTIONS + bool "Enable long options" + default y + depends on DIFF && LONG_OPTS + help + Enable use of long options. + +config FEATURE_DIFF_DIR + bool "Enable directory support" + default y + depends on DIFF + help + This option enables support for directory and subdirectory + comparison. + +config ED + bool "ed" + default y + help + The original 1970's Unix text editor, from the days of teletypes. + Small, simple, evil. Part of SUSv3. If you're not already using + this, you don't need it. + +config SED + bool "sed" + default y + help + sed is used to perform text transformations on a file + or input from a pipeline. + +config FEATURE_ALLOW_EXEC + bool "Allow vi and awk to execute shell commands" + default y + depends on VI || AWK + help + Enables vi and awk features which allows user to execute + shell commands (using system() C call). + +endmenu diff --git a/editors/Kbuild.src b/editors/Kbuild.src new file mode 100644 index 000000000..8888cba12 --- /dev/null +++ b/editors/Kbuild.src @@ -0,0 +1,14 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT +lib-$(CONFIG_AWK) += awk.o +lib-$(CONFIG_CMP) += cmp.o +lib-$(CONFIG_DIFF) += diff.o +lib-$(CONFIG_ED) += ed.o +lib-$(CONFIG_SED) += sed.o diff --git a/editors/awk.c b/editors/awk.c new file mode 100644 index 000000000..71abca215 --- /dev/null +++ b/editors/awk.c @@ -0,0 +1,3192 @@ +/* vi: set sw=4 ts=4: */ +/* + * awk implementation for busybox + * + * Copyright (C) 2002 by Dmitry Zakharov + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//usage:#define awk_trivial_usage +//usage: "[OPTIONS] [AWK_PROGRAM] [FILE]..." +//usage:#define awk_full_usage "\n\n" +//usage: " -v VAR=VAL Set variable" +//usage: "\n -F SEP Use SEP as field separator" +//usage: "\n -f FILE Read program from FILE" + +#include "libbb.h" +#include "xregex.h" +#include + +/* This is a NOEXEC applet. Be very careful! */ + + +/* If you comment out one of these below, it will be #defined later + * to perform debug printfs to stderr: */ +#define debug_printf_walker(...) do {} while (0) +#define debug_printf_eval(...) do {} while (0) +#define debug_printf_parse(...) do {} while (0) + +#ifndef debug_printf_walker +# define debug_printf_walker(...) (fprintf(stderr, __VA_ARGS__)) +#endif +#ifndef debug_printf_eval +# define debug_printf_eval(...) (fprintf(stderr, __VA_ARGS__)) +#endif +#ifndef debug_printf_parse +# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__)) +#endif + + + +#define MAXVARFMT 240 +#define MINNVBLOCK 64 + +/* variable flags */ +#define VF_NUMBER 0x0001 /* 1 = primary type is number */ +#define VF_ARRAY 0x0002 /* 1 = it's an array */ + +#define VF_CACHED 0x0100 /* 1 = num/str value has cached str/num eq */ +#define VF_USER 0x0200 /* 1 = user input (may be numeric string) */ +#define VF_SPECIAL 0x0400 /* 1 = requires extra handling when changed */ +#define VF_WALK 0x0800 /* 1 = variable has alloc'd x.walker list */ +#define VF_FSTR 0x1000 /* 1 = var::string points to fstring buffer */ +#define VF_CHILD 0x2000 /* 1 = function arg; x.parent points to source */ +#define VF_DIRTY 0x4000 /* 1 = variable was set explicitly */ + +/* these flags are static, don't change them when value is changed */ +#define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY) + +typedef struct walker_list { + char *end; + char *cur; + struct walker_list *prev; + char wbuf[1]; +} walker_list; + +/* Variable */ +typedef struct var_s { + unsigned type; /* flags */ + double number; + char *string; + union { + int aidx; /* func arg idx (for compilation stage) */ + struct xhash_s *array; /* array ptr */ + struct var_s *parent; /* for func args, ptr to actual parameter */ + walker_list *walker; /* list of array elements (for..in) */ + } x; +} var; + +/* Node chain (pattern-action chain, BEGIN, END, function bodies) */ +typedef struct chain_s { + struct node_s *first; + struct node_s *last; + const char *programname; +} chain; + +/* Function */ +typedef struct func_s { + unsigned nargs; + struct chain_s body; +} func; + +/* I/O stream */ +typedef struct rstream_s { + FILE *F; + char *buffer; + int adv; + int size; + int pos; + smallint is_pipe; +} rstream; + +typedef struct hash_item_s { + union { + struct var_s v; /* variable/array hash */ + struct rstream_s rs; /* redirect streams hash */ + struct func_s f; /* functions hash */ + } data; + struct hash_item_s *next; /* next in chain */ + char name[1]; /* really it's longer */ +} hash_item; + +typedef struct xhash_s { + unsigned nel; /* num of elements */ + unsigned csize; /* current hash size */ + unsigned nprime; /* next hash size in PRIMES[] */ + unsigned glen; /* summary length of item names */ + struct hash_item_s **items; +} xhash; + +/* Tree node */ +typedef struct node_s { + uint32_t info; + unsigned lineno; + union { + struct node_s *n; + var *v; + int aidx; + char *new_progname; + regex_t *re; + } l; + union { + struct node_s *n; + regex_t *ire; + func *f; + } r; + union { + struct node_s *n; + } a; +} node; + +/* Block of temporary variables */ +typedef struct nvblock_s { + int size; + var *pos; + struct nvblock_s *prev; + struct nvblock_s *next; + var nv[]; +} nvblock; + +typedef struct tsplitter_s { + node n; + regex_t re[2]; +} tsplitter; + +/* simple token classes */ +/* Order and hex values are very important!!! See next_token() */ +#define TC_SEQSTART 1 /* ( */ +#define TC_SEQTERM (1 << 1) /* ) */ +#define TC_REGEXP (1 << 2) /* /.../ */ +#define TC_OUTRDR (1 << 3) /* | > >> */ +#define TC_UOPPOST (1 << 4) /* unary postfix operator */ +#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */ +#define TC_BINOPX (1 << 6) /* two-opnd operator */ +#define TC_IN (1 << 7) +#define TC_COMMA (1 << 8) +#define TC_PIPE (1 << 9) /* input redirection pipe */ +#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */ +#define TC_ARRTERM (1 << 11) /* ] */ +#define TC_GRPSTART (1 << 12) /* { */ +#define TC_GRPTERM (1 << 13) /* } */ +#define TC_SEMICOL (1 << 14) +#define TC_NEWLINE (1 << 15) +#define TC_STATX (1 << 16) /* ctl statement (for, next...) */ +#define TC_WHILE (1 << 17) +#define TC_ELSE (1 << 18) +#define TC_BUILTIN (1 << 19) +#define TC_GETLINE (1 << 20) +#define TC_FUNCDECL (1 << 21) /* `function' `func' */ +#define TC_BEGIN (1 << 22) +#define TC_END (1 << 23) +#define TC_EOF (1 << 24) +#define TC_VARIABLE (1 << 25) +#define TC_ARRAY (1 << 26) +#define TC_FUNCTION (1 << 27) +#define TC_STRING (1 << 28) +#define TC_NUMBER (1 << 29) + +#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2) + +/* combined token classes */ +#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN) +#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST) +#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \ + | TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER) + +#define TC_STATEMNT (TC_STATX | TC_WHILE) +#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE) + +/* word tokens, cannot mean something else if not expected */ +#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN \ + | TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END) + +/* discard newlines after these */ +#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM \ + | TC_BINOP | TC_OPTERM) + +/* what can expression begin with */ +#define TC_OPSEQ (TC_OPERAND | TC_UOPPRE | TC_REGEXP) +/* what can group begin with */ +#define TC_GRPSEQ (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART) + +/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */ +/* operator is inserted between them */ +#define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM \ + | TC_STRING | TC_NUMBER | TC_UOPPOST) +#define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE) + +#define OF_RES1 0x010000 +#define OF_RES2 0x020000 +#define OF_STR1 0x040000 +#define OF_STR2 0x080000 +#define OF_NUM1 0x100000 +#define OF_CHECKED 0x200000 + +/* combined operator flags */ +#define xx 0 +#define xV OF_RES2 +#define xS (OF_RES2 | OF_STR2) +#define Vx OF_RES1 +#define VV (OF_RES1 | OF_RES2) +#define Nx (OF_RES1 | OF_NUM1) +#define NV (OF_RES1 | OF_NUM1 | OF_RES2) +#define Sx (OF_RES1 | OF_STR1) +#define SV (OF_RES1 | OF_STR1 | OF_RES2) +#define SS (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2) + +#define OPCLSMASK 0xFF00 +#define OPNMASK 0x007F + +/* operator priority is a highest byte (even: r->l, odd: l->r grouping) + * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1, + * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string + */ +#undef P +#undef PRIMASK +#undef PRIMASK2 +#define P(x) (x << 24) +#define PRIMASK 0x7F000000 +#define PRIMASK2 0x7E000000 + +/* Operation classes */ + +#define SHIFT_TIL_THIS 0x0600 +#define RECUR_FROM_THIS 0x1000 + +enum { + OC_DELETE = 0x0100, OC_EXEC = 0x0200, OC_NEWSOURCE = 0x0300, + OC_PRINT = 0x0400, OC_PRINTF = 0x0500, OC_WALKINIT = 0x0600, + + OC_BR = 0x0700, OC_BREAK = 0x0800, OC_CONTINUE = 0x0900, + OC_EXIT = 0x0a00, OC_NEXT = 0x0b00, OC_NEXTFILE = 0x0c00, + OC_TEST = 0x0d00, OC_WALKNEXT = 0x0e00, + + OC_BINARY = 0x1000, OC_BUILTIN = 0x1100, OC_COLON = 0x1200, + OC_COMMA = 0x1300, OC_COMPARE = 0x1400, OC_CONCAT = 0x1500, + OC_FBLTIN = 0x1600, OC_FIELD = 0x1700, OC_FNARG = 0x1800, + OC_FUNC = 0x1900, OC_GETLINE = 0x1a00, OC_IN = 0x1b00, + OC_LAND = 0x1c00, OC_LOR = 0x1d00, OC_MATCH = 0x1e00, + OC_MOVE = 0x1f00, OC_PGETLINE = 0x2000, OC_REGEXP = 0x2100, + OC_REPLACE = 0x2200, OC_RETURN = 0x2300, OC_SPRINTF = 0x2400, + OC_TERNARY = 0x2500, OC_UNARY = 0x2600, OC_VAR = 0x2700, + OC_DONE = 0x2800, + + ST_IF = 0x3000, ST_DO = 0x3100, ST_FOR = 0x3200, + ST_WHILE = 0x3300 +}; + +/* simple builtins */ +enum { + F_in, F_rn, F_co, F_ex, F_lg, F_si, F_sq, F_sr, + F_ti, F_le, F_sy, F_ff, F_cl +}; + +/* builtins */ +enum { + B_a2, B_ix, B_ma, B_sp, B_ss, B_ti, B_mt, B_lo, B_up, + B_ge, B_gs, B_su, + B_an, B_co, B_ls, B_or, B_rs, B_xo, +}; + +/* tokens and their corresponding info values */ + +#define NTC "\377" /* switch to next token class (tc<<1) */ +#define NTCC '\377' + +#define OC_B OC_BUILTIN + +static const char tokenlist[] ALIGN1 = + "\1(" NTC + "\1)" NTC + "\1/" NTC /* REGEXP */ + "\2>>" "\1>" "\1|" NTC /* OUTRDR */ + "\2++" "\2--" NTC /* UOPPOST */ + "\2++" "\2--" "\1$" NTC /* UOPPRE1 */ + "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */ + "\2*=" "\2/=" "\2%=" "\2^=" + "\1+" "\1-" "\3**=" "\2**" + "\1/" "\1%" "\1^" "\1*" + "\2!=" "\2>=" "\2<=" "\1>" + "\1<" "\2!~" "\1~" "\2&&" + "\2||" "\1?" "\1:" NTC + "\2in" NTC + "\1," NTC + "\1|" NTC + "\1+" "\1-" "\1!" NTC /* UOPPRE2 */ + "\1]" NTC + "\1{" NTC + "\1}" NTC + "\1;" NTC + "\1\n" NTC + "\2if" "\2do" "\3for" "\5break" /* STATX */ + "\10continue" "\6delete" "\5print" + "\6printf" "\4next" "\10nextfile" + "\6return" "\4exit" NTC + "\5while" NTC + "\4else" NTC + + "\3and" "\5compl" "\6lshift" "\2or" + "\6rshift" "\3xor" + "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */ + "\3cos" "\3exp" "\3int" "\3log" + "\4rand" "\3sin" "\4sqrt" "\5srand" + "\6gensub" "\4gsub" "\5index" "\6length" + "\5match" "\5split" "\7sprintf" "\3sub" + "\6substr" "\7systime" "\10strftime" "\6mktime" + "\7tolower" "\7toupper" NTC + "\7getline" NTC + "\4func" "\10function" NTC + "\5BEGIN" NTC + "\3END" + /* compiler adds trailing "\0" */ + ; + +static const uint32_t tokeninfo[] = { + 0, + 0, + OC_REGEXP, + xS|'a', xS|'w', xS|'|', + OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', + OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', OC_FIELD|xV|P(5), + OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', + OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', + OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', + OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', + OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, + OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), + OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':', + OC_IN|SV|P(49), /* in */ + OC_COMMA|SS|P(80), + OC_PGETLINE|SV|P(37), + OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-', OC_UNARY|xV|P(19)|'!', + 0, /* ] */ + 0, + 0, + 0, + 0, /* \n */ + ST_IF, ST_DO, ST_FOR, OC_BREAK, + OC_CONTINUE, OC_DELETE|Vx, OC_PRINT, + OC_PRINTF, OC_NEXT, OC_NEXTFILE, + OC_RETURN|Vx, OC_EXIT|Nx, + ST_WHILE, + 0, /* else */ + + OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83), + OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83), + OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83), + OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg, + OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr, + OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le, + OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6), + OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b), + OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49), + OC_GETLINE|SV|P(0), + 0, 0, + 0, + 0 /* END */ +}; + +/* internal variable names and their initial values */ +/* asterisk marks SPECIAL vars; $ is just no-named Field0 */ +enum { + CONVFMT, OFMT, FS, OFS, + ORS, RS, RT, FILENAME, + SUBSEP, F0, ARGIND, ARGC, + ARGV, ERRNO, FNR, NR, + NF, IGNORECASE, ENVIRON, NUM_INTERNAL_VARS +}; + +static const char vNames[] ALIGN1 = + "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0" + "ORS\0" "RS\0*" "RT\0" "FILENAME\0" + "SUBSEP\0" "$\0*" "ARGIND\0" "ARGC\0" + "ARGV\0" "ERRNO\0" "FNR\0" "NR\0" + "NF\0*" "IGNORECASE\0*" "ENVIRON\0" "\0"; + +static const char vValues[] ALIGN1 = + "%.6g\0" "%.6g\0" " \0" " \0" + "\n\0" "\n\0" "\0" "\0" + "\034\0" "\0" "\377"; + +/* hash size may grow to these values */ +#define FIRST_PRIME 61 +static const uint16_t PRIMES[] ALIGN2 = { 251, 1021, 4093, 16381, 65521 }; + + +/* Globals. Split in two parts so that first one is addressed + * with (mostly short) negative offsets. + * NB: it's unsafe to put members of type "double" + * into globals2 (gcc may fail to align them). + */ +struct globals { + double t_double; + chain beginseq, mainseq, endseq; + chain *seq; + node *break_ptr, *continue_ptr; + rstream *iF; + xhash *vhash, *ahash, *fdhash, *fnhash; + const char *g_progname; + int g_lineno; + int nfields; + int maxfields; /* used in fsrealloc() only */ + var *Fields; + nvblock *g_cb; + char *g_pos; + char *g_buf; + smallint icase; + smallint exiting; + smallint nextrec; + smallint nextfile; + smallint is_f0_split; + smallint t_rollback; +}; +struct globals2 { + uint32_t t_info; /* often used */ + uint32_t t_tclass; + char *t_string; + int t_lineno; + + var *intvar[NUM_INTERNAL_VARS]; /* often used */ + + /* former statics from various functions */ + char *split_f0__fstrings; + + uint32_t next_token__save_tclass; + uint32_t next_token__save_info; + uint32_t next_token__ltclass; + smallint next_token__concat_inserted; + + smallint next_input_file__files_happen; + rstream next_input_file__rsm; + + var *evaluate__fnargs; + unsigned evaluate__seed; + regex_t evaluate__sreg; + + var ptest__v; + + tsplitter exec_builtin__tspl; + + /* biggest and least used members go last */ + tsplitter fsplitter, rsplitter; +}; +#define G1 (ptr_to_globals[-1]) +#define G (*(struct globals2 *)ptr_to_globals) +/* For debug. nm --size-sort awk.o | grep -vi ' [tr] ' */ +/*char G1size[sizeof(G1)]; - 0x74 */ +/*char Gsize[sizeof(G)]; - 0x1c4 */ +/* Trying to keep most of members accessible with short offsets: */ +/*char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */ +#define t_double (G1.t_double ) +#define beginseq (G1.beginseq ) +#define mainseq (G1.mainseq ) +#define endseq (G1.endseq ) +#define seq (G1.seq ) +#define break_ptr (G1.break_ptr ) +#define continue_ptr (G1.continue_ptr) +#define iF (G1.iF ) +#define vhash (G1.vhash ) +#define ahash (G1.ahash ) +#define fdhash (G1.fdhash ) +#define fnhash (G1.fnhash ) +#define g_progname (G1.g_progname ) +#define g_lineno (G1.g_lineno ) +#define nfields (G1.nfields ) +#define maxfields (G1.maxfields ) +#define Fields (G1.Fields ) +#define g_cb (G1.g_cb ) +#define g_pos (G1.g_pos ) +#define g_buf (G1.g_buf ) +#define icase (G1.icase ) +#define exiting (G1.exiting ) +#define nextrec (G1.nextrec ) +#define nextfile (G1.nextfile ) +#define is_f0_split (G1.is_f0_split ) +#define t_rollback (G1.t_rollback ) +#define t_info (G.t_info ) +#define t_tclass (G.t_tclass ) +#define t_string (G.t_string ) +#define t_lineno (G.t_lineno ) +#define intvar (G.intvar ) +#define fsplitter (G.fsplitter ) +#define rsplitter (G.rsplitter ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS((char*)xzalloc(sizeof(G1)+sizeof(G)) + sizeof(G1)); \ + G.next_token__ltclass = TC_OPTERM; \ + G.evaluate__seed = 1; \ +} while (0) + + +/* function prototypes */ +static void handle_special(var *); +static node *parse_expr(uint32_t); +static void chain_group(void); +static var *evaluate(node *, var *); +static rstream *next_input_file(void); +static int fmt_num(char *, int, const char *, double, int); +static int awk_exit(int) NORETURN; + +/* ---- error handling ---- */ + +static const char EMSG_INTERNAL_ERROR[] ALIGN1 = "Internal error"; +static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string"; +static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token"; +static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero"; +static const char EMSG_INV_FMT[] ALIGN1 = "Invalid format specifier"; +static const char EMSG_TOO_FEW_ARGS[] ALIGN1 = "Too few arguments for builtin"; +static const char EMSG_NOT_ARRAY[] ALIGN1 = "Not an array"; +static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error"; +static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function"; +static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in"; + +static void zero_out_var(var *vp) +{ + memset(vp, 0, sizeof(*vp)); +} + +static void syntax_error(const char *message) NORETURN; +static void syntax_error(const char *message) +{ + bb_error_msg_and_die("%s:%i: %s", g_progname, g_lineno, message); +} + +/* ---- hash stuff ---- */ + +static unsigned hashidx(const char *name) +{ + unsigned idx = 0; + + while (*name) + idx = *name++ + (idx << 6) - idx; + return idx; +} + +/* create new hash */ +static xhash *hash_init(void) +{ + xhash *newhash; + + newhash = xzalloc(sizeof(*newhash)); + newhash->csize = FIRST_PRIME; + newhash->items = xzalloc(FIRST_PRIME * sizeof(newhash->items[0])); + + return newhash; +} + +/* find item in hash, return ptr to data, NULL if not found */ +static void *hash_search(xhash *hash, const char *name) +{ + hash_item *hi; + + hi = hash->items[hashidx(name) % hash->csize]; + while (hi) { + if (strcmp(hi->name, name) == 0) + return &hi->data; + hi = hi->next; + } + return NULL; +} + +/* grow hash if it becomes too big */ +static void hash_rebuild(xhash *hash) +{ + unsigned newsize, i, idx; + hash_item **newitems, *hi, *thi; + + if (hash->nprime == ARRAY_SIZE(PRIMES)) + return; + + newsize = PRIMES[hash->nprime++]; + newitems = xzalloc(newsize * sizeof(newitems[0])); + + for (i = 0; i < hash->csize; i++) { + hi = hash->items[i]; + while (hi) { + thi = hi; + hi = thi->next; + idx = hashidx(thi->name) % newsize; + thi->next = newitems[idx]; + newitems[idx] = thi; + } + } + + free(hash->items); + hash->csize = newsize; + hash->items = newitems; +} + +/* find item in hash, add it if necessary. Return ptr to data */ +static void *hash_find(xhash *hash, const char *name) +{ + hash_item *hi; + unsigned idx; + int l; + + hi = hash_search(hash, name); + if (!hi) { + if (++hash->nel / hash->csize > 10) + hash_rebuild(hash); + + l = strlen(name) + 1; + hi = xzalloc(sizeof(*hi) + l); + strcpy(hi->name, name); + + idx = hashidx(name) % hash->csize; + hi->next = hash->items[idx]; + hash->items[idx] = hi; + hash->glen += l; + } + return &hi->data; +} + +#define findvar(hash, name) ((var*) hash_find((hash), (name))) +#define newvar(name) ((var*) hash_find(vhash, (name))) +#define newfile(name) ((rstream*)hash_find(fdhash, (name))) +#define newfunc(name) ((func*) hash_find(fnhash, (name))) + +static void hash_remove(xhash *hash, const char *name) +{ + hash_item *hi, **phi; + + phi = &hash->items[hashidx(name) % hash->csize]; + while (*phi) { + hi = *phi; + if (strcmp(hi->name, name) == 0) { + hash->glen -= (strlen(name) + 1); + hash->nel--; + *phi = hi->next; + free(hi); + break; + } + phi = &hi->next; + } +} + +/* ------ some useful functions ------ */ + +static char *skip_spaces(char *p) +{ + while (1) { + if (*p == '\\' && p[1] == '\n') { + p++; + t_lineno++; + } else if (*p != ' ' && *p != '\t') { + break; + } + p++; + } + return p; +} + +/* returns old *s, advances *s past word and terminating NUL */ +static char *nextword(char **s) +{ + char *p = *s; + while (*(*s)++ != '\0') + continue; + return p; +} + +static char nextchar(char **s) +{ + char c, *pps; + + c = *(*s)++; + pps = *s; + if (c == '\\') + c = bb_process_escape_sequence((const char**)s); + if (c == '\\' && *s == pps) { /* unrecognized \z? */ + c = *(*s); /* yes, fetch z */ + if (c) + (*s)++; /* advance unless z = NUL */ + } + return c; +} + +static ALWAYS_INLINE int isalnum_(int c) +{ + return (isalnum(c) || c == '_'); +} + +static double my_strtod(char **pp) +{ + char *cp = *pp; + if (ENABLE_DESKTOP && cp[0] == '0') { + /* Might be hex or octal integer: 0x123abc or 07777 */ + char c = (cp[1] | 0x20); + if (c == 'x' || isdigit(cp[1])) { + unsigned long long ull = strtoull(cp, pp, 0); + if (c == 'x') + return ull; + c = **pp; + if (!isdigit(c) && c != '.') + return ull; + /* else: it may be a floating number. Examples: + * 009.123 (*pp points to '9') + * 000.123 (*pp points to '.') + * fall through to strtod. + */ + } + } + return strtod(cp, pp); +} + +/* -------- working with variables (set/get/copy/etc) -------- */ + +static xhash *iamarray(var *v) +{ + var *a = v; + + while (a->type & VF_CHILD) + a = a->x.parent; + + if (!(a->type & VF_ARRAY)) { + a->type |= VF_ARRAY; + a->x.array = hash_init(); + } + return a->x.array; +} + +static void clear_array(xhash *array) +{ + unsigned i; + hash_item *hi, *thi; + + for (i = 0; i < array->csize; i++) { + hi = array->items[i]; + while (hi) { + thi = hi; + hi = hi->next; + free(thi->data.v.string); + free(thi); + } + array->items[i] = NULL; + } + array->glen = array->nel = 0; +} + +/* clear a variable */ +static var *clrvar(var *v) +{ + if (!(v->type & VF_FSTR)) + free(v->string); + + v->type &= VF_DONTTOUCH; + v->type |= VF_DIRTY; + v->string = NULL; + return v; +} + +/* assign string value to variable */ +static var *setvar_p(var *v, char *value) +{ + clrvar(v); + v->string = value; + handle_special(v); + return v; +} + +/* same as setvar_p but make a copy of string */ +static var *setvar_s(var *v, const char *value) +{ + return setvar_p(v, (value && *value) ? xstrdup(value) : NULL); +} + +/* same as setvar_s but sets USER flag */ +static var *setvar_u(var *v, const char *value) +{ + v = setvar_s(v, value); + v->type |= VF_USER; + return v; +} + +/* set array element to user string */ +static void setari_u(var *a, int idx, const char *s) +{ + var *v; + + v = findvar(iamarray(a), itoa(idx)); + setvar_u(v, s); +} + +/* assign numeric value to variable */ +static var *setvar_i(var *v, double value) +{ + clrvar(v); + v->type |= VF_NUMBER; + v->number = value; + handle_special(v); + return v; +} + +static const char *getvar_s(var *v) +{ + /* if v is numeric and has no cached string, convert it to string */ + if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { + fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE); + v->string = xstrdup(g_buf); + v->type |= VF_CACHED; + } + return (v->string == NULL) ? "" : v->string; +} + +static double getvar_i(var *v) +{ + char *s; + + if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) { + v->number = 0; + s = v->string; + if (s && *s) { + debug_printf_eval("getvar_i: '%s'->", s); + v->number = my_strtod(&s); + debug_printf_eval("%f (s:'%s')\n", v->number, s); + if (v->type & VF_USER) { + s = skip_spaces(s); + if (*s != '\0') + v->type &= ~VF_USER; + } + } else { + debug_printf_eval("getvar_i: '%s'->zero\n", s); + v->type &= ~VF_USER; + } + v->type |= VF_CACHED; + } + debug_printf_eval("getvar_i: %f\n", v->number); + return v->number; +} + +/* Used for operands of bitwise ops */ +static unsigned long getvar_i_int(var *v) +{ + double d = getvar_i(v); + + /* Casting doubles to longs is undefined for values outside + * of target type range. Try to widen it as much as possible */ + if (d >= 0) + return (unsigned long)d; + /* Why? Think about d == -4294967295.0 (assuming 32bit longs) */ + return - (long) (unsigned long) (-d); +} + +static var *copyvar(var *dest, const var *src) +{ + if (dest != src) { + clrvar(dest); + dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR)); + debug_printf_eval("copyvar: number:%f string:'%s'\n", src->number, src->string); + dest->number = src->number; + if (src->string) + dest->string = xstrdup(src->string); + } + handle_special(dest); + return dest; +} + +static var *incvar(var *v) +{ + return setvar_i(v, getvar_i(v) + 1.0); +} + +/* return true if v is number or numeric string */ +static int is_numeric(var *v) +{ + getvar_i(v); + return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY)); +} + +/* return 1 when value of v corresponds to true, 0 otherwise */ +static int istrue(var *v) +{ + if (is_numeric(v)) + return (v->number != 0); + return (v->string && v->string[0]); +} + +/* temporary variables allocator. Last allocated should be first freed */ +static var *nvalloc(int n) +{ + nvblock *pb = NULL; + var *v, *r; + int size; + + while (g_cb) { + pb = g_cb; + if ((g_cb->pos - g_cb->nv) + n <= g_cb->size) + break; + g_cb = g_cb->next; + } + + if (!g_cb) { + size = (n <= MINNVBLOCK) ? MINNVBLOCK : n; + g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var)); + g_cb->size = size; + g_cb->pos = g_cb->nv; + g_cb->prev = pb; + /*g_cb->next = NULL; - xzalloc did it */ + if (pb) + pb->next = g_cb; + } + + v = r = g_cb->pos; + g_cb->pos += n; + + while (v < g_cb->pos) { + v->type = 0; + v->string = NULL; + v++; + } + + return r; +} + +static void nvfree(var *v) +{ + var *p; + + if (v < g_cb->nv || v >= g_cb->pos) + syntax_error(EMSG_INTERNAL_ERROR); + + for (p = v; p < g_cb->pos; p++) { + if ((p->type & (VF_ARRAY | VF_CHILD)) == VF_ARRAY) { + clear_array(iamarray(p)); + free(p->x.array->items); + free(p->x.array); + } + if (p->type & VF_WALK) { + walker_list *n; + walker_list *w = p->x.walker; + debug_printf_walker("nvfree: freeing walker @%p\n", &p->x.walker); + p->x.walker = NULL; + while (w) { + n = w->prev; + debug_printf_walker(" free(%p)\n", w); + free(w); + w = n; + } + } + clrvar(p); + } + + g_cb->pos = v; + while (g_cb->prev && g_cb->pos == g_cb->nv) { + g_cb = g_cb->prev; + } +} + +/* ------- awk program text parsing ------- */ + +/* Parse next token pointed by global pos, place results into global ttt. + * If token isn't expected, give away. Return token class + */ +static uint32_t next_token(uint32_t expected) +{ +#define concat_inserted (G.next_token__concat_inserted) +#define save_tclass (G.next_token__save_tclass) +#define save_info (G.next_token__save_info) +/* Initialized to TC_OPTERM: */ +#define ltclass (G.next_token__ltclass) + + char *p, *s; + const char *tl; + uint32_t tc; + const uint32_t *ti; + + if (t_rollback) { + t_rollback = FALSE; + + } else if (concat_inserted) { + concat_inserted = FALSE; + t_tclass = save_tclass; + t_info = save_info; + + } else { + p = g_pos; + readnext: + p = skip_spaces(p); + g_lineno = t_lineno; + if (*p == '#') + while (*p != '\n' && *p != '\0') + p++; + + if (*p == '\n') + t_lineno++; + + if (*p == '\0') { + tc = TC_EOF; + debug_printf_parse("%s: token found: TC_EOF\n", __func__); + + } else if (*p == '\"') { + /* it's a string */ + t_string = s = ++p; + while (*p != '\"') { + char *pp; + if (*p == '\0' || *p == '\n') + syntax_error(EMSG_UNEXP_EOS); + pp = p; + *s++ = nextchar(&pp); + p = pp; + } + p++; + *s = '\0'; + tc = TC_STRING; + debug_printf_parse("%s: token found:'%s' TC_STRING\n", __func__, t_string); + + } else if ((expected & TC_REGEXP) && *p == '/') { + /* it's regexp */ + t_string = s = ++p; + while (*p != '/') { + if (*p == '\0' || *p == '\n') + syntax_error(EMSG_UNEXP_EOS); + *s = *p++; + if (*s++ == '\\') { + char *pp = p; + s[-1] = bb_process_escape_sequence((const char **)&pp); + if (*p == '\\') + *s++ = '\\'; + if (pp == p) + *s++ = *p++; + else + p = pp; + } + } + p++; + *s = '\0'; + tc = TC_REGEXP; + debug_printf_parse("%s: token found:'%s' TC_REGEXP\n", __func__, t_string); + + } else if (*p == '.' || isdigit(*p)) { + /* it's a number */ + char *pp = p; + t_double = my_strtod(&pp); + p = pp; + if (*p == '.') + syntax_error(EMSG_UNEXP_TOKEN); + tc = TC_NUMBER; + debug_printf_parse("%s: token found:%f TC_NUMBER\n", __func__, t_double); + + } else { + /* search for something known */ + tl = tokenlist; + tc = 0x00000001; + ti = tokeninfo; + while (*tl) { + int l = (unsigned char) *tl++; + if (l == (unsigned char) NTCC) { + tc <<= 1; + continue; + } + /* if token class is expected, + * token matches, + * and it's not a longer word, + */ + if ((tc & (expected | TC_WORD | TC_NEWLINE)) + && strncmp(p, tl, l) == 0 + && !((tc & TC_WORD) && isalnum_(p[l])) + ) { + /* then this is what we are looking for */ + t_info = *ti; + debug_printf_parse("%s: token found:'%.*s' t_info:%x\n", __func__, l, p, t_info); + p += l; + goto token_found; + } + ti++; + tl += l; + } + /* not a known token */ + + /* is it a name? (var/array/function) */ + if (!isalnum_(*p)) + syntax_error(EMSG_UNEXP_TOKEN); /* no */ + /* yes */ + t_string = --p; + while (isalnum_(*++p)) { + p[-1] = *p; + } + p[-1] = '\0'; + tc = TC_VARIABLE; + /* also consume whitespace between functionname and bracket */ + if (!(expected & TC_VARIABLE) || (expected & TC_ARRAY)) + p = skip_spaces(p); + if (*p == '(') { + tc = TC_FUNCTION; + debug_printf_parse("%s: token found:'%s' TC_FUNCTION\n", __func__, t_string); + } else { + if (*p == '[') { + p++; + tc = TC_ARRAY; + debug_printf_parse("%s: token found:'%s' TC_ARRAY\n", __func__, t_string); + } else + debug_printf_parse("%s: token found:'%s' TC_VARIABLE\n", __func__, t_string); + } + } + token_found: + g_pos = p; + + /* skipping newlines in some cases */ + if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE)) + goto readnext; + + /* insert concatenation operator when needed */ + if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)) { + concat_inserted = TRUE; + save_tclass = tc; + save_info = t_info; + tc = TC_BINOP; + t_info = OC_CONCAT | SS | P(35); + } + + t_tclass = tc; + } + ltclass = t_tclass; + + /* Are we ready for this? */ + if (!(ltclass & expected)) + syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ? + EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN); + + return ltclass; +#undef concat_inserted +#undef save_tclass +#undef save_info +#undef ltclass +} + +static void rollback_token(void) +{ + t_rollback = TRUE; +} + +static node *new_node(uint32_t info) +{ + node *n; + + n = xzalloc(sizeof(node)); + n->info = info; + n->lineno = g_lineno; + return n; +} + +static void mk_re_node(const char *s, node *n, regex_t *re) +{ + n->info = OC_REGEXP; + n->l.re = re; + n->r.ire = re + 1; + xregcomp(re, s, REG_EXTENDED); + xregcomp(re + 1, s, REG_EXTENDED | REG_ICASE); +} + +static node *condition(void) +{ + next_token(TC_SEQSTART); + return parse_expr(TC_SEQTERM); +} + +/* parse expression terminated by given argument, return ptr + * to built subtree. Terminator is eaten by parse_expr */ +static node *parse_expr(uint32_t iexp) +{ + node sn; + node *cn = &sn; + node *vn, *glptr; + uint32_t tc, xtc; + var *v; + + debug_printf_parse("%s(%x)\n", __func__, iexp); + + sn.info = PRIMASK; + sn.r.n = glptr = NULL; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp; + + while (!((tc = next_token(xtc)) & iexp)) { + + if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) { + /* input redirection (<) attached to glptr node */ + debug_printf_parse("%s: input redir\n", __func__); + cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); + cn->a.n = glptr; + xtc = TC_OPERAND | TC_UOPPRE; + glptr = NULL; + + } else if (tc & (TC_BINOP | TC_UOPPOST)) { + debug_printf_parse("%s: TC_BINOP | TC_UOPPOST\n", __func__); + /* for binary and postfix-unary operators, jump back over + * previous operators with higher priority */ + vn = cn; + while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) + || ((t_info == vn->info) && ((t_info & OPCLSMASK) == OC_COLON)) + ) { + vn = vn->a.n; + } + if ((t_info & OPCLSMASK) == OC_TERNARY) + t_info += P(6); + cn = vn->a.n->r.n = new_node(t_info); + cn->a.n = vn->a.n; + if (tc & TC_BINOP) { + cn->l.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; + if ((t_info & OPCLSMASK) == OC_PGETLINE) { + /* it's a pipe */ + next_token(TC_GETLINE); + /* give maximum priority to this pipe */ + cn->info &= ~PRIMASK; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + } + } else { + cn->r.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + } + vn->a.n = cn; + + } else { + debug_printf_parse("%s: other\n", __func__); + /* for operands and prefix-unary operators, attach them + * to last node */ + vn = cn; + cn = vn->r.n = new_node(t_info); + cn->a.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; + if (tc & (TC_OPERAND | TC_REGEXP)) { + debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__); + xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp; + /* one should be very careful with switch on tclass - + * only simple tclasses should be used! */ + switch (tc) { + case TC_VARIABLE: + case TC_ARRAY: + debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__); + cn->info = OC_VAR; + v = hash_search(ahash, t_string); + if (v != NULL) { + cn->info = OC_FNARG; + cn->l.aidx = v->x.aidx; + } else { + cn->l.v = newvar(t_string); + } + if (tc & TC_ARRAY) { + cn->info |= xS; + cn->r.n = parse_expr(TC_ARRTERM); + } + break; + + case TC_NUMBER: + case TC_STRING: + debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__); + cn->info = OC_VAR; + v = cn->l.v = xzalloc(sizeof(var)); + if (tc & TC_NUMBER) + setvar_i(v, t_double); + else + setvar_s(v, t_string); + break; + + case TC_REGEXP: + debug_printf_parse("%s: TC_REGEXP\n", __func__); + mk_re_node(t_string, cn, xzalloc(sizeof(regex_t)*2)); + break; + + case TC_FUNCTION: + debug_printf_parse("%s: TC_FUNCTION\n", __func__); + cn->info = OC_FUNC; + cn->r.f = newfunc(t_string); + cn->l.n = condition(); + break; + + case TC_SEQSTART: + debug_printf_parse("%s: TC_SEQSTART\n", __func__); + cn = vn->r.n = parse_expr(TC_SEQTERM); + if (!cn) + syntax_error("Empty sequence"); + cn->a.n = vn; + break; + + case TC_GETLINE: + debug_printf_parse("%s: TC_GETLINE\n", __func__); + glptr = cn; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + break; + + case TC_BUILTIN: + debug_printf_parse("%s: TC_BUILTIN\n", __func__); + cn->l.n = condition(); + break; + } + } + } + } + + debug_printf_parse("%s() returns %p\n", __func__, sn.r.n); + return sn.r.n; +} + +/* add node to chain. Return ptr to alloc'd node */ +static node *chain_node(uint32_t info) +{ + node *n; + + if (!seq->first) + seq->first = seq->last = new_node(0); + + if (seq->programname != g_progname) { + seq->programname = g_progname; + n = chain_node(OC_NEWSOURCE); + n->l.new_progname = xstrdup(g_progname); + } + + n = seq->last; + n->info = info; + seq->last = n->a.n = new_node(OC_DONE); + + return n; +} + +static void chain_expr(uint32_t info) +{ + node *n; + + n = chain_node(info); + n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM); + if (t_tclass & TC_GRPTERM) + rollback_token(); +} + +static node *chain_loop(node *nn) +{ + node *n, *n2, *save_brk, *save_cont; + + save_brk = break_ptr; + save_cont = continue_ptr; + + n = chain_node(OC_BR | Vx); + continue_ptr = new_node(OC_EXEC); + break_ptr = new_node(OC_EXEC); + chain_group(); + n2 = chain_node(OC_EXEC | Vx); + n2->l.n = nn; + n2->a.n = n; + continue_ptr->a.n = n2; + break_ptr->a.n = n->r.n = seq->last; + + continue_ptr = save_cont; + break_ptr = save_brk; + + return n; +} + +/* parse group and attach it to chain */ +static void chain_group(void) +{ + uint32_t c; + node *n, *n2, *n3; + + do { + c = next_token(TC_GRPSEQ); + } while (c & TC_NEWLINE); + + if (c & TC_GRPSTART) { + debug_printf_parse("%s: TC_GRPSTART\n", __func__); + while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) { + debug_printf_parse("%s: !TC_GRPTERM\n", __func__); + if (t_tclass & TC_NEWLINE) + continue; + rollback_token(); + chain_group(); + } + debug_printf_parse("%s: TC_GRPTERM\n", __func__); + } else if (c & (TC_OPSEQ | TC_OPTERM)) { + debug_printf_parse("%s: TC_OPSEQ | TC_OPTERM\n", __func__); + rollback_token(); + chain_expr(OC_EXEC | Vx); + } else { + /* TC_STATEMNT */ + debug_printf_parse("%s: TC_STATEMNT(?)\n", __func__); + switch (t_info & OPCLSMASK) { + case ST_IF: + debug_printf_parse("%s: ST_IF\n", __func__); + n = chain_node(OC_BR | Vx); + n->l.n = condition(); + chain_group(); + n2 = chain_node(OC_EXEC); + n->r.n = seq->last; + if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE) == TC_ELSE) { + chain_group(); + n2->a.n = seq->last; + } else { + rollback_token(); + } + break; + + case ST_WHILE: + debug_printf_parse("%s: ST_WHILE\n", __func__); + n2 = condition(); + n = chain_loop(NULL); + n->l.n = n2; + break; + + case ST_DO: + debug_printf_parse("%s: ST_DO\n", __func__); + n2 = chain_node(OC_EXEC); + n = chain_loop(NULL); + n2->a.n = n->a.n; + next_token(TC_WHILE); + n->l.n = condition(); + break; + + case ST_FOR: + debug_printf_parse("%s: ST_FOR\n", __func__); + next_token(TC_SEQSTART); + n2 = parse_expr(TC_SEMICOL | TC_SEQTERM); + if (t_tclass & TC_SEQTERM) { /* for-in */ + if ((n2->info & OPCLSMASK) != OC_IN) + syntax_error(EMSG_UNEXP_TOKEN); + n = chain_node(OC_WALKINIT | VV); + n->l.n = n2->l.n; + n->r.n = n2->r.n; + n = chain_loop(NULL); + n->info = OC_WALKNEXT | Vx; + n->l.n = n2->l.n; + } else { /* for (;;) */ + n = chain_node(OC_EXEC | Vx); + n->l.n = n2; + n2 = parse_expr(TC_SEMICOL); + n3 = parse_expr(TC_SEQTERM); + n = chain_loop(n3); + n->l.n = n2; + if (!n2) + n->info = OC_EXEC; + } + break; + + case OC_PRINT: + case OC_PRINTF: + debug_printf_parse("%s: OC_PRINT[F]\n", __func__); + n = chain_node(t_info); + n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM); + if (t_tclass & TC_OUTRDR) { + n->info |= t_info; + n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM); + } + if (t_tclass & TC_GRPTERM) + rollback_token(); + break; + + case OC_BREAK: + debug_printf_parse("%s: OC_BREAK\n", __func__); + n = chain_node(OC_EXEC); + n->a.n = break_ptr; + break; + + case OC_CONTINUE: + debug_printf_parse("%s: OC_CONTINUE\n", __func__); + n = chain_node(OC_EXEC); + n->a.n = continue_ptr; + break; + + /* delete, next, nextfile, return, exit */ + default: + debug_printf_parse("%s: default\n", __func__); + chain_expr(t_info); + } + } +} + +static void parse_program(char *p) +{ + uint32_t tclass; + node *cn; + func *f; + var *v; + + g_pos = p; + t_lineno = 1; + while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART | + TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) { + + if (tclass & TC_OPTERM) { + debug_printf_parse("%s: TC_OPTERM\n", __func__); + continue; + } + + seq = &mainseq; + if (tclass & TC_BEGIN) { + debug_printf_parse("%s: TC_BEGIN\n", __func__); + seq = &beginseq; + chain_group(); + + } else if (tclass & TC_END) { + debug_printf_parse("%s: TC_END\n", __func__); + seq = &endseq; + chain_group(); + + } else if (tclass & TC_FUNCDECL) { + debug_printf_parse("%s: TC_FUNCDECL\n", __func__); + next_token(TC_FUNCTION); + g_pos++; + f = newfunc(t_string); + f->body.first = NULL; + f->nargs = 0; + while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { + v = findvar(ahash, t_string); + v->x.aidx = f->nargs++; + + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) + break; + } + seq = &f->body; + chain_group(); + clear_array(ahash); + + } else if (tclass & TC_OPSEQ) { + debug_printf_parse("%s: TC_OPSEQ\n", __func__); + rollback_token(); + cn = chain_node(OC_TEST); + cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART); + if (t_tclass & TC_GRPSTART) { + debug_printf_parse("%s: TC_GRPSTART\n", __func__); + rollback_token(); + chain_group(); + } else { + debug_printf_parse("%s: !TC_GRPSTART\n", __func__); + chain_node(OC_PRINT); + } + cn->r.n = mainseq.last; + + } else /* if (tclass & TC_GRPSTART) */ { + debug_printf_parse("%s: TC_GRPSTART(?)\n", __func__); + rollback_token(); + chain_group(); + } + } + debug_printf_parse("%s: TC_EOF\n", __func__); +} + + +/* -------- program execution part -------- */ + +static node *mk_splitter(const char *s, tsplitter *spl) +{ + regex_t *re, *ire; + node *n; + + re = &spl->re[0]; + ire = &spl->re[1]; + n = &spl->n; + if ((n->info & OPCLSMASK) == OC_REGEXP) { + regfree(re); + regfree(ire); // TODO: nuke ire, use re+1? + } + if (s[0] && s[1]) { /* strlen(s) > 1 */ + mk_re_node(s, n, re); + } else { + n->info = (uint32_t) s[0]; + } + + return n; +} + +/* use node as a regular expression. Supplied with node ptr and regex_t + * storage space. Return ptr to regex (if result points to preg, it should + * be later regfree'd manually + */ +static regex_t *as_regex(node *op, regex_t *preg) +{ + int cflags; + var *v; + const char *s; + + if ((op->info & OPCLSMASK) == OC_REGEXP) { + return icase ? op->r.ire : op->l.re; + } + v = nvalloc(1); + s = getvar_s(evaluate(op, v)); + + cflags = icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED; + /* Testcase where REG_EXTENDED fails (unpaired '{'): + * echo Hi | awk 'gsub("@(samp|code|file)\{","");' + * gawk 3.1.5 eats this. We revert to ~REG_EXTENDED + * (maybe gsub is not supposed to use REG_EXTENDED?). + */ + if (regcomp(preg, s, cflags)) { + cflags &= ~REG_EXTENDED; + xregcomp(preg, s, cflags); + } + nvfree(v); + return preg; +} + +/* gradually increasing buffer. + * note that we reallocate even if n == old_size, + * and thus there is at least one extra allocated byte. + */ +static char* qrealloc(char *b, int n, int *size) +{ + if (!b || n >= *size) { + *size = n + (n>>1) + 80; + b = xrealloc(b, *size); + } + return b; +} + +/* resize field storage space */ +static void fsrealloc(int size) +{ + int i; + + if (size >= maxfields) { + i = maxfields; + maxfields = size + 16; + Fields = xrealloc(Fields, maxfields * sizeof(Fields[0])); + for (; i < maxfields; i++) { + Fields[i].type = VF_SPECIAL; + Fields[i].string = NULL; + } + } + /* if size < nfields, clear extra field variables */ + for (i = size; i < nfields; i++) { + clrvar(Fields + i); + } + nfields = size; +} + +static int awk_split(const char *s, node *spl, char **slist) +{ + int l, n; + char c[4]; + char *s1; + regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough... + + /* in worst case, each char would be a separate field */ + *slist = s1 = xzalloc(strlen(s) * 2 + 3); + strcpy(s1, s); + + c[0] = c[1] = (char)spl->info; + c[2] = c[3] = '\0'; + if (*getvar_s(intvar[RS]) == '\0') + c[2] = '\n'; + + n = 0; + if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */ + if (!*s) + return n; /* "": zero fields */ + n++; /* at least one field will be there */ + do { + l = strcspn(s, c+2); /* len till next NUL or \n */ + if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 + && pmatch[0].rm_so <= l + ) { + l = pmatch[0].rm_so; + if (pmatch[0].rm_eo == 0) { + l++; + pmatch[0].rm_eo++; + } + n++; /* we saw yet another delimiter */ + } else { + pmatch[0].rm_eo = l; + if (s[l]) + pmatch[0].rm_eo++; + } + memcpy(s1, s, l); + /* make sure we remove *all* of the separator chars */ + do { + s1[l] = '\0'; + } while (++l < pmatch[0].rm_eo); + nextword(&s1); + s += pmatch[0].rm_eo; + } while (*s); + return n; + } + if (c[0] == '\0') { /* null split */ + while (*s) { + *s1++ = *s++; + *s1++ = '\0'; + n++; + } + return n; + } + if (c[0] != ' ') { /* single-character split */ + if (icase) { + c[0] = toupper(c[0]); + c[1] = tolower(c[1]); + } + if (*s1) + n++; + while ((s1 = strpbrk(s1, c)) != NULL) { + *s1++ = '\0'; + n++; + } + return n; + } + /* space split */ + while (*s) { + s = skip_whitespace(s); + if (!*s) + break; + n++; + while (*s && !isspace(*s)) + *s1++ = *s++; + *s1++ = '\0'; + } + return n; +} + +static void split_f0(void) +{ +/* static char *fstrings; */ +#define fstrings (G.split_f0__fstrings) + + int i, n; + char *s; + + if (is_f0_split) + return; + + is_f0_split = TRUE; + free(fstrings); + fsrealloc(0); + n = awk_split(getvar_s(intvar[F0]), &fsplitter.n, &fstrings); + fsrealloc(n); + s = fstrings; + for (i = 0; i < n; i++) { + Fields[i].string = nextword(&s); + Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY); + } + + /* set NF manually to avoid side effects */ + clrvar(intvar[NF]); + intvar[NF]->type = VF_NUMBER | VF_SPECIAL; + intvar[NF]->number = nfields; +#undef fstrings +} + +/* perform additional actions when some internal variables changed */ +static void handle_special(var *v) +{ + int n; + char *b; + const char *sep, *s; + int sl, l, len, i, bsize; + + if (!(v->type & VF_SPECIAL)) + return; + + if (v == intvar[NF]) { + n = (int)getvar_i(v); + fsrealloc(n); + + /* recalculate $0 */ + sep = getvar_s(intvar[OFS]); + sl = strlen(sep); + b = NULL; + len = 0; + for (i = 0; i < n; i++) { + s = getvar_s(&Fields[i]); + l = strlen(s); + if (b) { + memcpy(b+len, sep, sl); + len += sl; + } + b = qrealloc(b, len+l+sl, &bsize); + memcpy(b+len, s, l); + len += l; + } + if (b) + b[len] = '\0'; + setvar_p(intvar[F0], b); + is_f0_split = TRUE; + + } else if (v == intvar[F0]) { + is_f0_split = FALSE; + + } else if (v == intvar[FS]) { + mk_splitter(getvar_s(v), &fsplitter); + + } else if (v == intvar[RS]) { + mk_splitter(getvar_s(v), &rsplitter); + + } else if (v == intvar[IGNORECASE]) { + icase = istrue(v); + + } else { /* $n */ + n = getvar_i(intvar[NF]); + setvar_i(intvar[NF], n > v-Fields ? n : v-Fields+1); + /* right here v is invalid. Just to note... */ + } +} + +/* step through func/builtin/etc arguments */ +static node *nextarg(node **pn) +{ + node *n; + + n = *pn; + if (n && (n->info & OPCLSMASK) == OC_COMMA) { + *pn = n->r.n; + n = n->l.n; + } else { + *pn = NULL; + } + return n; +} + +static void hashwalk_init(var *v, xhash *array) +{ + hash_item *hi; + unsigned i; + walker_list *w; + walker_list *prev_walker; + + if (v->type & VF_WALK) { + prev_walker = v->x.walker; + } else { + v->type |= VF_WALK; + prev_walker = NULL; + } + debug_printf_walker("hashwalk_init: prev_walker:%p\n", prev_walker); + + w = v->x.walker = xzalloc(sizeof(*w) + array->glen + 1); /* why + 1? */ + debug_printf_walker(" walker@%p=%p\n", &v->x.walker, w); + w->cur = w->end = w->wbuf; + w->prev = prev_walker; + for (i = 0; i < array->csize; i++) { + hi = array->items[i]; + while (hi) { + strcpy(w->end, hi->name); + nextword(&w->end); + hi = hi->next; + } + } +} + +static int hashwalk_next(var *v) +{ + walker_list *w = v->x.walker; + + if (w->cur >= w->end) { + walker_list *prev_walker = w->prev; + + debug_printf_walker("end of iteration, free(walker@%p:%p), prev_walker:%p\n", &v->x.walker, w, prev_walker); + free(w); + v->x.walker = prev_walker; + return FALSE; + } + + setvar_s(v, nextword(&w->cur)); + return TRUE; +} + +/* evaluate node, return 1 when result is true, 0 otherwise */ +static int ptest(node *pattern) +{ + /* ptest__v is "static": to save stack space? */ + return istrue(evaluate(pattern, &G.ptest__v)); +} + +/* read next record from stream rsm into a variable v */ +static int awk_getline(rstream *rsm, var *v) +{ + char *b; + regmatch_t pmatch[2]; + int size, a, p, pp = 0; + int fd, so, eo, r, rp; + char c, *m, *s; + + debug_printf_eval("entered %s()\n", __func__); + + /* we're using our own buffer since we need access to accumulating + * characters + */ + fd = fileno(rsm->F); + m = rsm->buffer; + a = rsm->adv; + p = rsm->pos; + size = rsm->size; + c = (char) rsplitter.n.info; + rp = 0; + + if (!m) + m = qrealloc(m, 256, &size); + + do { + b = m + a; + so = eo = p; + r = 1; + if (p > 0) { + if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) { + if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re, + b, 1, pmatch, 0) == 0) { + so = pmatch[0].rm_so; + eo = pmatch[0].rm_eo; + if (b[eo] != '\0') + break; + } + } else if (c != '\0') { + s = strchr(b+pp, c); + if (!s) + s = memchr(b+pp, '\0', p - pp); + if (s) { + so = eo = s-b; + eo++; + break; + } + } else { + while (b[rp] == '\n') + rp++; + s = strstr(b+rp, "\n\n"); + if (s) { + so = eo = s-b; + while (b[eo] == '\n') + eo++; + if (b[eo] != '\0') + break; + } + } + } + + if (a > 0) { + memmove(m, m+a, p+1); + b = m; + a = 0; + } + + m = qrealloc(m, a+p+128, &size); + b = m + a; + pp = p; + p += safe_read(fd, b+p, size-p-1); + if (p < pp) { + p = 0; + r = 0; + setvar_i(intvar[ERRNO], errno); + } + b[p] = '\0'; + + } while (p > pp); + + if (p == 0) { + r--; + } else { + c = b[so]; b[so] = '\0'; + setvar_s(v, b+rp); + v->type |= VF_USER; + b[so] = c; + c = b[eo]; b[eo] = '\0'; + setvar_s(intvar[RT], b+so); + b[eo] = c; + } + + rsm->buffer = m; + rsm->adv = a + eo; + rsm->pos = p - eo; + rsm->size = size; + + debug_printf_eval("returning from %s(): %d\n", __func__, r); + + return r; +} + +static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) +{ + int r = 0; + char c; + const char *s = format; + + if (int_as_int && n == (int)n) { + r = snprintf(b, size, "%d", (int)n); + } else { + do { c = *s; } while (c && *++s); + if (strchr("diouxX", c)) { + r = snprintf(b, size, format, (int)n); + } else if (strchr("eEfgG", c)) { + r = snprintf(b, size, format, n); + } else { + syntax_error(EMSG_INV_FMT); + } + } + return r; +} + +/* formatted output into an allocated buffer, return ptr to buffer */ +static char *awk_printf(node *n) +{ + char *b = NULL; + char *fmt, *s, *f; + const char *s1; + int i, j, incr, bsize; + char c, c1; + var *v, *arg; + + v = nvalloc(1); + fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v))); + + i = 0; + while (*f) { + s = f; + while (*f && (*f != '%' || *++f == '%')) + f++; + while (*f && !isalpha(*f)) { + if (*f == '*') + syntax_error("%*x formats are not supported"); + f++; + } + + incr = (f - s) + MAXVARFMT; + b = qrealloc(b, incr + i, &bsize); + c = *f; + if (c != '\0') + f++; + c1 = *f; + *f = '\0'; + arg = evaluate(nextarg(&n), v); + + j = i; + if (c == 'c' || !c) { + i += sprintf(b+i, s, is_numeric(arg) ? + (char)getvar_i(arg) : *getvar_s(arg)); + } else if (c == 's') { + s1 = getvar_s(arg); + b = qrealloc(b, incr+i+strlen(s1), &bsize); + i += sprintf(b+i, s, s1); + } else { + i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); + } + *f = c1; + + /* if there was an error while sprintf, return value is negative */ + if (i < j) + i = j; + } + + free(fmt); + nvfree(v); + b = xrealloc(b, i + 1); + b[i] = '\0'; + return b; +} + +/* Common substitution routine. + * Replace (nm)'th substring of (src) that matches (rn) with (repl), + * store result into (dest), return number of substitutions. + * If nm = 0, replace all matches. + * If src or dst is NULL, use $0. + * If subexp != 0, enable subexpression matching (\1-\9). + */ +static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp) +{ + char *resbuf; + const char *sp; + int match_no, residx, replen, resbufsize; + int regexec_flags; + regmatch_t pmatch[10]; + regex_t sreg, *regex; + + resbuf = NULL; + residx = 0; + match_no = 0; + regexec_flags = 0; + regex = as_regex(rn, &sreg); + sp = getvar_s(src ? src : intvar[F0]); + replen = strlen(repl); + while (regexec(regex, sp, 10, pmatch, regexec_flags) == 0) { + int so = pmatch[0].rm_so; + int eo = pmatch[0].rm_eo; + + //bb_error_msg("match %u: [%u,%u] '%s'%p", match_no+1, so, eo, sp,sp); + resbuf = qrealloc(resbuf, residx + eo + replen, &resbufsize); + memcpy(resbuf + residx, sp, eo); + residx += eo; + if (++match_no >= nm) { + const char *s; + int nbs; + + /* replace */ + residx -= (eo - so); + nbs = 0; + for (s = repl; *s; s++) { + char c = resbuf[residx++] = *s; + if (c == '\\') { + nbs++; + continue; + } + if (c == '&' || (subexp && c >= '0' && c <= '9')) { + int j; + residx -= ((nbs + 3) >> 1); + j = 0; + if (c != '&') { + j = c - '0'; + nbs++; + } + if (nbs % 2) { + resbuf[residx++] = c; + } else { + int n = pmatch[j].rm_eo - pmatch[j].rm_so; + resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize); + memcpy(resbuf + residx, sp + pmatch[j].rm_so, n); + residx += n; + } + } + nbs = 0; + } + } + + regexec_flags = REG_NOTBOL; + sp += eo; + if (match_no == nm) + break; + if (eo == so) { + /* Empty match (e.g. "b*" will match anywhere). + * Advance by one char. */ +//BUG (bug 1333): +//gsub(/\info; + op = op->l.n; + + av[2] = av[3] = NULL; + for (i = 0; i < 4 && op; i++) { + an[i] = nextarg(&op); + if (isr & 0x09000000) + av[i] = evaluate(an[i], &tv[i]); + if (isr & 0x08000000) + as[i] = getvar_s(av[i]); + isr >>= 1; + } + + nargs = i; + if ((uint32_t)nargs < (info >> 30)) + syntax_error(EMSG_TOO_FEW_ARGS); + + info &= OPNMASK; + switch (info) { + + case B_a2: + if (ENABLE_FEATURE_AWK_LIBM) + setvar_i(res, atan2(getvar_i(av[0]), getvar_i(av[1]))); + else + syntax_error(EMSG_NO_MATH); + break; + + case B_sp: { + char *s, *s1; + + if (nargs > 2) { + spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ? + an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl); + } else { + spl = &fsplitter.n; + } + + n = awk_split(as[0], spl, &s); + s1 = s; + clear_array(iamarray(av[1])); + for (i = 1; i <= n; i++) + setari_u(av[1], i, nextword(&s)); + free(s1); + setvar_i(res, n); + break; + } + + case B_ss: { + char *s; + + l = strlen(as[0]); + i = getvar_i(av[1]) - 1; + if (i > l) + i = l; + if (i < 0) + i = 0; + n = (nargs > 2) ? getvar_i(av[2]) : l-i; + if (n < 0) + n = 0; + s = xstrndup(as[0]+i, n); + setvar_p(res, s); + break; + } + + /* Bitwise ops must assume that operands are unsigned. GNU Awk 3.1.5: + * awk '{ print or(-1,1) }' gives "4.29497e+09", not "-2.xxxe+09" */ + case B_an: + setvar_i(res, getvar_i_int(av[0]) & getvar_i_int(av[1])); + break; + + case B_co: + setvar_i(res, ~getvar_i_int(av[0])); + break; + + case B_ls: + setvar_i(res, getvar_i_int(av[0]) << getvar_i_int(av[1])); + break; + + case B_or: + setvar_i(res, getvar_i_int(av[0]) | getvar_i_int(av[1])); + break; + + case B_rs: + setvar_i(res, getvar_i_int(av[0]) >> getvar_i_int(av[1])); + break; + + case B_xo: + setvar_i(res, getvar_i_int(av[0]) ^ getvar_i_int(av[1])); + break; + + case B_lo: + case B_up: { + char *s, *s1; + s1 = s = xstrdup(as[0]); + while (*s1) { + //*s1 = (info == B_up) ? toupper(*s1) : tolower(*s1); + if ((unsigned char)((*s1 | 0x20) - 'a') <= ('z' - 'a')) + *s1 = (info == B_up) ? (*s1 & 0xdf) : (*s1 | 0x20); + s1++; + } + setvar_p(res, s); + break; + } + + case B_ix: + n = 0; + ll = strlen(as[1]); + l = strlen(as[0]) - ll; + if (ll > 0 && l >= 0) { + if (!icase) { + char *s = strstr(as[0], as[1]); + if (s) + n = (s - as[0]) + 1; + } else { + /* this piece of code is terribly slow and + * really should be rewritten + */ + for (i = 0; i <= l; i++) { + if (strncasecmp(as[0]+i, as[1], ll) == 0) { + n = i+1; + break; + } + } + } + } + setvar_i(res, n); + break; + + case B_ti: + if (nargs > 1) + tt = getvar_i(av[1]); + else + time(&tt); + //s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"; + i = strftime(g_buf, MAXVARFMT, + ((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"), + localtime(&tt)); + g_buf[i] = '\0'; + setvar_s(res, g_buf); + break; + + case B_mt: + setvar_i(res, do_mktime(as[0])); + break; + + case B_ma: + re = as_regex(an[1], &sreg); + n = regexec(re, as[0], 1, pmatch, 0); + if (n == 0) { + pmatch[0].rm_so++; + pmatch[0].rm_eo++; + } else { + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = -1; + } + setvar_i(newvar("RSTART"), pmatch[0].rm_so); + setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so); + setvar_i(res, pmatch[0].rm_so); + if (re == &sreg) + regfree(re); + break; + + case B_ge: + awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE); + break; + + case B_gs: + setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE)); + break; + + case B_su: + setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE)); + break; + } + + nvfree(tv); + return res; +#undef tspl +} + +/* + * Evaluate node - the heart of the program. Supplied with subtree + * and place where to store result. returns ptr to result. + */ +#define XC(n) ((n) >> 8) + +static var *evaluate(node *op, var *res) +{ +/* This procedure is recursive so we should count every byte */ +#define fnargs (G.evaluate__fnargs) +/* seed is initialized to 1 */ +#define seed (G.evaluate__seed) +#define sreg (G.evaluate__sreg) + + var *v1; + + if (!op) + return setvar_s(res, NULL); + + debug_printf_eval("entered %s()\n", __func__); + + v1 = nvalloc(2); + + while (op) { + struct { + var *v; + const char *s; + } L = L; /* for compiler */ + struct { + var *v; + const char *s; + } R = R; + double L_d = L_d; + uint32_t opinfo; + int opn; + node *op1; + + opinfo = op->info; + opn = (opinfo & OPNMASK); + g_lineno = op->lineno; + op1 = op->l.n; + debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn); + + /* execute inevitable things */ + if (opinfo & OF_RES1) + L.v = evaluate(op1, v1); + if (opinfo & OF_RES2) + R.v = evaluate(op->r.n, v1+1); + if (opinfo & OF_STR1) { + L.s = getvar_s(L.v); + debug_printf_eval("L.s:'%s'\n", L.s); + } + if (opinfo & OF_STR2) { + R.s = getvar_s(R.v); + debug_printf_eval("R.s:'%s'\n", R.s); + } + if (opinfo & OF_NUM1) { + L_d = getvar_i(L.v); + debug_printf_eval("L_d:%f\n", L_d); + } + + debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); + switch (XC(opinfo & OPCLSMASK)) { + + /* -- iterative node type -- */ + + /* test pattern */ + case XC( OC_TEST ): + if ((op1->info & OPCLSMASK) == OC_COMMA) { + /* it's range pattern */ + if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { + op->info |= OF_CHECKED; + if (ptest(op1->r.n)) + op->info &= ~OF_CHECKED; + op = op->a.n; + } else { + op = op->r.n; + } + } else { + op = ptest(op1) ? op->a.n : op->r.n; + } + break; + + /* just evaluate an expression, also used as unconditional jump */ + case XC( OC_EXEC ): + break; + + /* branch, used in if-else and various loops */ + case XC( OC_BR ): + op = istrue(L.v) ? op->a.n : op->r.n; + break; + + /* initialize for-in loop */ + case XC( OC_WALKINIT ): + hashwalk_init(L.v, iamarray(R.v)); + break; + + /* get next array item */ + case XC( OC_WALKNEXT ): + op = hashwalk_next(L.v) ? op->a.n : op->r.n; + break; + + case XC( OC_PRINT ): + case XC( OC_PRINTF ): { + FILE *F = stdout; + + if (op->r.n) { + rstream *rsm = newfile(R.s); + if (!rsm->F) { + if (opn == '|') { + rsm->F = popen(R.s, "w"); + if (rsm->F == NULL) + bb_perror_msg_and_die("popen"); + rsm->is_pipe = 1; + } else { + rsm->F = xfopen(R.s, opn=='w' ? "w" : "a"); + } + } + F = rsm->F; + } + + if ((opinfo & OPCLSMASK) == OC_PRINT) { + if (!op1) { + fputs(getvar_s(intvar[F0]), F); + } else { + while (op1) { + var *v = evaluate(nextarg(&op1), v1); + if (v->type & VF_NUMBER) { + fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), + getvar_i(v), TRUE); + fputs(g_buf, F); + } else { + fputs(getvar_s(v), F); + } + + if (op1) + fputs(getvar_s(intvar[OFS]), F); + } + } + fputs(getvar_s(intvar[ORS]), F); + + } else { /* OC_PRINTF */ + char *s = awk_printf(op1); + fputs(s, F); + free(s); + } + fflush(F); + break; + } + + case XC( OC_DELETE ): { + uint32_t info = op1->info & OPCLSMASK; + var *v; + + if (info == OC_VAR) { + v = op1->l.v; + } else if (info == OC_FNARG) { + v = &fnargs[op1->l.aidx]; + } else { + syntax_error(EMSG_NOT_ARRAY); + } + + if (op1->r.n) { + const char *s; + clrvar(L.v); + s = getvar_s(evaluate(op1->r.n, v1)); + hash_remove(iamarray(v), s); + } else { + clear_array(iamarray(v)); + } + break; + } + + case XC( OC_NEWSOURCE ): + g_progname = op->l.new_progname; + break; + + case XC( OC_RETURN ): + copyvar(res, L.v); + break; + + case XC( OC_NEXTFILE ): + nextfile = TRUE; + case XC( OC_NEXT ): + nextrec = TRUE; + case XC( OC_DONE ): + clrvar(res); + break; + + case XC( OC_EXIT ): + awk_exit(L_d); + + /* -- recursive node type -- */ + + case XC( OC_VAR ): + L.v = op->l.v; + if (L.v == intvar[NF]) + split_f0(); + goto v_cont; + + case XC( OC_FNARG ): + L.v = &fnargs[op->l.aidx]; + v_cont: + res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v; + break; + + case XC( OC_IN ): + setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); + break; + + case XC( OC_REGEXP ): + op1 = op; + L.s = getvar_s(intvar[F0]); + goto re_cont; + + case XC( OC_MATCH ): + op1 = op->r.n; + re_cont: + { + regex_t *re = as_regex(op1, &sreg); + int i = regexec(re, L.s, 0, NULL, 0); + if (re == &sreg) + regfree(re); + setvar_i(res, (i == 0) ^ (opn == '!')); + } + break; + + case XC( OC_MOVE ): + debug_printf_eval("MOVE\n"); + /* if source is a temporary string, jusk relink it to dest */ +//Disabled: if R.v is numeric but happens to have cached R.v->string, +//then L.v ends up being a string, which is wrong +// if (R.v == v1+1 && R.v->string) { +// res = setvar_p(L.v, R.v->string); +// R.v->string = NULL; +// } else { + res = copyvar(L.v, R.v); +// } + break; + + case XC( OC_TERNARY ): + if ((op->r.n->info & OPCLSMASK) != OC_COLON) + syntax_error(EMSG_POSSIBLE_ERROR); + res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); + break; + + case XC( OC_FUNC ): { + var *vbeg, *v; + const char *sv_progname; + + if (!op->r.f->body.first) + syntax_error(EMSG_UNDEF_FUNC); + + vbeg = v = nvalloc(op->r.f->nargs + 1); + while (op1) { + var *arg = evaluate(nextarg(&op1), v1); + copyvar(v, arg); + v->type |= VF_CHILD; + v->x.parent = arg; + if (++v - vbeg >= op->r.f->nargs) + break; + } + + v = fnargs; + fnargs = vbeg; + sv_progname = g_progname; + + res = evaluate(op->r.f->body.first, res); + + g_progname = sv_progname; + nvfree(fnargs); + fnargs = v; + + break; + } + + case XC( OC_GETLINE ): + case XC( OC_PGETLINE ): { + rstream *rsm; + int i; + + if (op1) { + rsm = newfile(L.s); + if (!rsm->F) { + if ((opinfo & OPCLSMASK) == OC_PGETLINE) { + rsm->F = popen(L.s, "r"); + rsm->is_pipe = TRUE; + } else { + rsm->F = fopen_for_read(L.s); /* not xfopen! */ + } + } + } else { + if (!iF) + iF = next_input_file(); + rsm = iF; + } + + if (!rsm || !rsm->F) { + setvar_i(intvar[ERRNO], errno); + setvar_i(res, -1); + break; + } + + if (!op->r.n) + R.v = intvar[F0]; + + i = awk_getline(rsm, R.v); + if (i > 0 && !op1) { + incvar(intvar[FNR]); + incvar(intvar[NR]); + } + setvar_i(res, i); + break; + } + + /* simple builtins */ + case XC( OC_FBLTIN ): { + double R_d = R_d; /* for compiler */ + + switch (opn) { + case F_in: + R_d = (int)L_d; + break; + + case F_rn: + R_d = (double)rand() / (double)RAND_MAX; + break; + + case F_co: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = cos(L_d); + break; + } + + case F_ex: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = exp(L_d); + break; + } + + case F_lg: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = log(L_d); + break; + } + + case F_si: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = sin(L_d); + break; + } + + case F_sq: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = sqrt(L_d); + break; + } + + syntax_error(EMSG_NO_MATH); + break; + + case F_sr: + R_d = (double)seed; + seed = op1 ? (unsigned)L_d : (unsigned)time(NULL); + srand(seed); + break; + + case F_ti: + R_d = time(NULL); + break; + + case F_le: + if (!op1) + L.s = getvar_s(intvar[F0]); + R_d = strlen(L.s); + break; + + case F_sy: + fflush_all(); + R_d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s) + ? (system(L.s) >> 8) : 0; + break; + + case F_ff: + if (!op1) { + fflush(stdout); + } else if (L.s && *L.s) { + rstream *rsm = newfile(L.s); + fflush(rsm->F); + } else { + fflush_all(); + } + break; + + case F_cl: { + rstream *rsm; + int err = 0; + rsm = (rstream *)hash_search(fdhash, L.s); + debug_printf_eval("OC_FBLTIN F_cl rsm:%p\n", rsm); + if (rsm) { + debug_printf_eval("OC_FBLTIN F_cl " + "rsm->is_pipe:%d, ->F:%p\n", + rsm->is_pipe, rsm->F); + /* Can be NULL if open failed. Example: + * getline line <"doesnt_exist"; + * close("doesnt_exist"); <--- here rsm->F is NULL + */ + if (rsm->F) + err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F); + free(rsm->buffer); + hash_remove(fdhash, L.s); + } + if (err) + setvar_i(intvar[ERRNO], errno); + R_d = (double)err; + break; + } + } /* switch */ + setvar_i(res, R_d); + break; + } + + case XC( OC_BUILTIN ): + res = exec_builtin(op, res); + break; + + case XC( OC_SPRINTF ): + setvar_p(res, awk_printf(op1)); + break; + + case XC( OC_UNARY ): { + double Ld, R_d; + + Ld = R_d = getvar_i(R.v); + switch (opn) { + case 'P': + Ld = ++R_d; + goto r_op_change; + case 'p': + R_d++; + goto r_op_change; + case 'M': + Ld = --R_d; + goto r_op_change; + case 'm': + R_d--; + r_op_change: + setvar_i(R.v, R_d); + break; + case '!': + Ld = !istrue(R.v); + break; + case '-': + Ld = -R_d; + break; + } + setvar_i(res, Ld); + break; + } + + case XC( OC_FIELD ): { + int i = (int)getvar_i(R.v); + if (i == 0) { + res = intvar[F0]; + } else { + split_f0(); + if (i > nfields) + fsrealloc(i); + res = &Fields[i - 1]; + } + break; + } + + /* concatenation (" ") and index joining (",") */ + case XC( OC_CONCAT ): + case XC( OC_COMMA ): { + const char *sep = ""; + if ((opinfo & OPCLSMASK) == OC_COMMA) + sep = getvar_s(intvar[SUBSEP]); + setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); + break; + } + + case XC( OC_LAND ): + setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); + break; + + case XC( OC_LOR ): + setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); + break; + + case XC( OC_BINARY ): + case XC( OC_REPLACE ): { + double R_d = getvar_i(R.v); + debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn); + switch (opn) { + case '+': + L_d += R_d; + break; + case '-': + L_d -= R_d; + break; + case '*': + L_d *= R_d; + break; + case '/': + if (R_d == 0) + syntax_error(EMSG_DIV_BY_ZERO); + L_d /= R_d; + break; + case '&': + if (ENABLE_FEATURE_AWK_LIBM) + L_d = pow(L_d, R_d); + else + syntax_error(EMSG_NO_MATH); + break; + case '%': + if (R_d == 0) + syntax_error(EMSG_DIV_BY_ZERO); + L_d -= (int)(L_d / R_d) * R_d; + break; + } + debug_printf_eval("BINARY/REPLACE result:%f\n", L_d); + res = setvar_i(((opinfo & OPCLSMASK) == OC_BINARY) ? res : L.v, L_d); + break; + } + + case XC( OC_COMPARE ): { + int i = i; /* for compiler */ + double Ld; + + if (is_numeric(L.v) && is_numeric(R.v)) { + Ld = getvar_i(L.v) - getvar_i(R.v); + } else { + const char *l = getvar_s(L.v); + const char *r = getvar_s(R.v); + Ld = icase ? strcasecmp(l, r) : strcmp(l, r); + } + switch (opn & 0xfe) { + case 0: + i = (Ld > 0); + break; + case 2: + i = (Ld >= 0); + break; + case 4: + i = (Ld == 0); + break; + } + setvar_i(res, (i == 0) ^ (opn & 1)); + break; + } + + default: + syntax_error(EMSG_POSSIBLE_ERROR); + } + if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS) + op = op->a.n; + if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS) + break; + if (nextrec) + break; + } /* while (op) */ + + nvfree(v1); + debug_printf_eval("returning from %s(): %p\n", __func__, res); + return res; +#undef fnargs +#undef seed +#undef sreg +} + + +/* -------- main & co. -------- */ + +static int awk_exit(int r) +{ + var tv; + unsigned i; + hash_item *hi; + + zero_out_var(&tv); + + if (!exiting) { + exiting = TRUE; + nextrec = FALSE; + evaluate(endseq.first, &tv); + } + + /* waiting for children */ + for (i = 0; i < fdhash->csize; i++) { + hi = fdhash->items[i]; + while (hi) { + if (hi->data.rs.F && hi->data.rs.is_pipe) + pclose(hi->data.rs.F); + hi = hi->next; + } + } + + exit(r); +} + +/* if expr looks like "var=value", perform assignment and return 1, + * otherwise return 0 */ +static int is_assignment(const char *expr) +{ + char *exprc, *val, *s, *s1; + + if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) { + return FALSE; + } + + exprc = xstrdup(expr); + val = exprc + (val - expr); + *val++ = '\0'; + + s = s1 = val; + while ((*s1 = nextchar(&s)) != '\0') + s1++; + + setvar_u(newvar(exprc), val); + free(exprc); + return TRUE; +} + +/* switch to next input file */ +static rstream *next_input_file(void) +{ +#define rsm (G.next_input_file__rsm) +#define files_happen (G.next_input_file__files_happen) + + FILE *F; + const char *fname, *ind; + + if (rsm.F) + fclose(rsm.F); + rsm.F = NULL; + rsm.pos = rsm.adv = 0; + + for (;;) { + if (getvar_i(intvar[ARGIND])+1 >= getvar_i(intvar[ARGC])) { + if (files_happen) + return NULL; + fname = "-"; + F = stdin; + break; + } + ind = getvar_s(incvar(intvar[ARGIND])); + fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind)); + if (fname && *fname && !is_assignment(fname)) { + F = xfopen_stdin(fname); + break; + } + } + + files_happen = TRUE; + setvar_s(intvar[FILENAME], fname); + rsm.F = F; + return &rsm; +#undef rsm +#undef files_happen +} + +int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int awk_main(int argc, char **argv) +{ + unsigned opt; + char *opt_F; + llist_t *list_v = NULL; + llist_t *list_f = NULL; + int i, j; + var *v; + var tv; + char **envp; + char *vnames = (char *)vNames; /* cheat */ + char *vvalues = (char *)vValues; + + INIT_G(); + + /* Undo busybox.c, or else strtod may eat ','! This breaks parsing: + * $1,$2 == '$1,' '$2', NOT '$1' ',' '$2' */ + if (ENABLE_LOCALE_SUPPORT) + setlocale(LC_NUMERIC, "C"); + + zero_out_var(&tv); + + /* allocate global buffer */ + g_buf = xmalloc(MAXVARFMT + 1); + + vhash = hash_init(); + ahash = hash_init(); + fdhash = hash_init(); + fnhash = hash_init(); + + /* initialize variables */ + for (i = 0; *vnames; i++) { + intvar[i] = v = newvar(nextword(&vnames)); + if (*vvalues != '\377') + setvar_s(v, nextword(&vvalues)); + else + setvar_i(v, 0); + + if (*vnames == '*') { + v->type |= VF_SPECIAL; + vnames++; + } + } + + handle_special(intvar[FS]); + handle_special(intvar[RS]); + + newfile("/dev/stdin")->F = stdin; + newfile("/dev/stdout")->F = stdout; + newfile("/dev/stderr")->F = stderr; + + /* Huh, people report that sometimes environ is NULL. Oh well. */ + if (environ) for (envp = environ; *envp; envp++) { + /* environ is writable, thus we don't strdup it needlessly */ + char *s = *envp; + char *s1 = strchr(s, '='); + if (s1) { + *s1 = '\0'; + /* Both findvar and setvar_u take const char* + * as 2nd arg -> environment is not trashed */ + setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1); + *s1 = '='; + } + } + opt_complementary = "v::f::"; /* -v and -f can occur multiple times */ + opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, NULL); + argv += optind; + argc -= optind; + if (opt & 0x1) + setvar_s(intvar[FS], opt_F); // -F + while (list_v) { /* -v */ + if (!is_assignment(llist_pop(&list_v))) + bb_show_usage(); + } + if (list_f) { /* -f */ + do { + char *s = NULL; + FILE *from_file; + + g_progname = llist_pop(&list_f); + from_file = xfopen_stdin(g_progname); + /* one byte is reserved for some trick in next_token */ + for (i = j = 1; j > 0; i += j) { + s = xrealloc(s, i + 4096); + j = fread(s + i, 1, 4094, from_file); + } + s[i] = '\0'; + fclose(from_file); + parse_program(s + 1); + free(s); + } while (list_f); + argc++; + } else { // no -f: take program from 1st parameter + if (!argc) + bb_show_usage(); + g_progname = "cmd. line"; + parse_program(*argv++); + } + if (opt & 0x8) // -W + bb_error_msg("warning: option -W is ignored"); + + /* fill in ARGV array */ + setvar_i(intvar[ARGC], argc); + setari_u(intvar[ARGV], 0, "awk"); + i = 0; + while (*argv) + setari_u(intvar[ARGV], ++i, *argv++); + + evaluate(beginseq.first, &tv); + if (!mainseq.first && !endseq.first) + awk_exit(EXIT_SUCCESS); + + /* input file could already be opened in BEGIN block */ + if (!iF) + iF = next_input_file(); + + /* passing through input files */ + while (iF) { + nextfile = FALSE; + setvar_i(intvar[FNR], 0); + + while ((i = awk_getline(iF, intvar[F0])) > 0) { + nextrec = FALSE; + incvar(intvar[NR]); + incvar(intvar[FNR]); + evaluate(mainseq.first, &tv); + + if (nextfile) + break; + } + + if (i < 0) + syntax_error(strerror(errno)); + + iF = next_input_file(); + } + + awk_exit(EXIT_SUCCESS); + /*return 0;*/ +} diff --git a/editors/cmp.c b/editors/cmp.c new file mode 100644 index 000000000..fbe6b9753 --- /dev/null +++ b/editors/cmp.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cmp implementation for busybox + * + * Copyright (C) 2000,2001 by Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ + +//usage:#define cmp_trivial_usage +//usage: "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]" +//usage:#define cmp_full_usage "\n\n" +//usage: "Compare FILE1 with FILE2 (or stdin)\n" +//usage: "\n -l Write the byte numbers (decimal) and values (octal)" +//usage: "\n for all differing bytes" +//usage: "\n -s Quiet" + +#include "libbb.h" + +static const char fmt_eof[] ALIGN1 = "cmp: EOF on %s\n"; +static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"u, line %u\n"; +// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n" +static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n"; + +static const char opt_chars[] ALIGN1 = "sl"; +#define CMP_OPT_s (1<<0) +#define CMP_OPT_l (1<<1) + +int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cmp_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *fp1, *fp2, *outfile = stdout; + const char *filename1, *filename2 = "-"; + off_t skip1 = 0, skip2 = 0, char_pos = 0; + int line_pos = 1; /* Hopefully won't overflow... */ + const char *fmt; + int c1, c2; + unsigned opt; + int retval = 0; + + opt_complementary = "-1" + IF_DESKTOP(":?4") + IF_NOT_DESKTOP(":?2") + ":l--s:s--l"; + opt = getopt32(argv, opt_chars); + argv += optind; + + filename1 = *argv; + if (*++argv) { + filename2 = *argv; + if (ENABLE_DESKTOP && *++argv) { + skip1 = XATOOFF(*argv); + if (*++argv) { + skip2 = XATOOFF(*argv); + } + } + } + + xfunc_error_retval = 2; /* missing file results in exitcode 2 */ + if (opt & CMP_OPT_s) + logmode = 0; /* -s suppresses open error messages */ + fp1 = xfopen_stdin(filename1); + fp2 = xfopen_stdin(filename2); + if (fp1 == fp2) { /* Paranoia check... stdin == stdin? */ + /* Note that we don't bother reading stdin. Neither does gnu wc. + * But perhaps we should, so that other apps down the chain don't + * get the input. Consider 'echo hello | (cmp - - && cat -)'. + */ + return 0; + } + logmode = LOGMODE_STDIO; + + if (opt & CMP_OPT_l) + fmt = fmt_l_opt; + else + fmt = fmt_differ; + + if (ENABLE_DESKTOP) { + while (skip1) { getc(fp1); skip1--; } + while (skip2) { getc(fp2); skip2--; } + } + do { + c1 = getc(fp1); + c2 = getc(fp2); + ++char_pos; + if (c1 != c2) { /* Remember: a read error may have occurred. */ + retval = 1; /* But assume the files are different for now. */ + if (c2 == EOF) { + /* We know that fp1 isn't at EOF or in an error state. But to + * save space below, things are setup to expect an EOF in fp1 + * if an EOF occurred. So, swap things around. + */ + fp1 = fp2; + filename1 = filename2; + c1 = c2; + } + if (c1 == EOF) { + die_if_ferror(fp1, filename1); + fmt = fmt_eof; /* Well, no error, so it must really be EOF. */ + outfile = stderr; + /* There may have been output to stdout (option -l), so + * make sure we fflush before writing to stderr. */ + fflush_all(); + } + if (!(opt & CMP_OPT_s)) { + if (opt & CMP_OPT_l) { + line_pos = c1; /* line_pos is unused in the -l case. */ + } + fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2); + if (opt) { /* This must be -l since not -s. */ + /* If we encountered an EOF, + * the while check will catch it. */ + continue; + } + } + break; + } + if (c1 == '\n') { + ++line_pos; + } + } while (c1 != EOF); + + die_if_ferror(fp1, filename1); + die_if_ferror(fp2, filename2); + + fflush_stdout_and_exit(retval); +} diff --git a/editors/diff.c b/editors/diff.c new file mode 100644 index 000000000..3a3334640 --- /dev/null +++ b/editors/diff.c @@ -0,0 +1,1025 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini diff implementation for busybox, adapted from OpenBSD diff. + * + * Copyright (C) 2010 by Matheus Izvekov + * Copyright (C) 2006 by Robert Sullivan + * Copyright (c) 2003 Todd C. Miller + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* + * The following code uses an algorithm due to Harold Stone, + * which finds a pair of longest identical subsequences in + * the two files. + * + * The major goal is to generate the match vector J. + * J[i] is the index of the line in file1 corresponding + * to line i in file0. J[i] = 0 if there is no + * such line in file1. + * + * Lines are hashed so as to work in core. All potential + * matches are located by sorting the lines of each file + * on the hash (called "value"). In particular, this + * collects the equivalence classes in file1 together. + * Subroutine equiv replaces the value of each line in + * file0 by the index of the first element of its + * matching equivalence in (the reordered) file1. + * To save space equiv squeezes file1 into a single + * array member in which the equivalence classes + * are simply concatenated, except that their first + * members are flagged by changing sign. + * + * Next the indices that point into member are unsorted into + * array class according to the original order of file0. + * + * The cleverness lies in routine stone. This marches + * through the lines of file0, developing a vector klist + * of "k-candidates". At step i a k-candidate is a matched + * pair of lines x,y (x in file0, y in file1) such that + * there is a common subsequence of length k + * between the first i lines of file0 and the first y + * lines of file1, but there is no such subsequence for + * any smaller y. x is the earliest possible mate to y + * that occurs in such a subsequence. + * + * Whenever any of the members of the equivalence class of + * lines in file1 matable to a line in file0 has serial number + * less than the y of some k-candidate, that k-candidate + * with the smallest such y is replaced. The new + * k-candidate is chained (via pred) to the current + * k-1 candidate so that the actual subsequence can + * be recovered. When a member has serial number greater + * that the y of all k-candidates, the klist is extended. + * At the end, the longest subsequence is pulled out + * and placed in the array J by unravel + * + * With J in hand, the matches there recorded are + * checked against reality to assure that no spurious + * matches have crept in due to hashing. If they have, + * they are broken, and "jackpot" is recorded--a harmless + * matter except that a true match for a spuriously + * mated line may now be unnecessarily reported as a change. + * + * Much of the complexity of the program comes simply + * from trying to minimize core utilization and + * maximize the range of doable problems by dynamically + * allocating what is needed and reusing what is not. + * The core requirements for problems larger than somewhat + * are (in words) 2*length(file0) + length(file1) + + * 3*(number of k-candidates installed), typically about + * 6n words for files of length n. + */ + +//usage:#define diff_trivial_usage +//usage: "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" +//usage:#define diff_full_usage "\n\n" +//usage: "Compare files line by line and output the differences between them.\n" +//usage: "This implementation supports unified diffs only.\n" +//usage: "\n -a Treat all files as text" +//usage: "\n -b Ignore changes in the amount of whitespace" +//usage: "\n -B Ignore changes whose lines are all blank" +//usage: "\n -d Try hard to find a smaller set of changes" +//usage: "\n -i Ignore case differences" +//usage: "\n -L Use LABEL instead of the filename in the unified header" +//usage: "\n -N Treat absent files as empty" +//usage: "\n -q Output only whether files differ" +//usage: "\n -r Recurse" +//usage: "\n -S Start with FILE when comparing directories" +//usage: "\n -T Make tabs line up by prefixing a tab when necessary" +//usage: "\n -s Report when two files are the same" +//usage: "\n -t Expand tabs to spaces in output" +//usage: "\n -U Output LINES lines of context" +//usage: "\n -w Ignore all whitespace" + +#include "libbb.h" + +#if 0 +# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg_error_msg(...) ((void)0) +#endif + +enum { /* print_status() and diffreg() return values */ + STATUS_SAME, /* files are the same */ + STATUS_DIFFER, /* files differ */ + STATUS_BINARY, /* binary files differ */ +}; + +enum { /* Commandline flags */ + FLAG_a, + FLAG_b, + FLAG_d, + FLAG_i, + FLAG_L, /* never used, handled by getopt32 */ + FLAG_N, + FLAG_q, + FLAG_r, + FLAG_s, + FLAG_S, /* never used, handled by getopt32 */ + FLAG_t, + FLAG_T, + FLAG_U, /* never used, handled by getopt32 */ + FLAG_w, + FLAG_u, /* ignored, this is the default */ + FLAG_p, /* not implemented */ + FLAG_B, + FLAG_E, /* not implemented */ +}; +#define FLAG(x) (1 << FLAG_##x) + +/* We cache file position to avoid excessive seeking */ +typedef struct FILE_and_pos_t { + FILE *ft_fp; + off_t ft_pos; +} FILE_and_pos_t; + +struct globals { + smallint exit_status; + int opt_U_context; + const char *other_dir; + char *label[2]; + struct stat stb[2]; +}; +#define G (*ptr_to_globals) +#define exit_status (G.exit_status ) +#define opt_U_context (G.opt_U_context ) +#define label (G.label ) +#define stb (G.stb ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + opt_U_context = 3; \ +} while (0) + +typedef int token_t; + +enum { + /* Public */ + TOK_EMPTY = 1 << 9, /* Line fully processed, you can proceed to the next */ + TOK_EOF = 1 << 10, /* File ended */ + /* Private (Only to be used by read_token() */ + TOK_EOL = 1 << 11, /* we saw EOL (sticky) */ + TOK_SPACE = 1 << 12, /* used -b code, means we are skipping spaces */ + SHIFT_EOF = (sizeof(token_t)*8 - 8) - 1, + CHAR_MASK = 0x1ff, /* 8th bit is used to distinguish EOF from 0xff */ +}; + +/* Restores full EOF from one 8th bit: */ +//#define TOK2CHAR(t) (((t) << SHIFT_EOF) >> SHIFT_EOF) +/* We don't really need the above, we only need to have EOF != any_real_char: */ +#define TOK2CHAR(t) ((t) & CHAR_MASK) + +static void seek_ft(FILE_and_pos_t *ft, off_t pos) +{ + if (ft->ft_pos != pos) { + ft->ft_pos = pos; + fseeko(ft->ft_fp, pos, SEEK_SET); + } +} + +/* Reads tokens from given fp, handling -b and -w flags + * The user must reset tok every line start + */ +static int read_token(FILE_and_pos_t *ft, token_t tok) +{ + tok |= TOK_EMPTY; + while (!(tok & TOK_EOL)) { + bool is_space; + int t; + + t = fgetc(ft->ft_fp); + if (t != EOF) + ft->ft_pos++; + is_space = (t == EOF || isspace(t)); + + /* If t == EOF (-1), set both TOK_EOF and TOK_EOL */ + tok |= (t & (TOK_EOF + TOK_EOL)); + /* Only EOL? */ + if (t == '\n') + tok |= TOK_EOL; + + if (option_mask32 & FLAG(i)) /* Handcoded tolower() */ + t = (t >= 'A' && t <= 'Z') ? t - ('A' - 'a') : t; + + if ((option_mask32 & FLAG(w)) && is_space) + continue; + + /* Trim char value to low 9 bits */ + t &= CHAR_MASK; + + if (option_mask32 & FLAG(b)) { + /* Was prev char whitespace? */ + if (tok & TOK_SPACE) { /* yes */ + if (is_space) /* this one too, ignore it */ + continue; + tok &= ~TOK_SPACE; + } else if (is_space) { + /* 1st whitespace char. + * Set TOK_SPACE and replace char by ' ' */ + t = TOK_SPACE + ' '; + } + } + /* Clear EMPTY */ + tok &= ~(TOK_EMPTY + CHAR_MASK); + /* Assign char value (low 9 bits) and maybe set TOK_SPACE */ + tok |= t; + break; + } +#if 0 + bb_error_msg("fp:%p tok:%x '%c'%s%s%s%s", fp, tok, tok & 0xff + , tok & TOK_EOF ? " EOF" : "" + , tok & TOK_EOL ? " EOL" : "" + , tok & TOK_EMPTY ? " EMPTY" : "" + , tok & TOK_SPACE ? " SPACE" : "" + ); +#endif + return tok; +} + +struct cand { + int x; + int y; + int pred; +}; + +static int search(const int *c, int k, int y, const struct cand *list) +{ + int i, j; + + if (list[c[k]].y < y) /* quick look for typical case */ + return k + 1; + + for (i = 0, j = k + 1;;) { + const int l = (i + j) >> 1; + if (l > i) { + const int t = list[c[l]].y; + if (t > y) + j = l; + else if (t < y) + i = l; + else + return l; + } else + return l + 1; + } +} + +static unsigned isqrt(unsigned n) +{ + unsigned x = 1; + while (1) { + const unsigned y = x; + x = ((n / x) + x) >> 1; + if (x <= (y + 1) && x >= (y - 1)) + return x; + } +} + +static void stone(const int *a, int n, const int *b, int *J, int pref) +{ + const unsigned isq = isqrt(n); + const unsigned bound = + (option_mask32 & FLAG(d)) ? UINT_MAX : MAX(256, isq); + int clen = 1; + int clistlen = 100; + int k = 0; + struct cand *clist = xzalloc(clistlen * sizeof(clist[0])); + struct cand cand; + struct cand *q; + int *klist = xzalloc((n + 2) * sizeof(klist[0])); + /*clist[0] = (struct cand){0}; - xzalloc did it */ + /*klist[0] = 0; */ + + for (cand.x = 1; cand.x <= n; cand.x++) { + int j = a[cand.x], oldl = 0; + unsigned numtries = 0; + if (j == 0) + continue; + cand.y = -b[j]; + cand.pred = klist[0]; + do { + int l, tc; + if (cand.y <= clist[cand.pred].y) + continue; + l = search(klist, k, cand.y, clist); + if (l != oldl + 1) + cand.pred = klist[l - 1]; + if (l <= k && clist[klist[l]].y <= cand.y) + continue; + if (clen == clistlen) { + clistlen = clistlen * 11 / 10; + clist = xrealloc(clist, clistlen * sizeof(clist[0])); + } + clist[clen] = cand; + tc = klist[l]; + klist[l] = clen++; + if (l <= k) { + cand.pred = tc; + oldl = l; + numtries++; + } else { + k++; + break; + } + } while ((cand.y = b[++j]) > 0 && numtries < bound); + } + /* Unravel */ + for (q = clist + klist[k]; q->y; q = clist + q->pred) + J[q->x + pref] = q->y + pref; + free(klist); + free(clist); +} + +struct line { + /* 'serial' is not used in the begining, so we reuse it + * to store line offsets, thus reducing memory pressure + */ + union { + unsigned serial; + off_t offset; + }; + unsigned value; +}; + +static void equiv(struct line *a, int n, struct line *b, int m, int *c) +{ + int i = 1, j = 1; + + while (i <= n && j <= m) { + if (a[i].value < b[j].value) + a[i++].value = 0; + else if (a[i].value == b[j].value) + a[i++].value = j; + else + j++; + } + while (i <= n) + a[i++].value = 0; + b[m + 1].value = 0; + j = 0; + while (++j <= m) { + c[j] = -b[j].serial; + while (b[j + 1].value == b[j].value) { + j++; + c[j] = b[j].serial; + } + } + c[j] = -1; +} + +static void unsort(const struct line *f, int l, int *b) +{ + int i; + int *a = xmalloc((l + 1) * sizeof(a[0])); + for (i = 1; i <= l; i++) + a[f[i].serial] = f[i].value; + for (i = 1; i <= l; i++) + b[i] = a[i]; + free(a); +} + +static int line_compar(const void *a, const void *b) +{ +#define l0 ((const struct line*)a) +#define l1 ((const struct line*)b) + int r = l0->value - l1->value; + if (r) + return r; + return l0->serial - l1->serial; +#undef l0 +#undef l1 +} + +static void fetch(FILE_and_pos_t *ft, const off_t *ix, int a, int b, int ch) +{ + int i, j, col; + for (i = a; i <= b; i++) { + seek_ft(ft, ix[i - 1]); + putchar(ch); + if (option_mask32 & FLAG(T)) + putchar('\t'); + for (j = 0, col = 0; j < ix[i] - ix[i - 1]; j++) { + int c = fgetc(ft->ft_fp); + if (c == EOF) { + printf("\n\\ No newline at end of file\n"); + return; + } + ft->ft_pos++; + if (c == '\t' && (option_mask32 & FLAG(t))) + do putchar(' '); while (++col & 7); + else { + putchar(c); + col++; + } + } + } +} + +/* Creates the match vector J, where J[i] is the index + * of the line in the new file corresponding to the line i + * in the old file. Lines start at 1 instead of 0, that value + * being used instead to denote no corresponding line. + * This vector is dynamically allocated and must be freed by the caller. + * + * * fp is an input parameter, where fp[0] and fp[1] are the open + * old file and new file respectively. + * * nlen is an output variable, where nlen[0] and nlen[1] + * gets the number of lines in the old and new file respectively. + * * ix is an output variable, where ix[0] and ix[1] gets + * assigned dynamically allocated vectors of the offsets of the lines + * of the old and new file respectively. These must be freed by the caller. + */ +static NOINLINE int *create_J(FILE_and_pos_t ft[2], int nlen[2], off_t *ix[2]) +{ + int *J, slen[2], *class, *member; + struct line *nfile[2], *sfile[2]; + int pref = 0, suff = 0, i, j, delta; + + /* Lines of both files are hashed, and in the process + * their offsets are stored in the array ix[fileno] + * where fileno == 0 points to the old file, and + * fileno == 1 points to the new one. + */ + for (i = 0; i < 2; i++) { + unsigned hash; + token_t tok; + size_t sz = 100; + nfile[i] = xmalloc((sz + 3) * sizeof(nfile[i][0])); + /* ft gets here without the correct position, cant use seek_ft */ + ft[i].ft_pos = 0; + fseeko(ft[i].ft_fp, 0, SEEK_SET); + + nlen[i] = 0; + /* We could zalloc nfile, but then zalloc starts showing in gprof at ~1% */ + nfile[i][0].offset = 0; + goto start; /* saves code */ + while (1) { + tok = read_token(&ft[i], tok); + if (!(tok & TOK_EMPTY)) { + /* Hash algorithm taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. */ + /*hash = hash * 128 - hash + TOK2CHAR(tok); + * gcc insists on optimizing above to "hash * 127 + ...", thus... */ + unsigned o = hash - TOK2CHAR(tok); + hash = hash * 128 - o; /* we want SPEED here */ + continue; + } + if (nlen[i]++ == sz) { + sz = sz * 3 / 2; + nfile[i] = xrealloc(nfile[i], (sz + 3) * sizeof(nfile[i][0])); + } + /* line_compar needs hashes fit into positive int */ + nfile[i][nlen[i]].value = hash & INT_MAX; + /* like ftello(ft[i].ft_fp) but faster (avoids lseek syscall) */ + nfile[i][nlen[i]].offset = ft[i].ft_pos; + if (tok & TOK_EOF) { + /* EOF counts as a token, so we have to adjust it here */ + nfile[i][nlen[i]].offset++; + break; + } +start: + hash = tok = 0; + } + /* Exclude lone EOF line from the end of the file, to make fetch()'s job easier */ + if (nfile[i][nlen[i]].offset - nfile[i][nlen[i] - 1].offset == 1) + nlen[i]--; + /* Now we copy the line offsets into ix */ + ix[i] = xmalloc((nlen[i] + 2) * sizeof(ix[i][0])); + for (j = 0; j < nlen[i] + 1; j++) + ix[i][j] = nfile[i][j].offset; + } + + /* length of prefix and suffix is calculated */ + for (; pref < nlen[0] && pref < nlen[1] && + nfile[0][pref + 1].value == nfile[1][pref + 1].value; + pref++); + for (; suff < nlen[0] - pref && suff < nlen[1] - pref && + nfile[0][nlen[0] - suff].value == nfile[1][nlen[1] - suff].value; + suff++); + /* Arrays are pruned by the suffix and prefix length, + * the result being sorted and stored in sfile[fileno], + * and their sizes are stored in slen[fileno] + */ + for (j = 0; j < 2; j++) { + sfile[j] = nfile[j] + pref; + slen[j] = nlen[j] - pref - suff; + for (i = 0; i <= slen[j]; i++) + sfile[j][i].serial = i; + qsort(sfile[j] + 1, slen[j], sizeof(*sfile[j]), line_compar); + } + /* nfile arrays are reused to reduce memory pressure + * The #if zeroed out section performs the same task as the + * one in the #else section. + * Peak memory usage is higher, but one array copy is avoided + * by not using unsort() + */ +#if 0 + member = xmalloc((slen[1] + 2) * sizeof(member[0])); + equiv(sfile[0], slen[0], sfile[1], slen[1], member); + free(nfile[1]); + + class = xmalloc((slen[0] + 1) * sizeof(class[0])); + for (i = 1; i <= slen[0]; i++) /* Unsorting */ + class[sfile[0][i].serial] = sfile[0][i].value; + free(nfile[0]); +#else + member = (int *)nfile[1]; + equiv(sfile[0], slen[0], sfile[1], slen[1], member); + member = xrealloc(member, (slen[1] + 2) * sizeof(member[0])); + + class = (int *)nfile[0]; + unsort(sfile[0], slen[0], (int *)nfile[0]); + class = xrealloc(class, (slen[0] + 2) * sizeof(class[0])); +#endif + J = xmalloc((nlen[0] + 2) * sizeof(J[0])); + /* The elements of J which fall inside the prefix and suffix regions + * are marked as unchanged, while the ones which fall outside + * are initialized with 0 (no matches), so that function stone can + * then assign them their right values + */ + for (i = 0, delta = nlen[1] - nlen[0]; i <= nlen[0]; i++) + J[i] = i <= pref ? i : + i > (nlen[0] - suff) ? (i + delta) : 0; + /* Here the magic is performed */ + stone(class, slen[0], member, J, pref); + J[nlen[0] + 1] = nlen[1] + 1; + + free(class); + free(member); + + /* Both files are rescanned, in an effort to find any lines + * which, due to limitations intrinsic to any hashing algorithm, + * are different but ended up confounded as the same + */ + for (i = 1; i <= nlen[0]; i++) { + if (!J[i]) + continue; + + seek_ft(&ft[0], ix[0][i - 1]); + seek_ft(&ft[1], ix[1][J[i] - 1]); + + for (j = J[i]; i <= nlen[0] && J[i] == j; i++, j++) { + token_t tok0 = 0, tok1 = 0; + do { + tok0 = read_token(&ft[0], tok0); + tok1 = read_token(&ft[1], tok1); + + if (((tok0 ^ tok1) & TOK_EMPTY) != 0 /* one is empty (not both) */ + || (!(tok0 & TOK_EMPTY) && TOK2CHAR(tok0) != TOK2CHAR(tok1)) + ) { + J[i] = 0; /* Break the correspondence */ + } + } while (!(tok0 & tok1 & TOK_EMPTY)); + } + } + + return J; +} + +static bool diff(FILE* fp[2], char *file[2]) +{ + int nlen[2]; + off_t *ix[2]; + FILE_and_pos_t ft[2]; + typedef struct { int a, b; } vec_t[2]; + vec_t *vec = NULL; + int i = 1, j, k, idx = -1; + bool anychange = false; + int *J; + + ft[0].ft_fp = fp[0]; + ft[1].ft_fp = fp[1]; + /* note that ft[i].ft_pos is unintitalized, create_J() + * must not assume otherwise */ + J = create_J(ft, nlen, ix); + + do { + bool nonempty = false; + + while (1) { + vec_t v; + + for (v[0].a = i; v[0].a <= nlen[0] && J[v[0].a] == J[v[0].a - 1] + 1; v[0].a++) + continue; + v[1].a = J[v[0].a - 1] + 1; + + for (v[0].b = v[0].a - 1; v[0].b < nlen[0] && !J[v[0].b + 1]; v[0].b++) + continue; + v[1].b = J[v[0].b + 1] - 1; + /* + * Indicate that there is a difference between lines a and b of the 'from' file + * to get to lines c to d of the 'to' file. If a is greater than b then there + * are no lines in the 'from' file involved and this means that there were + * lines appended (beginning at b). If c is greater than d then there are + * lines missing from the 'to' file. + */ + if (v[0].a <= v[0].b || v[1].a <= v[1].b) { + /* + * If this change is more than 'context' lines from the + * previous change, dump the record and reset it. + */ + int ct = (2 * opt_U_context) + 1; + if (idx >= 0 + && v[0].a > vec[idx][0].b + ct + && v[1].a > vec[idx][1].b + ct + ) { + break; + } + + for (j = 0; j < 2; j++) + for (k = v[j].a; k < v[j].b; k++) + nonempty |= (ix[j][k+1] - ix[j][k] != 1); + + vec = xrealloc_vector(vec, 6, ++idx); + memcpy(vec[idx], v, sizeof(v)); + } + + i = v[0].b + 1; + if (i > nlen[0]) + break; + J[v[0].b] = v[1].b; + } + if (idx < 0 || ((option_mask32 & FLAG(B)) && !nonempty)) + goto cont; + if (!(option_mask32 & FLAG(q))) { + int lowa; + vec_t span, *cvp = vec; + + if (!anychange) { + /* Print the context/unidiff header first time through */ + printf("--- %s\n", label[0] ? label[0] : file[0]); + printf("+++ %s\n", label[1] ? label[1] : file[1]); + } + + printf("@@"); + for (j = 0; j < 2; j++) { + int a = span[j].a = MAX(1, (*cvp)[j].a - opt_U_context); + int b = span[j].b = MIN(nlen[j], vec[idx][j].b + opt_U_context); + + printf(" %c%d", j ? '+' : '-', MIN(a, b)); + if (a == b) + continue; + printf(",%d", (a < b) ? b - a + 1 : 0); + } + printf(" @@\n"); + /* + * Output changes in "unified" diff format--the old and new lines + * are printed together. + */ + for (lowa = span[0].a; ; lowa = (*cvp++)[0].b + 1) { + bool end = cvp > &vec[idx]; + fetch(&ft[0], ix[0], lowa, end ? span[0].b : (*cvp)[0].a - 1, ' '); + if (end) + break; + for (j = 0; j < 2; j++) + fetch(&ft[j], ix[j], (*cvp)[j].a, (*cvp)[j].b, j ? '+' : '-'); + } + } + anychange = true; + cont: + idx = -1; + } while (i <= nlen[0]); + + free(vec); + free(ix[0]); + free(ix[1]); + free(J); + return anychange; +} + +static int diffreg(char *file[2]) +{ + FILE *fp[2]; + bool binary = false, differ = false; + int status = STATUS_SAME, i; + + fp[0] = stdin; + fp[1] = stdin; + for (i = 0; i < 2; i++) { + int fd = open_or_warn_stdin(file[i]); + if (fd == -1) + goto out; + /* Our diff implementation is using seek. + * When we meet non-seekable file, we must make a temp copy. + */ + if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { + char name[] = "/tmp/difXXXXXX"; + int fd_tmp = xmkstemp(name); + + unlink(name); + if (bb_copyfd_eof(fd, fd_tmp) < 0) + xfunc_die(); + if (fd) /* Prevents closing of stdin */ + close(fd); + fd = fd_tmp; + } + fp[i] = fdopen(fd, "r"); + } + + while (1) { + const size_t sz = COMMON_BUFSIZE / 2; + char *const buf0 = bb_common_bufsiz1; + char *const buf1 = buf0 + sz; + int j, k; + i = fread(buf0, 1, sz, fp[0]); + j = fread(buf1, 1, sz, fp[1]); + if (i != j) { + differ = true; + i = MIN(i, j); + } + if (i == 0) + break; + for (k = 0; k < i; k++) { + if (!buf0[k] || !buf1[k]) + binary = true; + if (buf0[k] != buf1[k]) + differ = true; + } + } + if (differ) { + if (binary && !(option_mask32 & FLAG(a))) + status = STATUS_BINARY; + else if (diff(fp, file)) + status = STATUS_DIFFER; + } + if (status != STATUS_SAME) + exit_status |= 1; +out: + fclose_if_not_stdin(fp[0]); + fclose_if_not_stdin(fp[1]); + + return status; +} + +static void print_status(int status, char *path[2]) +{ + switch (status) { + case STATUS_BINARY: + case STATUS_DIFFER: + if ((option_mask32 & FLAG(q)) || status == STATUS_BINARY) + printf("Files %s and %s differ\n", path[0], path[1]); + break; + case STATUS_SAME: + if (option_mask32 & FLAG(s)) + printf("Files %s and %s are identical\n", path[0], path[1]); + break; + } +} + +#if ENABLE_FEATURE_DIFF_DIR +struct dlist { + size_t len; + int s, e; + char **dl; +}; + +/* This function adds a filename to dl, the directory listing. */ +static int FAST_FUNC add_to_dirlist(const char *filename, + struct stat *sb UNUSED_PARAM, + void *userdata, int depth UNUSED_PARAM) +{ + struct dlist *const l = userdata; + const char *file = filename + l->len; + while (*file == '/') + file++; + l->dl = xrealloc_vector(l->dl, 6, l->e); + l->dl[l->e] = xstrdup(file); + l->e++; + return TRUE; +} + +/* If recursion is not set, this function adds the directory + * to the list and prevents recursive_action from recursing into it. + */ +static int FAST_FUNC skip_dir(const char *filename, + struct stat *sb, void *userdata, + int depth) +{ + if (!(option_mask32 & FLAG(r)) && depth) { + add_to_dirlist(filename, sb, userdata, depth); + return SKIP; + } + if (!(option_mask32 & FLAG(N))) { + /* -r without -N: no need to recurse into dirs + * which do not exist on the "other side". + * Testcase: diff -r /tmp / + * (it would recurse deep into /proc without this code) */ + struct dlist *const l = userdata; + filename += l->len; + if (filename[0]) { + struct stat osb; + char *othername = concat_path_file(G.other_dir, filename); + int r = stat(othername, &osb); + free(othername); + if (r != 0 || !S_ISDIR(osb.st_mode)) { + /* other dir doesn't have similarly named + * directory, don't recurse; return 1 upon + * exit, just like diffutils' diff */ + exit_status |= 1; + return SKIP; + } + } + } + return TRUE; +} + +static void diffdir(char *p[2], const char *s_start) +{ + struct dlist list[2]; + int i; + + memset(&list, 0, sizeof(list)); + for (i = 0; i < 2; i++) { + /*list[i].s = list[i].e = 0; - memset did it */ + /*list[i].dl = NULL; */ + + G.other_dir = p[1 - i]; + /* We need to trim root directory prefix. + * Using list.len to specify its length, + * add_to_dirlist will remove it. */ + list[i].len = strlen(p[i]); + recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS, + add_to_dirlist, skip_dir, &list[i], 0); + /* Sort dl alphabetically. + * GNU diff does this ignoring any number of trailing dots. + * We don't, so for us dotted files almost always are + * first on the list. + */ + qsort_string_vector(list[i].dl, list[i].e); + /* If -S was set, find the starting point. */ + if (!s_start) + continue; + while (list[i].s < list[i].e && strcmp(list[i].dl[list[i].s], s_start) < 0) + list[i].s++; + } + /* Now that both dirlist1 and dirlist2 contain sorted directory + * listings, we can start to go through dirlist1. If both listings + * contain the same file, then do a normal diff. Otherwise, behaviour + * is determined by whether the -N flag is set. */ + while (1) { + char *dp[2]; + int pos; + int k; + + dp[0] = list[0].s < list[0].e ? list[0].dl[list[0].s] : NULL; + dp[1] = list[1].s < list[1].e ? list[1].dl[list[1].s] : NULL; + if (!dp[0] && !dp[1]) + break; + pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1])); + k = pos > 0; + if (pos && !(option_mask32 & FLAG(N))) { + printf("Only in %s: %s\n", p[k], dp[k]); + exit_status |= 1; + } else { + char *fullpath[2], *path[2]; /* if -N */ + + for (i = 0; i < 2; i++) { + if (pos == 0 || i == k) { + path[i] = fullpath[i] = concat_path_file(p[i], dp[i]); + stat(fullpath[i], &stb[i]); + } else { + fullpath[i] = concat_path_file(p[i], dp[1 - i]); + path[i] = (char *)bb_dev_null; + } + } + if (pos) + stat(fullpath[k], &stb[1 - k]); + + if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode)) + printf("Common subdirectories: %s and %s\n", fullpath[0], fullpath[1]); + else if (!S_ISREG(stb[0].st_mode) && !S_ISDIR(stb[0].st_mode)) + printf("File %s is not a regular file or directory and was skipped\n", fullpath[0]); + else if (!S_ISREG(stb[1].st_mode) && !S_ISDIR(stb[1].st_mode)) + printf("File %s is not a regular file or directory and was skipped\n", fullpath[1]); + else if (S_ISDIR(stb[0].st_mode) != S_ISDIR(stb[1].st_mode)) { + if (S_ISDIR(stb[0].st_mode)) + printf("File %s is a %s while file %s is a %s\n", fullpath[0], "directory", fullpath[1], "regular file"); + else + printf("File %s is a %s while file %s is a %s\n", fullpath[0], "regular file", fullpath[1], "directory"); + } else + print_status(diffreg(path), fullpath); + + free(fullpath[0]); + free(fullpath[1]); + } + free(dp[k]); + list[k].s++; + if (pos == 0) { + free(dp[1 - k]); + list[1 - k].s++; + } + } + if (ENABLE_FEATURE_CLEAN_UP) { + free(list[0].dl); + free(list[1].dl); + } +} +#endif + +#if ENABLE_FEATURE_DIFF_LONG_OPTIONS +static const char diff_longopts[] ALIGN1 = + "ignore-case\0" No_argument "i" + "ignore-tab-expansion\0" No_argument "E" + "ignore-space-change\0" No_argument "b" + "ignore-all-space\0" No_argument "w" + "ignore-blank-lines\0" No_argument "B" + "text\0" No_argument "a" + "unified\0" Required_argument "U" + "label\0" Required_argument "L" + "show-c-function\0" No_argument "p" + "brief\0" No_argument "q" + "expand-tabs\0" No_argument "t" + "initial-tab\0" No_argument "T" + "recursive\0" No_argument "r" + "new-file\0" No_argument "N" + "report-identical-files\0" No_argument "s" + "starting-file\0" Required_argument "S" + "minimal\0" No_argument "d" + ; +#endif + +int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int diff_main(int argc UNUSED_PARAM, char **argv) +{ + int gotstdin = 0, i; + char *file[2], *s_start = NULL; + llist_t *L_arg = NULL; + + INIT_G(); + + /* exactly 2 params; collect multiple -L